1 //===-- RenameTests.cpp -----------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "Annotations.h"
10 #include "ClangdServer.h"
14 #include "index/Ref.h"
15 #include "refactor/Rename.h"
16 #include "support/TestTracer.h"
17 #include "clang/Tooling/Core/Replacement.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/Support/MemoryBuffer.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
28 using testing::ElementsAre
;
30 using testing::IsEmpty
;
32 using testing::SizeIs
;
33 using testing::UnorderedElementsAre
;
34 using testing::UnorderedElementsAreArray
;
36 llvm::IntrusiveRefCntPtr
<llvm::vfs::OverlayFileSystem
>
37 createOverlay(llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> Base
,
38 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> Overlay
) {
40 llvm::makeIntrusiveRefCnt
<llvm::vfs::OverlayFileSystem
>(std::move(Base
));
41 OFS
->pushOverlay(std::move(Overlay
));
45 llvm::IntrusiveRefCntPtr
<llvm::vfs::FileSystem
> getVFSFromAST(ParsedAST
&AST
) {
46 return &AST
.getSourceManager().getFileManager().getVirtualFileSystem();
49 // Convert a Range to a Ref.
50 Ref
refWithRange(const clangd::Range
&Range
, const std::string
&URI
) {
52 Result
.Kind
= RefKind::Reference
| RefKind::Spelled
;
53 Result
.Location
.Start
.setLine(Range
.start
.line
);
54 Result
.Location
.Start
.setColumn(Range
.start
.character
);
55 Result
.Location
.End
.setLine(Range
.end
.line
);
56 Result
.Location
.End
.setColumn(Range
.end
.character
);
57 Result
.Location
.FileURI
= URI
.c_str();
61 // Build a RefSlab from all marked ranges in the annotation. The ranges are
62 // assumed to associate with the given SymbolName.
63 std::unique_ptr
<RefSlab
> buildRefSlab(const Annotations
&Code
,
64 llvm::StringRef SymbolName
,
65 llvm::StringRef Path
) {
66 RefSlab::Builder Builder
;
68 TU
.HeaderCode
= std::string(Code
.code());
69 auto Symbols
= TU
.headerSymbols();
70 const auto &SymbolID
= findSymbol(Symbols
, SymbolName
).ID
;
71 std::string PathURI
= URI::create(Path
).toString();
72 for (const auto &Range
: Code
.ranges())
73 Builder
.insert(SymbolID
, refWithRange(Range
, PathURI
));
75 return std::make_unique
<RefSlab
>(std::move(Builder
).build());
79 std::pair
</*FilePath*/ std::string
, /*CodeAfterRename*/ std::string
>>
80 applyEdits(FileEdits FE
) {
81 std::vector
<std::pair
<std::string
, std::string
>> Results
;
85 llvm::cantFail(tooling::applyAllReplacements(
86 It
.getValue().InitialCode
, It
.getValue().Replacements
)));
90 // Generates an expected rename result by replacing all ranges in the given
91 // annotation with the NewName.
92 std::string
expectedResult(Annotations Test
, llvm::StringRef NewName
) {
94 unsigned NextChar
= 0;
95 llvm::StringRef Code
= Test
.code();
96 for (const auto &R
: Test
.llvm::Annotations::ranges()) {
97 assert(R
.Begin
<= R
.End
&& NextChar
<= R
.Begin
);
98 Result
+= Code
.substr(NextChar
, R
.Begin
- NextChar
);
102 Result
+= Code
.substr(NextChar
);
106 TEST(RenameTest
, WithinFileRename
) {
107 // For each "^" this test moves cursor to its location and applies renaming
108 // while checking that all identifiers in [[]] ranges are also renamed.
109 llvm::StringRef Tests
[] = {
129 if (auto [[^foo]] = 5) {
135 // Class, its constructor and destructor.
140 [[F^oo]] *foo(int x);
144 [[F^oo]]::[[Fo^o]]() {}
145 [[F^oo]]::~[[Fo^o]]() {}
146 [[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
149 // Template class, its constructor and destructor.
151 template <typename T>
159 [[F^oo]]<T>::[[Fo^o]]() {}
162 [[F^oo]]<T>::~[[Fo^o]]() {}
165 // Template class constructor.
176 [[F^oo]]::[[Fo^o]]() {}
179 // Class in template argument.
182 template <typename T> void func();
183 template <typename T> class Baz {};
191 // Forward class declaration without definition.
201 void Baz() { [[F^oo]](); }
205 // Templated method instantiation.
210 static T [[f^oo]]() {}
214 Foo<int>::[[f^oo]]();
225 Foo<int>().[[f^oo]]();
229 // Template class (partial) specializations.
231 template <typename T>
235 class [[F^oo]]<bool> {};
236 template <typename T>
237 class [[F^oo]]<T*> {};
246 // Incomplete class specializations
248 template <typename T>
250 void func([[F^oo]]<int>);
253 // Template class instantiations.
255 template <typename T>
258 T foo(T arg, T& ref, T* ptr) {
262 value = static_cast<T>(number);
265 static void foo(T value) {}
269 template <typename T>
279 [[F^oo]]<int>::foo(0);
283 [[F^oo]]<bool>::foo(false);
287 // Template class methods.
289 template <typename T>
297 A<double>().[[f^oo]]();
298 A<float>().[[f^oo]]();
302 // Templated class specialization.
304 template<typename T, typename U=bool>
307 template<typename T, typename U>
310 template<typename T=int, typename U>
314 template<typename T=float, typename U=int>
317 template<typename T, typename U>
321 // Function template specialization.
323 template<typename T=int, typename U=bool>
326 template<typename T, typename U>
330 template<typename T, typename U>
333 template<typename T=int, typename U=bool>
337 template<typename T=int, typename U=bool>
340 template<typename T, typename U>
344 template <typename T>
348 void [[f^oo]](int a);
355 // Variable template.
357 template <typename T, int U>
358 bool [[F^oo]] = true;
360 // Explicit template specialization
362 bool [[F^oo]]<int, 0> = false;
364 // Partial template specialization
365 template <typename T>
366 bool [[F^oo]]<T, 1> = false;
369 // Ref to the explicit template specialization
371 // Ref to the primary template.
376 // Complicated class type.
378 // Forward declaration.
381 virtual int getValue() const = 0;
384 class [[F^oo]] : public Baz {
386 [[F^oo]](int value = 0) : x(value) {}
388 [[F^oo]] &operator++(int);
390 bool operator<([[Foo]] const &rhs);
391 int getValue() const;
397 [[F^oo]] *Pointer = 0;
398 [[F^oo]] Variable = [[Foo]](10);
399 for ([[F^oo]] it; it < Variable; it++);
400 const [[F^oo]] *C = new [[Foo]]();
401 const_cast<[[F^oo]] *>(C)->getValue();
403 const Baz &BazReference = foo;
404 const Baz *BazPointer = &foo;
405 reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
406 static_cast<const [[^Foo]] &>(BazReference).getValue();
407 static_cast<const [[^Foo]] *>(BazPointer)->getValue();
411 // Static class member.
414 static Foo *[[Static^Member]];
417 Foo* Foo::[[Static^Member]] = nullptr;
420 Foo* Pointer = Foo::[[Static^Member]];
424 // Reference in lambda parameters.
428 template <class R, class... ArgTypes>
429 class function<R(ArgTypes...)> {
431 template <typename Functor>
432 function(Functor f) {}
436 R operator()(ArgTypes...) const {}
442 function<void([[Old]])> func;
447 // Destructor explicit call.
454 [[Foo^]]::~[[^Foo]]() {}
458 f.~/*something*/[[^Foo]]();
463 // Derived destructor explicit call.
466 class Derived : public [[Bas^e]] {};
469 [[Bas^e]] *foo = new Derived();
470 foo->[[^Base]]::~[[^Base]]();
474 // CXXConstructor initializer list.
482 Qux::Qux() : [[F^oo]]() {}
493 #define MACRO(a) foo(a)
504 // no rename inside macro body.
516 M2(M1()); // foo is inside the nested macro body.
520 // MemberExpr in macros
527 #define MACRO(a) qux(a)
533 int y = baz.[[F^oo]];
537 // Fields in classes & partial and full specialiations.
541 T [[Vari^able]] = 42;
546 f.[[Varia^ble]] = 9000;
550 template<typename T, typename U>
559 struct Foo<T, bool> {
561 void bar() { ++[[Var^iable]]; }
565 Foo<unsigned, bool> f;
566 f.[[Var^iable]] = 9000;
570 template<typename T, typename U>
579 struct Foo<T, bool> {
581 void bar() { ++Variable; }
585 struct Foo<unsigned, bool> {
586 unsigned [[Var^iable]];
587 void bar() { ++[[Var^iable]]; }
591 Foo<unsigned, bool> f;
592 f.[[Var^iable]] = 9000;
598 static int [[Var^iable]];
601 int Foo::[[Var^iable]] = 42;
604 int LocalInt = Foo::[[Var^iable]];
610 static T [[Var^iable]];
614 int Foo<int>::[[Var^iable]] = 42;
617 bool Foo<bool>::[[Var^iable]] = true;
620 int LocalInt = Foo<int>::[[Var^iable]];
621 bool LocalBool = Foo<bool>::[[Var^iable]];
625 // Template parameters.
627 template <typename [[^T]]>
629 [[T^]] foo([[T^]] arg, [[T^]]& ref, [[^T]]* ptr) {
632 value = ([[T^]])number;
633 value = static_cast<[[^T]]>(number);
636 static void foo([[T^]] value) {}
644 class basic_string {};
645 typedef basic_string [[s^tring]];
648 ns::[[s^tring]] foo();
658 int Baz = A::[[^Foo]];
668 Foo = A::[[F^oo]] + Baz;
677 namespace a { namespace b { void foo(); } }
678 namespace [[^x]] = a::b;
686 enum [[C^olor]] { Red, Green, Blue };
689 c = [[C^olor]]::Blue;
695 enum class [[K^ind]] { ABC };
702 // Template class in template argument list.
706 template <template<typename> class Z> struct Bar { };
707 template <> struct Bar<[[F^oo]]> {};
710 // Designated initializer.
715 Bar bar { .[[^Foo]] = 42 };
718 // Nested designated initializer.
726 // FIXME: v selecting here results in renaming Field.
727 Bar bar { .[[Foo]].Field = 42 };
736 Bar bar { .Foo.[[^Field]] = 42 };
741 template <typename T>
744 template <typename T>
745 using [[Fo^o]] = X<T>;
762 // Alias within a namespace.
764 namespace x { class X {}; }
766 using [[Fo^o]] = x::X;
774 // Alias within macros.
776 namespace x { class Old {}; }
778 #define REF(alias) alias alias_var;
781 using old##Alias = x::old; \
786 [[Old^Alias]] old_alias;
790 ns::[[Old^Alias]] Bar;
794 // User defined conversion.
803 operator [[F^oo]]() {
810 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
814 // ObjC, should not crash.
821 // Issue 170: Rename symbol introduced by UsingDecl
823 namespace ns { void [[f^oo]](); }
833 // Issue 170: using decl that imports multiple overloads
834 // -> Only the overload under the cursor is renamed
836 namespace ns { int [[^foo]](int); char foo(char); }
844 // ObjC class with a category.
848 @implementation [[F^oo]]
850 @interface [[Fo^o]] (Category)
852 @implementation [[F^oo]] (Category)
855 void func([[Fo^o]] *f) {}
858 llvm::StringRef NewName
= "NewName";
859 for (llvm::StringRef T
: Tests
) {
862 auto TU
= TestTU::withCode(Code
.code());
863 TU
.ExtraArgs
.push_back("-xobjective-c++");
864 auto AST
= TU
.build();
865 auto Index
= TU
.index();
866 for (const auto &RenamePos
: Code
.points()) {
868 rename({RenamePos
, NewName
, AST
, testPath(TU
.Filename
),
869 getVFSFromAST(AST
), Index
.get()});
870 ASSERT_TRUE(bool(RenameResult
)) << RenameResult
.takeError();
871 ASSERT_EQ(1u, RenameResult
->GlobalChanges
.size());
873 applyEdits(std::move(RenameResult
->GlobalChanges
)).front().second
,
874 expectedResult(Code
, NewName
));
879 TEST(RenameTest
, Renameable
) {
882 const char* ErrorMessage
; // null if no error
884 llvm::StringRef NewName
= "MockName";
886 const bool HeaderFile
= true;
888 {R
"cpp(// allow -- function-local
889 void f(int [[Lo^cal]]) {
893 nullptr, HeaderFile
},
895 {R
"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
897 class Unin^dexable {};
900 "not eligible for indexing", HeaderFile
},
902 {R
"cpp(// disallow -- namespace symbol isn't supported
905 "not a supported kind", HeaderFile
},
907 {R
"cpp(// disallow - category rename.
910 @interface Foo (Cate^gory)
913 "Cannot rename symbol: there is no symbol at the given location",
921 "not a supported kind", HeaderFile
},
925 struct X { X operator++(int); };
926 void f(X x) {x+^+;})cpp",
927 "no symbol", HeaderFile
},
929 {R
"cpp(// disallow rename on non-normal identifiers.
931 -(int) fo^o:(int)x; // Token is an identifier, but declaration name isn't a simple identifier.
934 "not a supported kind", HeaderFile
},
938 template <typename T> void f(T t) {
941 "multiple symbols", !HeaderFile
},
943 {R
"cpp(// disallow rename on unrelated token.
946 "no symbol", !HeaderFile
},
948 {R
"cpp(// disallow rename on unrelated token.
949 temp^late<typename T>
952 "no symbol", !HeaderFile
},
960 "conflict", !HeaderFile
, "Conflict"},
966 "conflict", !HeaderFile
, "Conflict"},
974 "conflict", !HeaderFile
, "Conflict"},
982 "conflict", !HeaderFile
, "Conflict"},
986 enum E { // transparent context.
990 "conflict", !HeaderFile
, "Conflict"},
999 "conflict", !HeaderFile
, "Conflict"},
1003 if (int Conflict = 42) {
1008 "conflict", !HeaderFile
, "Conflict"},
1012 if (int Conflict = 42) {
1018 "conflict", !HeaderFile
, "Conflict"},
1022 if (int V^ar = 42) {
1028 "conflict", !HeaderFile
, "Conflict"},
1032 while (int V^ar = 10) {
1033 bool Conflict = true;
1037 "conflict", !HeaderFile
, "Conflict"},
1041 for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
1047 "conflict", !HeaderFile
, "Conflict"},
1051 for (int V^ar = 14, Conflict = 42;;) {
1055 "conflict", !HeaderFile
, "Conflict"},
1058 void func(int Conflict) {
1062 "conflict", !HeaderFile
, "Conflict"},
1067 void func(int V^ar) {
1071 "conflict", !HeaderFile
, "Conflict"},
1073 {R
"cpp(// No conflict: only forward declaration's argument is renamed.
1074 void func(int [[V^ar]]);
1076 void func(int Var) {
1080 nullptr, !HeaderFile
, "Conflict"},
1083 void func(int V^ar, int Conflict) {
1086 "conflict", !HeaderFile
, "Conflict"},
1090 void [[o^therFunc]](double);
1092 nullptr, !HeaderFile
, "func"},
1096 void [[o^therFunc]](double);
1099 nullptr, !HeaderFile
, "func"},
1104 "\"const\" is a keyword", !HeaderFile
, "const"},
1106 {R
"cpp(// Trying to rename into the same name, SameName == SameName.
1111 "new name is the same", !HeaderFile
, "SameName"},
1112 {R
"cpp(// Ensure it doesn't associate base specifier with base name.
1114 struct B : priv^ate A {};
1116 "Cannot rename symbol: there is no symbol at the given location", false},
1117 {R
"cpp(// Ensure it doesn't associate base specifier with base name.
1120 A() : inva^lid(0) {}
1123 "no symbol", false},
1125 {R
"cpp(// FIXME we probably want to rename both overloads here,
1126 // but renaming currently assumes there's only a
1127 // single canonical declaration.
1128 namespace ns { int foo(int); char foo(char); }
1131 "there are multiple symbols at the given location", !HeaderFile
},
1134 for (const auto& Case
: Cases
) {
1135 SCOPED_TRACE(Case
.Code
);
1136 Annotations
T(Case
.Code
);
1137 TestTU TU
= TestTU::withCode(T
.code());
1138 TU
.ExtraArgs
.push_back("-fno-delayed-template-parsing");
1139 if (Case
.IsHeaderFile
) {
1140 // We open the .h file as the main file.
1141 TU
.Filename
= "test.h";
1142 // Parsing the .h file as C++ include.
1143 TU
.ExtraArgs
.push_back("-xobjective-c++-header");
1145 auto AST
= TU
.build();
1146 llvm::StringRef NewName
= Case
.NewName
;
1147 auto Results
= rename({T
.point(), NewName
, AST
, testPath(TU
.Filename
)});
1148 bool WantRename
= true;
1149 if (T
.ranges().empty())
1152 assert(Case
.ErrorMessage
&& "Error message must be set!");
1153 EXPECT_FALSE(Results
)
1154 << "expected rename returned an error: " << T
.code();
1155 auto ActualMessage
= llvm::toString(Results
.takeError());
1156 EXPECT_THAT(ActualMessage
, testing::HasSubstr(Case
.ErrorMessage
));
1158 EXPECT_TRUE(bool(Results
)) << "rename returned an error: "
1159 << llvm::toString(Results
.takeError());
1160 EXPECT_EQ(Results
->LocalChanges
, T
.ranges());
1165 MATCHER_P(newText
, T
, "") { return arg
.newText
== T
; }
1167 TEST(RenameTest
, IndexMergeMainFile
) {
1168 Annotations
Code("int ^x();");
1169 TestTU TU
= TestTU::withCode(Code
.code());
1170 TU
.Filename
= "main.cc";
1171 auto AST
= TU
.build();
1173 auto Main
= testPath("main.cc");
1174 auto InMemFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::InMemoryFileSystem
>();
1175 InMemFS
->addFile(testPath("main.cc"), 0,
1176 llvm::MemoryBuffer::getMemBuffer(Code
.code()));
1177 InMemFS
->addFile(testPath("other.cc"), 0,
1178 llvm::MemoryBuffer::getMemBuffer(Code
.code()));
1180 auto Rename
= [&](const SymbolIndex
*Idx
) {
1181 RenameInputs Inputs
{Code
.point(),
1185 Idx
? createOverlay(getVFSFromAST(AST
), InMemFS
)
1189 auto Results
= rename(Inputs
);
1190 EXPECT_TRUE(bool(Results
)) << llvm::toString(Results
.takeError());
1191 return std::move(*Results
);
1194 // We do not expect to see duplicated edits from AST vs index.
1195 auto Results
= Rename(TU
.index().get());
1196 EXPECT_THAT(Results
.GlobalChanges
.keys(), ElementsAre(Main
));
1197 EXPECT_THAT(Results
.GlobalChanges
[Main
].asTextEdits(),
1198 ElementsAre(newText("xPrime")));
1200 // Sanity check: we do expect to see index results!
1201 TU
.Filename
= "other.cc";
1202 Results
= Rename(TU
.index().get());
1203 EXPECT_THAT(Results
.GlobalChanges
.keys(),
1204 UnorderedElementsAre(Main
, testPath("other.cc")));
1206 #ifdef CLANGD_PATH_CASE_INSENSITIVE
1207 // On case-insensitive systems, no duplicates if AST vs index case differs.
1208 // https://github.com/clangd/clangd/issues/665
1209 TU
.Filename
= "MAIN.CC";
1210 Results
= Rename(TU
.index().get());
1211 EXPECT_THAT(Results
.GlobalChanges
.keys(), ElementsAre(Main
));
1212 EXPECT_THAT(Results
.GlobalChanges
[Main
].asTextEdits(),
1213 ElementsAre(newText("xPrime")));
1217 TEST(RenameTest
, MainFileReferencesOnly
) {
1218 // filter out references not from main file.
1219 llvm::StringRef Test
=
1223 // rename references not from main file are not included.
1227 Annotations
Code(Test
);
1228 auto TU
= TestTU::withCode(Code
.code());
1229 TU
.AdditionalFiles
["foo.inc"] = R
"cpp(
1234 auto AST
= TU
.build();
1235 llvm::StringRef NewName
= "abcde";
1238 rename({Code
.point(), NewName
, AST
, testPath(TU
.Filename
)});
1239 ASSERT_TRUE(bool(RenameResult
)) << RenameResult
.takeError() << Code
.point();
1240 ASSERT_EQ(1u, RenameResult
->GlobalChanges
.size());
1241 EXPECT_EQ(applyEdits(std::move(RenameResult
->GlobalChanges
)).front().second
,
1242 expectedResult(Code
, NewName
));
1245 TEST(RenameTest
, NoRenameOnSymbolsFromSystemHeaders
) {
1246 llvm::StringRef Test
=
1253 void foo() { at^oi("9000"); }
1256 Annotations
Code(Test
);
1257 auto TU
= TestTU::withCode(Code
.code());
1258 TU
.AdditionalFiles
["system"] = R
"cpp(
1259 class SystemSymbol {};
1261 TU
.AdditionalFiles
["cstdlib"] = R
"cpp(
1262 int atoi(const char *str);
1264 TU
.ExtraArgs
= {"-isystem", testRoot()};
1265 auto AST
= TU
.build();
1266 llvm::StringRef NewName
= "abcde";
1268 // Clangd will not allow renaming symbols from the system headers for
1270 for (auto &Point
: Code
.points()) {
1271 auto Results
= rename({Point
, NewName
, AST
, testPath(TU
.Filename
)});
1272 EXPECT_FALSE(Results
) << "expected rename returned an error: "
1274 auto ActualMessage
= llvm::toString(Results
.takeError());
1275 EXPECT_THAT(ActualMessage
, testing::HasSubstr("not a supported kind"));
1279 TEST(RenameTest
, ProtobufSymbolIsExcluded
) {
1280 Annotations
Code("Prot^obuf buf;");
1281 auto TU
= TestTU::withCode(Code
.code());
1283 R
"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1286 TU
.HeaderFilename
= "protobuf.pb.h";
1287 auto AST
= TU
.build();
1288 auto Results
= rename({Code
.point(), "newName", AST
, testPath(TU
.Filename
)});
1289 EXPECT_FALSE(Results
);
1290 EXPECT_THAT(llvm::toString(Results
.takeError()),
1291 testing::HasSubstr("not a supported kind"));
1294 TEST(RenameTest
, PrepareRename
) {
1295 Annotations
FooH("void func();");
1296 Annotations
FooCC(R
"cpp(
1300 std::string FooHPath
= testPath("foo.h");
1301 std::string FooCCPath
= testPath("foo.cc");
1303 FS
.Files
[FooHPath
] = std::string(FooH
.code());
1304 FS
.Files
[FooCCPath
] = std::string(FooCC
.code());
1306 auto ServerOpts
= ClangdServer::optsForTest();
1307 ServerOpts
.BuildDynamicSymbolIndex
= true;
1309 trace::TestTracer Tracer
;
1310 MockCompilationDatabase CDB
;
1311 ClangdServer
Server(CDB
, FS
, ServerOpts
);
1312 runAddDocument(Server
, FooHPath
, FooH
.code());
1313 runAddDocument(Server
, FooCCPath
, FooCC
.code());
1315 auto Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1316 /*NewName=*/std::nullopt
, {});
1317 // Verify that for multi-file rename, we only return main-file occurrences.
1318 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1319 // We don't know the result is complete in prepareRename (passing a nullptr
1320 // index internally), so GlobalChanges should be empty.
1321 EXPECT_TRUE(Results
->GlobalChanges
.empty());
1322 EXPECT_THAT(FooCC
.ranges(),
1323 testing::UnorderedElementsAreArray(Results
->LocalChanges
));
1326 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1327 /*NewName=*/std::string("int"), {});
1328 EXPECT_FALSE(Results
);
1329 EXPECT_THAT(llvm::toString(Results
.takeError()),
1330 testing::HasSubstr("keyword"));
1331 EXPECT_THAT(Tracer
.takeMetric("rename_name_invalid", "Keywords"),
1334 for (std::string BadIdent
: {"foo!bar", "123foo", "😀@"}) {
1335 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1336 /*NewName=*/BadIdent
, {});
1337 EXPECT_FALSE(Results
);
1338 EXPECT_THAT(llvm::toString(Results
.takeError()),
1339 testing::HasSubstr("identifier"));
1340 EXPECT_THAT(Tracer
.takeMetric("rename_name_invalid", "BadIdentifier"),
1343 for (std::string GoodIdent
: {"fooBar", "__foo$", "😀"}) {
1344 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1345 /*NewName=*/GoodIdent
, {});
1346 EXPECT_TRUE(bool(Results
));
1350 TEST(CrossFileRenameTests
, DirtyBuffer
) {
1351 Annotations
FooCode("class [[Foo]] {};");
1352 std::string FooPath
= testPath("foo.cc");
1353 Annotations
FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1354 Annotations
BarCode("void [[Bar]]() {}");
1355 std::string BarPath
= testPath("bar.cc");
1356 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1357 // references from bar.cc.
1358 FileSymbols
FSymbols(IndexContents::All
);
1359 FSymbols
.update(FooPath
, nullptr, buildRefSlab(FooCode
, "Foo", FooPath
),
1361 FSymbols
.update(BarPath
, nullptr, buildRefSlab(BarCode
, "Bar", BarPath
),
1363 auto Index
= FSymbols
.buildIndex(IndexType::Light
);
1365 Annotations
MainCode("class [[Fo^o]] {};");
1366 auto MainFilePath
= testPath("main.cc");
1367 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemFS
=
1368 new llvm::vfs::InMemoryFileSystem
;
1369 InMemFS
->addFile(FooPath
, 0,
1370 llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer
.code()));
1372 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1373 // respect the dirty buffer.
1374 TestTU TU
= TestTU::withCode(MainCode
.code());
1375 auto AST
= TU
.build();
1376 llvm::StringRef NewName
= "newName";
1378 rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1379 createOverlay(getVFSFromAST(AST
), InMemFS
), Index
.get()});
1380 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1382 applyEdits(std::move(Results
->GlobalChanges
)),
1383 UnorderedElementsAre(
1384 Pair(Eq(FooPath
), Eq(expectedResult(FooDirtyBuffer
, NewName
))),
1385 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1387 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1388 // so we should read file content from VFS.
1389 MainCode
= Annotations("void [[Bar]]() { [[B^ar]](); }");
1390 TU
= TestTU::withCode(MainCode
.code());
1391 // Set a file "bar.cc" on disk.
1392 TU
.AdditionalFiles
["bar.cc"] = std::string(BarCode
.code());
1394 Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1395 createOverlay(getVFSFromAST(AST
), InMemFS
), Index
.get()});
1396 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1398 applyEdits(std::move(Results
->GlobalChanges
)),
1399 UnorderedElementsAre(
1400 Pair(Eq(BarPath
), Eq(expectedResult(BarCode
, NewName
))),
1401 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1403 // Run rename on a pagination index which couldn't return all refs in one
1404 // request, we reject rename on this case.
1405 class PaginationIndex
: public SymbolIndex
{
1406 bool refs(const RefsRequest
&Req
,
1407 llvm::function_ref
<void(const Ref
&)> Callback
) const override
{
1408 return true; // has more references
1412 const FuzzyFindRequest
&Req
,
1413 llvm::function_ref
<void(const Symbol
&)> Callback
) const override
{
1417 lookup(const LookupRequest
&Req
,
1418 llvm::function_ref
<void(const Symbol
&)> Callback
) const override
{}
1420 void relations(const RelationsRequest
&Req
,
1421 llvm::function_ref
<void(const SymbolID
&, const Symbol
&)>
1422 Callback
) const override
{}
1424 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
1425 indexedFiles() const override
{
1426 return [](llvm::StringRef
) { return IndexContents::None
; };
1429 size_t estimateMemoryUsage() const override
{ return 0; }
1431 Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1432 createOverlay(getVFSFromAST(AST
), InMemFS
), &PIndex
});
1433 EXPECT_FALSE(Results
);
1434 EXPECT_THAT(llvm::toString(Results
.takeError()),
1435 testing::HasSubstr("too many occurrences"));
1438 TEST(CrossFileRenameTests
, DeduplicateRefsFromIndex
) {
1439 auto MainCode
= Annotations("int [[^x]] = 2;");
1440 auto MainFilePath
= testPath("main.cc");
1441 auto BarCode
= Annotations("int [[x]];");
1442 auto BarPath
= testPath("bar.cc");
1443 auto TU
= TestTU::withCode(MainCode
.code());
1444 // Set a file "bar.cc" on disk.
1445 TU
.AdditionalFiles
["bar.cc"] = std::string(BarCode
.code());
1446 auto AST
= TU
.build();
1447 std::string BarPathURI
= URI::create(BarPath
).toString();
1448 Ref XRefInBarCC
= refWithRange(BarCode
.range(), BarPathURI
);
1449 // The index will return duplicated refs, our code should be robost to handle
1451 class DuplicatedXRefIndex
: public SymbolIndex
{
1453 DuplicatedXRefIndex(const Ref
&ReturnedRef
) : ReturnedRef(ReturnedRef
) {}
1454 bool refs(const RefsRequest
&Req
,
1455 llvm::function_ref
<void(const Ref
&)> Callback
) const override
{
1456 // Return two duplicated refs.
1457 Callback(ReturnedRef
);
1458 Callback(ReturnedRef
);
1462 bool fuzzyFind(const FuzzyFindRequest
&,
1463 llvm::function_ref
<void(const Symbol
&)>) const override
{
1466 void lookup(const LookupRequest
&,
1467 llvm::function_ref
<void(const Symbol
&)>) const override
{}
1469 void relations(const RelationsRequest
&,
1470 llvm::function_ref
<void(const SymbolID
&, const Symbol
&)>)
1473 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
1474 indexedFiles() const override
{
1475 return [](llvm::StringRef
) { return IndexContents::None
; };
1478 size_t estimateMemoryUsage() const override
{ return 0; }
1480 } DIndex(XRefInBarCC
);
1481 llvm::StringRef NewName
= "newName";
1482 auto Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1483 getVFSFromAST(AST
), &DIndex
});
1484 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1486 applyEdits(std::move(Results
->GlobalChanges
)),
1487 UnorderedElementsAre(
1488 Pair(Eq(BarPath
), Eq(expectedResult(BarCode
, NewName
))),
1489 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1492 TEST(CrossFileRenameTests
, WithUpToDateIndex
) {
1493 MockCompilationDatabase CDB
;
1494 CDB
.ExtraClangFlags
= {"-xobjective-c++"};
1495 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1496 // expected rename occurrences.
1498 llvm::StringRef FooH
;
1499 llvm::StringRef FooCC
;
1511 [[Foo]]::[[Foo]]() {}
1512 [[Foo]]::~[[Foo]]() {}
1522 template <typename T>
1524 // FIXME: explicit template specializations are not supported due the
1525 // clangd index limitations.
1527 class Foo<double> {};
1545 void Foo::[[foo]]() {}
1556 virtual void [[foo]]();
1558 class Derived1 : public Base {
1559 void [[f^oo]]() override;
1567 void Base::[[foo]]() {}
1568 void Derived1::[[foo]]() {}
1570 class Derived2 : public Derived1 {
1571 void [[foo]]() override {};
1574 void func(Base* b, Derived1* d1,
1575 Derived2* d2, NotDerived* nd) {
1583 {// virtual templated method
1585 template <typename> class Foo { virtual void [[m]](); };
1586 class Bar : Foo<int> { void [[^m]]() override; };
1591 template<typename T> void Foo<T>::[[m]]() {}
1592 // FIXME: not renamed as the index doesn't see this as an override of
1593 // the canonical Foo<T>::m().
1594 // https://github.com/clangd/clangd/issues/1325
1595 class Baz : Foo<float> { void m() override; };
1598 // rename on constructor and destructor.
1607 [[Foo]]::[[Foo]]() {}
1608 [[Foo]]::~[[Foo]]() {}
1632 typedef int [[IN^T]];
1643 using [[I^NT]] = int;
1654 static const int [[VA^R]] = 123;
1664 enum class [[K^ind]] { ABC };
1669 return [[Kind]]::ABC;
1676 enum class Kind { [[A^BC]] };
1681 return Kind::[[ABC]];
1686 // Implicit references in macro expansions.
1702 // Objective-C classes.
1709 @implementation [[Foo]]
1712 void func([[Foo]] *f) {}
1717 trace::TestTracer Tracer
;
1718 for (const auto &T
: Cases
) {
1719 SCOPED_TRACE(T
.FooH
);
1720 Annotations
FooH(T
.FooH
);
1721 Annotations
FooCC(T
.FooCC
);
1722 std::string FooHPath
= testPath("foo.h");
1723 std::string FooCCPath
= testPath("foo.cc");
1726 FS
.Files
[FooHPath
] = std::string(FooH
.code());
1727 FS
.Files
[FooCCPath
] = std::string(FooCC
.code());
1729 auto ServerOpts
= ClangdServer::optsForTest();
1730 ServerOpts
.BuildDynamicSymbolIndex
= true;
1731 ClangdServer
Server(CDB
, FS
, ServerOpts
);
1733 // Add all files to clangd server to make sure the dynamic index has been
1735 runAddDocument(Server
, FooHPath
, FooH
.code());
1736 runAddDocument(Server
, FooCCPath
, FooCC
.code());
1738 llvm::StringRef NewName
= "NewName";
1739 for (const auto &RenamePos
: FooH
.points()) {
1740 EXPECT_THAT(Tracer
.takeMetric("rename_files"), SizeIs(0));
1741 auto FileEditsList
=
1742 llvm::cantFail(runRename(Server
, FooHPath
, RenamePos
, NewName
, {}));
1743 EXPECT_THAT(Tracer
.takeMetric("rename_files"), ElementsAre(2));
1745 applyEdits(std::move(FileEditsList
.GlobalChanges
)),
1746 UnorderedElementsAre(
1747 Pair(Eq(FooHPath
), Eq(expectedResult(T
.FooH
, NewName
))),
1748 Pair(Eq(FooCCPath
), Eq(expectedResult(T
.FooCC
, NewName
)))));
1753 TEST(CrossFileRenameTests
, CrossFileOnLocalSymbol
) {
1754 // cross-file rename should work for function-local symbols, even there is no
1756 Annotations
Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
1757 auto TU
= TestTU::withCode(Code
.code());
1758 auto Path
= testPath(TU
.Filename
);
1759 auto AST
= TU
.build();
1760 llvm::StringRef NewName
= "newName";
1761 auto Results
= rename({Code
.point(), NewName
, AST
, Path
});
1762 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1764 applyEdits(std::move(Results
->GlobalChanges
)),
1765 UnorderedElementsAre(Pair(Eq(Path
), Eq(expectedResult(Code
, NewName
)))));
1768 TEST(CrossFileRenameTests
, BuildRenameEdits
) {
1769 Annotations
Code("[[😂]]");
1770 auto LSPRange
= Code
.range();
1771 llvm::StringRef FilePath
= "/test/TestTU.cpp";
1772 llvm::StringRef NewName
= "abc";
1773 auto Edit
= buildRenameEdit(FilePath
, Code
.code(), {LSPRange
}, NewName
);
1774 ASSERT_TRUE(bool(Edit
)) << Edit
.takeError();
1775 ASSERT_EQ(1UL, Edit
->Replacements
.size());
1776 EXPECT_EQ(FilePath
, Edit
->Replacements
.begin()->getFilePath());
1777 EXPECT_EQ(4UL, Edit
->Replacements
.begin()->getLength());
1779 // Test invalid range.
1780 LSPRange
.end
= {10, 0}; // out of range
1781 Edit
= buildRenameEdit(FilePath
, Code
.code(), {LSPRange
}, NewName
);
1783 EXPECT_THAT(llvm::toString(Edit
.takeError()),
1784 testing::HasSubstr("fail to convert"));
1786 // Normal ascii characters.
1787 Annotations
T(R
"cpp(
1792 Edit
= buildRenameEdit(FilePath
, T
.code(), T
.ranges(), NewName
);
1793 ASSERT_TRUE(bool(Edit
)) << Edit
.takeError();
1794 EXPECT_EQ(applyEdits(FileEdits
{{T
.code(), std::move(*Edit
)}}).front().second
,
1795 expectedResult(T
, NewName
));
1798 TEST(CrossFileRenameTests
, adjustRenameRanges
) {
1799 // Ranges in IndexedCode indicate the indexed occurrences;
1800 // ranges in DraftCode indicate the expected mapped result, empty indicates
1801 // we expect no matched result found.
1803 llvm::StringRef IndexedCode
;
1804 llvm::StringRef DraftCode
;
1807 // both line and column are changed, not a near miss.
1828 R
"cpp(int [[x]] = 0; void foo(int x);)cpp",
1829 R
"cpp(double [[x]] = 0; void foo(double x);)cpp",
1844 LangOptions LangOpts
;
1845 LangOpts
.CPlusPlus
= true;
1846 for (const auto &T
: Tests
) {
1847 SCOPED_TRACE(T
.DraftCode
);
1848 Annotations
Draft(T
.DraftCode
);
1849 auto ActualRanges
= adjustRenameRanges(
1850 Draft
.code(), "x", Annotations(T
.IndexedCode
).ranges(), LangOpts
);
1852 EXPECT_THAT(Draft
.ranges(), testing::IsEmpty());
1854 EXPECT_THAT(Draft
.ranges(),
1855 testing::UnorderedElementsAreArray(*ActualRanges
));
1859 TEST(RangePatchingHeuristic
, GetMappedRanges
) {
1860 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
1861 // there are no mapped ranges.
1863 llvm::StringRef IndexedCode
;
1864 llvm::StringRef LexedCode
;
1872 // both line and column are changed, not a near miss.
1910 ^[[]] ^[[]] // column is shifted.
1923 [[]] [[]] // not mapped (both line and column are changed).
1937 // insert a new line
1940 [[]] // additional range
1943 [[]] // additional range
1947 // non-distinct result (two best results), not a near miss
1961 for (const auto &T
: Tests
) {
1962 SCOPED_TRACE(T
.IndexedCode
);
1963 auto Lexed
= Annotations(T
.LexedCode
);
1964 auto LexedRanges
= Lexed
.ranges();
1965 std::vector
<Range
> ExpectedMatches
;
1966 for (auto P
: Lexed
.points()) {
1967 auto Match
= llvm::find_if(LexedRanges
, [&P
](const Range
& R
) {
1968 return R
.start
== P
;
1970 ASSERT_NE(Match
, LexedRanges
.end());
1971 ExpectedMatches
.push_back(*Match
);
1975 getMappedRanges(Annotations(T
.IndexedCode
).ranges(), LexedRanges
);
1977 EXPECT_THAT(ExpectedMatches
, IsEmpty());
1979 EXPECT_THAT(ExpectedMatches
, UnorderedElementsAreArray(*Mapped
));
1983 TEST(CrossFileRenameTests
, adjustmentCost
) {
1985 llvm::StringRef RangeCode
;
1986 size_t ExpectedCost
;
1990 $idx[[]]$lex[[]] // diff: 0
1997 $lex[[]] // line diff: +1
1999 $lex[[]] // line diff: +1
2001 $lex[[]] // line diff: +1
2005 $lex[[]] // line diff: +2
2012 $lex[[]] // line diff: +1
2015 $lex[[]] // line diff: +2
2019 $lex[[]] // line diff: +3
2028 $lex[[]] // line diff: +3
2031 $lex[[]] // line diff: +2
2033 $lex[[]] // line diff: +1
2040 $lex[[]] // line diff: +1
2041 $lex[[]] // line diff: -2
2047 $lex[[]] // line diff: +3
2053 $idx[[]] $lex[[]] // column diff: +1
2054 $idx[[]]$lex[[]] // diff: 0
2061 $lex[[]] // diff: +1
2062 $idx[[]] $lex[[]] // column diff: +1
2063 $idx[[]]$lex[[]] // diff: 0
2069 $idx[[]] $lex[[]] // column diff: +1
2075 // column diffs: +1, +2, +3
2076 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2081 for (const auto &T
: Tests
) {
2082 SCOPED_TRACE(T
.RangeCode
);
2083 Annotations
C(T
.RangeCode
);
2084 std::vector
<size_t> MappedIndex
;
2085 for (size_t I
= 0; I
< C
.ranges("lex").size(); ++I
)
2086 MappedIndex
.push_back(I
);
2087 EXPECT_EQ(renameRangeAdjustmentCost(C
.ranges("idx"), C
.ranges("lex"),
2094 } // namespace clangd
2095 } // namespace clang