diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index 33a9c845beaa..d10a7a4574ba 100644 --- 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,58 @@ llvm::Optional getOtherRefFile(const Decl &D, StringRef MainFile, return OtherFile; } +// Canonical declarations help simplify the process of renaming. Examples: +// - Template's canonical decl is the templated declaration (i.e. +// ClassTemplateDecl is canonicalized to its child CXXRecordDecl, +// FunctionTemplateDecl - to child FunctionDecl) +// - Given a constructor/destructor, canonical declaration is the parent +// CXXRecordDecl because we want to rename both type name and its ctor/dtor. +// - All specializations are canonicalized to the primary template. For example: +// +// template +// bool Foo = true; (1) +// +// template +// bool Foo = true; (2) +// +// template <> +// bool Foo = true; (3) +// +// Here, both partial (2) and full (3) specializations are canonicalized to (1) +// which ensures all three of them are renamed. +const NamedDecl *canonicalRenameDecl(const NamedDecl *D) { + if (const auto *VarTemplate = dyn_cast(D)) + return canonicalRenameDecl( + VarTemplate->getSpecializedTemplate()->getTemplatedDecl()); + if (const auto *Template = dyn_cast(D)) + if (const NamedDecl *TemplatedDecl = Template->getTemplatedDecl()) + return canonicalRenameDecl(TemplatedDecl); + if (const auto *ClassTemplateSpecialization = + dyn_cast(D)) + return canonicalRenameDecl( + ClassTemplateSpecialization->getSpecializedTemplate() + ->getTemplatedDecl()); + if (const auto *Method = dyn_cast(D)) { + if (Method->getDeclKind() == Decl::Kind::CXXConstructor || + Method->getDeclKind() == Decl::Kind::CXXDestructor) + return canonicalRenameDecl(Method->getParent()); + if (const FunctionDecl *InstantiatedMethod = + Method->getInstantiatedFromMemberFunction()) + Method = cast(InstantiatedMethod); + // FIXME(kirillbobyrev): For virtual methods with + // size_overridden_methods() > 1, this will not rename all functions it + // overrides, because this code assumes there is a single canonical + // declaration. + while (Method->isVirtual() && Method->size_overridden_methods()) + Method = *Method->overridden_methods().begin(); + return dyn_cast(Method->getCanonicalDecl()); + } + if (const auto *Function = dyn_cast(D)) + if (const FunctionTemplateDecl *Template = Function->getPrimaryTemplate()) + return canonicalRenameDecl(Template); + return dyn_cast(D->getCanonicalDecl()); +} + llvm::DenseSet locateDeclAt(ParsedAST &AST, SourceLocation TokenStartLoc) { unsigned Offset = @@ -91,9 +142,7 @@ llvm::DenseSet locateDeclAt(ParsedAST &AST, 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; } @@ -226,19 +275,8 @@ llvm::Error makeError(ReasonToReject Reason) { 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 +284,11 @@ std::vector findOccurrencesWithinFile(ParsedAST &AST, 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 +595,7 @@ llvm::Expected rename(const RenameInputs &RInputs) { return makeError(ReasonToReject::NoSymbolFound); if (DeclsUnderCursor.size() > 1) return makeError(ReasonToReject::AmbiguousSymbol); - const auto &RenameDecl = - llvm::cast(*(*DeclsUnderCursor.begin())->getCanonicalDecl()); + const auto &RenameDecl = **DeclsUnderCursor.begin(); if (RenameDecl.getName() == RInputs.NewName) return makeError(ReasonToReject::SameName); auto Invalid = checkName(RenameDecl, RInputs.NewName);