diff options
7 files changed, 211 insertions, 17 deletions
diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp index 31f867799b6..7a56e1c1250 100644 --- a/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAuto.cpp @@ -35,9 +35,13 @@ int UseAutoTransform::apply(const FileContentsByPath &InputStates, unsigned AcceptedChanges = 0; MatchFinder Finder; - UseAutoFixer Fixer(UseAutoTool.getReplacements(), AcceptedChanges, MaxRisk); + IteratorReplacer ReplaceIterators(UseAutoTool.getReplacements(), + AcceptedChanges, MaxRisk); + NewReplacer ReplaceNew(UseAutoTool.getReplacements(), AcceptedChanges, + MaxRisk); - Finder.addMatcher(makeIteratorMatcher(), &Fixer); + Finder.addMatcher(makeIteratorDeclMatcher(), &ReplaceIterators); + Finder.addMatcher(makeDeclWithNewMatcher(), &ReplaceNew); if (int Result = UseAutoTool.run(newFrontendActionFactory(&Finder))) { llvm::errs() << "Error encountered during translation.\n"; diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp index 14986d8dca9..bd2599c4685 100644 --- a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.cpp @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file contains the implementation of the UseAutoFixer class. +/// \brief This file contains the implementation of callbacks for the UseAuto +/// transform. /// //===----------------------------------------------------------------------===// #include "UseAutoActions.h" @@ -19,8 +20,8 @@ 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); +void IteratorReplacer::run(const MatchFinder::MatchResult &Result) { + const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>(IteratorDeclId); assert(D && "Bad Callback. No node provided"); @@ -68,3 +69,48 @@ void UseAutoFixer::run(const MatchFinder::MatchResult &Result) { ++AcceptedChanges; } } + +void NewReplacer::run(const MatchFinder::MatchResult &Result) { + const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>(DeclWithNewId); + assert(D && "Bad Callback. No node provided"); + + SourceManager &SM = *Result.SourceManager; + if (!SM.isFromMainFile(D->getLocStart())) + return; + + const CXXNewExpr *NewExpr = Result.Nodes.getNodeAs<CXXNewExpr>(NewExprId); + assert(NewExpr && "Bad Callback. No CXXNewExpr bound"); + + // If declaration and initializer have exactly the same type, just replace + // with 'auto'. + if (Result.Context->hasSameType(D->getType(), NewExpr->getType())) { + TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc(); + CharSourceRange Range(TL.getSourceRange(), /*IsTokenRange=*/ true); + // Space after 'auto' to handle cases where the '*' in the pointer type + // is next to the identifier. This avoids changing 'int *p' into 'autop'. + Replace.insert(tooling::Replacement(SM, Range, "auto ")); + ++AcceptedChanges; + return; + } + + // If the CV qualifiers for the pointer differ then we still use auto, just + // need to leave the qualifier behind. + if (Result.Context->hasSameUnqualifiedType(D->getType(), + NewExpr->getType())) { + TypeLoc TL = D->getTypeSourceInfo()->getTypeLoc(); + CharSourceRange Range(TL.getSourceRange(), /*IsTokenRange=*/ true); + // Space after 'auto' to handle cases where the '*' in the pointer type + // is next to the identifier. This avoids changing 'int *p' into 'autop'. + Replace.insert(tooling::Replacement(SM, Range, "auto ")); + ++AcceptedChanges; + return; + } + + // The VarDecl and Initializer have mismatching types. + return; + + // FIXME: There is, however, one case we can address: when the VarDecl + // pointee is the same as the initializer, just more CV-qualified. However, + // TypeLoc information is not reliable where CV qualifiers are concerned so + // we can't do anything about this case for now. +} diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h index d6ea557b369..e28a45b10d1 100644 --- a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoActions.h @@ -8,8 +8,8 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file contains the declaration of the UseAutoFixer class which -/// is used as an ASTMatcher callback. +/// \brief This file contains the declarations for callbacks used by the +/// UseAuto transform. /// //===----------------------------------------------------------------------===// #ifndef LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_ACTIONS_H @@ -19,16 +19,37 @@ #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 { +/// \brief The callback to be used when replacing type specifiers of variable +/// declarations that are iterators. +class IteratorReplacer + : public clang::ast_matchers::MatchFinder::MatchCallback { public: - UseAutoFixer(clang::tooling::Replacements &Replace, unsigned &AcceptedChanges, - RiskLevel) + IteratorReplacer(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); + virtual void run(const clang::ast_matchers::MatchFinder::MatchResult &Result) + LLVM_OVERRIDE; + +private: + clang::tooling::Replacements &Replace; + unsigned &AcceptedChanges; +}; + +/// \brief The callback used when replacing type specifiers of variable +/// declarations initialized by a C++ new expression. +class NewReplacer : public clang::ast_matchers::MatchFinder::MatchCallback { +public: + NewReplacer(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) + LLVM_OVERRIDE; private: clang::tooling::Replacements &Replace; diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp index b17708eabd4..08f9a7e52f6 100644 --- a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.cpp @@ -18,7 +18,9 @@ using namespace clang::ast_matchers; using namespace clang; -const char *DeclNodeId = "decl"; +const char *IteratorDeclId = "iterator_decl"; +const char *DeclWithNewId = "decl_new"; +const char *NewExprId = "new_expr"; namespace clang { namespace ast_matchers { @@ -230,7 +232,7 @@ TypeMatcher iteratorFromUsingDeclaration() { } } // namespace -DeclarationMatcher makeIteratorMatcher() { +DeclarationMatcher makeIteratorDeclMatcher() { return varDecl(allOf( hasWrittenNonListInitializer(), unless(hasType(autoType())), @@ -243,5 +245,32 @@ DeclarationMatcher makeIteratorMatcher() { ) ) ) - )).bind(DeclNodeId); + )).bind(IteratorDeclId); +} + +DeclarationMatcher makeDeclWithNewMatcher() { + return varDecl( + hasInitializer( + ignoringParenImpCasts( + newExpr().bind(NewExprId) + ) + ), + + // FIXME: TypeLoc information is not reliable where CV qualifiers are + // concerned so these types can't be handled for now. + unless(hasType(pointerType(pointee(hasLocalQualifiers())))), + + // FIXME: Handle function pointers. For now we ignore them because + // the replacement replaces the entire type specifier source range + // which includes the identifier. + unless( + hasType( + pointsTo( + pointsTo( + parenType(innerType(functionType())) + ) + ) + ) + ) + ).bind(DeclWithNewId); } diff --git a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h index db160f8766e..1f09ad407cd 100644 --- a/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h +++ b/clang-tools-extra/cpp11-migrate/UseAuto/UseAutoMatchers.h @@ -17,11 +17,17 @@ #include "clang/ASTMatchers/ASTMatchers.h" -extern const char *DeclNodeId; +extern const char *IteratorDeclId; +extern const char *DeclWithNewId; +extern const char *NewExprId; /// \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(); +clang::ast_matchers::DeclarationMatcher makeIteratorDeclMatcher(); + +/// \brief Create a matcher that matches variable declarations that are +/// initialized by a C++ new expression. +clang::ast_matchers::DeclarationMatcher makeDeclWithNewMatcher(); #endif // LLVM_TOOLS_CLANG_TOOLS_EXTRA_CPP11_MIGRATE_USE_AUTO_MATCHERS_H diff --git a/clang-tools-extra/test/cpp11-migrate/UseAuto/new.cpp b/clang-tools-extra/test/cpp11-migrate/UseAuto/new.cpp new file mode 100644 index 00000000000..911c0cd76bd --- /dev/null +++ b/clang-tools-extra/test/cpp11-migrate/UseAuto/new.cpp @@ -0,0 +1,52 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: cpp11-migrate -use-auto %t.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%t.cpp %s + +class MyType { +}; + +class MyDerivedType : public MyType { +}; + +int main(int argc, char **argv) { + MyType *a = new MyType(); + // CHECK: auto a = new MyType(); + + static MyType *a_static = new MyType(); + // CHECK: static auto a_static = new MyType(); + + MyType *b = new MyDerivedType(); + // CHECK: MyType *b = new MyDerivedType(); + + void *c = new MyType(); + // CHECK: void *c = new MyType(); + + // CV-qualifier tests. + // + // NOTE : the form "type const" is expected here because of a deficiency in + // TypeLoc where CV qualifiers are not considered part of the type location + // info. That is, all that is being replaced in each case is "MyType *" and + // not "MyType * const". + { + static MyType * const d_static = new MyType(); + // CHECK: static auto const d_static = new MyType(); + + MyType * const d3 = new MyType(); + // CHECK: auto const d3 = new MyType(); + + MyType * volatile d4 = new MyType(); + // CHECK: auto volatile d4 = new MyType(); + } + + int (**func)(int, int) = new (int(*[5])(int,int)); + // CHECK: int (**func)(int, int) = new (int(*[5])(int,int)); + + int *e = new int[5]; + // CHECK: auto e = new int[5]; + + MyType *f(new MyType); + // CHECK: auto f(new MyType); + + MyType *g{new MyType}; + // CHECK: MyType *g{new MyType}; +} diff --git a/clang-tools-extra/test/cpp11-migrate/UseAuto/new_cv_failing.cpp b/clang-tools-extra/test/cpp11-migrate/UseAuto/new_cv_failing.cpp new file mode 100644 index 00000000000..8e21018ef90 --- /dev/null +++ b/clang-tools-extra/test/cpp11-migrate/UseAuto/new_cv_failing.cpp @@ -0,0 +1,36 @@ +// RUN: grep -Ev "// *[A-Z-]+:" %s > %t.cpp +// RUN: cpp11-migrate -use-auto %t.cpp -- -std=c++11 +// RUN: FileCheck -input-file=%t.cpp %s +// XFAIL: * + +// None of these tests can pass right now because TypeLoc information where CV +// qualifiers are concerned is not reliable/available. + +class MyType { +}; + +int main (int argc, char **argv) { + const MyType *d = new MyType(); + // CHECK: const auto *d = new MyType(); + + volatile MyType *d2 = new MyType(); + // CHECK: volatile auto *d2 = new MyType(); + + const MyType * volatile e = new MyType(); + // CHECK: const auto * volatile d = new MyType(); + + volatile MyType * const f = new MyType(); + // CHECK: volatile auto * const d2 = new MyType(); + + const MyType *d5 = new const MyType(); + // CHECK: auto d5 = new const MyType(); + + volatile MyType *d6 = new volatile MyType(); + // CHECK: auto d6 = new volatile MyType(); + + const MyType * const d7 = new const MyType(); + // CHECK: const auto d7 = new const MyType(); + + volatile MyType * volatile d8 = new volatile MyType(); + // CHECK: volatile auto d8 = new volatile MyType(); +} |