diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -18,12 +18,12 @@ #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/Basic/SourceLocation.h" -#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/None.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" #include @@ -76,6 +76,8 @@ return OtherFile; } +const NamedDecl *canonicalRenameDecl(const NamedDecl *D); + llvm::DenseSet locateDeclAt(ParsedAST &AST, SourceLocation TokenStartLoc) { unsigned Offset = @@ -92,8 +94,7 @@ targetDecl(SelectedNode->ASTNode, DeclRelation::Alias | DeclRelation::TemplatePattern)) { // Get to CXXRecordDecl from constructor or destructor. - D = tooling::getCanonicalSymbolDeclaration(D); - Result.insert(D); + Result.insert(canonicalRenameDecl(D)); } return Result; } @@ -222,23 +223,73 @@ return error("Cannot rename symbol: {0}", Message(Reason)); } +const NamedDecl *canonicalRenameDecl(const TemplateDecl *D) { + return D->getTemplatedDecl(); +} + +const NamedDecl *canonicalRenameDecl(const CXXMethodDecl *D) { + const auto *Result = D; + if (const auto *InstantiatedMethod = D->getInstantiatedFromMemberFunction()) + Result = cast(InstantiatedMethod); + while (Result->isVirtual() && Result->size_overridden_methods()) + Result = *Result->overridden_methods().begin(); + return Result; +} + +const NamedDecl *canonicalRenameDecl(const FunctionDecl *D) { + if (const auto *Template = D->getPrimaryTemplate()) + return canonicalRenameDecl(Template); + return D; +} + +const NamedDecl *canonicalRenameDecl(const FunctionTemplateDecl *D) { + const auto *TemplatedDecl = D->getTemplatedDecl(); + if (const auto *Primary = TemplatedDecl->getPrimaryTemplate()) + return Primary; + return TemplatedDecl; +} + +const NamedDecl *canonicalRenameDecl(const VarTemplateSpecializationDecl *D) { + return D->getSpecializedTemplate()->getTemplatedDecl(); +} + +const NamedDecl *canonicalRenameDecl(const ClassTemplateSpecializationDecl *D) { + return D->getSpecializedTemplate()->getTemplatedDecl(); +} + +// Canonical declarations help simplify the process of renaming. Examples: +// - Given a constructor/destructor, canonical declaration is the parent +// CXXRecordDecl +// - Specializations should point to the specialized declaration. +// - Instantiations should point to instantiated declaration. +const NamedDecl *canonicalRenameDecl(const NamedDecl *D) { + D = dyn_cast(D->getCanonicalDecl()); + if (const auto *Constructor = dyn_cast(D)) + return canonicalRenameDecl(Constructor->getParent()); + if (const auto *Destructor = dyn_cast(D)) + return canonicalRenameDecl(Destructor->getParent()); + if (const auto *FuctionTemplate = dyn_cast(D)) + return canonicalRenameDecl(FuctionTemplate); + if (const auto *VarTemplate = dyn_cast(D)) + return canonicalRenameDecl(VarTemplate); + if (const auto *Template = dyn_cast(D)) + return canonicalRenameDecl(Template); + if (const auto *ClassTemplateSpecialization = + dyn_cast(D)) + return canonicalRenameDecl(ClassTemplateSpecialization); + if (const auto *Method = dyn_cast(D)) + return canonicalRenameDecl(Method); + if (const auto *Function = dyn_cast(D)) + return canonicalRenameDecl(Function); + return D; +} + // Return all rename occurrences in the main file. std::vector findOccurrencesWithinFile(ParsedAST &AST, const NamedDecl &ND) { trace::Span Tracer("FindOccurrencesWithinFile"); - // If the cursor is at the underlying CXXRecordDecl of the - // ClassTemplateDecl, ND will be the CXXRecordDecl. In this case, we need to - // get the primary template manually. - // getUSRsForDeclaration will find other related symbols, e.g. virtual and its - // overriddens, primary template and all explicit specializations. - // FIXME: Get rid of the remaining tooling APIs. - const auto *RenameDecl = - ND.getDescribedTemplate() ? ND.getDescribedTemplate() : &ND; - std::vector RenameUSRs = - tooling::getUSRsForDeclaration(RenameDecl, AST.getASTContext()); - llvm::DenseSet TargetIDs; - for (auto &USR : RenameUSRs) - TargetIDs.insert(SymbolID(USR)); + assert(canonicalRenameDecl(&ND) == &ND && + "ND should be already canonicalized."); std::vector Results; for (Decl *TopLevelDecl : AST.getLocalTopLevelDecls()) { @@ -246,11 +297,11 @@ if (Ref.Targets.empty()) return; for (const auto *Target : Ref.Targets) { - auto ID = getSymbolID(Target); - if (!ID || TargetIDs.find(ID) == TargetIDs.end()) + if (canonicalRenameDecl(Target) == &ND) { + Results.push_back(Ref.NameLoc); return; + } } - Results.push_back(Ref.NameLoc); }); } diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -141,6 +141,12 @@ ~[[F^oo]](); void f([[F^oo]] x); }; + + template + [[F^oo]]::[[Fo^o]]() {} + + template + [[F^oo]]::~[[Fo^o]]() {} )cpp", // Template class constructor. @@ -152,6 +158,9 @@ template [[F^oo]](T t); }; + + template + [[F^oo]]::[[Fo^o]]() {} )cpp", // Class in template argument. @@ -191,11 +200,15 @@ struct C : B { void [[f^oo]]() override {} }; + struct D : B { + void [[f^oo]]() override {} + }; void func() { A().[[f^oo]](); B().[[f^oo]](); C().[[f^oo]](); + D().[[f^oo]](); } )cpp", @@ -588,6 +601,123 @@ ns::[[Old^Alias]] Bar; } )cpp", + + // Templated method instantiation. + R"cpp( + template + class Foo { + public: + static T [[f^oo]]() {} + }; + + void bar() { + Foo::[[f^oo]](); + } + )cpp", + R"cpp( + template + class Foo { + public: + T [[f^oo]]() {} + }; + + void bar() { + Foo().[[f^oo]](); + } + )cpp", + + // Templated class specialization. + R"cpp( + template + class [[Foo^]]; + + template + class [[Foo^]] {}; + + template + class [[Foo^]]; + )cpp", + R"cpp( + template + class [[Foo^]]; + + template + class [[Foo^]] {}; + )cpp", + + // Function template specialization. + R"cpp( + template + U [[foo^]](); + + template + U [[foo^]]() {}; + )cpp", + R"cpp( + template + U [[foo^]]() {}; + + template + U [[foo^]](); + )cpp", + R"cpp( + template + U [[foo^]](); + + template + U [[foo^]](); + )cpp", + + R"cpp( + template + bool [[F^oo]] = true; + + // Explicit template specialization + template <> + bool [[F^oo]] = false; + + // Partial template specialization + template + bool [[F^oo]] = false; + + void foo() { + // Ref to the explicit template specialization + [[F^oo]]; + // Ref to the primary template. + [[F^oo]]; + } + )cpp", + R"cpp( + template + void [[f^oo]](T t); + + template <> + void [[f^oo]](int a); + + void test() { + [[f^oo]](1); + } + )cpp", + + // User defined conversion. + R"cpp( + class [[F^oo]] { + public: + [[F^oo]]() {} + }; + + class Baz { + public: + operator [[F^oo]]() { + return [[F^oo]](); + } + }; + + int main() { + Baz boo; + [[F^oo]] foo = static_cast<[[F^oo]]>(boo); + } + )cpp", }; llvm::StringRef NewName = "NewName"; for (llvm::StringRef T : Tests) {