kadircet updated this revision to Diff 226655.
kadircet marked 2 inline comments as done.
kadircet added a comment.
- Address comments


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D68937/new/

https://reviews.llvm.org/D68937

Files:
  clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
  clang-tools-extra/clangd/unittests/TweakTests.cpp

Index: clang-tools-extra/clangd/unittests/TweakTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TweakTests.cpp
+++ clang-tools-extra/clangd/unittests/TweakTests.cpp
@@ -1489,6 +1489,45 @@
   EXPECT_EQ(apply(Test), Expected);
 }
 
+TEST_F(DefineInlineTest, TransformParamNames) {
+  EXPECT_EQ(apply(R"cpp(
+    void foo(int, bool b, int T\
+est);
+
+    void ^foo(int f, bool x, int z) {})cpp"), R"cpp(
+    void foo(int f, bool x, int z){}
+
+    )cpp");
+}
+
+TEST_F(DefineInlineTest, TransformTemplParamNames) {
+  EXPECT_EQ(apply(R"cpp(
+    struct Foo {
+      struct Bar {
+        template <class, class X,
+                  template<typename> class, template<typename> class Y,
+                  int, int Z>
+        void foo(X, Y<X>, int W = 5 * Z + 2);
+      };
+    };
+
+    template <class T, class U,
+              template<typename> class V, template<typename> class W,
+              int X, int Y>
+    void Foo::Bar::f^oo(U, W<U>, int Q) {})cpp"),
+            R"cpp(
+    struct Foo {
+      struct Bar {
+        template <class T, class U,
+                  template<typename> class V, template<typename> class W,
+                  int X, int Y>
+        void foo(U, W<U>, int Q = 5 * Y + 2){}
+      };
+    };
+
+    )cpp");
+}
+
 TEST_F(DefineInlineTest, TransformInlineNamespaces) {
   auto Test = R"cpp(
     namespace a { inline namespace b { namespace { struct Foo{}; } } }
Index: clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/DefineInline.cpp
@@ -226,6 +226,92 @@
   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<const Decl *, std::string> &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<tooling::Replacements>
+renameParameters(const FunctionDecl *Dest, const FunctionDecl *Source) {
+  llvm::DenseMap<const Decl *, std::string> 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();
+  const LangOptions &LangOpts = Dest->getASTContext().getLangOpts();
+  findExplicitReferences(
+      // Use function template in case of templated functions to visit template
+      // parameters.
+      HasTemplateParams
+          ? llvm::dyn_cast<Decl>(Dest->getDescribedFunctionTemplate())
+          : llvm::dyn_cast<Decl>(Dest),
+      [&](ReferenceLoc Ref) {
+        if (Ref.Targets.size() != 1)
+          return;
+        const auto *Target =
+            llvm::cast<NamedDecl>(Ref.Targets.front()->getCanonicalDecl());
+        auto It = ParamToNewName.find(Target);
+        if (It == ParamToNewName.end())
+          return;
+        // No need to rename if parameters already have the same name.
+        if (Target->getName() == It->second)
+          return;
+        // In case of an empty identifier, we can be sure about zero length, but
+        // we need to lex to get token length otherwise because of multi-line
+        // identifiers, e.g. int T\
+        // est
+        // We don't want to lex in the case of empty names, since the NameLoc
+        // will still likely have some non-identifier token underneath, and it
+        // will have some length, e.g. void foo(int, char c); for the first
+        // parameter name will point at ','.
+        const size_t OldNameLen =
+            Target->getName().empty()
+                ? 0
+                : Lexer::MeasureTokenLength(Ref.NameLoc, SM, LangOpts);
+        // 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 +418,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 +444,9 @@
 
     llvm::SmallVector<std::pair<std::string, Edit>, 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));
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to