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
},
1136 using namespace std;
1140 nullptr, !HeaderFile
},
1143 for (const auto& Case
: Cases
) {
1144 SCOPED_TRACE(Case
.Code
);
1145 Annotations
T(Case
.Code
);
1146 TestTU TU
= TestTU::withCode(T
.code());
1147 TU
.ExtraArgs
.push_back("-fno-delayed-template-parsing");
1148 if (Case
.IsHeaderFile
) {
1149 // We open the .h file as the main file.
1150 TU
.Filename
= "test.h";
1151 // Parsing the .h file as C++ include.
1152 TU
.ExtraArgs
.push_back("-xobjective-c++-header");
1154 auto AST
= TU
.build();
1155 llvm::StringRef NewName
= Case
.NewName
;
1156 auto Results
= rename({T
.point(), NewName
, AST
, testPath(TU
.Filename
)});
1157 bool WantRename
= true;
1158 if (T
.ranges().empty())
1161 assert(Case
.ErrorMessage
&& "Error message must be set!");
1162 EXPECT_FALSE(Results
)
1163 << "expected rename returned an error: " << T
.code();
1164 auto ActualMessage
= llvm::toString(Results
.takeError());
1165 EXPECT_THAT(ActualMessage
, testing::HasSubstr(Case
.ErrorMessage
));
1167 EXPECT_TRUE(bool(Results
)) << "rename returned an error: "
1168 << llvm::toString(Results
.takeError());
1169 EXPECT_EQ(Results
->LocalChanges
, T
.ranges());
1174 MATCHER_P(newText
, T
, "") { return arg
.newText
== T
; }
1176 TEST(RenameTest
, IndexMergeMainFile
) {
1177 Annotations
Code("int ^x();");
1178 TestTU TU
= TestTU::withCode(Code
.code());
1179 TU
.Filename
= "main.cc";
1180 auto AST
= TU
.build();
1182 auto Main
= testPath("main.cc");
1183 auto InMemFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::InMemoryFileSystem
>();
1184 InMemFS
->addFile(testPath("main.cc"), 0,
1185 llvm::MemoryBuffer::getMemBuffer(Code
.code()));
1186 InMemFS
->addFile(testPath("other.cc"), 0,
1187 llvm::MemoryBuffer::getMemBuffer(Code
.code()));
1189 auto Rename
= [&](const SymbolIndex
*Idx
) {
1190 RenameInputs Inputs
{Code
.point(),
1194 Idx
? createOverlay(getVFSFromAST(AST
), InMemFS
)
1198 auto Results
= rename(Inputs
);
1199 EXPECT_TRUE(bool(Results
)) << llvm::toString(Results
.takeError());
1200 return std::move(*Results
);
1203 // We do not expect to see duplicated edits from AST vs index.
1204 auto Results
= Rename(TU
.index().get());
1205 EXPECT_THAT(Results
.GlobalChanges
.keys(), ElementsAre(Main
));
1206 EXPECT_THAT(Results
.GlobalChanges
[Main
].asTextEdits(),
1207 ElementsAre(newText("xPrime")));
1209 // Sanity check: we do expect to see index results!
1210 TU
.Filename
= "other.cc";
1211 Results
= Rename(TU
.index().get());
1212 EXPECT_THAT(Results
.GlobalChanges
.keys(),
1213 UnorderedElementsAre(Main
, testPath("other.cc")));
1215 #ifdef CLANGD_PATH_CASE_INSENSITIVE
1216 // On case-insensitive systems, no duplicates if AST vs index case differs.
1217 // https://github.com/clangd/clangd/issues/665
1218 TU
.Filename
= "MAIN.CC";
1219 Results
= Rename(TU
.index().get());
1220 EXPECT_THAT(Results
.GlobalChanges
.keys(), ElementsAre(Main
));
1221 EXPECT_THAT(Results
.GlobalChanges
[Main
].asTextEdits(),
1222 ElementsAre(newText("xPrime")));
1226 TEST(RenameTest
, MainFileReferencesOnly
) {
1227 // filter out references not from main file.
1228 llvm::StringRef Test
=
1232 // rename references not from main file are not included.
1236 Annotations
Code(Test
);
1237 auto TU
= TestTU::withCode(Code
.code());
1238 TU
.AdditionalFiles
["foo.inc"] = R
"cpp(
1243 auto AST
= TU
.build();
1244 llvm::StringRef NewName
= "abcde";
1247 rename({Code
.point(), NewName
, AST
, testPath(TU
.Filename
)});
1248 ASSERT_TRUE(bool(RenameResult
)) << RenameResult
.takeError() << Code
.point();
1249 ASSERT_EQ(1u, RenameResult
->GlobalChanges
.size());
1250 EXPECT_EQ(applyEdits(std::move(RenameResult
->GlobalChanges
)).front().second
,
1251 expectedResult(Code
, NewName
));
1254 TEST(RenameTest
, NoRenameOnSymbolsFromSystemHeaders
) {
1255 llvm::StringRef Test
=
1262 void foo() { at^oi("9000"); }
1265 Annotations
Code(Test
);
1266 auto TU
= TestTU::withCode(Code
.code());
1267 TU
.AdditionalFiles
["system"] = R
"cpp(
1268 class SystemSymbol {};
1270 TU
.AdditionalFiles
["cstdlib"] = R
"cpp(
1271 int atoi(const char *str);
1273 TU
.ExtraArgs
= {"-isystem", testRoot()};
1274 auto AST
= TU
.build();
1275 llvm::StringRef NewName
= "abcde";
1277 // Clangd will not allow renaming symbols from the system headers for
1279 for (auto &Point
: Code
.points()) {
1280 auto Results
= rename({Point
, NewName
, AST
, testPath(TU
.Filename
)});
1281 EXPECT_FALSE(Results
) << "expected rename returned an error: "
1283 auto ActualMessage
= llvm::toString(Results
.takeError());
1284 EXPECT_THAT(ActualMessage
, testing::HasSubstr("not a supported kind"));
1288 TEST(RenameTest
, ProtobufSymbolIsExcluded
) {
1289 Annotations
Code("Prot^obuf buf;");
1290 auto TU
= TestTU::withCode(Code
.code());
1292 R
"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1295 TU
.HeaderFilename
= "protobuf.pb.h";
1296 auto AST
= TU
.build();
1297 auto Results
= rename({Code
.point(), "newName", AST
, testPath(TU
.Filename
)});
1298 EXPECT_FALSE(Results
);
1299 EXPECT_THAT(llvm::toString(Results
.takeError()),
1300 testing::HasSubstr("not a supported kind"));
1303 TEST(RenameTest
, PrepareRename
) {
1304 Annotations
FooH("void func();");
1305 Annotations
FooCC(R
"cpp(
1309 std::string FooHPath
= testPath("foo.h");
1310 std::string FooCCPath
= testPath("foo.cc");
1312 FS
.Files
[FooHPath
] = std::string(FooH
.code());
1313 FS
.Files
[FooCCPath
] = std::string(FooCC
.code());
1315 auto ServerOpts
= ClangdServer::optsForTest();
1316 ServerOpts
.BuildDynamicSymbolIndex
= true;
1318 trace::TestTracer Tracer
;
1319 MockCompilationDatabase CDB
;
1320 ClangdServer
Server(CDB
, FS
, ServerOpts
);
1321 runAddDocument(Server
, FooHPath
, FooH
.code());
1322 runAddDocument(Server
, FooCCPath
, FooCC
.code());
1324 auto Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1325 /*NewName=*/std::nullopt
, {});
1326 // Verify that for multi-file rename, we only return main-file occurrences.
1327 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1328 // We don't know the result is complete in prepareRename (passing a nullptr
1329 // index internally), so GlobalChanges should be empty.
1330 EXPECT_TRUE(Results
->GlobalChanges
.empty());
1331 EXPECT_THAT(FooCC
.ranges(),
1332 testing::UnorderedElementsAreArray(Results
->LocalChanges
));
1335 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1336 /*NewName=*/std::string("int"), {});
1337 EXPECT_FALSE(Results
);
1338 EXPECT_THAT(llvm::toString(Results
.takeError()),
1339 testing::HasSubstr("keyword"));
1340 EXPECT_THAT(Tracer
.takeMetric("rename_name_invalid", "Keywords"),
1343 for (std::string BadIdent
: {"foo!bar", "123foo", "😀@"}) {
1344 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1345 /*NewName=*/BadIdent
, {});
1346 EXPECT_FALSE(Results
);
1347 EXPECT_THAT(llvm::toString(Results
.takeError()),
1348 testing::HasSubstr("identifier"));
1349 EXPECT_THAT(Tracer
.takeMetric("rename_name_invalid", "BadIdentifier"),
1352 for (std::string GoodIdent
: {"fooBar", "__foo$", "😀"}) {
1353 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1354 /*NewName=*/GoodIdent
, {});
1355 EXPECT_TRUE(bool(Results
));
1359 TEST(CrossFileRenameTests
, DirtyBuffer
) {
1360 Annotations
FooCode("class [[Foo]] {};");
1361 std::string FooPath
= testPath("foo.cc");
1362 Annotations
FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1363 Annotations
BarCode("void [[Bar]]() {}");
1364 std::string BarPath
= testPath("bar.cc");
1365 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1366 // references from bar.cc.
1367 FileSymbols
FSymbols(IndexContents::All
);
1368 FSymbols
.update(FooPath
, nullptr, buildRefSlab(FooCode
, "Foo", FooPath
),
1370 FSymbols
.update(BarPath
, nullptr, buildRefSlab(BarCode
, "Bar", BarPath
),
1372 auto Index
= FSymbols
.buildIndex(IndexType::Light
);
1374 Annotations
MainCode("class [[Fo^o]] {};");
1375 auto MainFilePath
= testPath("main.cc");
1376 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemFS
=
1377 new llvm::vfs::InMemoryFileSystem
;
1378 InMemFS
->addFile(FooPath
, 0,
1379 llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer
.code()));
1381 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1382 // respect the dirty buffer.
1383 TestTU TU
= TestTU::withCode(MainCode
.code());
1384 auto AST
= TU
.build();
1385 llvm::StringRef NewName
= "newName";
1387 rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1388 createOverlay(getVFSFromAST(AST
), InMemFS
), Index
.get()});
1389 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1391 applyEdits(std::move(Results
->GlobalChanges
)),
1392 UnorderedElementsAre(
1393 Pair(Eq(FooPath
), Eq(expectedResult(FooDirtyBuffer
, NewName
))),
1394 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1396 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1397 // so we should read file content from VFS.
1398 MainCode
= Annotations("void [[Bar]]() { [[B^ar]](); }");
1399 TU
= TestTU::withCode(MainCode
.code());
1400 // Set a file "bar.cc" on disk.
1401 TU
.AdditionalFiles
["bar.cc"] = std::string(BarCode
.code());
1403 Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1404 createOverlay(getVFSFromAST(AST
), InMemFS
), Index
.get()});
1405 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1407 applyEdits(std::move(Results
->GlobalChanges
)),
1408 UnorderedElementsAre(
1409 Pair(Eq(BarPath
), Eq(expectedResult(BarCode
, NewName
))),
1410 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1412 // Run rename on a pagination index which couldn't return all refs in one
1413 // request, we reject rename on this case.
1414 class PaginationIndex
: public SymbolIndex
{
1415 bool refs(const RefsRequest
&Req
,
1416 llvm::function_ref
<void(const Ref
&)> Callback
) const override
{
1417 return true; // has more references
1421 const FuzzyFindRequest
&Req
,
1422 llvm::function_ref
<void(const Symbol
&)> Callback
) const override
{
1426 lookup(const LookupRequest
&Req
,
1427 llvm::function_ref
<void(const Symbol
&)> Callback
) const override
{}
1429 void relations(const RelationsRequest
&Req
,
1430 llvm::function_ref
<void(const SymbolID
&, const Symbol
&)>
1431 Callback
) const override
{}
1433 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
1434 indexedFiles() const override
{
1435 return [](llvm::StringRef
) { return IndexContents::None
; };
1438 size_t estimateMemoryUsage() const override
{ return 0; }
1440 Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1441 createOverlay(getVFSFromAST(AST
), InMemFS
), &PIndex
});
1442 EXPECT_FALSE(Results
);
1443 EXPECT_THAT(llvm::toString(Results
.takeError()),
1444 testing::HasSubstr("too many occurrences"));
1447 TEST(CrossFileRenameTests
, DeduplicateRefsFromIndex
) {
1448 auto MainCode
= Annotations("int [[^x]] = 2;");
1449 auto MainFilePath
= testPath("main.cc");
1450 auto BarCode
= Annotations("int [[x]];");
1451 auto BarPath
= testPath("bar.cc");
1452 auto TU
= TestTU::withCode(MainCode
.code());
1453 // Set a file "bar.cc" on disk.
1454 TU
.AdditionalFiles
["bar.cc"] = std::string(BarCode
.code());
1455 auto AST
= TU
.build();
1456 std::string BarPathURI
= URI::create(BarPath
).toString();
1457 Ref XRefInBarCC
= refWithRange(BarCode
.range(), BarPathURI
);
1458 // The index will return duplicated refs, our code should be robost to handle
1460 class DuplicatedXRefIndex
: public SymbolIndex
{
1462 DuplicatedXRefIndex(const Ref
&ReturnedRef
) : ReturnedRef(ReturnedRef
) {}
1463 bool refs(const RefsRequest
&Req
,
1464 llvm::function_ref
<void(const Ref
&)> Callback
) const override
{
1465 // Return two duplicated refs.
1466 Callback(ReturnedRef
);
1467 Callback(ReturnedRef
);
1471 bool fuzzyFind(const FuzzyFindRequest
&,
1472 llvm::function_ref
<void(const Symbol
&)>) const override
{
1475 void lookup(const LookupRequest
&,
1476 llvm::function_ref
<void(const Symbol
&)>) const override
{}
1478 void relations(const RelationsRequest
&,
1479 llvm::function_ref
<void(const SymbolID
&, const Symbol
&)>)
1482 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
1483 indexedFiles() const override
{
1484 return [](llvm::StringRef
) { return IndexContents::None
; };
1487 size_t estimateMemoryUsage() const override
{ return 0; }
1489 } DIndex(XRefInBarCC
);
1490 llvm::StringRef NewName
= "newName";
1491 auto Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1492 getVFSFromAST(AST
), &DIndex
});
1493 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1495 applyEdits(std::move(Results
->GlobalChanges
)),
1496 UnorderedElementsAre(
1497 Pair(Eq(BarPath
), Eq(expectedResult(BarCode
, NewName
))),
1498 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1501 TEST(CrossFileRenameTests
, WithUpToDateIndex
) {
1502 MockCompilationDatabase CDB
;
1503 CDB
.ExtraClangFlags
= {"-xobjective-c++"};
1504 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1505 // expected rename occurrences.
1507 llvm::StringRef FooH
;
1508 llvm::StringRef FooCC
;
1520 [[Foo]]::[[Foo]]() {}
1521 [[Foo]]::~[[Foo]]() {}
1531 template <typename T>
1533 // FIXME: explicit template specializations are not supported due the
1534 // clangd index limitations.
1536 class Foo<double> {};
1554 void Foo::[[foo]]() {}
1565 virtual void [[foo]]();
1567 class Derived1 : public Base {
1568 void [[f^oo]]() override;
1576 void Base::[[foo]]() {}
1577 void Derived1::[[foo]]() {}
1579 class Derived2 : public Derived1 {
1580 void [[foo]]() override {};
1583 void func(Base* b, Derived1* d1,
1584 Derived2* d2, NotDerived* nd) {
1592 {// virtual templated method
1594 template <typename> class Foo { virtual void [[m]](); };
1595 class Bar : Foo<int> { void [[^m]]() override; };
1600 template<typename T> void Foo<T>::[[m]]() {}
1601 // FIXME: not renamed as the index doesn't see this as an override of
1602 // the canonical Foo<T>::m().
1603 // https://github.com/clangd/clangd/issues/1325
1604 class Baz : Foo<float> { void m() override; };
1607 // rename on constructor and destructor.
1616 [[Foo]]::[[Foo]]() {}
1617 [[Foo]]::~[[Foo]]() {}
1641 typedef int [[IN^T]];
1652 using [[I^NT]] = int;
1663 static const int [[VA^R]] = 123;
1673 enum class [[K^ind]] { ABC };
1678 return [[Kind]]::ABC;
1685 enum class Kind { [[A^BC]] };
1690 return Kind::[[ABC]];
1695 // Implicit references in macro expansions.
1711 // Objective-C classes.
1718 @implementation [[Foo]]
1721 void func([[Foo]] *f) {}
1726 trace::TestTracer Tracer
;
1727 for (const auto &T
: Cases
) {
1728 SCOPED_TRACE(T
.FooH
);
1729 Annotations
FooH(T
.FooH
);
1730 Annotations
FooCC(T
.FooCC
);
1731 std::string FooHPath
= testPath("foo.h");
1732 std::string FooCCPath
= testPath("foo.cc");
1735 FS
.Files
[FooHPath
] = std::string(FooH
.code());
1736 FS
.Files
[FooCCPath
] = std::string(FooCC
.code());
1738 auto ServerOpts
= ClangdServer::optsForTest();
1739 ServerOpts
.BuildDynamicSymbolIndex
= true;
1740 ClangdServer
Server(CDB
, FS
, ServerOpts
);
1742 // Add all files to clangd server to make sure the dynamic index has been
1744 runAddDocument(Server
, FooHPath
, FooH
.code());
1745 runAddDocument(Server
, FooCCPath
, FooCC
.code());
1747 llvm::StringRef NewName
= "NewName";
1748 for (const auto &RenamePos
: FooH
.points()) {
1749 EXPECT_THAT(Tracer
.takeMetric("rename_files"), SizeIs(0));
1750 auto FileEditsList
=
1751 llvm::cantFail(runRename(Server
, FooHPath
, RenamePos
, NewName
, {}));
1752 EXPECT_THAT(Tracer
.takeMetric("rename_files"), ElementsAre(2));
1754 applyEdits(std::move(FileEditsList
.GlobalChanges
)),
1755 UnorderedElementsAre(
1756 Pair(Eq(FooHPath
), Eq(expectedResult(T
.FooH
, NewName
))),
1757 Pair(Eq(FooCCPath
), Eq(expectedResult(T
.FooCC
, NewName
)))));
1762 TEST(CrossFileRenameTests
, CrossFileOnLocalSymbol
) {
1763 // cross-file rename should work for function-local symbols, even there is no
1765 Annotations
Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
1766 auto TU
= TestTU::withCode(Code
.code());
1767 auto Path
= testPath(TU
.Filename
);
1768 auto AST
= TU
.build();
1769 llvm::StringRef NewName
= "newName";
1770 auto Results
= rename({Code
.point(), NewName
, AST
, Path
});
1771 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1773 applyEdits(std::move(Results
->GlobalChanges
)),
1774 UnorderedElementsAre(Pair(Eq(Path
), Eq(expectedResult(Code
, NewName
)))));
1777 TEST(CrossFileRenameTests
, BuildRenameEdits
) {
1778 Annotations
Code("[[😂]]");
1779 auto LSPRange
= Code
.range();
1780 llvm::StringRef FilePath
= "/test/TestTU.cpp";
1781 llvm::StringRef NewName
= "abc";
1782 auto Edit
= buildRenameEdit(FilePath
, Code
.code(), {LSPRange
}, NewName
);
1783 ASSERT_TRUE(bool(Edit
)) << Edit
.takeError();
1784 ASSERT_EQ(1UL, Edit
->Replacements
.size());
1785 EXPECT_EQ(FilePath
, Edit
->Replacements
.begin()->getFilePath());
1786 EXPECT_EQ(4UL, Edit
->Replacements
.begin()->getLength());
1788 // Test invalid range.
1789 LSPRange
.end
= {10, 0}; // out of range
1790 Edit
= buildRenameEdit(FilePath
, Code
.code(), {LSPRange
}, NewName
);
1792 EXPECT_THAT(llvm::toString(Edit
.takeError()),
1793 testing::HasSubstr("fail to convert"));
1795 // Normal ascii characters.
1796 Annotations
T(R
"cpp(
1801 Edit
= buildRenameEdit(FilePath
, T
.code(), T
.ranges(), NewName
);
1802 ASSERT_TRUE(bool(Edit
)) << Edit
.takeError();
1803 EXPECT_EQ(applyEdits(FileEdits
{{T
.code(), std::move(*Edit
)}}).front().second
,
1804 expectedResult(T
, NewName
));
1807 TEST(CrossFileRenameTests
, adjustRenameRanges
) {
1808 // Ranges in IndexedCode indicate the indexed occurrences;
1809 // ranges in DraftCode indicate the expected mapped result, empty indicates
1810 // we expect no matched result found.
1812 llvm::StringRef IndexedCode
;
1813 llvm::StringRef DraftCode
;
1816 // both line and column are changed, not a near miss.
1837 R
"cpp(int [[x]] = 0; void foo(int x);)cpp",
1838 R
"cpp(double [[x]] = 0; void foo(double x);)cpp",
1853 LangOptions LangOpts
;
1854 LangOpts
.CPlusPlus
= true;
1855 for (const auto &T
: Tests
) {
1856 SCOPED_TRACE(T
.DraftCode
);
1857 Annotations
Draft(T
.DraftCode
);
1858 auto ActualRanges
= adjustRenameRanges(
1859 Draft
.code(), "x", Annotations(T
.IndexedCode
).ranges(), LangOpts
);
1861 EXPECT_THAT(Draft
.ranges(), testing::IsEmpty());
1863 EXPECT_THAT(Draft
.ranges(),
1864 testing::UnorderedElementsAreArray(*ActualRanges
));
1868 TEST(RangePatchingHeuristic
, GetMappedRanges
) {
1869 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
1870 // there are no mapped ranges.
1872 llvm::StringRef IndexedCode
;
1873 llvm::StringRef LexedCode
;
1881 // both line and column are changed, not a near miss.
1919 ^[[]] ^[[]] // column is shifted.
1932 [[]] [[]] // not mapped (both line and column are changed).
1946 // insert a new line
1949 [[]] // additional range
1952 [[]] // additional range
1956 // non-distinct result (two best results), not a near miss
1970 for (const auto &T
: Tests
) {
1971 SCOPED_TRACE(T
.IndexedCode
);
1972 auto Lexed
= Annotations(T
.LexedCode
);
1973 auto LexedRanges
= Lexed
.ranges();
1974 std::vector
<Range
> ExpectedMatches
;
1975 for (auto P
: Lexed
.points()) {
1976 auto Match
= llvm::find_if(LexedRanges
, [&P
](const Range
& R
) {
1977 return R
.start
== P
;
1979 ASSERT_NE(Match
, LexedRanges
.end());
1980 ExpectedMatches
.push_back(*Match
);
1984 getMappedRanges(Annotations(T
.IndexedCode
).ranges(), LexedRanges
);
1986 EXPECT_THAT(ExpectedMatches
, IsEmpty());
1988 EXPECT_THAT(ExpectedMatches
, UnorderedElementsAreArray(*Mapped
));
1992 TEST(CrossFileRenameTests
, adjustmentCost
) {
1994 llvm::StringRef RangeCode
;
1995 size_t ExpectedCost
;
1999 $idx[[]]$lex[[]] // diff: 0
2006 $lex[[]] // line diff: +1
2008 $lex[[]] // line diff: +1
2010 $lex[[]] // line diff: +1
2014 $lex[[]] // line diff: +2
2021 $lex[[]] // line diff: +1
2024 $lex[[]] // line diff: +2
2028 $lex[[]] // line diff: +3
2037 $lex[[]] // line diff: +3
2040 $lex[[]] // line diff: +2
2042 $lex[[]] // line diff: +1
2049 $lex[[]] // line diff: +1
2050 $lex[[]] // line diff: -2
2056 $lex[[]] // line diff: +3
2062 $idx[[]] $lex[[]] // column diff: +1
2063 $idx[[]]$lex[[]] // diff: 0
2070 $lex[[]] // diff: +1
2071 $idx[[]] $lex[[]] // column diff: +1
2072 $idx[[]]$lex[[]] // diff: 0
2078 $idx[[]] $lex[[]] // column diff: +1
2084 // column diffs: +1, +2, +3
2085 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2090 for (const auto &T
: Tests
) {
2091 SCOPED_TRACE(T
.RangeCode
);
2092 Annotations
C(T
.RangeCode
);
2093 std::vector
<size_t> MappedIndex
;
2094 for (size_t I
= 0; I
< C
.ranges("lex").size(); ++I
)
2095 MappedIndex
.push_back(I
);
2096 EXPECT_EQ(renameRangeAdjustmentCost(C
.ranges("idx"), C
.ranges("lex"),
2103 } // namespace clangd
2104 } // namespace clang