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"
28using testing::ElementsAre;
30using testing::IsEmpty;
33using testing::UnorderedElementsAre;
34using testing::UnorderedElementsAreArray;
36llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
37createOverlay(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));
45llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getVFSFromAST(ParsedAST &
AST) {
46 return &
AST.getSourceManager().getFileManager().getVirtualFileSystem();
50Ref refWithRange(
const clangd::Range &Range,
const std::string &URI) {
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();
63std::unique_ptr<RefSlab> buildRefSlab(
const Annotations &Code,
64 llvm::StringRef SymbolName,
65 llvm::StringRef
Path) {
68 TU.HeaderCode = std::string(Code.code());
69 auto Symbols = TU.headerSymbols();
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< std::string, std::string>>
81 std::vector<std::pair<std::string, std::string>>
Results;
85 llvm::cantFail(tooling::applyAllReplacements(
86 It.getValue().InitialCode, It.getValue().Replacements)));
92std::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);
106std::vector<SymbolRange> symbolRanges(llvm::ArrayRef<Range> Ranges) {
107 std::vector<SymbolRange> Result;
108 for (
const auto &R : Ranges)
109 Result.emplace_back(R);
113TEST(RenameTest, WithinFileRename) {
116 llvm::StringRef Tests[] = {
136 if (auto [[^foo]] = 5) {
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; }
158 template <typename T>
166 [[F^oo]]<T>::[[Fo^o]]() {}
169 [[F^oo]]<T>::~[[Fo^o]]() {}
183 [[F^oo]]::[[Fo^o]]() {}
189 template <typename T> void func();
190 template <typename T> class Baz {};
208 void Baz() { [[F^oo]](); }
217 static T [[f^oo]]() {}
221 Foo<int>::[[f^oo]]();
232 Foo<int>().[[f^oo]]();
238 template <typename T>
242 class [[F^oo]]<bool> {};
243 template <typename T>
244 class [[F^oo]]<T*> {};
255 template <typename T>
257 void func([[F^oo]]<int>);
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);
296 template <typename T>
304 A<double>().[[f^oo]]();
305 A<float>().[[f^oo]]();
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>
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);
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.
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();
421 static Foo *[[Static^Member]];
424 Foo* Foo::[[Static^Member]] = nullptr;
427 Foo* Pointer = Foo::[[Static^Member]];
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;
461 [[Foo^]]::~[[^Foo]]() {}
465 f.~/*something*/[[^Foo]]();
473 class Derived : public [[Bas^e]] {};
476 [[Bas^e]] *foo = new Derived();
477 foo->[[^Base]]::~[[^Base]]();
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.
534 #define MACRO(a) qux(a)
540 int y = baz.[[F^oo]];
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]];
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 };
713 template <template<typename> class Z> struct Bar { };
714 template <> struct Bar<[[F^oo]]> {};
722 Bar bar { .[[^Foo]] = 42 };
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>;
771 namespace x { class X {}; }
773 using [[Fo^o]] = x::X;
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;
810 operator [[F^oo]]() {
817 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
830 namespace ns { void [[f^oo]](); }
843 namespace ns { int [[^foo]](int); char foo(char); }
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) {
870 TU.ExtraArgs.push_back(
"-xobjective-c++");
871 auto AST = TU.build();
872 auto Index = TU.index();
873 for (
const auto &RenamePos : Code.points()) {
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));
886TEST(RenameTest, ObjCWithinFileRename) {
890 llvm::StringRef Input;
892 llvm::StringRef NewName;
895 std::optional<llvm::StringRef>
Expected;
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];
928 #define mySelector - (int)performAction:(int)action with:(int)value
934 return [self performAction:action with:value];
939 "performNewAction:by:",
947 #define mySelector - (int)perform^Action:(int)action with:(int)value
953 return [self performAction:action with:value];
958 "performNewAction:by:",
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:);
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);
1016 TU.ExtraArgs.push_back(
"-xobjective-c");
1017 auto AST = TU.build();
1018 auto Index = TU.index();
1019 for (
const auto &RenamePos : Code.points()) {
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());
1037TEST(RenameTest, Renameable) {
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);
1331 TU.ExtraArgs.push_back(
"-fno-delayed-template-parsing");
1332 if (Case.IsHeaderFile) {
1334 TU.Filename =
"test.h";
1336 TU.ExtraArgs.push_back(
"-xobjective-c++-header");
1338 auto AST = TU.build();
1339 llvm::StringRef NewName = Case.NewName;
1341 bool WantRename =
true;
1342 if (
T.ranges().empty())
1345 assert(Case.ErrorMessage &&
"Error message must be set!");
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());
1358MATCHER_P(newText, T,
"") {
return arg.newText ==
T; }
1360TEST(RenameTest, IndexMergeMainFile) {
1361 Annotations Code(
"int ^x();");
1363 TU.Filename =
"main.cc";
1364 auto AST = TU.build();
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)
1383 EXPECT_TRUE(
bool(
Results)) << llvm::toString(
Results.takeError());
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")));
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
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")));
1410TEST(RenameTest, MainFileReferencesOnly) {
1412 llvm::StringRef Test =
1416 // rename references not from main file are not included.
1420 Annotations Code(Test);
1422 TU.AdditionalFiles[
"foo.inc"] = R
"cpp(
1427 auto AST = TU.build();
1428 llvm::StringRef NewName =
"abcde";
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));
1438TEST(RenameTest, NoRenameOnSymbolsFromSystemHeaders) {
1439 llvm::StringRef Test =
1446 void foo() { at^oi("9000"); }
1449 Annotations Code(Test);
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";
1463 for (
auto &Point : Code.points()) {
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"));
1472TEST(RenameTest, ProtobufSymbolIsExcluded) {
1473 Annotations Code(
"Prot^obuf buf;");
1476 R
"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1479 TU.HeaderFilename = "protobuf.pb.h";
1480 auto AST = TU.build();
1483 EXPECT_THAT(llvm::toString(
Results.takeError()),
1484 testing::HasSubstr(
"not a supported kind"));
1487TEST(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());
1500 ServerOpts.BuildDynamicSymbolIndex =
true;
1502 trace::TestTracer Tracer;
1503 MockCompilationDatabase CDB;
1504 ClangdServer Server(CDB, FS, ServerOpts);
1514 EXPECT_TRUE(
Results->GlobalChanges.empty());
1515 EXPECT_THAT(FooCC.ranges(),
1516 testing::UnorderedElementsAreArray(
Results->LocalChanges));
1520 std::string(
"int"), {});
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",
"😀@"}) {
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$",
"😀"}) {
1543TEST(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");
1552 FSymbols.update(FooPath,
nullptr, buildRefSlab(FooCode,
"Foo", FooPath),
1554 FSymbols.update(BarPath,
nullptr, buildRefSlab(BarCode,
"Bar", BarPath),
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()));
1568 auto AST = TU.build();
1569 llvm::StringRef NewName =
"newName";
1571 rename({MainCode.point(), NewName,
AST, MainFilePath,
1572 createOverlay(getVFSFromAST(
AST), InMemFS), Index.get()});
1575 applyEdits(std::move(
Results->GlobalChanges)),
1576 UnorderedElementsAre(
1577 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
1578 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1582 MainCode = Annotations(
"void [[Bar]]() { [[B^ar]](); }");
1585 TU.AdditionalFiles[
"bar.cc"] = std::string(BarCode.code());
1588 createOverlay(getVFSFromAST(
AST), InMemFS), Index.get()});
1591 applyEdits(std::move(
Results->GlobalChanges)),
1592 UnorderedElementsAre(
1593 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1594 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1598 class PaginationIndex :
public SymbolIndex {
1599 bool refs(
const RefsRequest &Req,
1600 llvm::function_ref<
void(
const Ref &)> Callback)
const override {
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 {}
1624 indexedFiles()
const override {
1625 return [](llvm::StringRef) {
return IndexContents::None; };
1628 size_t estimateMemoryUsage()
const override {
return 0; }
1631 createOverlay(getVFSFromAST(
AST), InMemFS), &PIndex});
1633 EXPECT_THAT(llvm::toString(
Results.takeError()),
1634 testing::HasSubstr(
"too many occurrences"));
1637TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1638 auto MainCode = Annotations(
"int [[^x]] = 2;");
1639 auto MainFilePath =
testPath(
"main.cc");
1640 auto BarCode = Annotations(
"int [[x]];");
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);
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 {
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 &)>)
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";
1688 getVFSFromAST(
AST), &DIndex});
1691 applyEdits(std::move(
Results->GlobalChanges)),
1692 UnorderedElementsAre(
1693 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1694 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1697TEST(CrossFileRenameTests, WithUpToDateIndex) {
1698 MockCompilationDatabase CDB;
1699 CDB.ExtraClangFlags = {
"-xobjective-c++"};
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) {
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; };
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]];
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());
1935 ServerOpts.BuildDynamicSymbolIndex =
true;
1936 ClangdServer Server(CDB, FS, ServerOpts);
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)))));
1958TEST(CrossFileRenameTests, ObjC) {
1959 MockCompilationDatabase CDB;
1960 CDB.ExtraClangFlags = {
"-xobjective-c"};
1963 llvm::StringRef FooH;
1964 llvm::StringRef FooM;
1965 llvm::StringRef NewName;
1966 llvm::StringRef ExpectedFooH;
1967 llvm::StringRef ExpectedFooM;
1974 - (int)performA^ction;
1979 - (int)performAction {
1980 [self performAction];
1989 - (int)performNewAction;
1994 - (int)performNewAction {
1995 [self performNewAction];
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];
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());
2076 ServerOpts.BuildDynamicSymbolIndex =
true;
2077 ClangdServer Server(CDB, FS, ServerOpts);
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))));
2096TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2099 Annotations Code(
"void f(int [[abc]]) { [[a^bc]] = 3; }");
2102 auto AST = TU.build();
2103 llvm::StringRef NewName =
"newName";
2107 applyEdits(std::move(
Results->GlobalChanges)),
2108 UnorderedElementsAre(
Pair(Eq(
Path), Eq(expectedResult(Code, NewName)))));
2111TEST(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());
2123 LSPRange.end = {10, 0};
2126 EXPECT_THAT(llvm::toString(Edit.takeError()),
2127 testing::HasSubstr(
"fail to convert"));
2130 Annotations
T(R
"cpp(
2137 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2138 EXPECT_EQ(applyEdits(
FileEdits{{
T.code(), std::move(*Edit)}}).front().second,
2139 expectedResult(T, NewNames[0]));
2147 llvm::StringRef IndexedCode;
2148 llvm::StringRef DraftCode;
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);
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));
2204TEST(RangePatchingHeuristic, GetMappedRanges) {
2208 llvm::StringRef IndexedCode;
2209 llvm::StringRef LexedCode;
2255 ^[[]] ^[[]] // column is shifted.
2268 [[]] [[]] // not mapped (both line and column are changed).
2282 // insert a new line
2285 [[]] // additional range
2288 [[]] // additional range
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);
2322 EXPECT_THAT(ExpectedMatches, IsEmpty());
2324 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2328TEST(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);
2433 C.ranges(
"idx"), symbolRanges(
C.ranges(
"lex")), MappedIndex),
std::vector< CodeCompletionResult > Results
CodeCompletionBuilder Builder
CharSourceRange Range
SourceRange for the file name.
std::vector< const char * > Expected
static Options optsForTest()
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
IndexContents
Describes what data is covered by an index.
std::string Path
A typedef to represent a file path.
llvm::Expected< RenameResult > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &RenameOpts)
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
llvm::Expected< Edit > buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode, std::vector< SymbolRange > Occurrences, llvm::ArrayRef< llvm::StringRef > NewNames)
Generates rename edits that replaces all given occurrences with the NewName.
llvm::Expected< RenameResult > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
TEST(BackgroundQueueTest, Priority)
std::optional< std::vector< SymbolRange > > adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, std::vector< Range > Indexed, const LangOptions &LangOpts, std::optional< Selector > Selector)
Adjusts indexed occurrences to match the current state of the file.
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, llvm::StringRef Version, WantDiagnostics WantDiags, bool ForceRebuild)
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< SymbolRange > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
llvm::Expected< RenameResult > runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional< std::string > NewName, const RenameOptions &RenameOpts)
std::optional< std::vector< SymbolRange > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< SymbolRange > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
std::array< uint8_t, 20 > SymbolID
const char ErrorMessage[]
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
SymbolID ID
The ID of the symbol.
static TestTU withCode(llvm::StringRef Code)