diff --git a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp @@ -226,6 +226,80 @@ return QualifiedFunc->substr(BodyBegin, BodyEnd - BodyBegin + 1); } +/// Returns true if Dest is templated function. Fills in \p ParamToNewName with +/// a mapping from template decls in \p Dest to theire respective names in \p +/// Source. +bool fillTemplateParameterMapping( + const FunctionDecl *Dest, const FunctionDecl *Source, + llvm::DenseMap &ParamToNewName) { + auto *DestTempl = Dest->getDescribedFunctionTemplate(); + auto *SourceTempl = Source->getDescribedFunctionTemplate(); + assert(bool(DestTempl) == bool(SourceTempl)); + if (!DestTempl) + return false; + const auto *DestTPL = DestTempl->getTemplateParameters(); + const auto *SourceTPL = SourceTempl->getTemplateParameters(); + assert(DestTPL->size() == SourceTPL->size()); + + for (size_t I = 0, EP = DestTPL->size(); I != EP; ++I) + ParamToNewName[DestTPL->getParam(I)->getCanonicalDecl()] = + SourceTPL->getParam(I)->getName(); + return true; +} + +/// Generates Replacements for changing template and function parameter names in +/// \p Dest to be the same as in \p Source. +llvm::Expected +renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source) { + llvm::DenseMap ParamToNewName; + bool HasTemplateParams = + fillTemplateParameterMapping(Dest, Source, ParamToNewName); + tooling::Replacements Replacements; + + // Populate mapping for function params. + for (size_t I = 0, E = Dest->param_size(); I != E; ++I) { + auto *DestParam = Dest->getParamDecl(I); + auto *SourceParam = Source->getParamDecl(I); + ParamToNewName[DestParam] = SourceParam->getName(); + } + + llvm::Error RenamingErrors = llvm::Error::success(); + const SourceManager &SM = Dest->getASTContext().getSourceManager(); + findExplicitReferences( + // Use function template in case of templated functions to visit template + // parameters. + HasTemplateParams + ? llvm::dyn_cast(Dest->getDescribedFunctionTemplate()) + : llvm::dyn_cast(Dest), + [&](ReferenceLoc Ref) { + if (Ref.Targets.size() != 1) + return; + const auto *Target = + llvm::dyn_cast(Ref.Targets.front()->getCanonicalDecl()); + auto It = ParamToNewName.find(Target); + if (It == ParamToNewName.end()) + return; + const size_t OldNameLen = Target->getName().size(); + // If decl is unnamed in both source and destination, just skip. + if (OldNameLen == 0 && It->second.size() == 0) + return; + // If decl is unnamed in destination we pad the new name to avoid gluing + // with previous token, e.g. foo(int^) shouldn't turn into foo(intx). + auto ReplacementText = (OldNameLen == 0 ? " " : "") + It->second; + if (auto Err = Replacements.add(tooling::Replacement( + SM, Ref.NameLoc, OldNameLen, ReplacementText))) { + elog("define inline: Couldn't replace parameter name for {0} to {1}: " + "{2}", + Target->getName(), ReplacementText, Err); + RenamingErrors = + llvm::joinErrors(std::move(RenamingErrors), std::move(Err)); + } + }); + if (RenamingErrors) + return std::move(RenamingErrors); + return Replacements; +} + // Returns the canonical declaration for the given FunctionDecl. This will // usually be the first declaration in current translation unit with the // exception of template specialization. @@ -332,6 +406,10 @@ "Couldn't find semicolon for target declaration."); } + auto ParamReplacements = renameParameters(Target, Source); + if (!ParamReplacements) + return ParamReplacements.takeError(); + auto QualifiedBody = qualifyAllDecls(Source); if (!QualifiedBody) return QualifiedBody.takeError(); @@ -354,8 +432,9 @@ llvm::SmallVector, 2> Edits; // Edit for Target. - auto FE = Effect::fileEdit(SM, SM.getFileID(*Semicolon), - tooling::Replacements(SemicolonToFuncBody)); + auto FE = Effect::fileEdit( + SM, SM.getFileID(*Semicolon), + tooling::Replacements(SemicolonToFuncBody).merge(*ParamReplacements)); if (!FE) return FE.takeError(); Edits.push_back(std::move(*FE)); diff --git a/clang-tools-extra/clangd/unittests/TweakTests.cpp b/clang-tools-extra/clangd/unittests/TweakTests.cpp --- a/clang-tools-extra/clangd/unittests/TweakTests.cpp +++ b/clang-tools-extra/clangd/unittests/TweakTests.cpp @@ -1469,6 +1469,44 @@ )cpp"); } +TEST_F(DefineInlineTest, TransformParamNames) { + EXPECT_EQ(apply(R"cpp( + void foo(int, bool b); + + void ^foo(int f, bool x) {})cpp"), R"cpp( + void foo(int f, bool x){} + + )cpp"); +} + +TEST_F(DefineInlineTest, TransformTemplParamNames) { + EXPECT_EQ(apply(R"cpp( + struct Foo { + struct Bar { + template class, template class Y, + int, int Z> + void foo(X, Y, int W = 5 * Z + 2); + }; + }; + + template class V, template class W, + int X, int Y> + void Foo::Bar::f^oo(U, W, int Q) {})cpp"), + R"cpp( + struct Foo { + struct Bar { + template class V, template class W, + int X, int Y> + void foo(U, W, int Q = 5 * Y + 2){} + }; + }; + + )cpp"); +} + TEST_F(DefineInlineTest, TransformInlineNamespaces) { EXPECT_EQ(apply(R"cpp( namespace a { inline namespace b { namespace { struct Foo{}; } } }