diff --git a/clang-tools-extra/clangd/unittests/RenameTests.cpp b/clang-tools-extra/clangd/unittests/RenameTests.cpp --- a/clang-tools-extra/clangd/unittests/RenameTests.cpp +++ b/clang-tools-extra/clangd/unittests/RenameTests.cpp @@ -608,6 +608,218 @@ } } +struct Case { + std::string Before; + std::string After; + std::string NewName; +}; + +class ComplicatedRenameTest : public testing::Test, + public testing::WithParamInterface { +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; + // FIXME: This is much worse than comparing modified Before and After but + // after the rename we lose track of where Before was. We can't put + // HeaderCode into an actual header because rename within file does not + // allow references outside of the main file. + EXPECT_TRUE(llvm::StringRef(Result).endswith(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 class ptr {}; + template + + using TPtr = ptr; + )"); + } +}; + +INSTANTIATE_TEST_CASE_P( + RenameAliasTests, RenameAliasTest, + testing::ValuesIn(std::vector({ + // 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_; };", "struct F { ptr a_; };", + "TB"}, + {"struct F { ptr a_; };", "struct F { ptr 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 struct Foo { T t; }; void f() { " + "Foo " + "foo; }", + "template struct Foo { T t; }; void f() { Foo " + "foo; }", + "TB"}, + {"template struct Foo { a::[[T^A]] a; };", + "template struct Foo { a::TB a; };", "TB"}, + {"template void f(T t) {} void g() { " + "f(a::[[T^A]]()); }", + "template void f(T t) {} void g() { f(a::TB()); }", + "TB"}, + {"template void f(T t) {} void g() { " + "f(a::[[U^A]]()); }", + "template void f(T t) {} void g() { f(a::UB()); }", + "UB"}, + {"template int f() { return 1; } template <> int " + "f() { return 2; } int g() { return f(); }", + "template int f() { return 1; } template <> int " + "f() { return 2; } int g() { return f(); }", + "TB"}, + {"struct Foo { template T foo(); }; void g() { Foo f; " + "auto a = f.template foo(); }", + "struct Foo { template T foo(); }; void g() { Foo f; " + "auto a = f.template foo(); }", + "TB"}, + {"struct Foo { template T foo(); }; void g() { Foo f; " + "auto a = f.template foo(); }", + "struct Foo { template T foo(); }; void g() { Foo f; " + "auto a = f.template foo(); }", + "UB"}, + + // The following two templates are distilled from regressions found in + // unique_ptr<> and type_traits.h + {"template struct outer { typedef T type; type Baz(); }; " + "outer g_A;", + "template struct outer { typedef T type; type Baz(); }; " + "outer g_A;", + "TB"}, + {"template struct nested { typedef T type; }; template " + " struct outer { typename nested::type Foo(); }; " + "outer g_A;", + "template struct nested { typedef T type; }; template " + " struct outer { typename nested::type Foo(); }; " + "outer 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]] p) {}", "void f(NewTPtr p) {}", "NewTPtr"}, + {"void f(::[[T^Ptr]] p) {}", "void f(::NewTPtr 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;