kbobyrev created this revision.
kbobyrev added a reviewer: hokein.
Herald added subscribers: cfe-commits, usaxena95, arphaman.
Herald added a project: clang.
kbobyrev requested review of this revision.
Herald added subscribers: MaskRay, ilya-biryukov.

Tests can not be adopted 1:1 (e.g. Clang-Rename modifies namespace for some
symbols) and some tests appear to not be working due to our selection/other
factors. However, this helps us expose some unexpected bugs and prepares for
the decl canonicalization fix.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D91240

Files:
  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
@@ -608,6 +608,222 @@
   }
 }
 
+struct Case {
+  std::string Before;
+  std::string After;
+  std::string NewName;
+};
+
+// FIXME: Unfortunately, we can not have actual header + source file for
+// within-file rename (all references to renamed symbol must be within the
+// file). The workaround is to have the "header" finish with --END--\n and split
+// the "source" and "header" within the test. Is there a better way to do that?
+class ComplicatedRenameTest : public testing::Test,
+                              public testing::WithParamInterface<Case> {
+protected:
+  void appendToHeader(StringRef AdditionalCode) {
+    HeaderCode += AdditionalCode.str();
+  }
+
+  void runRenameOnCode(llvm::StringRef Before, llvm::StringRef After,
+                       llvm::StringRef NewName) {
+    SCOPED_TRACE(Before);
+    Annotations Code((HeaderCode + Before).str());
+    auto TU = TestTU::withCode(Code.code());
+    TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
+    auto AST = TU.build();
+    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());
+      const auto Result =
+          applyEdits(std::move(RenameResult->GlobalChanges)).front().second;
+      const std::string Delimiter = "---END---\n";
+      EXPECT_EQ(Result.substr(Result.find(Delimiter) + Delimiter.size()),
+                After);
+    }
+  }
+
+  std::string HeaderCode;
+};
+
+class RenameAliasTest : public ComplicatedRenameTest {
+public:
+  RenameAliasTest() {
+    appendToHeader(R"(
+        #define MACRO(x) x
+        namespace some_ns {
+        class A {
+         public:
+          void foo() {}
+          struct Nested {
+           enum NestedEnum {
+             E1, E2,
+           };
+          };
+        };
+        } // namespace some_ns
+        namespace a {
+        typedef some_ns::A TA;
+        using UA = some_ns::A;
+        } // namespace a
+        namespace b {
+        typedef some_ns::A TA;
+        using UA = some_ns::A;
+        }
+        template <typename T> class ptr {};
+        template <typename T>
+
+        using TPtr = ptr<int>;
+
+        // ---END---
+)");
+  }
+};
+
+INSTANTIATE_TEST_CASE_P(
+    RenameAliasTests, RenameAliasTest,
+    testing::ValuesIn(std::vector<Case>({
+        // basic functions
+        {"void f(a::[[T^A]] a1) {}", "void f(a::TB a1) {}", "TB"},
+        {"void f(a::[[U^A]] a1) {}", "void f(a::UB a1) {}", "UB"},
+        {"void f(a::[[T^A]]* a1) {}", "void f(a::TB* a1) {}", "TB"},
+        {"void f(a::[[T^A]]** a1) {}", "void f(a::TB** a1) {}", "TB"},
+        {"a::[[T^A]] f() { return a::[[T^A]](); }",
+         "a::TB f() { return a::TB(); }", "TB"},
+        {"a::[[T^A]] f() { return a::UA(); }", "a::TB f() { return a::UA(); }",
+         "TB"},
+        {"a::TA f() { return a::[[U^A]](); }", "a::TA f() { return a::UB(); }",
+         "UB"},
+        {"void f() { a::[[T^A]] a; }", "void f() { a::TB a; }", "TB"},
+        {"void f(const a::[[T^A]]& a1) {}", "void f(const a::TB& a1) {}", "TB"},
+        {"void f(const a::[[U^A]]& a1) {}", "void f(const a::UB& a1) {}", "UB"},
+        {"void f(const a::[[T^A]]* a1) {}", "void f(const a::TB* a1) {}", "TB"},
+        {"namespace a { void f([[T^A]] a1) {} }",
+         "namespace a { void f(TB a1) {} }", "TB"},
+        {"void f(MACRO(a::[[T^A]]) a1) {}", "void f(MACRO(a::TB) a1) {}", "TB"},
+        {"void f(MACRO(a::[[T^A]] a1)) {}", "void f(MACRO(a::TB a1)) {}", "TB"},
+
+        // use namespace and typedefs
+        {"struct S { using T = a::[[T^A]]; T a_; };",
+         "struct S { using T = a::TB; T a_; };", "TB"},
+        {"using T = a::[[T^A]]; T gA;", "using T = a::TB; T gA;", "TB"},
+        {"using T = a::[[U^A]]; T gA;", "using T = a::UB; T gA;", "UB"},
+        {"typedef a::[[T^A]] T; T gA;", "typedef a::TB T; T gA;", "TB"},
+        {"typedef a::[[U^A]] T; T gA;", "typedef a::UB T; T gA;", "UB"},
+        {"typedef MACRO(a::[[T^A]]) T; T gA;", "typedef MACRO(a::TB) T; T gA;",
+         "TB"},
+
+        // struct members and other oddities
+        {"struct S : public a::[[T^A]] {};", "struct S : public a::TB {};",
+         "TB"},
+        {"struct S : public a::[[U^A]] {};", "struct S : public a::UB {};",
+         "UB"},
+        {"struct F { void f(a::[[T^A]] a1) {} };",
+         "struct F { void f(a::TB a1) {} };", "TB"},
+        {"struct F { a::[[T^A]] a_; };", "struct F { a::TB a_; };", "TB"},
+        {"struct F { ptr<a::[[T^A]]> a_; };", "struct F { ptr<a::TB> a_; };",
+         "TB"},
+        {"struct F { ptr<a::[[U^A]]> a_; };", "struct F { ptr<a::UB> a_; };",
+         "UB"},
+
+        // types in nested name specifiers
+        {"void f() { a::[[T^A]]::Nested ne; }",
+         "void f() { a::TB::Nested ne; }", "TB"},
+        {"void f() { a::[[U^A]]::Nested ne; }",
+         "void f() { a::UB::Nested ne; }", "UB"},
+        {"void f() { a::[[T^A]]::Nested::NestedEnum e; }",
+         "void f() { a::TB::Nested::NestedEnum e; }", "TB"},
+        {"void f() { auto e = a::[[T^A]]::Nested::NestedEnum::E1; }",
+         "void f() { auto e = a::TB::Nested::NestedEnum::E1; }", "TB"},
+        {"void f() { auto e = a::[[T^A]]::Nested::E1; }",
+         "void f() { auto e = a::TB::Nested::E1; }", "TB"},
+
+        // templates
+        {"template <typename T> struct Foo { T t; }; void f() { "
+         "Foo<a::[[T^A]]> "
+         "foo; }",
+         "template <typename T> struct Foo { T t; }; void f() { Foo<a::TB> "
+         "foo; }",
+         "TB"},
+        {"template <typename T> struct Foo { a::[[T^A]] a; };",
+         "template <typename T> struct Foo { a::TB a; };", "TB"},
+        {"template <typename T> void f(T t) {} void g() { "
+         "f<a::[[T^A]]>(a::[[T^A]]()); }",
+         "template <typename T> void f(T t) {} void g() { f<a::TB>(a::TB()); }",
+         "TB"},
+        {"template <typename T> void f(T t) {} void g() { "
+         "f<a::[[U^A]]>(a::[[U^A]]()); }",
+         "template <typename T> void f(T t) {} void g() { f<a::UB>(a::UB()); }",
+         "UB"},
+        {"template <typename T> int f() { return 1; } template <> int "
+         "f<a::[[T^A]]>() { return 2; } int g() { return f<a::[[T^A]]>(); }",
+         "template <typename T> int f() { return 1; } template <> int "
+         "f<a::TB>() { return 2; } int g() { return f<a::TB>(); }",
+         "TB"},
+        {"struct Foo { template <typename T> T foo(); }; void g() { Foo f;  "
+         "auto a = f.template foo<a::[[T^A]]>(); }",
+         "struct Foo { template <typename T> T foo(); }; void g() { Foo f;  "
+         "auto a = f.template foo<a::TB>(); }",
+         "TB"},
+        {"struct Foo { template <typename T> T foo(); }; void g() { Foo f;  "
+         "auto a = f.template foo<a::[[U^A]]>(); }",
+         "struct Foo { template <typename T> T foo(); }; void g() { Foo f;  "
+         "auto a = f.template foo<a::UB>(); }",
+         "UB"},
+
+        // The following two templates are distilled from regressions found in
+        // unique_ptr<> and type_traits.h
+        {"template <typename T> struct outer { typedef T type; type Baz(); }; "
+         "outer<a::[[T^A]]> g_A;",
+         "template <typename T> struct outer { typedef T type; type Baz(); }; "
+         "outer<a::TB> g_A;",
+         "TB"},
+        {"template <typename T> struct nested { typedef T type; }; template "
+         "<typename T> struct outer { typename nested<T>::type Foo(); }; "
+         "outer<a::[[T^A]]> g_A;",
+         "template <typename T> struct nested { typedef T type; }; template "
+         "<typename T> struct outer { typename nested<T>::type Foo(); }; "
+         "outer<a::TB> g_A;",
+         "TB"},
+
+        // macros
+        {"#define FOO(T, t) T t\nvoid f() { FOO(a::[[T^A]], a1); "
+         "FOO(a::[[T^A]], "
+         "a2); }",
+         "#define FOO(T, t) T t\nvoid f() { FOO(a::TB, a1); FOO(a::TB, a2); }",
+         "TB"},
+        // FIXME: These will not work: can't select within the macro.
+        // {"#define FOO(n) a::[[T^A]] n\nvoid f() { FOO(a1); FOO(a2); }",
+        //  "#define FOO(n) a::TB n\nvoid f() { FOO(a1); FOO(a2); }", "TB"},
+        // {"#define FOO(n) a::[[U^A]] n\nvoid f() { FOO(a1); FOO(a2); }",
+        //  "#define FOO(n) a::UB n\nvoid f() { FOO(a1); FOO(a2); }", "UB"},
+
+        // Pointer to member functions
+        {"auto gA = &a::[[T^A]]::foo;", "auto gA = &a::TB::foo;", "TB"},
+        // FIXME: This does not work yet: using a::TA points to multiple
+        // symbols? Selecting at the first location doesn't work and selecting
+        // at the second one does not rename the first instance.
+        // {"using a::[[T^A]]; auto gA = &[[T^A]]::foo;",
+        //  "using a::TB; auto gA = &a::TB::foo;", "TB"},
+        {"typedef a::[[T^A]] T; auto gA = &T::foo;",
+         "typedef a::TB T; auto gA = &T::foo;", "TB"},
+        {"auto gA = &MACRO(a::[[T^A]])::foo;", "auto gA = &MACRO(a::TB)::foo;",
+         "TB"},
+
+        // templated using alias.
+        {"void f([[T^Ptr]]<int> p) {}", "void f(NewTPtr<int> p) {}", "NewTPtr"},
+        {"void f(::[[T^Ptr]]<int> p) {}", "void f(::NewTPtr<int> p) {}",
+         "NewTPtr"},
+    })), );
+
+TEST_P(RenameAliasTest, RenameAlias) {
+  auto Param = GetParam();
+  assert(!Param.NewName.empty());
+  runRenameOnCode(Param.Before, Param.After, Param.NewName);
+}
+
 TEST(RenameTest, Renameable) {
   struct Case {
     const char *Code;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to