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 std::vector
<SymbolRange
> symbolRanges(llvm::ArrayRef
<Range
> Ranges
) {
107 std::vector
<SymbolRange
> Result
;
108 for (const auto &R
: Ranges
)
109 Result
.emplace_back(R
);
113 TEST(RenameTest
, WithinFileRename
) {
114 // For each "^" this test moves cursor to its location and applies renaming
115 // while checking that all identifiers in [[]] ranges are also renamed.
116 llvm::StringRef Tests
[] = {
136 if (auto [[^foo]] = 5) {
142 // Class, its constructor and destructor.
147 [[F^oo]] *foo(int x);
151 [[F^oo]]::[[Fo^o]]() {}
152 [[F^oo]]::~[[Fo^o]]() {}
153 [[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
156 // Template class, its constructor and destructor.
158 template <typename T>
166 [[F^oo]]<T>::[[Fo^o]]() {}
169 [[F^oo]]<T>::~[[Fo^o]]() {}
172 // Template class constructor.
183 [[F^oo]]::[[Fo^o]]() {}
186 // Class in template argument.
189 template <typename T> void func();
190 template <typename T> class Baz {};
198 // Forward class declaration without definition.
208 void Baz() { [[F^oo]](); }
212 // Templated method instantiation.
217 static T [[f^oo]]() {}
221 Foo<int>::[[f^oo]]();
232 Foo<int>().[[f^oo]]();
236 // Template class (partial) specializations.
238 template <typename T>
242 class [[F^oo]]<bool> {};
243 template <typename T>
244 class [[F^oo]]<T*> {};
253 // Incomplete class specializations
255 template <typename T>
257 void func([[F^oo]]<int>);
260 // Template class instantiations.
262 template <typename T>
265 T foo(T arg, T& ref, T* ptr) {
269 value = static_cast<T>(number);
272 static void foo(T value) {}
276 template <typename T>
286 [[F^oo]]<int>::foo(0);
290 [[F^oo]]<bool>::foo(false);
294 // Template class methods.
296 template <typename T>
304 A<double>().[[f^oo]]();
305 A<float>().[[f^oo]]();
309 // Templated class specialization.
311 template<typename T, typename U=bool>
314 template<typename T, typename U>
317 template<typename T=int, typename U>
321 template<typename T=float, typename U=int>
324 template<typename T, typename U>
328 // Function template specialization.
330 template<typename T=int, typename U=bool>
333 template<typename T, typename U>
337 template<typename T, typename U>
340 template<typename T=int, typename U=bool>
344 template<typename T=int, typename U=bool>
347 template<typename T, typename U>
351 template <typename T>
355 void [[f^oo]](int a);
362 // Variable template.
364 template <typename T, int U>
365 bool [[F^oo]] = true;
367 // Explicit template specialization
369 bool [[F^oo]]<int, 0> = false;
371 // Partial template specialization
372 template <typename T>
373 bool [[F^oo]]<T, 1> = false;
376 // Ref to the explicit template specialization
378 // Ref to the primary template.
383 // Complicated class type.
385 // Forward declaration.
388 virtual int getValue() const = 0;
391 class [[F^oo]] : public Baz {
393 [[F^oo]](int value = 0) : x(value) {}
395 [[F^oo]] &operator++(int);
397 bool operator<([[Foo]] const &rhs);
398 int getValue() const;
404 [[F^oo]] *Pointer = 0;
405 [[F^oo]] Variable = [[Foo]](10);
406 for ([[F^oo]] it; it < Variable; it++);
407 const [[F^oo]] *C = new [[Foo]]();
408 const_cast<[[F^oo]] *>(C)->getValue();
410 const Baz &BazReference = foo;
411 const Baz *BazPointer = &foo;
412 reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
413 static_cast<const [[^Foo]] &>(BazReference).getValue();
414 static_cast<const [[^Foo]] *>(BazPointer)->getValue();
418 // Static class member.
421 static Foo *[[Static^Member]];
424 Foo* Foo::[[Static^Member]] = nullptr;
427 Foo* Pointer = Foo::[[Static^Member]];
431 // Reference in lambda parameters.
435 template <class R, class... ArgTypes>
436 class function<R(ArgTypes...)> {
438 template <typename Functor>
439 function(Functor f) {}
443 R operator()(ArgTypes...) const {}
449 function<void([[Old]])> func;
454 // Destructor explicit call.
461 [[Foo^]]::~[[^Foo]]() {}
465 f.~/*something*/[[^Foo]]();
470 // Derived destructor explicit call.
473 class Derived : public [[Bas^e]] {};
476 [[Bas^e]] *foo = new Derived();
477 foo->[[^Base]]::~[[^Base]]();
481 // CXXConstructor initializer list.
489 Qux::Qux() : [[F^oo]]() {}
500 #define MACRO(a) foo(a)
511 // no rename inside macro body.
523 M2(M1()); // foo is inside the nested macro body.
527 // MemberExpr in macros
534 #define MACRO(a) qux(a)
540 int y = baz.[[F^oo]];
544 // Fields in classes & partial and full specialiations.
548 T [[Vari^able]] = 42;
553 f.[[Varia^ble]] = 9000;
557 template<typename T, typename U>
566 struct Foo<T, bool> {
568 void bar() { ++[[Var^iable]]; }
572 Foo<unsigned, bool> f;
573 f.[[Var^iable]] = 9000;
577 template<typename T, typename U>
586 struct Foo<T, bool> {
588 void bar() { ++Variable; }
592 struct Foo<unsigned, bool> {
593 unsigned [[Var^iable]];
594 void bar() { ++[[Var^iable]]; }
598 Foo<unsigned, bool> f;
599 f.[[Var^iable]] = 9000;
605 static int [[Var^iable]];
608 int Foo::[[Var^iable]] = 42;
611 int LocalInt = Foo::[[Var^iable]];
617 static T [[Var^iable]];
621 int Foo<int>::[[Var^iable]] = 42;
624 bool Foo<bool>::[[Var^iable]] = true;
627 int LocalInt = Foo<int>::[[Var^iable]];
628 bool LocalBool = Foo<bool>::[[Var^iable]];
632 // Template parameters.
634 template <typename [[^T]]>
636 [[T^]] foo([[T^]] arg, [[T^]]& ref, [[^T]]* ptr) {
639 value = ([[T^]])number;
640 value = static_cast<[[^T]]>(number);
643 static void foo([[T^]] value) {}
651 class basic_string {};
652 typedef basic_string [[s^tring]];
655 ns::[[s^tring]] foo();
665 int Baz = A::[[^Foo]];
675 Foo = A::[[F^oo]] + Baz;
684 namespace a { namespace b { void foo(); } }
685 namespace [[^x]] = a::b;
693 enum [[C^olor]] { Red, Green, Blue };
696 c = [[C^olor]]::Blue;
702 enum class [[K^ind]] { ABC };
709 // Template class in template argument list.
713 template <template<typename> class Z> struct Bar { };
714 template <> struct Bar<[[F^oo]]> {};
717 // Designated initializer.
722 Bar bar { .[[^Foo]] = 42 };
725 // Nested designated initializer.
733 // FIXME: v selecting here results in renaming Field.
734 Bar bar { .[[Foo]].Field = 42 };
743 Bar bar { .Foo.[[^Field]] = 42 };
748 template <typename T>
751 template <typename T>
752 using [[Fo^o]] = X<T>;
769 // Alias within a namespace.
771 namespace x { class X {}; }
773 using [[Fo^o]] = x::X;
781 // Alias within macros.
783 namespace x { class Old {}; }
785 #define REF(alias) alias alias_var;
788 using old##Alias = x::old; \
793 [[Old^Alias]] old_alias;
797 ns::[[Old^Alias]] Bar;
801 // User defined conversion.
810 operator [[F^oo]]() {
817 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
821 // ObjC, should not crash.
828 // Issue 170: Rename symbol introduced by UsingDecl
830 namespace ns { void [[f^oo]](); }
840 // Issue 170: using decl that imports multiple overloads
841 // -> Only the overload under the cursor is renamed
843 namespace ns { int [[^foo]](int); char foo(char); }
851 // ObjC class with a category.
855 @implementation [[F^oo]]
857 @interface [[Fo^o]] (Category)
859 @implementation [[F^oo]] (Category)
862 void func([[Fo^o]] *f) {}
865 llvm::StringRef NewName
= "NewName";
866 for (llvm::StringRef T
: Tests
) {
869 auto TU
= TestTU::withCode(Code
.code());
870 TU
.ExtraArgs
.push_back("-xobjective-c++");
871 auto AST
= TU
.build();
872 auto Index
= TU
.index();
873 for (const auto &RenamePos
: Code
.points()) {
875 rename({RenamePos
, NewName
, AST
, testPath(TU
.Filename
),
876 getVFSFromAST(AST
), Index
.get()});
877 ASSERT_TRUE(bool(RenameResult
)) << RenameResult
.takeError();
878 ASSERT_EQ(1u, RenameResult
->GlobalChanges
.size());
880 applyEdits(std::move(RenameResult
->GlobalChanges
)).front().second
,
881 expectedResult(Code
, NewName
));
886 TEST(RenameTest
, ObjCWithinFileRename
) {
888 /// Annotated source code that should be renamed. Every point (indicated by
889 /// `^`) will be used as a rename location.
890 llvm::StringRef Input
;
891 /// The new name that should be given to the rename locaitons.
892 llvm::StringRef NewName
;
893 /// The expected rename source code or `nullopt` if we expect rename to
895 std::optional
<llvm::StringRef
> Expected
;
897 TestCase Tests
[] = {// Simple rename
902 - (int)performA^ction:(int)action w^ith:(int)value;
905 - (int)performAc^tion:(int)action w^ith:(int)value {
906 return [self performAction:action with:value];
911 "performNewAction:by:",
915 - (int)performNewAction:(int)action by:(int)value;
918 - (int)performNewAction:(int)action by:(int)value {
919 return [self performNewAction:action by:value];
924 // Rename selector with macro
928 #define mySelector - (int)performAction:(int)action with:(int)value
934 return [self performAction:action with:value];
939 "performNewAction:by:",
943 // Rename selector in macro definition
947 #define mySelector - (int)perform^Action:(int)action with:(int)value
953 return [self performAction:action with:value];
958 "performNewAction:by:",
962 // Don't rename `@selector`
963 // `@selector` is not tied to a single selector. Eg. there
965 // classes in the codebase that implement that selector.
966 // It's thus more like
967 // a string literal and we shouldn't rename it.
972 - (void)performA^ction:(int)action with:(int)value;
975 - (void)performAction:(int)action with:(int)value {
976 SEL mySelector = @selector(performAction:with:);
981 "performNewAction:by:",
985 - (void)performNewAction:(int)action by:(int)value;
988 - (void)performNewAction:(int)action by:(int)value {
989 SEL mySelector = @selector(performAction:with:);
994 // Fail if rename initiated inside @selector
999 - (void)performAction:(int)action with:(int)value;
1002 - (void)performAction:(int)action with:(int)value {
1003 SEL mySelector = @selector(perfo^rmAction:with:);
1008 "performNewAction:by:",
1012 for (TestCase T
: Tests
) {
1013 SCOPED_TRACE(T
.Input
);
1014 Annotations
Code(T
.Input
);
1015 auto TU
= TestTU::withCode(Code
.code());
1016 TU
.ExtraArgs
.push_back("-xobjective-c");
1017 auto AST
= TU
.build();
1018 auto Index
= TU
.index();
1019 for (const auto &RenamePos
: Code
.points()) {
1021 rename({RenamePos
, T
.NewName
, AST
, testPath(TU
.Filename
),
1022 getVFSFromAST(AST
), Index
.get()});
1023 if (std::optional
<StringRef
> Expected
= T
.Expected
) {
1024 ASSERT_TRUE(bool(RenameResult
)) << RenameResult
.takeError();
1025 ASSERT_EQ(1u, RenameResult
->GlobalChanges
.size());
1027 applyEdits(std::move(RenameResult
->GlobalChanges
)).front().second
,
1030 ASSERT_FALSE(bool(RenameResult
));
1031 consumeError(RenameResult
.takeError());
1037 TEST(RenameTest
, Renameable
) {
1040 const char* ErrorMessage
; // null if no error
1042 llvm::StringRef NewName
= "MockName";
1044 const bool HeaderFile
= true;
1046 {R
"cpp(// allow -- function-local
1047 void f(int [[Lo^cal]]) {
1051 nullptr, HeaderFile
},
1053 {R
"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
1055 class Unin^dexable {};
1058 "not eligible for indexing", HeaderFile
},
1060 {R
"cpp(// disallow -- namespace symbol isn't supported
1063 "not a supported kind", HeaderFile
},
1065 {R
"cpp(// disallow - category rename.
1068 @interface Foo (Cate^gory)
1071 "Cannot rename symbol: there is no symbol at the given location",
1079 "not a supported kind", HeaderFile
},
1083 struct X { X operator++(int); };
1084 void f(X x) {x+^+;})cpp",
1085 "no symbol", HeaderFile
},
1089 - (int)[[fo^o]]:(int)x;
1092 nullptr, HeaderFile
, "newName:"},
1093 {R
"cpp(//disallow as : count must match
1098 "invalid name: the chosen name \"MockName\" is not a valid identifier",
1102 - (int)[[o^ne]]:(int)one two:(int)two;
1105 nullptr, HeaderFile
, "a:two:"},
1108 - (int)[[o^ne]]:(int)one [[two]]:(int)two;
1111 nullptr, HeaderFile
, "a:b:"},
1114 - (int)o^ne:(int)one [[two]]:(int)two;
1117 nullptr, HeaderFile
, "one:three:"},
1122 template <typename T> void f(T t) {
1125 "multiple symbols", !HeaderFile
},
1127 {R
"cpp(// disallow rename on unrelated token.
1130 "no symbol", !HeaderFile
},
1132 {R
"cpp(// disallow rename on unrelated token.
1133 temp^late<typename T>
1136 "no symbol", !HeaderFile
},
1144 "conflict", !HeaderFile
, "Conflict"},
1150 "conflict", !HeaderFile
, "Conflict"},
1158 "conflict", !HeaderFile
, "Conflict"},
1166 "conflict", !HeaderFile
, "Conflict"},
1170 enum E { // transparent context.
1174 "conflict", !HeaderFile
, "Conflict"},
1183 "conflict", !HeaderFile
, "Conflict"},
1187 if (int Conflict = 42) {
1192 "conflict", !HeaderFile
, "Conflict"},
1196 if (int Conflict = 42) {
1202 "conflict", !HeaderFile
, "Conflict"},
1206 if (int V^ar = 42) {
1212 "conflict", !HeaderFile
, "Conflict"},
1216 while (int V^ar = 10) {
1217 bool Conflict = true;
1221 "conflict", !HeaderFile
, "Conflict"},
1225 for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
1231 "conflict", !HeaderFile
, "Conflict"},
1235 for (int V^ar = 14, Conflict = 42;;) {
1239 "conflict", !HeaderFile
, "Conflict"},
1242 void func(int Conflict) {
1246 "conflict", !HeaderFile
, "Conflict"},
1251 void func(int V^ar) {
1255 "conflict", !HeaderFile
, "Conflict"},
1257 {R
"cpp(// No conflict: only forward declaration's argument is renamed.
1258 void func(int [[V^ar]]);
1260 void func(int Var) {
1264 nullptr, !HeaderFile
, "Conflict"},
1267 void func(int V^ar, int Conflict) {
1270 "conflict", !HeaderFile
, "Conflict"},
1274 void [[o^therFunc]](double);
1276 nullptr, !HeaderFile
, "func"},
1280 void [[o^therFunc]](double);
1283 nullptr, !HeaderFile
, "func"},
1288 "\"const\" is a keyword", !HeaderFile
, "const"},
1290 {R
"cpp(// Trying to rename into the same name, SameName == SameName.
1295 "new name is the same", !HeaderFile
, "SameName"},
1296 {R
"cpp(// Ensure it doesn't associate base specifier with base name.
1298 struct B : priv^ate A {};
1300 "Cannot rename symbol: there is no symbol at the given location", false},
1301 {R
"cpp(// Ensure it doesn't associate base specifier with base name.
1304 A() : inva^lid(0) {}
1307 "no symbol", false},
1309 {R
"cpp(// FIXME we probably want to rename both overloads here,
1310 // but renaming currently assumes there's only a
1311 // single canonical declaration.
1312 namespace ns { int foo(int); char foo(char); }
1315 "there are multiple symbols at the given location", !HeaderFile
},
1320 using namespace std;
1324 nullptr, !HeaderFile
},
1327 for (const auto& Case
: Cases
) {
1328 SCOPED_TRACE(Case
.Code
);
1329 Annotations
T(Case
.Code
);
1330 TestTU TU
= TestTU::withCode(T
.code());
1331 TU
.ExtraArgs
.push_back("-fno-delayed-template-parsing");
1332 if (Case
.IsHeaderFile
) {
1333 // We open the .h file as the main file.
1334 TU
.Filename
= "test.h";
1335 // Parsing the .h file as C++ include.
1336 TU
.ExtraArgs
.push_back("-xobjective-c++-header");
1338 auto AST
= TU
.build();
1339 llvm::StringRef NewName
= Case
.NewName
;
1340 auto Results
= rename({T
.point(), NewName
, AST
, testPath(TU
.Filename
)});
1341 bool WantRename
= true;
1342 if (T
.ranges().empty())
1345 assert(Case
.ErrorMessage
&& "Error message must be set!");
1346 EXPECT_FALSE(Results
)
1347 << "expected rename returned an error: " << T
.code();
1348 auto ActualMessage
= llvm::toString(Results
.takeError());
1349 EXPECT_THAT(ActualMessage
, testing::HasSubstr(Case
.ErrorMessage
));
1351 EXPECT_TRUE(bool(Results
)) << "rename returned an error: "
1352 << llvm::toString(Results
.takeError());
1353 EXPECT_EQ(Results
->LocalChanges
, T
.ranges());
1358 MATCHER_P(newText
, T
, "") { return arg
.newText
== T
; }
1360 TEST(RenameTest
, IndexMergeMainFile
) {
1361 Annotations
Code("int ^x();");
1362 TestTU TU
= TestTU::withCode(Code
.code());
1363 TU
.Filename
= "main.cc";
1364 auto AST
= TU
.build();
1366 auto Main
= testPath("main.cc");
1367 auto InMemFS
= llvm::makeIntrusiveRefCnt
<llvm::vfs::InMemoryFileSystem
>();
1368 InMemFS
->addFile(testPath("main.cc"), 0,
1369 llvm::MemoryBuffer::getMemBuffer(Code
.code()));
1370 InMemFS
->addFile(testPath("other.cc"), 0,
1371 llvm::MemoryBuffer::getMemBuffer(Code
.code()));
1373 auto Rename
= [&](const SymbolIndex
*Idx
) {
1374 RenameInputs Inputs
{Code
.point(),
1378 Idx
? createOverlay(getVFSFromAST(AST
), InMemFS
)
1382 auto Results
= rename(Inputs
);
1383 EXPECT_TRUE(bool(Results
)) << llvm::toString(Results
.takeError());
1384 return std::move(*Results
);
1387 // We do not expect to see duplicated edits from AST vs index.
1388 auto Results
= Rename(TU
.index().get());
1389 EXPECT_THAT(Results
.GlobalChanges
.keys(), ElementsAre(Main
));
1390 EXPECT_THAT(Results
.GlobalChanges
[Main
].asTextEdits(),
1391 ElementsAre(newText("xPrime")));
1393 // Sanity check: we do expect to see index results!
1394 TU
.Filename
= "other.cc";
1395 Results
= Rename(TU
.index().get());
1396 EXPECT_THAT(Results
.GlobalChanges
.keys(),
1397 UnorderedElementsAre(Main
, testPath("other.cc")));
1399 #ifdef CLANGD_PATH_CASE_INSENSITIVE
1400 // On case-insensitive systems, no duplicates if AST vs index case differs.
1401 // https://github.com/clangd/clangd/issues/665
1402 TU
.Filename
= "MAIN.CC";
1403 Results
= Rename(TU
.index().get());
1404 EXPECT_THAT(Results
.GlobalChanges
.keys(), ElementsAre(Main
));
1405 EXPECT_THAT(Results
.GlobalChanges
[Main
].asTextEdits(),
1406 ElementsAre(newText("xPrime")));
1410 TEST(RenameTest
, MainFileReferencesOnly
) {
1411 // filter out references not from main file.
1412 llvm::StringRef Test
=
1416 // rename references not from main file are not included.
1420 Annotations
Code(Test
);
1421 auto TU
= TestTU::withCode(Code
.code());
1422 TU
.AdditionalFiles
["foo.inc"] = R
"cpp(
1427 auto AST
= TU
.build();
1428 llvm::StringRef NewName
= "abcde";
1431 rename({Code
.point(), NewName
, AST
, testPath(TU
.Filename
)});
1432 ASSERT_TRUE(bool(RenameResult
)) << RenameResult
.takeError() << Code
.point();
1433 ASSERT_EQ(1u, RenameResult
->GlobalChanges
.size());
1434 EXPECT_EQ(applyEdits(std::move(RenameResult
->GlobalChanges
)).front().second
,
1435 expectedResult(Code
, NewName
));
1438 TEST(RenameTest
, NoRenameOnSymbolsFromSystemHeaders
) {
1439 llvm::StringRef Test
=
1446 void foo() { at^oi("9000"); }
1449 Annotations
Code(Test
);
1450 auto TU
= TestTU::withCode(Code
.code());
1451 TU
.AdditionalFiles
["system"] = R
"cpp(
1452 class SystemSymbol {};
1454 TU
.AdditionalFiles
["cstdlib"] = R
"cpp(
1455 int atoi(const char *str);
1457 TU
.ExtraArgs
= {"-isystem", testRoot()};
1458 auto AST
= TU
.build();
1459 llvm::StringRef NewName
= "abcde";
1461 // Clangd will not allow renaming symbols from the system headers for
1463 for (auto &Point
: Code
.points()) {
1464 auto Results
= rename({Point
, NewName
, AST
, testPath(TU
.Filename
)});
1465 EXPECT_FALSE(Results
) << "expected rename returned an error: "
1467 auto ActualMessage
= llvm::toString(Results
.takeError());
1468 EXPECT_THAT(ActualMessage
, testing::HasSubstr("not a supported kind"));
1472 TEST(RenameTest
, ProtobufSymbolIsExcluded
) {
1473 Annotations
Code("Prot^obuf buf;");
1474 auto TU
= TestTU::withCode(Code
.code());
1476 R
"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1479 TU
.HeaderFilename
= "protobuf.pb.h";
1480 auto AST
= TU
.build();
1481 auto Results
= rename({Code
.point(), "newName", AST
, testPath(TU
.Filename
)});
1482 EXPECT_FALSE(Results
);
1483 EXPECT_THAT(llvm::toString(Results
.takeError()),
1484 testing::HasSubstr("not a supported kind"));
1487 TEST(RenameTest
, PrepareRename
) {
1488 Annotations
FooH("void func();");
1489 Annotations
FooCC(R
"cpp(
1493 std::string FooHPath
= testPath("foo.h");
1494 std::string FooCCPath
= testPath("foo.cc");
1496 FS
.Files
[FooHPath
] = std::string(FooH
.code());
1497 FS
.Files
[FooCCPath
] = std::string(FooCC
.code());
1499 auto ServerOpts
= ClangdServer::optsForTest();
1500 ServerOpts
.BuildDynamicSymbolIndex
= true;
1502 trace::TestTracer Tracer
;
1503 MockCompilationDatabase CDB
;
1504 ClangdServer
Server(CDB
, FS
, ServerOpts
);
1505 runAddDocument(Server
, FooHPath
, FooH
.code());
1506 runAddDocument(Server
, FooCCPath
, FooCC
.code());
1508 auto Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1509 /*NewName=*/std::nullopt
, {});
1510 // Verify that for multi-file rename, we only return main-file occurrences.
1511 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1512 // We don't know the result is complete in prepareRename (passing a nullptr
1513 // index internally), so GlobalChanges should be empty.
1514 EXPECT_TRUE(Results
->GlobalChanges
.empty());
1515 EXPECT_THAT(FooCC
.ranges(),
1516 testing::UnorderedElementsAreArray(Results
->LocalChanges
));
1519 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1520 /*NewName=*/std::string("int"), {});
1521 EXPECT_FALSE(Results
);
1522 EXPECT_THAT(llvm::toString(Results
.takeError()),
1523 testing::HasSubstr("keyword"));
1524 EXPECT_THAT(Tracer
.takeMetric("rename_name_invalid", "Keywords"),
1527 for (std::string BadIdent
: {"foo!bar", "123foo", "😀@"}) {
1528 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1529 /*NewName=*/BadIdent
, {});
1530 EXPECT_FALSE(Results
);
1531 EXPECT_THAT(llvm::toString(Results
.takeError()),
1532 testing::HasSubstr("identifier"));
1533 EXPECT_THAT(Tracer
.takeMetric("rename_name_invalid", "BadIdentifier"),
1536 for (std::string GoodIdent
: {"fooBar", "__foo$", "😀"}) {
1537 Results
= runPrepareRename(Server
, FooCCPath
, FooCC
.point(),
1538 /*NewName=*/GoodIdent
, {});
1539 EXPECT_TRUE(bool(Results
));
1543 TEST(CrossFileRenameTests
, DirtyBuffer
) {
1544 Annotations
FooCode("class [[Foo]] {};");
1545 std::string FooPath
= testPath("foo.cc");
1546 Annotations
FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1547 Annotations
BarCode("void [[Bar]]() {}");
1548 std::string BarPath
= testPath("bar.cc");
1549 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1550 // references from bar.cc.
1551 FileSymbols
FSymbols(IndexContents::All
, true);
1552 FSymbols
.update(FooPath
, nullptr, buildRefSlab(FooCode
, "Foo", FooPath
),
1554 FSymbols
.update(BarPath
, nullptr, buildRefSlab(BarCode
, "Bar", BarPath
),
1556 auto Index
= FSymbols
.buildIndex(IndexType::Light
);
1558 Annotations
MainCode("class [[Fo^o]] {};");
1559 auto MainFilePath
= testPath("main.cc");
1560 llvm::IntrusiveRefCntPtr
<llvm::vfs::InMemoryFileSystem
> InMemFS
=
1561 new llvm::vfs::InMemoryFileSystem
;
1562 InMemFS
->addFile(FooPath
, 0,
1563 llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer
.code()));
1565 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1566 // respect the dirty buffer.
1567 TestTU TU
= TestTU::withCode(MainCode
.code());
1568 auto AST
= TU
.build();
1569 llvm::StringRef NewName
= "newName";
1571 rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1572 createOverlay(getVFSFromAST(AST
), InMemFS
), Index
.get()});
1573 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1575 applyEdits(std::move(Results
->GlobalChanges
)),
1576 UnorderedElementsAre(
1577 Pair(Eq(FooPath
), Eq(expectedResult(FooDirtyBuffer
, NewName
))),
1578 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1580 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1581 // so we should read file content from VFS.
1582 MainCode
= Annotations("void [[Bar]]() { [[B^ar]](); }");
1583 TU
= TestTU::withCode(MainCode
.code());
1584 // Set a file "bar.cc" on disk.
1585 TU
.AdditionalFiles
["bar.cc"] = std::string(BarCode
.code());
1587 Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1588 createOverlay(getVFSFromAST(AST
), InMemFS
), Index
.get()});
1589 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1591 applyEdits(std::move(Results
->GlobalChanges
)),
1592 UnorderedElementsAre(
1593 Pair(Eq(BarPath
), Eq(expectedResult(BarCode
, NewName
))),
1594 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1596 // Run rename on a pagination index which couldn't return all refs in one
1597 // request, we reject rename on this case.
1598 class PaginationIndex
: public SymbolIndex
{
1599 bool refs(const RefsRequest
&Req
,
1600 llvm::function_ref
<void(const Ref
&)> Callback
) const override
{
1601 return true; // has more references
1604 bool containedRefs(const ContainedRefsRequest
&Req
,
1605 llvm::function_ref
<void(const ContainedRefsResult
&)>
1606 Callback
) const override
{
1611 const FuzzyFindRequest
&Req
,
1612 llvm::function_ref
<void(const Symbol
&)> Callback
) const override
{
1616 lookup(const LookupRequest
&Req
,
1617 llvm::function_ref
<void(const Symbol
&)> Callback
) const override
{}
1619 void relations(const RelationsRequest
&Req
,
1620 llvm::function_ref
<void(const SymbolID
&, const Symbol
&)>
1621 Callback
) const override
{}
1623 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
1624 indexedFiles() const override
{
1625 return [](llvm::StringRef
) { return IndexContents::None
; };
1628 size_t estimateMemoryUsage() const override
{ return 0; }
1630 Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1631 createOverlay(getVFSFromAST(AST
), InMemFS
), &PIndex
});
1632 EXPECT_FALSE(Results
);
1633 EXPECT_THAT(llvm::toString(Results
.takeError()),
1634 testing::HasSubstr("too many occurrences"));
1637 TEST(CrossFileRenameTests
, DeduplicateRefsFromIndex
) {
1638 auto MainCode
= Annotations("int [[^x]] = 2;");
1639 auto MainFilePath
= testPath("main.cc");
1640 auto BarCode
= Annotations("int [[x]];");
1641 auto BarPath
= testPath("bar.cc");
1642 auto TU
= TestTU::withCode(MainCode
.code());
1643 // Set a file "bar.cc" on disk.
1644 TU
.AdditionalFiles
["bar.cc"] = std::string(BarCode
.code());
1645 auto AST
= TU
.build();
1646 std::string BarPathURI
= URI::create(BarPath
).toString();
1647 Ref XRefInBarCC
= refWithRange(BarCode
.range(), BarPathURI
);
1648 // The index will return duplicated refs, our code should be robost to handle
1650 class DuplicatedXRefIndex
: public SymbolIndex
{
1652 DuplicatedXRefIndex(const Ref
&ReturnedRef
) : ReturnedRef(ReturnedRef
) {}
1653 bool refs(const RefsRequest
&Req
,
1654 llvm::function_ref
<void(const Ref
&)> Callback
) const override
{
1655 // Return two duplicated refs.
1656 Callback(ReturnedRef
);
1657 Callback(ReturnedRef
);
1661 bool containedRefs(const ContainedRefsRequest
&Req
,
1662 llvm::function_ref
<void(const ContainedRefsResult
&)>
1663 Callback
) const override
{
1667 bool fuzzyFind(const FuzzyFindRequest
&,
1668 llvm::function_ref
<void(const Symbol
&)>) const override
{
1671 void lookup(const LookupRequest
&,
1672 llvm::function_ref
<void(const Symbol
&)>) const override
{}
1674 void relations(const RelationsRequest
&,
1675 llvm::function_ref
<void(const SymbolID
&, const Symbol
&)>)
1678 llvm::unique_function
<IndexContents(llvm::StringRef
) const>
1679 indexedFiles() const override
{
1680 return [](llvm::StringRef
) { return IndexContents::None
; };
1683 size_t estimateMemoryUsage() const override
{ return 0; }
1685 } DIndex(XRefInBarCC
);
1686 llvm::StringRef NewName
= "newName";
1687 auto Results
= rename({MainCode
.point(), NewName
, AST
, MainFilePath
,
1688 getVFSFromAST(AST
), &DIndex
});
1689 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
1691 applyEdits(std::move(Results
->GlobalChanges
)),
1692 UnorderedElementsAre(
1693 Pair(Eq(BarPath
), Eq(expectedResult(BarCode
, NewName
))),
1694 Pair(Eq(MainFilePath
), Eq(expectedResult(MainCode
, NewName
)))));
1697 TEST(CrossFileRenameTests
, WithUpToDateIndex
) {
1698 MockCompilationDatabase CDB
;
1699 CDB
.ExtraClangFlags
= {"-xobjective-c++"};
1700 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1701 // expected rename occurrences.
1703 llvm::StringRef FooH
;
1704 llvm::StringRef FooCC
;
1716 [[Foo]]::[[Foo]]() {}
1717 [[Foo]]::~[[Foo]]() {}
1727 template <typename T>
1729 // FIXME: explicit template specializations are not supported due the
1730 // clangd index limitations.
1732 class Foo<double> {};
1750 void Foo::[[foo]]() {}
1761 virtual void [[foo]]();
1763 class Derived1 : public Base {
1764 void [[f^oo]]() override;
1772 void Base::[[foo]]() {}
1773 void Derived1::[[foo]]() {}
1775 class Derived2 : public Derived1 {
1776 void [[foo]]() override {};
1779 void func(Base* b, Derived1* d1,
1780 Derived2* d2, NotDerived* nd) {
1788 {// virtual templated method
1790 template <typename> class Foo { virtual void [[m]](); };
1791 class Bar : Foo<int> { void [[^m]]() override; };
1796 template<typename T> void Foo<T>::[[m]]() {}
1797 // FIXME: not renamed as the index doesn't see this as an override of
1798 // the canonical Foo<T>::m().
1799 // https://github.com/clangd/clangd/issues/1325
1800 class Baz : Foo<float> { void m() override; };
1803 // rename on constructor and destructor.
1812 [[Foo]]::[[Foo]]() {}
1813 [[Foo]]::~[[Foo]]() {}
1837 typedef int [[IN^T]];
1848 using [[I^NT]] = int;
1859 static const int [[VA^R]] = 123;
1869 enum class [[K^ind]] { ABC };
1874 return [[Kind]]::ABC;
1881 enum class Kind { [[A^BC]] };
1886 return Kind::[[ABC]];
1891 // Implicit references in macro expansions.
1907 // Objective-C classes.
1914 @implementation [[Foo]]
1917 void func([[Foo]] *f) {}
1922 trace::TestTracer Tracer
;
1923 for (const auto &T
: Cases
) {
1924 SCOPED_TRACE(T
.FooH
);
1925 Annotations
FooH(T
.FooH
);
1926 Annotations
FooCC(T
.FooCC
);
1927 std::string FooHPath
= testPath("foo.h");
1928 std::string FooCCPath
= testPath("foo.cc");
1931 FS
.Files
[FooHPath
] = std::string(FooH
.code());
1932 FS
.Files
[FooCCPath
] = std::string(FooCC
.code());
1934 auto ServerOpts
= ClangdServer::optsForTest();
1935 ServerOpts
.BuildDynamicSymbolIndex
= true;
1936 ClangdServer
Server(CDB
, FS
, ServerOpts
);
1938 // Add all files to clangd server to make sure the dynamic index has been
1940 runAddDocument(Server
, FooHPath
, FooH
.code());
1941 runAddDocument(Server
, FooCCPath
, FooCC
.code());
1943 llvm::StringRef NewName
= "NewName";
1944 for (const auto &RenamePos
: FooH
.points()) {
1945 EXPECT_THAT(Tracer
.takeMetric("rename_files"), SizeIs(0));
1946 auto FileEditsList
=
1947 llvm::cantFail(runRename(Server
, FooHPath
, RenamePos
, NewName
, {}));
1948 EXPECT_THAT(Tracer
.takeMetric("rename_files"), ElementsAre(2));
1950 applyEdits(std::move(FileEditsList
.GlobalChanges
)),
1951 UnorderedElementsAre(
1952 Pair(Eq(FooHPath
), Eq(expectedResult(T
.FooH
, NewName
))),
1953 Pair(Eq(FooCCPath
), Eq(expectedResult(T
.FooCC
, NewName
)))));
1958 TEST(CrossFileRenameTests
, ObjC
) {
1959 MockCompilationDatabase CDB
;
1960 CDB
.ExtraClangFlags
= {"-xobjective-c"};
1961 // rename is runnning on all "^" points in FooH.
1963 llvm::StringRef FooH
;
1964 llvm::StringRef FooM
;
1965 llvm::StringRef NewName
;
1966 llvm::StringRef ExpectedFooH
;
1967 llvm::StringRef ExpectedFooM
;
1969 Case Cases
[] = {// --- Zero arg selector
1974 - (int)performA^ction;
1979 - (int)performAction {
1980 [self performAction];
1989 - (int)performNewAction;
1994 - (int)performNewAction {
1995 [self performNewAction];
2000 // --- Single arg selector
2005 - (int)performA^ction:(int)action;
2010 - (int)performAction:(int)action {
2011 [self performAction:action];
2016 "performNewAction:",
2020 - (int)performNewAction:(int)action;
2025 - (int)performNewAction:(int)action {
2026 [self performNewAction:action];
2031 // --- Multi arg selector
2036 - (int)performA^ction:(int)action with:(int)value;
2041 - (int)performAction:(int)action with:(int)value {
2042 [self performAction:action with:value];
2047 "performNewAction:by:",
2051 - (int)performNewAction:(int)action by:(int)value;
2056 - (int)performNewAction:(int)action by:(int)value {
2057 [self performNewAction:action by:value];
2063 trace::TestTracer Tracer
;
2064 for (const auto &T
: Cases
) {
2065 SCOPED_TRACE(T
.FooH
);
2066 Annotations
FooH(T
.FooH
);
2067 Annotations
FooM(T
.FooM
);
2068 std::string FooHPath
= testPath("foo.h");
2069 std::string FooMPath
= testPath("foo.m");
2072 FS
.Files
[FooHPath
] = std::string(FooH
.code());
2073 FS
.Files
[FooMPath
] = std::string(FooM
.code());
2075 auto ServerOpts
= ClangdServer::optsForTest();
2076 ServerOpts
.BuildDynamicSymbolIndex
= true;
2077 ClangdServer
Server(CDB
, FS
, ServerOpts
);
2079 // Add all files to clangd server to make sure the dynamic index has been
2081 runAddDocument(Server
, FooHPath
, FooH
.code());
2082 runAddDocument(Server
, FooMPath
, FooM
.code());
2084 for (const auto &RenamePos
: FooH
.points()) {
2085 EXPECT_THAT(Tracer
.takeMetric("rename_files"), SizeIs(0));
2086 auto FileEditsList
=
2087 llvm::cantFail(runRename(Server
, FooHPath
, RenamePos
, T
.NewName
, {}));
2088 EXPECT_THAT(Tracer
.takeMetric("rename_files"), ElementsAre(2));
2089 EXPECT_THAT(applyEdits(std::move(FileEditsList
.GlobalChanges
)),
2090 UnorderedElementsAre(Pair(Eq(FooHPath
), Eq(T
.ExpectedFooH
)),
2091 Pair(Eq(FooMPath
), Eq(T
.ExpectedFooM
))));
2096 TEST(CrossFileRenameTests
, CrossFileOnLocalSymbol
) {
2097 // cross-file rename should work for function-local symbols, even there is no
2099 Annotations
Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
2100 auto TU
= TestTU::withCode(Code
.code());
2101 auto Path
= testPath(TU
.Filename
);
2102 auto AST
= TU
.build();
2103 llvm::StringRef NewName
= "newName";
2104 auto Results
= rename({Code
.point(), NewName
, AST
, Path
});
2105 ASSERT_TRUE(bool(Results
)) << Results
.takeError();
2107 applyEdits(std::move(Results
->GlobalChanges
)),
2108 UnorderedElementsAre(Pair(Eq(Path
), Eq(expectedResult(Code
, NewName
)))));
2111 TEST(CrossFileRenameTests
, BuildRenameEdits
) {
2112 Annotations
Code("[[😂]]");
2113 auto LSPRange
= Code
.range();
2114 llvm::StringRef FilePath
= "/test/TestTU.cpp";
2115 llvm::SmallVector
<llvm::StringRef
, 2> NewNames
= {"abc"};
2116 auto Edit
= buildRenameEdit(FilePath
, Code
.code(), {LSPRange
}, NewNames
);
2117 ASSERT_TRUE(bool(Edit
)) << Edit
.takeError();
2118 ASSERT_EQ(1UL, Edit
->Replacements
.size());
2119 EXPECT_EQ(FilePath
, Edit
->Replacements
.begin()->getFilePath());
2120 EXPECT_EQ(4UL, Edit
->Replacements
.begin()->getLength());
2122 // Test invalid range.
2123 LSPRange
.end
= {10, 0}; // out of range
2124 Edit
= buildRenameEdit(FilePath
, Code
.code(), {LSPRange
}, NewNames
);
2126 EXPECT_THAT(llvm::toString(Edit
.takeError()),
2127 testing::HasSubstr("fail to convert"));
2129 // Normal ascii characters.
2130 Annotations
T(R
"cpp(
2136 buildRenameEdit(FilePath
, T
.code(), symbolRanges(T
.ranges()), NewNames
);
2137 ASSERT_TRUE(bool(Edit
)) << Edit
.takeError();
2138 EXPECT_EQ(applyEdits(FileEdits
{{T
.code(), std::move(*Edit
)}}).front().second
,
2139 expectedResult(T
, NewNames
[0]));
2142 TEST(CrossFileRenameTests
, adjustRenameRanges
) {
2143 // Ranges in IndexedCode indicate the indexed occurrences;
2144 // ranges in DraftCode indicate the expected mapped result, empty indicates
2145 // we expect no matched result found.
2147 llvm::StringRef IndexedCode
;
2148 llvm::StringRef DraftCode
;
2151 // both line and column are changed, not a near miss.
2172 R
"cpp(int [[x]] = 0; void foo(int x);)cpp",
2173 R
"cpp(double [[x]] = 0; void foo(double x);)cpp",
2188 LangOptions LangOpts
;
2189 LangOpts
.CPlusPlus
= true;
2190 for (const auto &T
: Tests
) {
2191 SCOPED_TRACE(T
.DraftCode
);
2192 Annotations
Draft(T
.DraftCode
);
2193 auto ActualRanges
= adjustRenameRanges(Draft
.code(), "x",
2194 Annotations(T
.IndexedCode
).ranges(),
2195 LangOpts
, std::nullopt
);
2197 EXPECT_THAT(Draft
.ranges(), testing::IsEmpty());
2199 EXPECT_THAT(Draft
.ranges(),
2200 testing::UnorderedElementsAreArray(*ActualRanges
));
2204 TEST(RangePatchingHeuristic
, GetMappedRanges
) {
2205 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
2206 // there are no mapped ranges.
2208 llvm::StringRef IndexedCode
;
2209 llvm::StringRef LexedCode
;
2217 // both line and column are changed, not a near miss.
2255 ^[[]] ^[[]] // column is shifted.
2268 [[]] [[]] // not mapped (both line and column are changed).
2282 // insert a new line
2285 [[]] // additional range
2288 [[]] // additional range
2292 // non-distinct result (two best results), not a near miss
2306 for (const auto &T
: Tests
) {
2307 SCOPED_TRACE(T
.IndexedCode
);
2308 auto Lexed
= Annotations(T
.LexedCode
);
2309 auto LexedRanges
= symbolRanges(Lexed
.ranges());
2310 std::vector
<SymbolRange
> ExpectedMatches
;
2311 for (auto P
: Lexed
.points()) {
2312 auto Match
= llvm::find_if(LexedRanges
, [&P
](const SymbolRange
&R
) {
2313 return R
.range().start
== P
;
2315 ASSERT_NE(Match
, LexedRanges
.end());
2316 ExpectedMatches
.push_back(*Match
);
2320 getMappedRanges(Annotations(T
.IndexedCode
).ranges(), LexedRanges
);
2322 EXPECT_THAT(ExpectedMatches
, IsEmpty());
2324 EXPECT_THAT(ExpectedMatches
, UnorderedElementsAreArray(*Mapped
));
2328 TEST(CrossFileRenameTests
, adjustmentCost
) {
2330 llvm::StringRef RangeCode
;
2331 size_t ExpectedCost
;
2335 $idx[[]]$lex[[]] // diff: 0
2342 $lex[[]] // line diff: +1
2344 $lex[[]] // line diff: +1
2346 $lex[[]] // line diff: +1
2350 $lex[[]] // line diff: +2
2357 $lex[[]] // line diff: +1
2360 $lex[[]] // line diff: +2
2364 $lex[[]] // line diff: +3
2373 $lex[[]] // line diff: +3
2376 $lex[[]] // line diff: +2
2378 $lex[[]] // line diff: +1
2385 $lex[[]] // line diff: +1
2386 $lex[[]] // line diff: -2
2392 $lex[[]] // line diff: +3
2398 $idx[[]] $lex[[]] // column diff: +1
2399 $idx[[]]$lex[[]] // diff: 0
2406 $lex[[]] // diff: +1
2407 $idx[[]] $lex[[]] // column diff: +1
2408 $idx[[]]$lex[[]] // diff: 0
2414 $idx[[]] $lex[[]] // column diff: +1
2420 // column diffs: +1, +2, +3
2421 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2426 for (const auto &T
: Tests
) {
2427 SCOPED_TRACE(T
.RangeCode
);
2428 Annotations
C(T
.RangeCode
);
2429 std::vector
<size_t> MappedIndex
;
2430 for (size_t I
= 0; I
< C
.ranges("lex").size(); ++I
)
2431 MappedIndex
.push_back(I
);
2432 EXPECT_EQ(renameRangeAdjustmentCost(
2433 C
.ranges("idx"), symbolRanges(C
.ranges("lex")), MappedIndex
),
2439 } // namespace clangd
2440 } // namespace clang