From: Edwin Vane Date: Thu, 28 Feb 2013 16:29:24 +0000 (+0000) Subject: Introducing Use-Auto transform for cpp11-migrate X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=266b625ca90de49dac72022232deac1a59f09ccd;p=platform%2Fupstream%2Fllvm.git Introducing Use-Auto transform for cpp11-migrate The new Use-Auto transform replaces the type specifier for variable declarations with the special C++11 'auto' type specifier. For now, the replacement is done only for variables that are iterators of any of the std containers and only if the type used is one of those explicitly allowed by the standard (i.e. not an implementation-specific type). Reviewers: gribozavr, silvas, klimek llvm-svn: 176266 --- diff --git a/clang-tools-extra/cpp11-migrate/CMakeLists.txt b/clang-tools-extra/cpp11-migrate/CMakeLists.txt index 30e33b45..591c0a3 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 5b806d4..4eca366 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 6b2ab5c..2b6dfd7 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)); + Options.push_back( + OptionVec::value_type( + new cl::opt("use-auto", + cl::desc("Use of 'auto' type specifier")), + &ConstructTransform)); + // 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 0000000..00723ca --- /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 &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 0000000..799dad9 --- /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 &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 0000000..ceb2239 --- /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(DeclNodeId); + + assert(D && "Bad Callback. No node provided"); + + SourceManager &SM = *Result.SourceManager; + if (!SM.isFromMainFile(D->getLocStart())) + return; + + const CXXConstructExpr *Construct = cast(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(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 0000000..d6ea557 --- /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 0000000..6522f4f --- /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(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, 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 0000000..db160f8 --- /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 06709ab..3f8be58 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 -struct iterator_wrapper { -}; - -template -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 +struct iterator_wrapper { + iterator_wrapper() {} + + // These are required for tests using iteration statements. + bool operator!=(const iterator_wrapper&) { return false; } + iterator_wrapper& operator++() { return *this; } + typename T::value_type operator*() { return typename T::value_type(); } +}; + +template +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 class {0} {{ public: + typedef T value_type; typedef typename internal::iterator_wrapper<{0}, 0> iterator; typedef typename internal::iterator_wrapper<{0}, 1> const_iterator; typedef typename internal::iterator_wrapper<{0}, 3> reverse_iterator; typedef typename internal::iterator_wrapper<{0}, 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 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 class {0} : internal::iterator_provider<{0} > {{ public: @@ -93,6 +181,8 @@ public: using typename internal::iterator_provider<{0} >::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 56b6f5c..8f11824 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 0000000..a6d00c7 --- /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::iterator int_iterator; + +namespace foo { + template + class vector { + public: + class iterator {}; + + iterator begin() { return iterator(); } + }; +} // namespace foo + +int main(int argc, char **argv) { + std::vector Vec; + // CHECK: std::vector Vec; + + std::unordered_map Map; + // CHECK: std::unordered_map 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, 0> less_sugar = Vec.begin(); + // CHECK: internal::iterator_wrapper, 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::iterator I{Map.begin()}; + // CHECK: std::unordered_map::iterator I{Map.begin()}; + + std::unordered_map::iterator I2 = {Map.begin()}; + // CHECK: std::unordered_map::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::iterator copy(Map.begin()); + // CHECK: auto copy(Map.begin()); + + std::unordered_map::iterator def; + // CHECK: std::unordered_map::iterator def; + + // const_iterator has no default constructor, just one that has >0 params + // with defaults. + std::unordered_map::const_iterator constI; + // CHECK: std::unordered_map::const_iterator constI; + + // Uses iterator_provider::const_iterator's conversion constructor. + + std::unordered_map::const_iterator constI2 = def; + // CHECK: std::unordered_map::const_iterator constI2 = def; + + std::unordered_map::const_iterator constI3(def); + // CHECK: std::unordered_map::const_iterator constI3(def); + + // Explicit use of conversion constructor + + std::unordered_map::const_iterator constI4 = std::unordered_map::const_iterator(def); + // CHECK: auto constI4 = std::unordered_map::const_iterator(def); + + // Uses iterator_provider::iterator's const_iterator conversion operator. + + std::unordered_map::iterator I = constI; + // CHECK: std::unordered_map::iterator I = constI; + + std::unordered_map::iterator I2(constI); + // CHECK: std::unordered_map::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::iterator I = Vec.begin(); I != Vec.end(); ++I) { + // CHECK: for (auto I = Vec.begin(); I != Vec.end(); ++I) { + } + + // Range-based for loops. + std::array::iterator> iter_arr; + for (std::vector::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 foo_vec; + vector::iterator I = foo_vec.begin(); + // CHECK: vector::iterator I = foo_vec.begin(); + } + + // Ensure using directives don't interfere with replacement. + { + using namespace std; + vector std_vec; + vector::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; +}