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 @@ -100,7 +100,8 @@ // // 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) { +const NamedDecl *canonicalRenameDecl(const NamedDecl *D, + bool CanonicalizeCtorToType = true) { if (const auto *VarTemplate = dyn_cast(D)) return canonicalRenameDecl( VarTemplate->getSpecializedTemplate()->getTemplatedDecl()); @@ -113,9 +114,17 @@ ClassTemplateSpecialization->getSpecializedTemplate() ->getTemplatedDecl()); if (const auto *Method = dyn_cast(D)) { - if (Method->getDeclKind() == Decl::Kind::CXXConstructor || + // By default ctors and dtors are canonicalized to the returned type but + // when CanonicalizeCtorToType is not set, we should still retrieve the + // canonical method. + if ((Method->getDeclKind() == Decl::Kind::CXXConstructor && + CanonicalizeCtorToType) || Method->getDeclKind() == Decl::Kind::CXXDestructor) return canonicalRenameDecl(Method->getParent()); + if (const FunctionTemplateDecl *Template = Method->getPrimaryTemplate()) + if (const auto *TemplatedDecl = + dyn_cast(Template->getTemplatedDecl())) + Method = TemplatedDecl; if (const FunctionDecl *InstantiatedMethod = Method->getInstantiatedFromMemberFunction()) Method = cast(InstantiatedMethod); @@ -151,6 +160,19 @@ if (const auto *VD = dyn_cast(D)) { if (const VarDecl *OriginalVD = VD->getInstantiatedFromStaticDataMember()) VD = OriginalVD; + // Arguments are canonicalized to their canonical function's arguments. + if (const auto *Argument = dyn_cast(VD)) { + if (const auto *Function = + dyn_cast(Argument->getDeclContext())) { + const auto *Canonical = dyn_cast( + canonicalRenameDecl(Function, /*CanonicalizeCtorToType=*/false)); + assert(Canonical && + "canonicalRenameDecl should return the declaration " + "of the same type with CanonicalizeCtorToType=true."); + return Canonical->getParamDecl(Argument->getFunctionScopeIndex()) + ->getCanonicalDecl(); + } + } return VD->getCanonicalDecl(); } return dyn_cast(D->getCanonicalDecl()); @@ -421,11 +443,18 @@ for (const auto *Parameter : EnclosingFunction->parameters()) if (Parameter != &RenamedDecl && Parameter->getName() == NewName) return Parameter; - // FIXME: We don't modify all references to function parameters when - // renaming from forward declaration now, so using a name colliding with - // something in the definition's body is a valid transformation. - if (!EnclosingFunction->doesThisDeclarationHaveABody()) - return nullptr; + EnclosingFunction = dyn_cast(canonicalRenameDecl( + EnclosingFunction, /*CanonicalizeCtorToType=*/false)); + assert(EnclosingFunction && + "canonicalRenameDecl should return the declaration " + "of the same type with CanonicalizeCtorToType=false."); + // Because we will be renaming parameter in all specializations, check + // their bodies for conflict, too. + if (const auto *Template = + EnclosingFunction->getDescribedFunctionTemplate()) + for (const auto *Spec : Template->specializations()) + if (const auto *Result = CheckCompoundStmt(Spec->getBody(), NewName)) + return Result; return CheckCompoundStmt(EnclosingFunction->getBody(), 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 @@ -678,6 +678,61 @@ } )cpp", + // Function argument. + R"cpp( + // Main.cpp + void foo(int [[Bar^]]); + + void foo(int [[Bar^]]) {} + + // Forward declarations somewhere else. + void foo(int [[Bar^]]); + void foo(int /*Bar*/); + )cpp", + + // Template function argument. + R"cpp( + template + void foo(T [[Bar^]]); + + template + void foo(T [[Bar^]]) {} + )cpp", + R"cpp( + template + void foo(T [[Bar^]]); + + template + void foo(T [[Bar^]]) {} + + template <> + void foo(int [[Bar^]]) {} + )cpp", + + // Constructor argument. + R"cpp( + class Foo { + Foo(int [[Bar^]]); + }; + Foo::Foo(int [[Bar^]]) {} + )cpp", + R"cpp( + class Foo { + template + Foo(T Something, U [[Bar^]]); + + Foo(); + }; + + template + Foo::Foo(T Something, U [[Bar^]]) {} + + template <> + Foo::Foo(int Something, int [[Bar^]]) {} + template <> + Foo::Foo(bool Something, bool [[Bar^]]) {} + )cpp", + // Namespace alias. R"cpp( namespace a { namespace b { void foo(); } } @@ -1091,14 +1146,21 @@ )cpp", "conflict", !HeaderFile, nullptr, "Conflict"}, - {R"cpp(// No conflict: only forward declaration's argument is renamed. - void func(int [[V^ar]]); + {R"cpp( + template + void func(T Var); + + template <> + void func(bool V^ar) {} + template <> void func(int Var) { - bool Conflict; + // We are renaming from the primary template but the parameter name + // will also be changed here introducing a new conflict. + int Conflict; } )cpp", - nullptr, !HeaderFile, nullptr, "Conflict"}, + "conflict", !HeaderFile, nullptr, "Conflict"}, {R"cpp( void func(int V^ar, int Conflict) {