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,7 +18,6 @@ #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" @@ -76,6 +75,8 @@ return OtherFile; } +const NamedDecl *canonicalRenameDecl(const NamedDecl *D); + llvm::DenseSet locateDeclAt(ParsedAST &AST, SourceLocation TokenStartLoc) { unsigned Offset = @@ -91,9 +92,7 @@ for (const NamedDecl *D : 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 +221,65 @@ return error("Cannot rename symbol: {0}", Message(Reason)); } +const NamedDecl *canonicalRenameDecl(const TemplateDecl *D) { + return D->getTemplatedDecl(); +} + +const NamedDecl *canonicalRenameDecl(const CXXMethodDecl *D) { + const CXXMethodDecl *Result = D; + if (const FunctionDecl *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 FunctionTemplateDecl *Template = D->getPrimaryTemplate()) + return canonicalRenameDecl(Template); + return D; +} + +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 *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 +287,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); }); } @@ -557,8 +598,7 @@ return makeError(ReasonToReject::NoSymbolFound); if (DeclsUnderCursor.size() > 1) return makeError(ReasonToReject::AmbiguousSymbol); - const auto &RenameDecl = - llvm::cast(*(*DeclsUnderCursor.begin())->getCanonicalDecl()); + const auto &RenameDecl = llvm::cast(*(*DeclsUnderCursor.begin())); if (RenameDecl.getName() == RInputs.NewName) return makeError(ReasonToReject::SameName); auto Invalid = checkName(RenameDecl, RInputs.NewName); 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 @@ -94,6 +94,15 @@ // For each "^" this test moves cursor to its location and applies renaming // while checking that all identifiers in [[]] ranges are also renamed. llvm::StringRef Tests[] = { + // Example. + R"cpp( + template + void [[f^oo]](); + + template<> + void [[f^oo]](); + )cpp", + // Function. R"cpp( void [[foo^]]() {