kbobyrev updated this revision to Diff 303940.
kbobyrev added a comment.

Finish fields canonicalization.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D71880

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

Index: clang-tools-extra/clangd/unittests/RenameTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/RenameTests.cpp
+++ clang-tools-extra/clangd/unittests/RenameTests.cpp
@@ -91,10 +91,86 @@
 }
 
 TEST(RenameTest, WithinFileRename) {
-  // rename is runnning on all "^" points, and "[[]]" ranges point to the
-  // identifier that is being renamed.
+  // For each "^" this test moves cursor to its location and applies renaming
+  // while checking that all identifiers enclosed in [[]] ranges are handled
+  // correctly.
   llvm::StringRef Tests[] = {
-      // Function.
+      // Templated static method instantiation.
+      R"cpp(
+        template<typename T>
+        class Foo {
+        public:
+          static T [[f^oo]]() {}
+        };
+
+        void bar() {
+          Foo<int>::[[f^oo]]();
+        }
+      )cpp",
+
+      // Templated method instantiation.
+      R"cpp(
+        template<typename T>
+        class Foo {
+        public:
+          T [[f^oo]]() {}
+        };
+
+        void bar() {
+          Foo<int>().[[f^oo]]();
+        }
+      )cpp",
+
+      // Class template (partial) specialization forward declarations.
+      R"cpp(
+        template<typename T, typename U=bool>
+        class [[Foo^]];
+
+        template<typename T, typename U>
+        class [[Foo^]] {};
+
+        template<typename T=int, typename U>
+        class [[Foo^]];
+      )cpp",
+
+      // Class template (full) specialization forward declaration.
+      R"cpp(
+        template<typename T=float, typename U=int>
+        class [[Foo^]];
+
+        template<typename T, typename U>
+        class [[Foo^]] {};
+      )cpp",
+
+      // Function template specialization forward declaration.
+      R"cpp(
+        template<typename T=int, typename U=bool>
+        U [[foo^]]();
+
+        template<typename T, typename U>
+        U [[foo^]]() {};
+      )cpp",
+
+      // Function template specialization forward declaration.
+      R"cpp(
+        template<typename T, typename U>
+        U [[foo^]]() {};
+
+        template<typename T=int, typename U=bool>
+        U [[foo^]]();
+      )cpp",
+
+      // Function template specialization forward declaration without function
+      // definition.
+      R"cpp(
+        template<typename T=int, typename U=bool>
+        U [[foo^]]();
+
+        template<typename T, typename U>
+        U [[foo^]]();
+      )cpp",
+
+      // Simple recursive function.
       R"cpp(
         void [[foo^]]() {
           [[fo^o]]();
@@ -104,7 +180,7 @@
       // Type.
       R"cpp(
         struct [[foo^]] {};
-        [[foo]] test() {
+        [[foo^]] test() {
            [[f^oo]] x;
            return x;
         }
@@ -114,20 +190,21 @@
       R"cpp(
         void bar() {
           if (auto [[^foo]] = 5) {
-            [[foo]] = 3;
+            [[fo^o]] = 3;
           }
         }
       )cpp",
 
-      // Rename class, including constructor/destructor.
+      // Class, its constructor and destructor.
       R"cpp(
         class [[F^oo]] {
+        public:
           [[F^oo]]();
-          ~[[Foo]]();
+          ~[[Fo^o]]();
           void foo(int x);
         };
-        [[Foo]]::[[Fo^o]]() {}
-        void [[Foo]]::foo(int x) {}
+        [[Fo^o]]::[[Fo^o]]() {}
+        void [[Fo^o]]::foo(int x) {}
       )cpp",
 
       // Rename template class, including constructor/destructor.
@@ -199,9 +276,9 @@
         class [[F^oo]]<T*> {};
 
         void test() {
-          [[Foo]]<int> x;
-          [[Foo]]<bool> y;
-          [[Foo]]<int*> z;
+          [[F^oo]]<int> x;
+          [[Fo^o]]<bool> y;
+          [[Foo^]]<int*> z;
         }
       )cpp",
 
@@ -360,8 +437,8 @@
         void boo(int);
 
         void qoo() {
-          [[foo]]();
-          boo([[foo]]());
+          [[f^oo]]();
+          boo([[fo^o]]());
           M1();
           boo(M1());
           M2([[foo]]());
@@ -454,7 +531,7 @@
         }
       )cpp",
 
-      // template class in template argument list.
+      // Template class in template argument list.
       R"cpp(
         template<typename T>
         class [[Fo^o]] {};
@@ -510,6 +587,263 @@
   }
 }
 
+TEST(RenameTest, Alias) {
+  // For each "^" this test moves cursor to its location and applies renaming
+  // while checking that all identifiers enclosed in [[]] ranges are handled
+  // correctly.
+  llvm::StringRef Tests[] = {
+      R"cpp(
+        class X {};
+        typedef X [[Fo^o]];
+      )cpp",
+
+      R"cpp(
+        class X {};
+        using [[U^Old]] = X;
+      )cpp",
+
+      R"cpp(
+        template <typename T>
+        class X { T t; };
+
+        template <typename T>
+        using [[O^ld]] = X<T>;
+      )cpp",
+
+      R"cpp(
+        namespace x { class X {}; }
+        namespace ns {
+        using [[F^oo]] = x::X;
+        }
+      )cpp",
+
+      R"cpp(
+        namespace x { class Old {}; }
+        namespace ns {
+        #define REF(alias) alias alias_var;
+
+        #define ALIAS(old) \
+          using old##Alias = x::old; \
+          REF(old##Alias);
+
+        ALIAS(Old);
+
+        [[Ol^dAlias]] old_alias;
+        }
+      )cpp",
+  };
+  for (llvm::StringRef T : Tests) {
+    SCOPED_TRACE(T);
+    Annotations Code(T);
+    auto TU = TestTU::withCode(Code.code());
+    TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
+    auto AST = TU.build();
+    llvm::StringRef NewName = "abcde";
+    for (const auto &RenamePos : Code.points()) {
+      auto RenameResult =
+          rename({RenamePos, NewName, AST, testPath(TU.Filename)});
+      ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
+      ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
+      EXPECT_EQ(
+          applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
+          expectedResult(Code, NewName));
+    }
+  }
+}
+
+TEST(RenameTest, Member) {
+  // For each "^" this test moves cursor to its location and applies renaming
+  // while checking that all identifiers enclosed in [[]] ranges are handled
+  // correctly.
+  llvm::StringRef Tests[] = {
+      R"cpp(
+        struct X {
+          int [[M^oo]];
+          void Baz() { [[M^oo]] = 1; }
+        };
+      )cpp",
+
+      R"cpp(
+        struct X {
+          void [[F^oo]]() {}
+          void Baz() { [[Foo^]](); }
+        };
+      )cpp",
+
+      R"cpp(
+        struct A {};
+        struct B {};
+        class X {
+        public:
+        X();
+        A [[a^]];
+        A a2;
+        B b;
+        };
+
+        X::X():[[a^]](), b() {}
+      )cpp",
+
+      R"cpp(
+        class Foo {
+        public:
+          Foo(int Variable) : [[Variabl^e]](Variable) {}
+
+          int [[Va^riable]] = 42;
+
+        private:
+          void foo() { ++[[Vari^able]]; }
+        };
+
+        void bar() {
+          Foo f(9000);
+          f.[[Variable^]] = -1;
+        }
+      )cpp",
+
+      R"cpp(
+        template<typename T>
+        struct Foo {
+          T [[Vari^able]] = 42;
+        };
+
+        void foo() {
+          Foo<int> f;
+          f.[[Varia^ble]] = 9000;
+        }
+      )cpp",
+
+      R"cpp(
+        template<typename T, typename U>
+        struct Foo {
+          T [[Variab^le]] = 42;
+          U Another;
+
+          void bar() {}
+        };
+
+        // Variables here are not related to the ones in the fully templated
+        // class.
+        template<typename T>
+        struct Foo<T, bool> {
+          T Variable;
+          void bar() { ++Variable; }
+        };
+        template<>
+        struct Foo<int, bool> {
+          int Variable;
+          void bar() { ++Variable; }
+        };
+
+        void foo() {
+          Foo<unsigned, char> f;
+          f.[[Varia^ble]] = 9000;
+        }
+      )cpp",
+
+      R"cpp(
+        template<typename T, typename U>
+        struct Foo {
+          T Variable[42];
+          U Another;
+
+          void bar() {}
+        };
+
+        template<typename T>
+        struct Foo<T, bool> {
+          T [[Var^iable]];
+          void bar() { ++[[Var^iable]]; }
+        };
+
+        void foo() {
+          Foo<unsigned, bool> f;
+          f.[[Var^iable]] = 9000;
+        }
+      )cpp",
+
+      R"cpp(
+        template<typename T, typename U>
+        struct Foo {
+          T Variable[42];
+          U Another;
+
+          void bar() {}
+        };
+
+        template<typename T>
+        struct Foo<T, bool> {
+          T Variable;
+          void bar() { ++Variable; }
+        };
+
+        template<>
+        struct Foo<unsigned, bool> {
+          unsigned [[Var^iable]];
+          void bar() { ++[[Var^iable]]; }
+        };
+
+        void foo() {
+          Foo<unsigned, bool> f;
+          f.[[Var^iable]] = 9000;
+        }
+      )cpp",
+  };
+  for (llvm::StringRef T : Tests) {
+    SCOPED_TRACE(T);
+    Annotations Code(T);
+    auto TU = TestTU::withCode(Code.code());
+    TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
+    auto AST = TU.build();
+    llvm::StringRef NewName = "abcde";
+    for (const auto &RenamePos : Code.points()) {
+      auto RenameResult =
+          rename({RenamePos, NewName, AST, testPath(TU.Filename)});
+      ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
+      ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
+      EXPECT_EQ(
+          applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
+          expectedResult(Code, NewName));
+    }
+  }
+}
+
+TEST(RenameTest, Enum) {
+  // For each "^" this test moves cursor to its location and applies renaming
+  // while checking that all identifiers enclosed in [[]] ranges are handled
+  // correctly.
+  llvm::StringRef Tests[] = {
+      R"cpp(
+        namespace ns {
+        enum [[Old^]] { Blue };
+        }
+      )cpp",
+
+      R"cpp(
+        namespace ns {
+        enum class [[Old^]] { Blue };
+        }
+      )cpp",
+  };
+  for (llvm::StringRef T : Tests) {
+    SCOPED_TRACE(T);
+    Annotations Code(T);
+    auto TU = TestTU::withCode(Code.code());
+    TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
+    auto AST = TU.build();
+    llvm::StringRef NewName = "abcde";
+    for (const auto &RenamePos : Code.points()) {
+      auto RenameResult =
+          rename({RenamePos, NewName, AST, testPath(TU.Filename)});
+      ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
+      ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
+      EXPECT_EQ(
+          applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
+          expectedResult(Code, NewName));
+    }
+  }
+}
+
 TEST(RenameTest, Renameable) {
   struct Case {
     const char *Code;
Index: clang-tools-extra/clangd/refactor/Rename.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/Rename.cpp
+++ clang-tools-extra/clangd/refactor/Rename.cpp
@@ -92,6 +92,7 @@
        targetDecl(SelectedNode->ASTNode,
                   DeclRelation::Alias | DeclRelation::TemplatePattern)) {
     // Get to CXXRecordDecl from constructor or destructor.
+    // FIXME(kirillbobyrev): Just use getRenameRoot?
     D = tooling::getCanonicalSymbolDeclaration(D);
     Result.insert(D);
   }
@@ -219,23 +220,80 @@
   return error("Cannot rename symbol: {0}", Message(Reason));
 }
 
+const Decl *getRenameRootDecl(const ClassTemplateSpecializationDecl *D) {
+  return D->getSpecializedTemplate()->getTemplatedDecl();
+}
+
+const Decl *getRenameRootDecl(const TemplateDecl *D) {
+  return D->getTemplatedDecl();
+}
+
+const Decl *getRenameRootDecl(const CXXMethodDecl *D) {
+  const auto *Result = D;
+  if (const auto *InstantiatedMethod = D->getInstantiatedFromMemberFunction())
+    Result = cast<CXXMethodDecl>(InstantiatedMethod);
+  while (Result->isVirtual() && Result->size_overridden_methods())
+    Result = *Result->overridden_methods().begin();
+  return Result;
+}
+
+const Decl *getRenameRootDecl(const FunctionDecl *D) {
+  const auto *Definition = D->getDefinition();
+  const auto *Candidate = Definition ? Definition : D->getMostRecentDecl();
+  return Candidate->isTemplateInstantiation()
+             ? Candidate->getPrimaryTemplate()->getTemplatedDecl()
+             : Candidate;
+}
+
+const Decl *getRenameRootDecl(const FieldDecl *D) {
+  // This is a hacky way to do something like
+  // CXXMethodDecl::getINstantiatedFromMemberFunction for the field because
+  // Clang AST does not store relevant information about the field that is
+  // instantiated.
+  const auto *TemplateSpec =
+      dyn_cast<ClassTemplateSpecializationDecl>(D->getParent());
+  if (!TemplateSpec)
+    return D;
+  const auto *FieldParent = TemplateSpec->getTemplateInstantiationPattern();
+  if (!FieldParent)
+    return D;
+  for (const auto *Field : FieldParent->fields())
+    if (Field->getFieldIndex() == D->getFieldIndex() &&
+        Field->getLocation() == D->getLocation())
+      return Field;
+  return D;
+}
+
+// FIXME(kirillbobyrev): Write documentation.
+const Decl *getRenameRootDecl(const Decl *D) {
+  const auto *Candidate = D;
+  if (const auto *RD = dyn_cast<RecordDecl>(D)) {
+    const auto *Definition = RD->getDefinition();
+    Candidate = Definition ? Definition : RD->getMostRecentDecl();
+  }
+  if (const auto *Template = dyn_cast<TemplateDecl>(Candidate))
+    return getRenameRootDecl(Template);
+  if (const auto *ClassTemplateSpecialization =
+          dyn_cast<ClassTemplateSpecializationDecl>(Candidate))
+    return getRenameRootDecl(ClassTemplateSpecialization);
+  if (const auto *Constructor = dyn_cast<CXXConstructorDecl>(Candidate))
+    return getRenameRootDecl(Constructor->getParent());
+  if (const auto *Destructor = dyn_cast<CXXDestructorDecl>(Candidate))
+    return getRenameRootDecl(Destructor->getParent());
+  if (const auto *Method = dyn_cast<CXXMethodDecl>(Candidate))
+    return getRenameRootDecl(Method);
+  if (const auto *Function = dyn_cast<FunctionDecl>(Candidate))
+    return getRenameRootDecl(Function);
+  if (const auto *Field = dyn_cast<FieldDecl>(Candidate))
+    return getRenameRootDecl(Field);
+  return Candidate;
+}
+
 // Return all rename occurrences in the main file.
 std::vector<SourceLocation> findOccurrencesWithinFile(ParsedAST &AST,
                                                       const NamedDecl &ND) {
   trace::Span Tracer("FindOccurrenceeWithinFile");
-  // 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 manually.
-  // 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.
-  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));
+  const auto *RenameDeclRoot = getRenameRootDecl(&ND);
 
   std::vector<SourceLocation> Results;
   for (Decl *TopLevelDecl : AST.getLocalTopLevelDecls()) {
@@ -243,11 +301,11 @@
       if (Ref.Targets.empty())
         return;
       for (const auto *Target : Ref.Targets) {
-        auto ID = getSymbolID(Target);
-        if (!ID || TargetIDs.find(ID) == TargetIDs.end())
+        if (getRenameRootDecl(Target) == RenameDeclRoot) {
+          Results.push_back(Ref.NameLoc);
           return;
+        }
       }
-      Results.push_back(Ref.NameLoc);
     });
   }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to