Index: clang-tools-extra/clangd/refactor/Rename.cpp =================================================================== --- clang-tools-extra/clangd/refactor/Rename.cpp +++ clang-tools-extra/clangd/refactor/Rename.cpp @@ -18,6 +18,7 @@ #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/Error.h" @@ -83,21 +84,20 @@ if (!SelectedNode) return {}; - // If the location points to a Decl, we check it is actually on the name - // range of the Decl. This would avoid allowing rename on unrelated tokens. - // ^class Foo {} // SelectionTree returns CXXRecordDecl, - // // we don't attempt to trigger rename on this position. - // FIXME: Make this work on destructors, e.g. "~F^oo()". - if (const auto *D = SelectedNode->ASTNode.get()) { - if (D->getLocation() != TokenStartLoc) - return {}; - } - llvm::DenseSet Result; for (const auto *D : targetDecl(SelectedNode->ASTNode, - DeclRelation::Alias | DeclRelation::TemplatePattern)) - Result.insert(D); + DeclRelation::Alias | DeclRelation::TemplatePattern)) { + // 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 maunally. + // FIXME: Do proper and well-defined canonicalization. + D = D->getDescribedTemplate() ? D->getDescribedTemplate() : D; + const auto *ND = llvm::dyn_cast(D); + // Get to CXXRecordDecl from constructor or destructor. + ND = tooling::getCanonicalSymbolDeclaration(ND); + Result.insert(ND); + } return Result; } @@ -214,17 +214,8 @@ // Return all rename occurrences in the main file. std::vector findOccurrencesWithinFile(ParsedAST &AST, const NamedDecl &ND) { - // In theory, locateDeclAt should return the primary template. However, if the - // cursor is under the underlying CXXRecordDecl of the ClassTemplateDecl, ND - // will be the CXXRecordDecl, for this case, we need to get the primary - // template maunally. - const auto &RenameDecl = - ND.getDescribedTemplate() ? *ND.getDescribedTemplate() : ND; - // 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. - std::vector RenameUSRs = tooling::getUSRsForDeclaration( - tooling::getCanonicalSymbolDeclaration(&RenameDecl), AST.getASTContext()); + std::vector RenameUSRs = + tooling::getUSRsForDeclaration(&ND, AST.getASTContext()); llvm::DenseSet TargetIDs; for (auto &USR : RenameUSRs) TargetIDs.insert(SymbolID(USR)); @@ -455,14 +446,21 @@ return (*Content)->getBuffer().str(); }; - SourceLocation SourceLocationBeg = SM.getMacroArgExpandedLocation( - getBeginningOfIdentifier(RInputs.Pos, SM, AST.getLangOpts())); + // Try to find the tokens adjacent to the cursor position. + auto Loc = sourceLocationInMainFile(SM, RInputs.Pos); + if (!Loc) + return Loc.takeError(); + const syntax::Token *IdentifierToken = + spelledIdentifierTouching(*Loc, AST.getTokens()); + // Renames should only triggered on identifiers. + if (!IdentifierToken) + return makeError(ReasonToReject::NoSymbolFound); // FIXME: Renaming macros is not supported yet, the macro-handling code should // be moved to rename tooling library. - if (locateMacroAt(SourceLocationBeg, AST.getPreprocessor())) + if (locateMacroAt(IdentifierToken->location(), AST.getPreprocessor())) return makeError(ReasonToReject::UnsupportedSymbol); - auto DeclsUnderCursor = locateDeclAt(AST, SourceLocationBeg); + auto DeclsUnderCursor = locateDeclAt(AST, IdentifierToken->location()); if (DeclsUnderCursor.empty()) return makeError(ReasonToReject::NoSymbolFound); if (DeclsUnderCursor.size() > 1) Index: clang-tools-extra/clangd/unittests/RenameTests.cpp =================================================================== --- clang-tools-extra/clangd/unittests/RenameTests.cpp +++ clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -487,11 +487,10 @@ "not a supported kind", HeaderFile, Index}, { - R"cpp( struct X { X operator++(int); }; void f(X x) {x+^+;})cpp", - "not a supported kind", HeaderFile, Index}, + "no symbol", HeaderFile, Index}, {R"cpp(// foo is declared outside the file. void fo^o() {} @@ -728,7 +727,81 @@ struct Case { llvm::StringRef FooH; llvm::StringRef FooCC; - } Cases [] = { + } Cases [] = { + { + // Constructor. + R"cpp( + class [[Foo]] { + [[Foo^]](); + ~[[Foo]](); + }; + )cpp", + R"cpp( + #include "foo.h" + [[Foo]]::[[Foo]]() {} + [[Foo]]::~[[Foo]]() {} + + void func() { + [[Foo]] foo; + } + )cpp", + }, + { + // Destructor (selecting before the identifier). + R"cpp( + class [[Foo]] { + [[Foo]](); + ~[[^Foo]](); + }; + )cpp", + R"cpp( + #include "foo.h" + [[Foo]]::[[Foo]]() {} + [[Foo]]::~[[Foo]]() {} + + void func() { + [[Foo]] foo; + } + )cpp", + }, + { + // Destructor (selecting within the identifier). + R"cpp( + class [[Foo]] { + [[Foo]](); + ~[[F^oo]](); + }; + )cpp", + R"cpp( + #include "foo.h" + [[Foo]]::[[Foo]]() {} + [[Foo]]::~[[Foo]]() {} + + void func() { + [[Foo]] foo; + } + )cpp", + }, + { + // Destructor (selecting after the identifier). + R"cpp( + class [[Foo]] { + [[Foo]](); + virtual ~ /*~Foo?*/[[Foo^]]() { + int a = 4; + } + }; + )cpp", + R"cpp( + #include "foo.h" + [[Foo]]::[[Foo]]() {} + [[Foo]]::~[[Foo]]() {} + + void func() { + [[Foo]] foo; + } + )cpp", + }, { // classes. R"cpp(