From ac21938fbdfa75f1eb4ca399dac4fbf3e90d472d Mon Sep 17 00:00:00 2001 From: Tom Praschan <13141438+tom-anders@users.noreply.github.com> Date: Sat, 8 Oct 2022 01:24:13 +0200 Subject: [PATCH] [clangd] Fix rename for symbol introduced by UsingDecl Fixes https://github.com/clangd/clangd/issues/170 This patch actually consists of 2 fixes: 1) Add handling for UsingShadowDecl to canonicalRenameDecl(). This fixes the issue described in https://github.com/clangd/clangd/issues/170. 2) Avoid the "there are multiple symbols under the cursor error" by applying similar logic as in https://reviews.llvm.org/D133664. This also partly fixes https://github.com/clangd/clangd/issues/586. Differential Revision: https://reviews.llvm.org/D135489 --- clang-tools-extra/clangd/refactor/Rename.cpp | 21 +++++++++++++++ clang-tools-extra/clangd/unittests/RenameTests.cpp | 31 ++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/clang-tools-extra/clangd/refactor/Rename.cpp b/clang-tools-extra/clangd/refactor/Rename.cpp index 22c72d6..daddd02 100644 --- a/clang-tools-extra/clangd/refactor/Rename.cpp +++ b/clang-tools-extra/clangd/refactor/Rename.cpp @@ -133,6 +133,10 @@ const NamedDecl *canonicalRenameDecl(const NamedDecl *D) { if (const VarDecl *OriginalVD = VD->getInstantiatedFromStaticDataMember()) return canonicalRenameDecl(OriginalVD); } + if (const auto *UD = dyn_cast(D)) { + if (const auto *TargetDecl = UD->getTargetDecl()) + return canonicalRenameDecl(TargetDecl); + } return dyn_cast(D->getCanonicalDecl()); } @@ -157,6 +161,22 @@ llvm::DenseSet locateDeclAt(ParsedAST &AST, return Result; } +void filterRenameTargets(llvm::DenseSet &Decls) { + // For something like + // namespace ns { void foo(); } + // void bar() { using ns::f^oo; foo(); } + // locateDeclAt() will return a UsingDecl and foo's actual declaration. + // For renaming, we're only interested in foo's declaration, so drop the other + // one. There should never be more than one UsingDecl here, otherwise the + // rename would be ambiguos anyway. + auto UD = std::find_if(Decls.begin(), Decls.end(), [](const NamedDecl *D) { + return llvm::isa(D); + }); + if (UD != Decls.end()) { + Decls.erase(UD); + } +} + // By default, we exclude symbols from system headers and protobuf symbols as // renaming these symbols would change system/generated files which are unlikely // to be good candidates for modification. @@ -737,6 +757,7 @@ llvm::Expected rename(const RenameInputs &RInputs) { return makeError(ReasonToReject::UnsupportedSymbol); auto DeclsUnderCursor = locateDeclAt(AST, IdentifierToken->location()); + filterRenameTargets(DeclsUnderCursor); if (DeclsUnderCursor.empty()) return makeError(ReasonToReject::NoSymbolFound); if (DeclsUnderCursor.size() > 1) diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp index 00104ff..39232a5 100644 --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -817,6 +817,29 @@ TEST(RenameTest, WithinFileRename) { char [[da^ta]]; } @end )cpp", + + // Issue 170: Rename symbol introduced by UsingDecl + R"cpp( + namespace ns { void [[f^oo]](); } + + using ns::[[f^oo]]; + + void f() { + [[f^oo]](); + auto p = &[[f^oo]]; + } + )cpp", + + // Issue 170: using decl that imports multiple overloads + // -> Only the overload under the cursor is renamed + R"cpp( + namespace ns { int [[^foo]](int); char foo(char); } + using ns::[[foo]]; + void f() { + [[^foo]](42); + foo('x'); + } + )cpp", }; llvm::StringRef NewName = "NewName"; for (llvm::StringRef T : Tests) { @@ -1062,6 +1085,14 @@ TEST(RenameTest, Renameable) { }; )cpp", "no symbol", false}, + + {R"cpp(// FIXME we probably want to rename both overloads here, + // but renaming currently assumes there's only a + // single canonical declaration. + namespace ns { int foo(int); char foo(char); } + using ns::^foo; + )cpp", + "there are multiple symbols at the given location", !HeaderFile}, }; for (const auto& Case : Cases) { -- 2.7.4