Author: Kirill Bobyrev
Date: 2019-12-12T13:10:59+01:00
New Revision: ec618826dfb91c5413353ebcc54f360e43df10a0

URL: 
https://github.com/llvm/llvm-project/commit/ec618826dfb91c5413353ebcc54f360e43df10a0
DIFF: 
https://github.com/llvm/llvm-project/commit/ec618826dfb91c5413353ebcc54f360e43df10a0.diff

LOG: [clangd] Rename constructors and destructors in cross-file case

* Use ad-hoc Decl canonicalization from Clang-Rename to allow renaming
  constructors and destructors while using cross-file rename.
* Manually handle the destructor selection
* Add unit tests to prevent regressions and ensure the correct behaviour

Reviewed by: sammccall

Differential Revision: https://reviews.llvm.org/D71247

Added: 
    

Modified: 
    clang-tools-extra/clangd/refactor/Rename.cpp
    clang-tools-extra/clangd/unittests/RenameTests.cpp

Removed: 
    


################################################################################
diff  --git a/clang-tools-extra/clangd/refactor/Rename.cpp 
b/clang-tools-extra/clangd/refactor/Rename.cpp
index 8ca90afba69a..010f7e388b55 100644
--- a/clang-tools-extra/clangd/refactor/Rename.cpp
+++ b/clang-tools-extra/clangd/refactor/Rename.cpp
@@ -18,8 +18,10 @@
 #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/Casting.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FormatVariadic.h"
 #include <algorithm>
@@ -83,21 +85,17 @@ llvm::DenseSet<const Decl *> locateDeclAt(ParsedAST &AST,
   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<Decl>()) {
-    if (D->getLocation() != TokenStartLoc)
-      return {};
-  }
-
   llvm::DenseSet<const Decl *> Result;
   for (const auto *D :
        targetDecl(SelectedNode->ASTNode,
-                  DeclRelation::Alias | DeclRelation::TemplatePattern))
-    Result.insert(D);
+                  DeclRelation::Alias | DeclRelation::TemplatePattern)) {
+    const auto *ND = llvm::dyn_cast<NamedDecl>(D);
+    if (!ND)
+      continue;
+    // Get to CXXRecordDecl from constructor or destructor.
+    ND = tooling::getCanonicalSymbolDeclaration(ND);
+    Result.insert(ND);
+  }
   return Result;
 }
 
@@ -214,17 +212,16 @@ llvm::Error makeError(ReasonToReject Reason) {
 // Return all rename occurrences in the main file.
 std::vector<SourceLocation> 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;
+  // 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.
   // 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<std::string> RenameUSRs = tooling::getUSRsForDeclaration(
-      tooling::getCanonicalSymbolDeclaration(&RenameDecl), 
AST.getASTContext());
+  const auto RenameDecl =
+      ND.getDescribedTemplate() ? ND.getDescribedTemplate() : &ND;
+  std::vector<std::string> RenameUSRs =
+      tooling::getUSRsForDeclaration(RenameDecl, AST.getASTContext());
   llvm::DenseSet<SymbolID> TargetIDs;
   for (auto &USR : RenameUSRs)
     TargetIDs.insert(SymbolID(USR));
@@ -455,14 +452,21 @@ llvm::Expected<FileEdits> rename(const RenameInputs 
&RInputs) {
 
     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)

diff  --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp 
b/clang-tools-extra/clangd/unittests/RenameTests.cpp
index f253625ed032..e98bf78323b9 100644
--- a/clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -487,11 +487,10 @@ TEST(RenameTest, Renameable) {
           "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() {}
@@ -720,24 +719,24 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
   MockCompilationDatabase CDB;
   CDB.ExtraClangFlags = {"-xc++"};
   class IgnoreDiagnostics : public DiagnosticsConsumer {
-  void onDiagnosticsReady(PathRef File,
-                          std::vector<Diag> Diagnostics) override {}
+    void onDiagnosticsReady(PathRef File,
+                            std::vector<Diag> Diagnostics) override {}
   } DiagConsumer;
   // rename is runnning on the "^" point in FooH, and "[[]]" ranges are the
-  // expcted rename occurrences.
+  // expected rename occurrences.
   struct Case {
     llvm::StringRef FooH;
     llvm::StringRef FooCC;
-  } Cases [] = {
-    {
-      // classes.
-      R"cpp(
+  } Cases[] = {
+      {
+          // classes.
+          R"cpp(
         class [[Fo^o]] {
           [[Foo]]();
           ~[[Foo]]();
         };
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         [[Foo]]::[[Foo]]() {}
         [[Foo]]::~[[Foo]]() {}
@@ -746,15 +745,15 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
           [[Foo]] foo;
         }
       )cpp",
-    },
-    {
-      // class methods.
-      R"cpp(
+      },
+      {
+          // class methods.
+          R"cpp(
         class Foo {
           void [[f^oo]]();
         };
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         void Foo::[[foo]]() {}
 
@@ -762,13 +761,49 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
           p->[[foo]]();
         }
       )cpp",
-    },
-    {
-      // functions.
-      R"cpp(
+      },
+      {
+          // 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",
+      },
+      {
+          // functions.
+          R"cpp(
         void [[f^oo]]();
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         void [[foo]]() {}
 
@@ -776,63 +811,63 @@ TEST(CrossFileRenameTests, WithUpToDateIndex) {
           [[foo]]();
         }
       )cpp",
-    },
-    {
-      // typedefs.
-      R"cpp(
+      },
+      {
+          // typedefs.
+          R"cpp(
       typedef int [[IN^T]];
       [[INT]] foo();
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         [[INT]] foo() {}
       )cpp",
-    },
-    {
-      // usings.
-      R"cpp(
+      },
+      {
+          // usings.
+          R"cpp(
       using [[I^NT]] = int;
       [[INT]] foo();
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         [[INT]] foo() {}
       )cpp",
-    },
-    {
-      // variables.
-      R"cpp(
+      },
+      {
+          // variables.
+          R"cpp(
       static const int [[VA^R]] = 123;
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         int s = [[VAR]];
       )cpp",
-    },
-    {
-      // scope enums.
-      R"cpp(
+      },
+      {
+          // scope enums.
+          R"cpp(
       enum class [[K^ind]] { ABC };
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         [[Kind]] ff() {
           return [[Kind]]::ABC;
         }
       )cpp",
-    },
-    {
-      // enum constants.
-      R"cpp(
+      },
+      {
+          // enum constants.
+          R"cpp(
       enum class Kind { [[A^BC]] };
       )cpp",
-      R"cpp(
+          R"cpp(
         #include "foo.h"
         Kind ff() {
           return Kind::[[ABC]];
         }
       )cpp",
-    },
+      },
   };
 
   for (const auto& T : Cases) {


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to