Index: clang-rename/USRFindingAction.cpp =================================================================== --- clang-rename/USRFindingAction.cpp +++ clang-rename/USRFindingAction.cpp @@ -8,7 +8,8 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Provides an action to rename every symbol at a point. +/// \brief Provides an action to find USR for the symbol at , as well as +/// all relevant USRs. /// //===----------------------------------------------------------------------===// @@ -17,6 +18,9 @@ #include "clang/AST/AST.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/FileManager.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Frontend/FrontendAction.h" @@ -25,47 +29,103 @@ #include "clang/Tooling/CommonOptionsParser.h" #include "clang/Tooling/Refactoring.h" #include "clang/Tooling/Tooling.h" +#include #include +#include #include + using namespace llvm; +using namespace clang::ast_matchers; namespace clang { namespace rename { -// Get the USRs for the constructors of the class. -static std::vector getAllConstructorUSRs( - const CXXRecordDecl *Decl) { - std::vector USRs; - - // We need to get the definition of the record (as opposed to any forward - // declarations) in order to find the constructor and destructor. - const auto *RecordDecl = Decl->getDefinition(); - - // Iterate over all the constructors and add their USRs. - for (const auto *CtorDecl : RecordDecl->ctors()) - USRs.push_back(getUSRForDecl(CtorDecl)); - - // Ignore destructors. GetLocationsOfUSR will find the declaration of and - // explicit calls to a destructor through TagTypeLoc (and it is better for the - // purpose of renaming). - // - // For example, in the following code segment, - // 1 class C { - // 2 ~C(); - // 3 }; - // At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc starting - // from 'C'. - - return USRs; -} +namespace { +// \brief NamedDeclFindingConsumer should delegate finding USRs relevant to +// given Decl to RelevantUSRFinder. +// +// FIXME: It's better to match ctors/dtors via typeLoc's instead of adding +// their USRs to the storage, because we can also match CXXConversionDecl's by +// typeLoc and we won't have to "manually" handle them here. +class RelevantUSRFinder : public MatchFinder::MatchCallback { +public: + explicit RelevantUSRFinder(const Decl *FoundDecl, ASTContext &Context, + std::vector *USRs) + : FoundDecl(FoundDecl), Context(Context), USRs(USRs), USRSet(), Finder() {} + + void Find() { + USRSet.insert(getUSRForDecl(FoundDecl)); + addRelevantUSRs(); + addMatchers(); + Finder.matchAST(Context); + USRs->insert(USRs->end(), USRSet.begin(), USRSet.end()); + } + +private: + void addMatchers() { + const auto CXXMethodDeclMatcher = + cxxMethodDecl(isVirtual()).bind("cxxMethodDecl"); + Finder.addMatcher(CXXMethodDeclMatcher, this); + } + + virtual void run(const MatchFinder::MatchResult &Result) { + const auto *VirtualMethod = + Result.Nodes.getNodeAs("cxxMethodDecl"); + bool Found = false; + for (const auto &OverriddenMethod : VirtualMethod->overridden_methods()) { + if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end()) { + Found = true; + } + } + if (Found) { + USRSet.insert(getUSRForDecl(VirtualMethod)); + } + } + + void addRelevantUSRs() { + // If D is CXXRecordDecl we should add all USRs of its constructors. + if (const auto *RecordDecl = dyn_cast(FoundDecl)) { + // Ignore destructors. Find the declaration of and explicit calls to a + // destructor through TagTypeLoc (and it is better for the purpose of + // renaming). + // + // For example, in the following code segment, + // 1 class C { + // 2 ~C(); + // 3 }; + // + // At line 3, there is a NamedDecl starting from '~' and a TagTypeLoc + // starting from 'C'. + RecordDecl = RecordDecl->getDefinition(); + + // Iterate over all the constructors and add their USRs. + for (const auto *CtorDecl : RecordDecl->ctors()) { + USRSet.insert(getUSRForDecl(CtorDecl)); + } + } + // If D is CXXMethodDecl we should add all USRs of its overriden methods. + if (const auto *MethodDecl = dyn_cast(FoundDecl)) { + for (auto &OverriddenMethod : MethodDecl->overridden_methods()) { + USRSet.insert(getUSRForDecl(OverriddenMethod)); + } + } + } + + const Decl *FoundDecl; + ASTContext &Context; + std::vector *USRs; + std::set USRSet; + MatchFinder Finder; +}; +} // namespace struct NamedDeclFindingConsumer : public ASTConsumer { void HandleTranslationUnit(ASTContext &Context) override { const auto &SourceMgr = Context.getSourceManager(); // The file we look for the USR in will always be the main source file. - const auto Point = SourceMgr.getLocForStartOfFile( - SourceMgr.getMainFileID()).getLocWithOffset(SymbolOffset); + const auto Point = SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()) + .getLocWithOffset(SymbolOffset); if (!Point.isValid()) return; const NamedDecl *FoundDecl = nullptr; @@ -84,20 +144,17 @@ return; } - // If the decl is a constructor or destructor, we want to instead take the - // decl of the parent record. - if (const auto *CtorDecl = dyn_cast(FoundDecl)) + // If FoundDecl is a constructor or destructor, we want to instead take the + // Decl of the corresponding class. + if (const auto *CtorDecl = dyn_cast(FoundDecl)) { FoundDecl = CtorDecl->getParent(); - else if (const auto *DtorDecl = dyn_cast(FoundDecl)) + } else if (const auto *DtorDecl = dyn_cast(FoundDecl)) { FoundDecl = DtorDecl->getParent(); - - // If the decl is in any way relatedpp to a class, we want to make sure we - // search for the constructor and destructor as well as everything else. - if (const auto *Record = dyn_cast(FoundDecl)) - *USRs = getAllConstructorUSRs(Record); - - USRs->push_back(getUSRForDecl(FoundDecl)); + } *SpellingName = FoundDecl->getNameAsString(); + + RelevantUSRFinder Finder(FoundDecl, Context, USRs); + Finder.Find(); } unsigned SymbolOffset; @@ -106,8 +163,7 @@ std::vector *USRs; }; -std::unique_ptr -USRFindingAction::newASTConsumer() { +std::unique_ptr USRFindingAction::newASTConsumer() { std::unique_ptr Consumer( new NamedDeclFindingConsumer); SpellingName = ""; Index: test/clang-rename/VirtualFunctionFindInBaseClass.cpp =================================================================== --- /dev/null +++ test/clang-rename/VirtualFunctionFindInBaseClass.cpp @@ -0,0 +1,33 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=165 -new-name=bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +class Foo { +public: + virtual void foo() {} // CHECK: virtual void bar() {} +}; + +class Bar : public Foo { +public: + void foo() override {} // CHECK: void bar() override {} +}; + +class Baz : public Bar { + public: + void foo() override {} // CHECK: void bar() override {} +}; + +int main() { + Foo f; + f.foo(); // CHECK: f.bar(); + + Bar b; + b.foo(); // CHECK: b.bar(); + + Baz obj; + obj.foo(); // CHECK: obj.bar(); + + return 0; +} + +// Use grep -FUbo 'foo' to get the correct offset of foo when changing Index: test/clang-rename/VirtualFunctionFindInDerivedClass.cpp =================================================================== --- /dev/null +++ test/clang-rename/VirtualFunctionFindInDerivedClass.cpp @@ -0,0 +1,33 @@ +// RUN: cat %s > %t.cpp +// RUN: clang-rename -offset=165 -new-name=bar %t.cpp -i -- +// RUN: sed 's,//.*,,' %t.cpp | FileCheck %s + +class Foo { +public: + virtual void foo() {} // CHECK: virtual void bar() {} +}; + +class Bar : public Foo { +public: + void foo() override {} // CHECK: void bar() override {} +}; + +class Baz : public Bar { + public: + void foo() override {} // CHECK: void bar() override {} +}; + +int main() { + Foo f; + f.foo(); // CHECK: f.bar(); + + Bar b; + b.foo(); // CHECK: b.bar(); + + Baz obj; + obj.foo(); // CHECK: obj.bar(); + + return 0; +} + +// Use grep -FUbo 'foo' to get the correct offset of foo when changing