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 {
1605 const FuzzyFindRequest &Req,
1606 llvm::function_ref<
void(
const Symbol &)> Callback)
const override {
1610 lookup(
const LookupRequest &Req,
1611 llvm::function_ref<
void(
const Symbol &)> Callback)
const override {}
1613 void relations(
const RelationsRequest &Req,
1614 llvm::function_ref<
void(
const SymbolID &,
const Symbol &)>
1615 Callback)
const override {}
1618 indexedFiles()
const override {
1619 return [](llvm::StringRef) {
return IndexContents::None; };
1622 size_t estimateMemoryUsage()
const override {
return 0; }
1625 createOverlay(getVFSFromAST(
AST), InMemFS), &PIndex});
1627 EXPECT_THAT(llvm::toString(
Results.takeError()),
1628 testing::HasSubstr(
"too many occurrences"));
1631TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1632 auto MainCode = Annotations(
"int [[^x]] = 2;");
1633 auto MainFilePath =
testPath(
"main.cc");
1634 auto BarCode = Annotations(
"int [[x]];");
1638 TU.AdditionalFiles[
"bar.cc"] = std::string(BarCode.code());
1639 auto AST = TU.build();
1640 std::string BarPathURI =
URI::create(BarPath).toString();
1641 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
1644 class DuplicatedXRefIndex :
public SymbolIndex {
1646 DuplicatedXRefIndex(
const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1647 bool refs(
const RefsRequest &Req,
1648 llvm::function_ref<
void(
const Ref &)> Callback)
const override {
1655 bool fuzzyFind(
const FuzzyFindRequest &,
1656 llvm::function_ref<
void(
const Symbol &)>)
const override {
1659 void lookup(
const LookupRequest &,
1660 llvm::function_ref<
void(
const Symbol &)>)
const override {}
1662 void relations(
const RelationsRequest &,
1663 llvm::function_ref<
void(
const SymbolID &,
const Symbol &)>)
1667 indexedFiles()
const override {
1668 return [](llvm::StringRef) {
return IndexContents::None; };
1671 size_t estimateMemoryUsage()
const override {
return 0; }
1673 } DIndex(XRefInBarCC);
1674 llvm::StringRef NewName =
"newName";
1676 getVFSFromAST(
AST), &DIndex});
1679 applyEdits(std::move(
Results->GlobalChanges)),
1680 UnorderedElementsAre(
1681 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1682 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1685TEST(CrossFileRenameTests, WithUpToDateIndex) {
1686 MockCompilationDatabase CDB;
1687 CDB.ExtraClangFlags = {
"-xobjective-c++"};
1691 llvm::StringRef FooH;
1692 llvm::StringRef FooCC;
1704 [[Foo]]::[[Foo]]() {}
1705 [[Foo]]::~[[Foo]]() {}
1715 template <typename T>
1717 // FIXME: explicit template specializations are not supported due the
1718 // clangd index limitations.
1720 class Foo<double> {};
1738 void Foo::[[foo]]() {}
1749 virtual void [[foo]]();
1751 class Derived1 : public Base {
1752 void [[f^oo]]() override;
1760 void Base::[[foo]]() {}
1761 void Derived1::[[foo]]() {}
1763 class Derived2 : public Derived1 {
1764 void [[foo]]() override {};
1767 void func(Base* b, Derived1* d1,
1768 Derived2* d2, NotDerived* nd) {
1778 template <typename> class Foo { virtual void [[m]](); };
1779 class Bar : Foo<int> { void [[^m]]() override; };
1784 template<typename T> void Foo<T>::[[m]]() {}
1785 // FIXME: not renamed as the index doesn't see this as an override of
1786 // the canonical Foo<T>::m().
1787 // https://github.com/clangd/clangd/issues/1325
1788 class Baz : Foo<float> { void m() override; };
1800 [[Foo]]::[[Foo]]() {}
1801 [[Foo]]::~[[Foo]]() {}
1825 typedef int [[IN^T]];
1836 using [[I^NT]] = int;
1847 static const int [[VA^R]] = 123;
1857 enum class [[K^ind]] { ABC };
1862 return [[Kind]]::ABC;
1869 enum class Kind { [[A^BC]] };
1874 return Kind::[[ABC]];
1902 @implementation [[Foo]]
1905 void func([[Foo]] *f) {}
1910 trace::TestTracer Tracer;
1911 for (
const auto &T : Cases) {
1912 SCOPED_TRACE(
T.FooH);
1913 Annotations FooH(
T.FooH);
1914 Annotations FooCC(
T.FooCC);
1915 std::string FooHPath =
testPath(
"foo.h");
1916 std::string FooCCPath =
testPath(
"foo.cc");
1919 FS.Files[FooHPath] = std::string(FooH.code());
1920 FS.Files[FooCCPath] = std::string(FooCC.code());
1923 ServerOpts.BuildDynamicSymbolIndex =
true;
1924 ClangdServer Server(CDB, FS, ServerOpts);
1931 llvm::StringRef NewName =
"NewName";
1932 for (
const auto &RenamePos : FooH.points()) {
1933 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), SizeIs(0));
1934 auto FileEditsList =
1935 llvm::cantFail(
runRename(Server, FooHPath, RenamePos, NewName, {}));
1936 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), ElementsAre(2));
1938 applyEdits(std::move(FileEditsList.GlobalChanges)),
1939 UnorderedElementsAre(
1940 Pair(Eq(FooHPath), Eq(expectedResult(
T.FooH, NewName))),
1941 Pair(Eq(FooCCPath), Eq(expectedResult(
T.FooCC, NewName)))));
1946TEST(CrossFileRenameTests, ObjC) {
1947 MockCompilationDatabase CDB;
1948 CDB.ExtraClangFlags = {
"-xobjective-c"};
1951 llvm::StringRef FooH;
1952 llvm::StringRef FooM;
1953 llvm::StringRef NewName;
1954 llvm::StringRef ExpectedFooH;
1955 llvm::StringRef ExpectedFooM;
1962 - (int)performA^ction;
1967 - (int)performAction {
1968 [self performAction];
1977 - (int)performNewAction;
1982 - (int)performNewAction {
1983 [self performNewAction];
1993 - (int)performA^ction:(int)action;
1998 - (int)performAction:(int)action {
1999 [self performAction:action];
2004 "performNewAction:",
2008 - (int)performNewAction:(int)action;
2013 - (int)performNewAction:(int)action {
2014 [self performNewAction:action];
2024 - (int)performA^ction:(int)action with:(int)value;
2029 - (int)performAction:(int)action with:(int)value {
2030 [self performAction:action with:value];
2035 "performNewAction:by:",
2039 - (int)performNewAction:(int)action by:(int)value;
2044 - (int)performNewAction:(int)action by:(int)value {
2045 [self performNewAction:action by:value];
2051 trace::TestTracer Tracer;
2052 for (
const auto &T : Cases) {
2053 SCOPED_TRACE(
T.FooH);
2054 Annotations FooH(
T.FooH);
2055 Annotations FooM(
T.FooM);
2056 std::string FooHPath =
testPath(
"foo.h");
2057 std::string FooMPath =
testPath(
"foo.m");
2060 FS.Files[FooHPath] = std::string(FooH.code());
2061 FS.Files[FooMPath] = std::string(FooM.code());
2064 ServerOpts.BuildDynamicSymbolIndex =
true;
2065 ClangdServer Server(CDB, FS, ServerOpts);
2072 for (
const auto &RenamePos : FooH.points()) {
2073 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), SizeIs(0));
2074 auto FileEditsList =
2075 llvm::cantFail(
runRename(Server, FooHPath, RenamePos,
T.NewName, {}));
2076 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), ElementsAre(2));
2077 EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
2078 UnorderedElementsAre(Pair(Eq(FooHPath), Eq(
T.ExpectedFooH)),
2079 Pair(Eq(FooMPath), Eq(
T.ExpectedFooM))));
2084TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2087 Annotations
Code(
"void f(int [[abc]]) { [[a^bc]] = 3; }");
2090 auto AST = TU.build();
2091 llvm::StringRef NewName =
"newName";
2095 applyEdits(std::move(
Results->GlobalChanges)),
2096 UnorderedElementsAre(Pair(Eq(
Path), Eq(expectedResult(
Code, NewName)))));
2099TEST(CrossFileRenameTests, BuildRenameEdits) {
2100 Annotations
Code(
"[[😂]]");
2101 auto LSPRange =
Code.range();
2102 llvm::StringRef FilePath =
"/test/TestTU.cpp";
2103 llvm::SmallVector<llvm::StringRef, 2> NewNames = {
"abc"};
2105 ASSERT_TRUE(
bool(Edit)) << Edit.takeError();
2106 ASSERT_EQ(1UL, Edit->Replacements.size());
2107 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
2108 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
2111 LSPRange.end = {10, 0};
2114 EXPECT_THAT(llvm::toString(Edit.takeError()),
2115 testing::HasSubstr(
"fail to convert"));
2118 Annotations
T(R
"cpp(
2125 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2126 EXPECT_EQ(applyEdits(
FileEdits{{
T.code(), std::move(*Edit)}}).front().second,
2127 expectedResult(T, NewNames[0]));
2135 llvm::StringRef IndexedCode;
2136 llvm::StringRef DraftCode;
2160 R
"cpp(int [[x]] = 0; void foo(int x);)cpp",
2161 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
2176 LangOptions LangOpts;
2177 LangOpts.CPlusPlus = true;
2178 for (
const auto &T : Tests) {
2179 SCOPED_TRACE(
T.DraftCode);
2180 Annotations Draft(
T.DraftCode);
2182 Annotations(
T.IndexedCode).ranges(),
2183 LangOpts, std::nullopt);
2185 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
2187 EXPECT_THAT(Draft.ranges(),
2188 testing::UnorderedElementsAreArray(*ActualRanges));
2192TEST(RangePatchingHeuristic, GetMappedRanges) {
2196 llvm::StringRef IndexedCode;
2197 llvm::StringRef LexedCode;
2243 ^[[]] ^[[]] // column is shifted.
2256 [[]] [[]] // not mapped (both line and column are changed).
2270 // insert a new line
2273 [[]] // additional range
2276 [[]] // additional range
2294 for (
const auto &T : Tests) {
2295 SCOPED_TRACE(
T.IndexedCode);
2296 auto Lexed = Annotations(
T.LexedCode);
2297 auto LexedRanges = symbolRanges(Lexed.ranges());
2298 std::vector<SymbolRange> ExpectedMatches;
2299 for (
auto P : Lexed.points()) {
2300 auto Match = llvm::find_if(LexedRanges, [&P](
const SymbolRange &R) {
2301 return R.range().start == P;
2303 ASSERT_NE(Match, LexedRanges.end());
2304 ExpectedMatches.push_back(*Match);
2310 EXPECT_THAT(ExpectedMatches, IsEmpty());
2312 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2316TEST(CrossFileRenameTests, adjustmentCost) {
2318 llvm::StringRef RangeCode;
2319 size_t ExpectedCost;
2323 $idx[[]]$lex[[]] // diff: 0
2330 $lex[[]] // line diff: +1
2332 $lex[[]] // line diff: +1
2334 $lex[[]] // line diff: +1
2338 $lex[[]] // line diff: +2
2345 $lex[[]] // line diff: +1
2348 $lex[[]] // line diff: +2
2352 $lex[[]] // line diff: +3
2361 $lex[[]] // line diff: +3
2364 $lex[[]] // line diff: +2
2366 $lex[[]] // line diff: +1
2373 $lex[[]] // line diff: +1
2374 $lex[[]] // line diff: -2
2380 $lex[[]] // line diff: +3
2386 $idx[[]] $lex[[]] // column diff: +1
2387 $idx[[]]$lex[[]] // diff: 0
2394 $lex[[]] // diff: +1
2395 $idx[[]] $lex[[]] // column diff: +1
2396 $idx[[]]$lex[[]] // diff: 0
2402 $idx[[]] $lex[[]] // column diff: +1
2408 // column diffs: +1, +2, +3
2409 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2414 for (
const auto &T : Tests) {
2415 SCOPED_TRACE(
T.RangeCode);
2416 Annotations
C(
T.RangeCode);
2417 std::vector<size_t> MappedIndex;
2418 for (
size_t I = 0; I <
C.ranges(
"lex").size(); ++I)
2419 MappedIndex.push_back(I);
2421 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)