Index: clang-rename/RenamingAction.cpp =================================================================== --- clang-rename/RenamingAction.cpp +++ clang-rename/RenamingAction.cpp @@ -57,19 +57,16 @@ } auto PrevNameLen = PrevName.length(); - if (PrintLocations) - for (const auto &Loc : RenamingCandidates) { + for (const auto &Loc : RenamingCandidates) { + if (PrintLocations) { FullSourceLoc FullLoc(Loc, SourceMgr); errs() << "clang-rename: renamed at: " << SourceMgr.getFilename(Loc) << ":" << FullLoc.getSpellingLineNumber() << ":" << FullLoc.getSpellingColumnNumber() << "\n"; - Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen, - NewName)); } - else - for (const auto &Loc : RenamingCandidates) - Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen, - NewName)); + Replaces.insert(tooling::Replacement(SourceMgr, Loc, PrevNameLen, + NewName)); + } } private: Index: clang-rename/USRFinder.h =================================================================== --- clang-rename/USRFinder.h +++ clang-rename/USRFinder.h @@ -15,8 +15,14 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANG_RENAME_USR_FINDER_H +#include "clang/AST/AST.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" #include +using namespace llvm; +using namespace clang::ast_matchers; + namespace clang { class ASTContext; class Decl; @@ -39,6 +45,37 @@ // Converts a Decl into a USR. std::string getUSRForDecl(const Decl *Decl); +// FIXME: This wouldn't be needed if +// RecursiveASTVisitor::VisitNestedNameSpecifier would have been implemented. +class NestedNameSpecifierLocFinder : public MatchFinder::MatchCallback { +public: + explicit NestedNameSpecifierLocFinder(ASTContext &Context) + : Context(Context) {} + + std::vector getNestedNameSpecifierLocations() { + addMatchers(); + Finder.matchAST(Context); + return Locations; + } + +private: + void addMatchers() { + const auto NestedNameSpecifierLocMatcher = + nestedNameSpecifierLoc().bind("nestedNameSpecifierLoc"); + Finder.addMatcher(NestedNameSpecifierLocMatcher, this); + } + + virtual void run(const MatchFinder::MatchResult &Result) { + const auto *NNS = + Result.Nodes.getNodeAs("nestedNameSpecifierLoc"); + Locations.push_back(*NNS); + } + + ASTContext &Context; + std::vector Locations; + MatchFinder Finder; +}; + } } Index: clang-rename/USRFinder.cpp =================================================================== --- clang-rename/USRFinder.cpp +++ clang-rename/USRFinder.cpp @@ -16,11 +16,13 @@ #include "clang/AST/AST.h" #include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Index/USRGeneration.h" #include "clang/Lex/Lexer.h" #include "llvm/ADT/SmallVector.h" using namespace llvm; +using namespace clang::ast_matchers; namespace clang { namespace rename { @@ -35,18 +37,15 @@ // \brief Finds the NamedDecl at a point in the source. // \param Point the location in the source to search for the NamedDecl. explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr, - const SourceLocation Point) - : Result(nullptr), SourceMgr(SourceMgr), - Point(Point) { - } + const SourceLocation Point, + const ASTContext *Context) + : Result(nullptr), SourceMgr(SourceMgr), Point(Point), Context(Context) {} // \brief Finds the NamedDecl for a name in the source. // \param Name the fully qualified name. explicit NamedDeclFindingASTVisitor(const SourceManager &SourceMgr, const std::string &Name) - : Result(nullptr), SourceMgr(SourceMgr), - Name(Name) { - } + : Result(nullptr), SourceMgr(SourceMgr), Name(Name) {} // Declaration visitors: @@ -62,10 +61,6 @@ // Expression visitors: bool VisitDeclRefExpr(const DeclRefExpr *Expr) { - // Check the namespace specifier first. - if (!checkNestedNameSpecifierLoc(Expr->getQualifierLoc())) - return false; - const auto *Decl = Expr->getFoundDecl(); return setResult(Decl, Expr->getLocation(), Decl->getNameAsString().length()); @@ -77,26 +72,33 @@ Decl->getNameAsString().length()); } - // Other: + // Other visitors: - const NamedDecl *getNamedDecl() { - return Result; + bool VisitTypeLoc(const TypeLoc Loc) { + const auto TypeEndLoc = Loc.getEndLoc(), + TypeBeginLoc = Lexer::GetBeginningOfToken( + TypeEndLoc, SourceMgr, Context->getLangOpts()); + return setResult(Loc.getType()->getAsCXXRecordDecl(), TypeBeginLoc, + TypeEndLoc); } -private: + // Other: + + const NamedDecl *getNamedDecl() { return Result; } + // \brief Determines if a namespace qualifier contains the point. // \returns false on success and sets Result. - bool checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { + void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { while (NameLoc) { const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace(); - if (Decl && !setResult(Decl, NameLoc.getLocalBeginLoc(), - Decl->getNameAsString().length())) - return false; + if (Decl) { + setResult(Decl, NameLoc.getLocalBeginLoc(), NameLoc.getLocalEndLoc()); + } NameLoc = NameLoc.getPrefix(); } - return true; } +private: // \brief Sets Result to Decl if the Point is within Start and End. // \returns false on success. bool setResult(const NamedDecl *Decl, SourceLocation Start, @@ -119,8 +121,7 @@ // \brief Sets Result to Decl if Point is within Loc and Loc + Offset. // \returns false on success. - bool setResult(const NamedDecl *Decl, SourceLocation Loc, - unsigned Offset) { + bool setResult(const NamedDecl *Decl, SourceLocation Loc, unsigned Offset) { // FIXME: Add test for Offset == 0. Add test for Offset - 1 (vs -2 etc). return Offset == 0 || setResult(Decl, Loc, Loc.getLocWithOffset(Offset - 1)); @@ -138,6 +139,7 @@ const SourceManager &SourceMgr; const SourceLocation Point; // The location to find the NamedDecl. const std::string Name; + const ASTContext *Context; }; } // namespace @@ -146,7 +148,7 @@ const auto &SourceMgr = Context.getSourceManager(); const auto SearchFile = SourceMgr.getFilename(Point); - NamedDeclFindingASTVisitor Visitor(SourceMgr, Point); + NamedDeclFindingASTVisitor Visitor(SourceMgr, Point, &Context); // We only want to search the decls that exist in the same file as the point. auto Decls = Context.getTranslationUnitDecl()->decls(); @@ -156,29 +158,24 @@ // FIXME: Add test. if (FileName == SearchFile) { Visitor.TraverseDecl(CurrDecl); - if (const NamedDecl *Result = Visitor.getNamedDecl()) { - return Result; - } } } - return nullptr; + NestedNameSpecifierLocFinder Finder(const_cast(Context)); + for (const auto &Location : Finder.getNestedNameSpecifierLocations()) { + Visitor.handleNestedNameSpecifierLoc(Location); + } + + return Visitor.getNamedDecl(); } const NamedDecl *getNamedDeclFor(const ASTContext &Context, const std::string &Name) { const auto &SourceMgr = Context.getSourceManager(); NamedDeclFindingASTVisitor Visitor(SourceMgr, Name); - auto Decls = Context.getTranslationUnitDecl()->decls(); - - for (auto &CurrDecl : Decls) { - Visitor.TraverseDecl(CurrDecl); - if (const NamedDecl *Result = Visitor.getNamedDecl()) { - return Result; - } - } + Visitor.TraverseDecl(Context.getTranslationUnitDecl()); - return nullptr; + return Visitor.getNamedDecl(); } std::string getUSRForDecl(const Decl *Decl) { Index: clang-rename/USRLocFinder.cpp =================================================================== --- clang-rename/USRLocFinder.cpp +++ clang-rename/USRLocFinder.cpp @@ -46,18 +46,6 @@ return true; } - bool VisitVarDecl(clang::VarDecl *Decl) { - clang::QualType Type = Decl->getType(); - const clang::RecordDecl *RecordDecl = Type->getPointeeCXXRecordDecl(); - if (RecordDecl) { - if (getUSRForDecl(RecordDecl) == USR) { - // The declaration refers to a type that is to be renamed. - LocationsFound.push_back(Decl->getTypeSpecStartLoc()); - } - } - return true; - } - bool VisitCXXConstructorDecl(clang::CXXConstructorDecl *ConstructorDecl) { const ASTContext &Context = ConstructorDecl->getASTContext(); for (auto &Initializer : ConstructorDecl->inits()) { @@ -116,7 +104,6 @@ bool VisitDeclRefExpr(const DeclRefExpr *Expr) { const auto *Decl = Expr->getFoundDecl(); - checkNestedNameSpecifierLoc(Expr->getQualifierLoc()); if (getUSRForDecl(Decl) == USR) { const SourceManager &Manager = Decl->getASTContext().getSourceManager(); SourceLocation Location = Manager.getSpellingLoc(Expr->getLocation()); @@ -136,17 +123,6 @@ return true; } - bool VisitCXXConstructExpr(const CXXConstructExpr *Expr) { - CXXConstructorDecl *Decl = Expr->getConstructor(); - - if (getUSRForDecl(Decl) == USR) { - // This takes care of 'new ' expressions. - LocationsFound.push_back(Expr->getLocation()); - } - - return true; - } - bool VisitCXXStaticCastExpr(clang::CXXStaticCastExpr *Expr) { return handleCXXNamedCastExpr(Expr); } @@ -163,6 +139,19 @@ return handleCXXNamedCastExpr(Expr); } + // Other visitors: + + bool VisitTypeLoc(const TypeLoc Loc) { + if (getUSRForDecl(Loc.getType()->getAsCXXRecordDecl()) == USR) { + const auto &Context = + Loc.getType()->getAsCXXRecordDecl()->getASTContext(); + auto TypeBeginLoc = Lexer::GetBeginningOfToken( + Loc.getEndLoc(), Context.getSourceManager(), Context.getLangOpts()); + LocationsFound.push_back(TypeBeginLoc); + } + return true; + } + // Non-visitors: // \brief Returns a list of unique locations. Duplicate or overlapping @@ -171,9 +160,8 @@ return LocationsFound; } -private: // Namespace traversal: - void checkNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { + void handleNestedNameSpecifierLoc(NestedNameSpecifierLoc NameLoc) { while (NameLoc) { const auto *Decl = NameLoc.getNestedNameSpecifier()->getAsNamespace(); if (Decl && getUSRForDecl(Decl) == USR) @@ -200,6 +188,7 @@ return true; } +private: // All the locations of the USR were found. const std::string USR; // Old name that is renamed. @@ -208,11 +197,15 @@ }; } // namespace + std::vector getLocationsOfUSR(StringRef USR, StringRef PrevName, Decl *Decl) { USRLocFindingASTVisitor Visitor(USR, PrevName); - Visitor.TraverseDecl(Decl); + NestedNameSpecifierLocFinder Finder(Decl->getASTContext()); + for (const auto &Location : Finder.getNestedNameSpecifierLocations()) { + Visitor.handleNestedNameSpecifierLoc(Location); + } return Visitor.getLocationsFound(); } Index: test/clang-rename/ClassNameInFunctionDefenition.cpp =================================================================== --- test/clang-rename/ClassNameInFunctionDefenition.cpp +++ test/clang-rename/ClassNameInFunctionDefenition.cpp @@ -1,17 +1,10 @@ -// Currently unsupported test. // RUN: cat %s > %t.cpp -// FIXME: clang-rename doesn't recognize symbol in class function definition. +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s -class Foo { +class Foo { // CHECK: class Bar { public: void foo(int x); }; -void Foo::foo(int x) {} -// ^ this one - -int main() { - Foo obj; - obj.foo(0); - return 0; -} +void Foo::foo(int x) {} // CHECK: void Bar::foo(int x) {} Index: test/clang-rename/ComplicatedClassType.cpp =================================================================== --- test/clang-rename/ComplicatedClassType.cpp +++ test/clang-rename/ComplicatedClassType.cpp @@ -1,30 +1,31 @@ -// Unsupported test. // RUN: cat %s > %t.cpp -// FIXME: This test contains very simple constructions likely to be seen in any -// project and therefore passing this test is a slight sign of success. -// Currently, the test fails badly. +// RUN: clang-rename -offset=220 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +// Forward declaration. +class Foo; // CHECK: class Bar; class Foo { // CHECK: class Bar { - public: - Foo(int value = 0) : x(value) {} // Bar(int value=0) : x(value) {} +public: + Foo(int value = 0) : x(value) {} // CHECK: Bar(int value = 0) : x(value) {} - Foo& operator++(int) { // Bar& operator++(int) { + Foo &operator++(int) { // CHECK: Bar &operator++(int) { x++; return *this; } - bool operator<(Foo const& rhs) { // bool operator<(Bar const &rhs) { + bool operator<(Foo const &rhs) { // CHECK: bool operator<(Bar const &rhs) { return this->x < rhs.x; } - private: +private: int x; }; int main() { - Foo* Pointer = 0; // CHECK: Bar *Pointer = 0; + Foo *Pointer = 0; // CHECK: Bar *Pointer = 0; Foo Variable = Foo(10); // CHECK: Bar Variable = Bar(10); - for (Foo it; it < Variable; it++) { // for (Bar it; it < Variable; it++) {} + for (Foo it; it < Variable; it++) { // CHECK: for (Bar it; it < Variable; it++) { } return 0; } Index: test/clang-rename/ComplicatedClassTypeInCustomNamespace.cpp =================================================================== --- /dev/null +++ test/clang-rename/ComplicatedClassTypeInCustomNamespace.cpp @@ -0,0 +1,37 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=241 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +namespace baz { + +// Forward declaration. +class Foo; // CHECK: class Bar; + +class Foo { // CHECK: class Bar { + public: + Foo(int value = 0) : x(value) {} // CHECK: Bar(int value = 0) : x(value) {} + + Foo &operator++(int) { // CHECK: Bar &operator++(int) { + x++; + return *this; + } + + bool operator<(Foo const &rhs) { // CHECK: bool operator<(Bar const &rhs) { + return this->x < rhs.x; + } + + private: + int x; +}; + +Foo Instance; // CHECK: Bar Instance; + +} + +int main() { + baz::Foo *Pointer = 0; // CHECK: baz::Bar *Pointer = 0; + baz::Foo Variable = baz::Foo(10); // CHECK: baz::Bar Variable = baz::Bar(10); + for (baz::Foo it; it < Variable; it++) { // CHECK: for (baz::Bar it; it < Variable; it++) { + } + return 0; +} Index: test/clang-rename/FindNameSpaceInNestedNameSpec.cpp =================================================================== --- /dev/null +++ test/clang-rename/FindNameSpaceInNestedNameSpec.cpp @@ -0,0 +1,37 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=583 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +namespace Baz { +namespace Foo { // CHECK: namespace Bar { +class A { +public: + static int aFunction() { return 0; } +}; +int FooFunction() { return 0; } +namespace Qux { +class B { +public: + static int bFunction() { return 0; } +}; +int QuxFunction() { return 0; } +} // namespace Qux +} // namespace Foo +} // namespace Baz + +int main() { + // Variable type tests. + Baz::Foo::A a; // CHECK: Baz::Bar::A a; + Baz::Foo::Qux::B b; // CHECK: Baz::Bar::Qux::B b; + unsigned i = 42; + for (Baz::Foo::A c; i < 100; i++); // CHECK: for (Baz::Bar::A c; i < 100; i++); + for (Baz::Foo::Qux::B d; i < 200; i++); // CHECK: for (Baz::Bar::Qux::B d; i < 200; i++); + + // Function tests. + int x = Baz::Foo::A::aFunction(); // CHECK: int x = Baz::Bar::A::aFunction(); + x = Baz::Foo::FooFunction(); // CHECK: x = Baz::Bar::FooFunction(); + x = Baz::Foo::Qux::B::bFunction(); // CHECK: x = Baz::Bar::Qux::B::bFunction(); + x = Baz::Foo::Qux::QuxFunction(); // CHECK: x = Baz::Bar::Qux::QuxFunction(); + + return 0; +} Index: test/clang-rename/NestedNamespace.cpp =================================================================== --- /dev/null +++ test/clang-rename/NestedNamespace.cpp @@ -0,0 +1,38 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=156 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +namespace Baz { +namespace Foo { // CHECK: namespace Bar { +class A { +public: + static int aFunction() { return 0; } +}; +int FooFunction() { return 0; } +namespace Qux { +class B { +public: + static int bFunction() { return 0; } +}; +int QuxFunction() { return 0; } +} // namespace Qux +} // namespace Foo +} // namespace Baz + +int main() { + + // Variable type tests. + Baz::Foo::A a; // CHECK: Baz::Bar::A a; + Baz::Foo::Qux::B b; // CHECK: Baz::Bar::Qux::B b; + unsigned i = 42; + for (Baz::Foo::A c; i < 100; i++); // CHECK: for (Baz::Bar::A c; i < 100; i++); + for (Baz::Foo::Qux::B d; i < 200; i++); // CHECK: for (Baz::Bar::Qux::B d; i < 200; i++); + + // Function tests. + int x = Baz::Foo::A::aFunction(); // CHECK: int x = Baz::Bar::A::aFunction(); + x = Baz::Foo::FooFunction(); // CHECK: x = Baz::Bar::FooFunction(); + x = Baz::Foo::Qux::B::bFunction(); // CHECK: x = Baz::Bar::Qux::B::bFunction(); + x = Baz::Foo::Qux::QuxFunction(); // CHECK: x = Baz::Bar::Qux::QuxFunction(); + + return 0; +} Index: test/clang-rename/TemplateTypename.cpp =================================================================== --- test/clang-rename/TemplateTypename.cpp +++ test/clang-rename/TemplateTypename.cpp @@ -1,12 +1,20 @@ -// Currently unsupported test. // RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=270 -new-name=U %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +// Currently unsupported test. // FIXME: clang-rename should be able to rename template parameters correctly. +// XFAIL: * -template -T foo(T arg, T& ref, T* ptr) { - T value; +template // CHECK: template +class Foo { +T foo(T arg, T& ref, T* ptr) { // CHECK: U foo(U arg, U& ref, U* ptr) { + T value; // CHECK: U value; int number = 42; - value = (T)number; - value = static_cast(number); + value = (T)number; // CHECK: value = (U)number; + value = static_cast(number); // CHECK: value = static_cast(number); return value; } + +T member; // CHECK: U member; +}; Index: test/clang-rename/UserDefinedConversion.cpp =================================================================== --- test/clang-rename/UserDefinedConversion.cpp +++ /dev/null @@ -1,13 +0,0 @@ -// Currently unsupported test. -// RUN: cat %s > %t.cpp -// FIXME: clang-rename should handle conversions from a class type to another -// type. - -class Foo {}; // CHECK: class Bar {}; - -class Baz { // CHECK: class Bar { - operator Foo() const { // CHECK: operator Bar() const { - Foo foo; // CHECK: Bar foo; - return foo; - } -}; Index: test/clang-rename/UserDefinedConversionFindByConversion.cpp =================================================================== --- /dev/null +++ test/clang-rename/UserDefinedConversionFindByConversion.cpp @@ -0,0 +1,29 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=441 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +// FIXME: clang-rename removes "operator " while renaming user-defined +// conversion only if offset points to the conversion for some reason. +// XFAIL: * + +class Foo { // CHECK: class Bar { +public: + Foo() {} // CHECK: Bar() {} +}; + +class Baz { +public: + operator Foo() const { // CHECK: operator Bar() const { + Foo foo; // CHECK: Bar foo; + return foo; + } +}; + +int main() { + Baz boo; + Foo foo = static_cast(boo); // CHECK: Bar foo = static_cast(boo); + return 0; +} + +// Use grep -FUbo 'Foo' to get the correct offset of Cla when changing +// this file. Index: test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp =================================================================== --- test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp +++ test/clang-rename/UserDefinedConversionFindByTypeDeclaration.cpp @@ -1,7 +1,6 @@ -// Currently unsupported test. // RUN: cat %s > %t.cpp -// FIXME: while renaming class/struct clang-rename should be able to change -// this type name corresponding user-defined conversions, too. +// RUN: clang-rename -offset=136 -new-name=Bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s class Foo { // CHECK: class Bar { // ^ offset must be here @@ -22,3 +21,6 @@ Foo foo = static_cast(boo); // CHECK: Bar foo = static_cast(boo); return 0; } + +// Use grep -FUbo 'Foo' to get the correct offset of Cla when changing +// this file.