diff options
12 files changed, 771 insertions, 44 deletions
diff --git a/clang-tools-extra/cpp11-migrate/CMakeLists.txt b/clang-tools-extra/cpp11-migrate/CMakeLists.txt index 30e33b457d2..591c0a3f204 100644 --- a/clang-tools-extra/cpp11-migrate/CMakeLists.txt +++ b/clang-tools-extra/cpp11-migrate/CMakeLists.txt @@ -15,6 +15,9 @@ list(APPEND Cpp11MigrateSources ${LoopConvertSources}) file(GLOB_RECURSE UseNullptrSources "UseNullptr/*.cpp") list(APPEND Cpp11MigrateSources ${UseNullptrSources}) +file(GLOB_RECURSE UseAutoSources "UseAuto/*.cpp") +list(APPEND Cpp11MigrateSources ${UseAutoSources}) + add_clang_executable(cpp11-migrate ${Cpp11MigrateSources} ) diff --git a/clang-tools-extra/cpp11-migrate/Makefile b/clang-tools-extra/cpp11-migrate/Makefile index 5b806d49203..4eca366538a 100644 --- a/clang-tools-extra/cpp11-migrate/Makefile +++ b/clang-tools-extra/cpp11-migrate/Makefile @@ -26,6 +26,8 @@ SOURCES += $(addprefix LoopConvert/,$(notdir $(wildcard $(PROJ_SRC_DIR)/LoopConv BUILT_SOURCES = $(ObjDir)/LoopConvert/.objdir SOURCES += $(addprefix UseNullptr/,$(notdir $(wildcard $(PROJ_SRC_DIR)/UseNullptr/*.cpp))) BUILT_SOURCES += $(ObjDir)/UseNullptr/.objdir +SOURCES += $(addprefix UseAuto/,$(notdir $(wildcard $(PROJ_SRC_DIR)/UseAuto/*.cpp))) +BUILT_SOURCES += $(ObjDir)/UseAuto/.objdir LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ diff --git a/clang-tools-extra/cpp11-migrate/Transforms.cpp b/clang-tools-extra/cpp11-migrate/Transforms.cpp index 6b2ab5c9fa2..2b6dfd79d6b 100644 --- a/clang-tools-extra/cpp11-migrate/Transforms.cpp +++ b/clang-tools-extra/cpp11-migrate/Transforms.cpp @@ -15,6 +15,7 @@ #include "Transforms.h" #include "LoopConvert/LoopConvert.h" #include "UseNullptr/UseNullptr.h" +#include "UseAuto/UseAuto.h" namespace cl = llvm::cl; @@ -47,6 +48,12 @@ void Transforms::createTransformOpts() { cl::desc("Make use of nullptr keyword where possible")), &ConstructTransform<UseNullptrTransform>)); + Options.push_back( + OptionVec::value_type( + new cl::opt<bool>("use-auto", + cl::desc("Use of 'auto' type specifier")), + &ConstructTransform<UseAutoTransform>)); + // Add more transform options here. } diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp new file mode 100644 index 00000000000..00723cab218 --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp @@ -0,0 +1,58 @@ +//===-- UseAuto/UseAuto.cpp - Use auto type specifier ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides the implementation of the UseAutoTransform class. +/// +//===----------------------------------------------------------------------===// + +#include "UseAuto.h" +#include "UseAutoActions.h" +#include "UseAutoMatchers.h" + +using clang::ast_matchers::MatchFinder; +using namespace clang; +using namespace clang::tooling; + +int UseAutoTransform::apply(const FileContentsByPath &InputStates, + RiskLevel MaxRisk, + const clang::tooling::CompilationDatabase &Database, + const std::vector<std::string> &SourcePaths, + FileContentsByPath &ResultStates) { + RefactoringTool UseAutoTool(Database, SourcePaths); + + for (FileContentsByPath::const_iterator I = InputStates.begin(), + E = InputStates.end(); + I != E; ++I) + UseAutoTool.mapVirtualFile(I->first, I->second); + + unsigned AcceptedChanges = 0; + + MatchFinder Finder; + UseAutoFixer Fixer(UseAutoTool.getReplacements(), AcceptedChanges, MaxRisk); + + Finder.addMatcher(makeIteratorMatcher(), &Fixer); + + if (int Result = UseAutoTool.run(newFrontendActionFactory(&Finder))) { + llvm::errs() << "Error encountered during translation.\n"; + return Result; + } + + RewriterContainer Rewrite(UseAutoTool.getFiles(), InputStates); + + // FIXME: Do something if some replacements didn't get applied? + UseAutoTool.applyAllReplacements(Rewrite.getRewriter()); + + collectResults(Rewrite.getRewriter(), InputStates, ResultStates); + + if (AcceptedChanges > 0) + setChangesMade(); + + return 0; +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.h new file mode 100644 index 00000000000..799dad9582d --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.h @@ -0,0 +1,40 @@ +//===-- UseAuto/UseAuto.h - Use auto type specifier -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file provides the definition of the UseAutoTransform class +/// which is the main interface to the use-auto transform that replaces +/// type specifiers with the special C++11 'auto' type specifier in certain +/// situations. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H +#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H + +#include "Transform.h" +#include "llvm/Support/Compiler.h" + +/// \brief Subclass of Transform that transforms type specifiers for variable +/// declarations into the special C++11 'auto' type specifier for certain cases: +/// * Iterators of std containers. +/// * More to come... +/// +/// Other uses of the auto type specifier as outlined in C++11 [dcl.spec.auto] +/// p2 are not handled by this transform. +class UseAutoTransform : public Transform { +public: + /// \see Transform::run(). + virtual int apply(const FileContentsByPath &InputStates, + RiskLevel MaxRiskLEvel, + const clang::tooling::CompilationDatabase &Database, + const std::vector<std::string> &SourcePaths, + FileContentsByPath &ResultStates) LLVM_OVERRIDE; +}; + +#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_H diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp new file mode 100644 index 00000000000..ceb22390a1f --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp @@ -0,0 +1,63 @@ +//===-- UseAuto/UseAutoActions.cpp - Matcher callback impl ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file contains the implementation of the UseAutoFixer class. +/// +//===----------------------------------------------------------------------===// +#include "UseAutoActions.h" +#include "UseAutoMatchers.h" +#include "clang/AST/ASTContext.h" + +using namespace clang::ast_matchers; +using namespace clang::tooling; +using namespace clang; + +void UseAutoFixer::run(const MatchFinder::MatchResult &Result) { + const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>(DeclNodeId); + + assert(D && "Bad Callback. No node provided"); + + SourceManager &SM = *Result.SourceManager; + if (!SM.isFromMainFile(D->getLocStart())) + return; + + const CXXConstructExpr *Construct = cast<CXXConstructExpr>(D->getInit()); + assert(Construct->getNumArgs() == 1u && + "Expected constructor with single argument"); + + // Drill down to the as-written initializer. + const Expr *E = Construct->arg_begin()->IgnoreParenImpCasts(); + if (E != E->IgnoreConversionOperator()) + // We hit a conversion operator. Early-out now as they imply an implicit + // conversion from a different type. Could also mean an explicit conversion + // from the same type but that's pretty rare. + return; + + if (const CXXConstructExpr *NestedConstruct = dyn_cast<CXXConstructExpr>(E)) + // If we ran into an implicit conversion constructor, can't convert. + // + // FIXME: The following only checks if the constructor can be used + // implicitly, not if it actually was. Cases where the converting constructor + // was used explicitly won't get converted. + if (NestedConstruct->getConstructor()->isConvertingConstructor(false)) + return; + + if (Result.Context->hasSameType(D->getType(), E->getType())) { + TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc(); + + // WARNING: TypeLoc::getSourceRange() will include the identifier for things + // like function pointers. Not a concern since this action only works with + // iterators but something to keep in mind in the future. + + CharSourceRange Range(TL.getSourceRange(), true); + Replace.insert(tooling::Replacement(SM, Range, "auto")); + ++AcceptedChanges; + } +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h new file mode 100644 index 00000000000..d6ea557b369 --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h @@ -0,0 +1,38 @@ +//===-- UseAuto/Actions.h - Matcher callback ---------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file contains the declaration of the UseAutoFixer class which +/// is used as an ASTMatcher callback. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H +#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H + +#include "Transform.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Tooling/Refactoring.h" + +/// \brief The callback to be used for use-auto AST matchers. +class UseAutoFixer : public clang::ast_matchers::MatchFinder::MatchCallback { +public: + UseAutoFixer(clang::tooling::Replacements &Replace, unsigned &AcceptedChanges, + RiskLevel) + : Replace(Replace), AcceptedChanges(AcceptedChanges) { + } + + /// \brief Entry point to the callback called when matches are made. + virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result); + +private: + clang::tooling::Replacements &Replace; + unsigned &AcceptedChanges; +}; + +#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp new file mode 100644 index 00000000000..6522f4f10bd --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp @@ -0,0 +1,247 @@ +//===-- UseAutoMatchers.cpp - Matchers for use-auto transform -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file contains the implementation for matcher-generating +/// functions and custom AST_MATCHERs. +/// +//===----------------------------------------------------------------------===// +#include "UseAutoMatchers.h" +#include "clang/AST/ASTContext.h" + +using namespace clang::ast_matchers; +using namespace clang; + +const char *DeclNodeId = "decl"; + +namespace clang { +namespace ast_matchers { + +/// \brief Matches variable declarations that have explicit initializers that +/// are not initializer lists. +/// +/// Given +/// \code +/// iterator I = Container.begin(); +/// MyType A(42); +/// MyType B{2}; +/// MyType C; +/// \endcode +/// varDecl(hasWrittenNonListInitializer()) matches \c I and \c A but not \c B +/// or \c C. +AST_MATCHER(VarDecl, hasWrittenNonListInitializer) { + const Expr *Init = Node.getAnyInitializer(); + if (!Init) + return false; + + // The following test is based on DeclPrinter::VisitVarDecl() to find if an + // initializer is implicit or not. + bool ImplicitInit = false; + if (const CXXConstructExpr *Construct = dyn_cast<CXXConstructExpr>(Init)) { + if (Construct->isListInitialization()) + return false; + ImplicitInit = Construct->getNumArgs() == 0 || + Construct->getArg(0)->isDefaultArgument(); + } else + if (Node.getInitStyle() == VarDecl::ListInit) + return false; + + return !ImplicitInit; +} + +/// \brief Matches QualTypes that are type sugar for QualTypes that match \c +/// SugarMatcher. +/// +/// Given +/// \code +/// class C {}; +/// typedef C my_type +/// typedef my_type my_other_type; +/// \code +/// +/// \c qualType(isSugarFor(recordType(hasDeclaration(namedDecl(hasName("C")))))) +/// matches \c my_type and \c my_other_type. +AST_MATCHER_P(QualType, isSugarFor, internal::Matcher<QualType>, SugarMatcher) { + QualType QT = Node; + for (;;) { + if (SugarMatcher.matches(QT, Finder, Builder)) + return true; + + QualType NewQT = QT.getSingleStepDesugaredType(Finder->getASTContext()); + if (NewQT == QT) + break; + QT = NewQT; + } + return false; +} + +/// \brief Matches named declarations that have one of the standard iterator +/// names: iterator, reverse_iterator, const_iterator, const_reverse_iterator. +/// +/// Given +/// \code +/// iterator I; +/// const_iterator CI; +/// \code +/// +/// \c namedDecl(hasStdIteratorName()) matches \c I and \c CI. +AST_MATCHER(NamedDecl, hasStdIteratorName) { + static const char *IteratorNames[] = { + "iterator", + "reverse_iterator", + "const_iterator", + "const_reverse_iterator" + }; + + for (unsigned int i = 0; + i < llvm::array_lengthof(IteratorNames); + ++i) { + if (hasName(IteratorNames[i]).matches(Node, Finder, Builder)) + return true; + } + return false; +} + +/// \brief Matches named declarations that have one of the standard container +/// names. +/// +/// Given +/// \code +/// class vector {}; +/// class forward_list {}; +/// class my_vec {}; +/// \code +/// +/// \c recordDecl(hasStdContainerName()) matches \c vector and \c forward_list +/// but not \c my_vec. +AST_MATCHER_P(NamedDecl, hasStdContainerName, bool, WithStd) { + static const char *ContainerNames[] = { + "array", + "deque", + "forward_list", + "list", + "vector", + + "map", + "multimap", + "set", + "multiset", + + "unordered_map", + "unordered_multimap", + "unordered_set", + "unordered_multiset", + + "queue", + "priority_queue", + "stack" + }; + + for (unsigned int i = 0; + i < llvm::array_lengthof(ContainerNames); + ++i) { + std::string Name(ContainerNames[i]); + if (WithStd) + Name = "std::" + Name; + if (hasName(Name).matches(Node, Finder, Builder)) + return true; + } + return false; +} + +} // namespace ast_matchers +} // namespace clang + +namespace { +// \brief Returns a TypeMatcher that matches typedefs for standard iterators +// inside records with a standard container name. +TypeMatcher typedefIterator() { + return typedefType( + hasDeclaration( + allOf( + namedDecl(hasStdIteratorName()), + hasDeclContext( + recordDecl(hasStdContainerName(true)) + ) + ) + ) + ); +} + +// \brief Returns a TypeMatcher that matches records named for standard +// iterators nested inside records named for standard containers. +TypeMatcher nestedIterator() { + return recordType( + hasDeclaration( + allOf( + namedDecl(hasStdIteratorName()), + hasDeclContext( + recordDecl(hasStdContainerName(true)) + ) + ) + ) + ); +} + +// \brief Returns a TypeMatcher that matches types declared with using +// declarations and which name standard iterators for standard containers. +TypeMatcher iteratorFromUsingDeclaration() { + // Types resulting from using declarations are + // represented by ElaboratedType. + return elaboratedType( + allOf( + // Unwrap the nested name specifier to test for + // one of the standard containers. + hasQualifier(allOf( + specifiesType( + templateSpecializationType( + hasDeclaration( + namedDecl(hasStdContainerName(false)) + ) + ) + ), + hasPrefix( + specifiesNamespace(hasName("std")) + ) + )), + // The named type is what comes after the final + // '::' in the type. It should name one of the + // standard iterator names. + namesType(anyOf( + typedefType( + hasDeclaration( + namedDecl(hasStdIteratorName()) + ) + ), + recordType( + hasDeclaration( + namedDecl(hasStdIteratorName()) + ) + ) + )) + ) + ); +} +} // namespace + +DeclarationMatcher makeIteratorMatcher() { + return varDecl(allOf( + hasWrittenNonListInitializer(), + unless(hasType(autoType())), + hasType( + isSugarFor( + anyOf( + typedefIterator(), + nestedIterator(), + iteratorFromUsingDeclaration() + ) + ) + ) + )).bind(DeclNodeId); +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h new file mode 100644 index 00000000000..db160f8766e --- /dev/null +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h @@ -0,0 +1,27 @@ +//===-- UseAutoMatchers.h - Matchers for use-auto transform ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief This file contains the declarations for matcher-generating functions +/// and names for bound nodes found by AST matchers. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H +#define LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H + +#include "clang/ASTMatchers/ASTMatchers.h" + +extern const char *DeclNodeId; + +/// \brief Create a matcher that matches variable declarations where the type +/// is an iterator for an std container and has an explicit initializer of the +/// same type. +clang::ast_matchers::DeclarationMatcher makeIteratorMatcher(); + +#endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H diff --git a/clang-tools-extra/test/cpp11-migrate/UseAuto/Inputs/gen_my_std.h.py b/clang-tools-extra/test/cpp11-migrate/UseAuto/Inputs/gen_my_std.h.py index 06709abcb4c..3f8be5862da 100644 --- a/clang-tools-extra/test/cpp11-migrate/UseAuto/Inputs/gen_my_std.h.py +++ b/clang-tools-extra/test/cpp11-migrate/UseAuto/Inputs/gen_my_std.h.py @@ -1,47 +1,57 @@ #!/usr/bin/python +# Each std container is represented below. To test the various ways in which +# a type may be defined, the containers are split into categories: +# * Define iterator types with typedefs +# * Define iterator types as nested classes +# * Define iterator types with using declarations +# +# Further, one class in each category is chosen to be defined in a way mimicing +# libc++: The container is actually defined in a different namespace (std::_1 +# is used here) and then imported into the std namespace with a using +# declaration. This is controlled with the 'using' key in the dictionary +# describing each container. typedef_containers = [ - "array", - "deque", - "forward_list", - "list", - "vector" + {"name" : "array", + "using" : True}, + {"name" : "deque", + "using" : False}, + {"name" : "forward_list", + "using" : False}, + {"name" : "list", + "using" : False}, + {"name" : "vector", + "using" : False} ] subclass_containers = [ - "map", - "multimap", - "set", - "multiset", + {"name" : "map", + "using" : True}, + {"name" : "multimap", + "using" : False}, + {"name" : "set", + "using" : False}, + {"name" : "multiset", + "using" : False}, ] using_containers = [ - "unordered_map", - "unordered_multimap", - "unordered_set", - "unordered_multiset", - "queue", - "priority_queue", - "stack" + {"name" : "unordered_map", + "using" : True}, + {"name" : "unordered_multimap", + "using" : False}, + {"name" : "unordered_set", + "using" : False}, + {"name" : "unordered_multiset", + "using" : False}, + {"name" : "queue", + "using" : False}, + {"name" : "priority_queue", + "using" : False}, + {"name" : "stack", + "using" : False} ] -print """namespace internal { - -template <typename T, int i> -struct iterator_wrapper { -}; - -template <typename T> -class iterator_provider { -public: - class iterator {}; - class const_iterator {}; - class reverse_iterator {}; - class const_reverse_iterator {}; -}; - -} // namespace internal - -namespace std {""" +# Every class requires these functions. iterator_generators = """ iterator begin() { return iterator(); } iterator end() { return iterator(); } @@ -56,21 +66,97 @@ iterator_generators = """ const_reverse_iterator rend() const { return const_reverse_iterator(); } """ + +# Convenience function for nested class definition within a special namespace +# to mimic libc++ style std container definitions. +def outputClassDef(Definition, ClassName, Import): + if Import: + print "namespace _1 {" + + print Definition + + if Import: + print """ +}} // namespace _1 +using _1::{0};""".format(ClassName) + + +# Output preamble and common functionality +print """ +//===-----------------------------------------------------------*- C++ -*--===// +// +// This file was automatically generated from gen_my_std.h.py by the build +// system as a dependency for cpp11-migrate's test suite. +// +// This file contains a shell implementation of std containers and iterators for +// testing the use-auto transform of cpp11-migrate. All std containers and +// iterators are present. Container and iterator implementations vary to cover +// various ways the std container and iterator types are made available: +// +// Variations for how iterator types are presented: +// * Typedef (array, deque, forward_list, list, vector) +// * Nested class (map, multimap, set, multiset) +// * Using declaration {unordered_} X {map, multimap, set, multiset} +// +// Variations for how container types are presented: +// * Defined directly in namespace std +// * Imported into namespace std with using declarations (a la libc++). +// +//===----------------------------------------------------------------------===// + +namespace internal { + +template <typename T, int i> +struct iterator_wrapper { + iterator_wrapper() {} + + // These are required for tests using iteration statements. + bool operator!=(const iterator_wrapper<T, i>&) { return false; } + iterator_wrapper& operator++() { return *this; } + typename T::value_type operator*() { return typename T::value_type(); } +}; + +template <typename T> +class iterator_provider { +public: + class iterator { + public: + iterator() {} + iterator(const iterator&) {} + }; + class const_iterator { + public: + const_iterator(int i=0) {} + const_iterator(const iterator &) {} + const_iterator(const const_iterator &) {} + operator iterator() { return iterator(); } + }; + class reverse_iterator {}; + class const_reverse_iterator {}; +}; + +} // namespace internal + +namespace std {""".lstrip() # Take off leading newline + for c in typedef_containers: - print """ + Definition = """ template <typename T> class {0} {{ public: + typedef T value_type; typedef typename internal::iterator_wrapper<{0}<T>, 0> iterator; typedef typename internal::iterator_wrapper<{0}<T>, 1> const_iterator; typedef typename internal::iterator_wrapper<{0}<T>, 3> reverse_iterator; typedef typename internal::iterator_wrapper<{0}<T>, 2> const_reverse_iterator; {0}() {{}} - {1}}};""".format(c, iterator_generators) + {1}}};""".format(c['name'], iterator_generators) + + outputClassDef(Definition, c['name'], c['using']) for c in subclass_containers: - print """ + Definition = """ template <typename T> class {0} {{ public: @@ -80,10 +166,12 @@ public: class const_reverse_iterator {{}}; {0}() {{}} - {1}}};""".format(c, iterator_generators) + {1}}};""".format(c['name'], iterator_generators) + + outputClassDef(Definition, c['name'], c['using']) for c in using_containers: - print """ + Definition = """ template <typename T> class {0} : internal::iterator_provider<{0}<T> > {{ public: @@ -93,6 +181,8 @@ public: using typename internal::iterator_provider<{0}<T> >::const_reverse_iterator; {0}() {{}} - {1}}};""".format(c, iterator_generators) + {1}}};""".format(c['name'], iterator_generators) + + outputClassDef(Definition, c['name'], c['using']) print "} // namespace std" diff --git a/clang-tools-extra/test/cpp11-migrate/UseAuto/gen_basic_std_iterator_tests.cpp.py b/clang-tools-extra/test/cpp11-migrate/UseAuto/gen_basic_std_iterator_tests.cpp.py index 56b6f5c41d2..8f118245db1 100644 --- a/clang-tools-extra/test/cpp11-migrate/UseAuto/gen_basic_std_iterator_tests.cpp.py +++ b/clang-tools-extra/test/cpp11-migrate/UseAuto/gen_basic_std_iterator_tests.cpp.py @@ -19,13 +19,26 @@ containers = [ "stack" ] -print """// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp -// RUN: cpp11-migrate -use-auto %t.cpp -- --std=c++11 -I %S/Inputs +print """ +//===----------------------------------------------------------------------===// +// +// This file was automatically generated from +// gen_basic_std_iterator_tests.cpp.py by the build system as a dependency for +// cpp11-migrate's test suite. +// +// This file contains basic positive tests for the use-auto transform's ability +// to replace standard iterators. Variables considered: +// * All std container names +// * All std iterator names +// +//===----------------------------------------------------------------------===// + +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: cpp11-migrate -use-auto %t.cpp -- -I %S/Inputs // RUN: FileCheck -input-file=%t.cpp %s -// XFAIL: * #include "my_std.h" -int main(int argc, char **argv) {""" +int main(int argc, char **argv) {""".lstrip() # Strip leading newline for c in containers: print """ diff --git a/clang-tools-extra/test/cpp11-migrate/UseAuto/iterator.cpp b/clang-tools-extra/test/cpp11-migrate/UseAuto/iterator.cpp new file mode 100644 index 00000000000..a6d00c7bf96 --- /dev/null +++ b/clang-tools-extra/test/cpp11-migrate/UseAuto/iterator.cpp @@ -0,0 +1,139 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: cpp11-migrate -use-auto %t.cpp -- --std=c++11 -I %gen_root/UseAuto/Inputs +// RUN: FileCheck -input-file=%t.cpp %s +#include "my_std.h" + +typedef std::vector<int>::iterator int_iterator; + +namespace foo { + template <typename T> + class vector { + public: + class iterator {}; + + iterator begin() { return iterator(); } + }; +} // namespace foo + +int main(int argc, char **argv) { + std::vector<int> Vec; + // CHECK: std::vector<int> Vec; + + std::unordered_map<int> Map; + // CHECK: std::unordered_map<int> Map; + + // Types with more sugar should work. Types with less should not. + { + int_iterator more_sugar = Vec.begin(); + // CHECK: auto more_sugar = Vec.begin(); + + internal::iterator_wrapper<std::vector<int>, 0> less_sugar = Vec.begin(); + // CHECK: internal::iterator_wrapper<std::vector<int>, 0> less_sugar = Vec.begin(); + } + + // Initialization from initializer lists isn't allowed. Using 'auto' + // would result in std::initializer_list being deduced for the type. + { + std::unordered_map<int>::iterator I{Map.begin()}; + // CHECK: std::unordered_map<int>::iterator I{Map.begin()}; + + std::unordered_map<int>::iterator I2 = {Map.begin()}; + // CHECK: std::unordered_map<int>::iterator I2 = {Map.begin()}; + } + + // Various forms of construction. Default constructors and constructors with + // all-default parameters shouldn't get transformed. Construction from other + // types is also not allowed. + { + std::unordered_map<int>::iterator copy(Map.begin()); + // CHECK: auto copy(Map.begin()); + + std::unordered_map<int>::iterator def; + // CHECK: std::unordered_map<int>::iterator def; + + // const_iterator has no default constructor, just one that has >0 params + // with defaults. + std::unordered_map<int>::const_iterator constI; + // CHECK: std::unordered_map<int>::const_iterator constI; + + // Uses iterator_provider::const_iterator's conversion constructor. + + std::unordered_map<int>::const_iterator constI2 = def; + // CHECK: std::unordered_map<int>::const_iterator constI2 = def; + + std::unordered_map<int>::const_iterator constI3(def); + // CHECK: std::unordered_map<int>::const_iterator constI3(def); + + // Explicit use of conversion constructor + + std::unordered_map<int>::const_iterator constI4 = std::unordered_map<int>::const_iterator(def); + // CHECK: auto constI4 = std::unordered_map<int>::const_iterator(def); + + // Uses iterator_provider::iterator's const_iterator conversion operator. + + std::unordered_map<int>::iterator I = constI; + // CHECK: std::unordered_map<int>::iterator I = constI; + + std::unordered_map<int>::iterator I2(constI); + // CHECK: std::unordered_map<int>::iterator I2(constI); + } + + // Weird cases of pointers and references to iterators are not transformed. + { + int_iterator I = Vec.begin(); + + int_iterator *IPtr = &I; + // CHECK: int_iterator *IPtr = &I; + + int_iterator &IRef = I; + // CHECK: int_iterator &IRef = I; + } + + { + // Variable declarations in iteration statements. + for (std::vector<int>::iterator I = Vec.begin(); I != Vec.end(); ++I) { + // CHECK: for (auto I = Vec.begin(); I != Vec.end(); ++I) { + } + + // Range-based for loops. + std::array<std::vector<int>::iterator> iter_arr; + for (std::vector<int>::iterator I: iter_arr) { + // CHECK: for (auto I: iter_arr) { + } + + // Test with init-declarator-list. + for (int_iterator I = Vec.begin(), + E = Vec.end(); I != E; ++I) { + // CHECK: for (auto I = Vec.begin(), + // CHECK-NEXT: E = Vec.end(); I != E; ++I) { + } + } + + // Only std containers should be changed. + { + using namespace foo; + vector<int> foo_vec; + vector<int>::iterator I = foo_vec.begin(); + // CHECK: vector<int>::iterator I = foo_vec.begin(); + } + + // Ensure using directives don't interfere with replacement. + { + using namespace std; + vector<int> std_vec; + vector<int>::iterator I = std_vec.begin(); + // CHECK: auto I = std_vec.begin(); + } + + // Make sure references and cv qualifiers don't get removed (i.e. replaced + // with just 'auto'). + { + const auto & I = Vec.begin(); + // CHECK: const auto & I = Vec.begin(); + + auto && I2 = Vec.begin(); + // CHECK: auto && I2 = Vec.begin(); + } + + return 0; +} |