17#include "clang/Tooling/Core/Replacement.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include "gmock/gmock.h"
21#include "gtest/gtest.h"
29using testing::ElementsAre;
31using testing::IsEmpty;
34using testing::UnorderedElementsAre;
35using testing::UnorderedElementsAreArray;
37llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
38createOverlay(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Base,
39 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Overlay) {
41 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(std::move(Base));
42 OFS->pushOverlay(std::move(Overlay));
46llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getVFSFromAST(
ParsedAST &
AST) {
47 return &
AST.getSourceManager().getFileManager().getVirtualFileSystem();
51Ref refWithRange(
const clangd::Range &
Range,
const std::string &
URI) {
58 Result.Location.FileURI =
URI.c_str();
64std::unique_ptr<RefSlab> buildRefSlab(
const Annotations &Code,
65 llvm::StringRef SymbolName,
66 llvm::StringRef
Path) {
70 auto Symbols = TU.headerSymbols();
73 for (
const auto &
Range : Code.ranges())
76 return std::make_unique<RefSlab>(std::move(Builder).build());
80 std::pair< std::string, std::string>>
82 std::vector<std::pair<std::string, std::string>> Results;
86 llvm::cantFail(tooling::applyAllReplacements(
87 It.getValue().InitialCode, It.getValue().Replacements)));
93std::string expectedResult(
Annotations Test, llvm::StringRef NewName) {
95 unsigned NextChar = 0;
96 llvm::StringRef Code = Test.code();
97 for (
const auto &R : Test.llvm::Annotations::ranges()) {
98 assert(R.Begin <= R.End && NextChar <= R.Begin);
99 Result += Code.substr(NextChar, R.Begin - NextChar);
103 Result += Code.substr(NextChar);
107std::vector<SymbolRange> symbolRanges(llvm::ArrayRef<Range> Ranges) {
108 std::vector<SymbolRange> Result;
109 for (
const auto &R : Ranges)
110 Result.emplace_back(R);
114TEST(RenameTest, WithinFileRename) {
117 llvm::StringRef Tests[] = {
137 if (auto [[^foo]] = 5) {
148 [[F^oo]] *foo(int x);
152 [[F^oo]]::[[Fo^o]]() {}
153 [[F^oo]]::~[[Fo^o]]() {}
154 [[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
159 template <typename T>
167 [[F^oo]]<T>::[[Fo^o]]() {}
170 [[F^oo]]<T>::~[[Fo^o]]() {}
184 [[F^oo]]::[[Fo^o]]() {}
190 template <typename T> void func();
191 template <typename T> class Baz {};
209 void Baz() { [[F^oo]](); }
218 static T [[f^oo]]() { return T(); }
222 Foo<int>::[[f^oo]]();
229 T [[f^oo]]() { return T(); }
233 Foo<int>().[[f^oo]]();
239 template <typename T>
243 class [[F^oo]]<bool> {};
244 template <typename T>
245 class [[F^oo]]<T*> {};
256 template <typename T>
258 void func([[F^oo]]<int>);
263 template <typename T>
266 T foo(T arg, T& ref, T* ptr) {
270 value = static_cast<T>(number);
273 static void foo(T value) {}
277 template <typename T>
287 [[F^oo]]<int>::foo(0);
291 [[F^oo]]<bool>::foo(false);
297 template <typename T>
305 A<double>().[[f^oo]]();
306 A<float>().[[f^oo]]();
312 template<typename T, typename U=bool>
315 template<typename T, typename U>
318 template<typename T=int, typename U>
322 template<typename T=float, typename U=int>
325 template<typename T, typename U>
331 template<typename T=int, typename U=bool>
334 template<typename T, typename U>
338 template<typename T, typename U>
341 template<typename T=int, typename U=bool>
345 template<typename T=int, typename U=bool>
348 template<typename T, typename U>
352 template <typename T>
356 void [[f^oo]](int a);
365 template <typename T, int U>
366 bool [[F^oo]] = true;
368 // Explicit template specialization
370 bool [[F^oo]]<int, 0> = false;
372 // Partial template specialization
373 template <typename T>
374 bool [[F^oo]]<T, 1> = false;
377 // Ref to the explicit template specialization
379 // Ref to the primary template.
386 // Forward declaration.
389 virtual int getValue() const = 0;
392 class [[F^oo]] : public Baz {
394 [[F^oo]](int value = 0) : x(value) {}
396 [[F^oo]] &operator++(int);
398 bool operator<([[Foo]] const &rhs);
399 int getValue() const;
405 [[F^oo]] *Pointer = 0;
406 [[F^oo]] Variable = [[Foo]](10);
407 for ([[F^oo]] it; it < Variable; it++);
408 const [[F^oo]] *C = new [[Foo]]();
409 const_cast<[[F^oo]] *>(C)->getValue();
411 const Baz &BazReference = foo;
412 const Baz *BazPointer = &foo;
413 reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
414 static_cast<const [[^Foo]] &>(BazReference).getValue();
415 static_cast<const [[^Foo]] *>(BazPointer)->getValue();
422 static Foo *[[Static^Member]];
425 Foo* Foo::[[Static^Member]] = nullptr;
428 Foo* Pointer = Foo::[[Static^Member]];
436 template <class R, class... ArgTypes>
437 class function<R(ArgTypes...)> {
439 template <typename Functor>
440 function(Functor f) {}
444 R operator()(ArgTypes...) const {}
450 function<void([[Old]])> func;
462 [[Foo^]]::~[[^Foo]]() {}
466 f.~/*something*/[[^Foo]]();
474 class Derived : public [[Bas^e]] {};
477 [[Bas^e]] *foo = new Derived();
478 foo->[[^Base]]::~[[^Base]]();
490 Qux::Qux() : [[F^oo]]() {}
501 #define MACRO(a) foo(a)
512 // no rename inside macro body.
524 M2(M1()); // foo is inside the nested macro body.
535 #define MACRO(a) qux(a)
541 int y = baz.[[F^oo]];
549 T [[Vari^able]] = 42;
554 f.[[Varia^ble]] = 9000;
558 template<typename T, typename U>
567 struct Foo<T, bool> {
569 void bar() { ++[[Var^iable]]; }
573 Foo<unsigned, bool> f;
574 f.[[Var^iable]] = 9000;
578 template<typename T, typename U>
587 struct Foo<T, bool> {
589 void bar() { ++Variable; }
593 struct Foo<unsigned, bool> {
594 unsigned [[Var^iable]];
595 void bar() { ++[[Var^iable]]; }
599 Foo<unsigned, bool> f;
600 f.[[Var^iable]] = 9000;
606 static int [[Var^iable]];
609 int Foo::[[Var^iable]] = 42;
612 int LocalInt = Foo::[[Var^iable]];
618 static T [[Var^iable]];
622 int Foo<int>::[[Var^iable]] = 42;
625 bool Foo<bool>::[[Var^iable]] = true;
628 int LocalInt = Foo<int>::[[Var^iable]];
629 bool LocalBool = Foo<bool>::[[Var^iable]];
635 template <typename [[^T]]>
637 [[T^]] foo([[T^]] arg, [[T^]]& ref, [[^T]]* ptr) {
640 value = ([[T^]])number;
641 value = static_cast<[[^T]]>(number);
644 static void foo([[T^]] value) {}
652 class basic_string {};
653 typedef basic_string [[s^tring]];
656 ns::[[s^tring]] foo();
666 int Baz = A::[[^Foo]];
676 Foo = A::[[F^oo]] + Baz;
685 namespace a { namespace b { void foo(); } }
686 namespace [[^x]] = a::b;
694 enum [[C^olor]] { Red, Green, Blue };
697 c = [[C^olor]]::Blue;
703 enum class [[K^ind]] { ABC };
714 template <template<typename> class Z> struct Bar { };
715 template <> struct Bar<[[F^oo]]> {};
723 Bar bar { .[[^Foo]] = 42 };
734 // FIXME: v selecting here results in renaming Field.
735 Bar bar { .[[Foo]].Field = 42 };
744 Bar bar { .Foo.[[^Field]] = 42 };
749 template <typename T>
752 template <typename T>
753 using [[Fo^o]] = X<T>;
772 namespace x { class X {}; }
774 using [[Fo^o]] = x::X;
784 namespace x { class Old {}; }
786 #define REF(alias) alias alias_var;
789 using old##Alias = x::old; \
794 [[Old^Alias]] old_alias;
798 ns::[[Old^Alias]] Bar;
811 operator [[F^oo]]() {
818 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
831 namespace ns { void [[f^oo]](); }
844 namespace ns { int [[^foo]](int); char foo(char); }
856 @implementation [[F^oo]]
858 @interface [[Fo^o]] (Category)
860 @implementation [[F^oo]] (Category)
863 void func([[Fo^o]] *f) {}
870 auto&& getter1(this auto&& self) {
872 return self.[[memb^er]];
874 return local + self.[[memb^er]];
876 auto&& getter2(this Foo&& self) {
877 return self.[[memb^er]];
880 return this->[[mem^ber]] + [[memb^er]];
885 llvm::StringRef NewName = "NewName";
886 for (llvm::StringRef T : Tests) {
890 TU.ExtraArgs.push_back(
"-xobjective-c++");
891 TU.ExtraArgs.push_back(
"-std=c++23");
892 auto AST = TU.build();
893 auto Index = TU.index();
894 for (
const auto &RenamePos : Code.points()) {
897 getVFSFromAST(
AST), Index.get()});
902 expectedResult(Code, NewName));
907TEST(RenameTest, ObjCWithinFileRename) {
911 llvm::StringRef Input;
913 llvm::StringRef NewName;
916 std::optional<llvm::StringRef> Expected;
923 - (int)performA^ction:(int)action w^ith:(int)value;
926 - (int)performAc^tion:(int)action w^ith:(int)value {
927 return [self performAction:action with:value];
932 "performNewAction:by:",
936 - (int)performNewAction:(int)action by:(int)value;
939 - (int)performNewAction:(int)action by:(int)value {
940 return [self performNewAction:action by:value];
949 #define mySelector - (int)performAction:(int)action with:(int)value
955 return [self performAction:action with:value];
960 "performNewAction:by:",
968 #define mySelector - (int)perform^Action:(int)action with:(int)value
974 return [self performAction:action with:value];
979 "performNewAction:by:",
993 - (void)performA^ction:(int)action with:(int)value;
996 - (void)performAction:(int)action with:(int)value {
997 SEL mySelector = @selector(performAction:with:);
1002 "performNewAction:by:",
1006 - (void)performNewAction:(int)action by:(int)value;
1009 - (void)performNewAction:(int)action by:(int)value {
1010 SEL mySelector = @selector(performAction:with:);
1020 - (void)performAction:(int)action with:(int)value;
1023 - (void)performAction:(int)action with:(int)value {
1024 SEL mySelector = @selector(perfo^rmAction:with:);
1029 "performNewAction:by:",
1033 for (TestCase T : Tests) {
1034 SCOPED_TRACE(
T.Input);
1037 TU.ExtraArgs.push_back(
"-xobjective-c");
1038 auto AST = TU.build();
1039 auto Index = TU.index();
1040 for (
const auto &RenamePos : Code.points()) {
1043 getVFSFromAST(
AST), Index.get()});
1044 if (std::optional<StringRef> Expected =
T.Expected) {
1058TEST(RenameTest, Renameable) {
1063 llvm::StringRef NewName =
"MockName";
1067 {R
"cpp(// allow -- function-local
1068 void f(int [[Lo^cal]]) {
1074 {R
"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
1076 class Unin^dexable {};
1081 {R
"cpp(// disallow -- namespace symbol isn't supported
1086 {R
"cpp(// disallow - category rename.
1089 @interface Foo (Cate^gory)
1092 "Cannot rename symbol: there is no symbol at the given location",
1104 struct X { X operator++(int); };
1105 void f(X x) {x+^+;})cpp",
1110 - (int)[[fo^o]]:(int)x;
1114 {R
"cpp(//disallow as : count must match
1119 "invalid name: the chosen name \"MockName\" is not a valid identifier",
1123 - (int)[[o^ne]]:(int)one two:(int)two;
1129 - (int)[[o^ne]]:(int)one [[two]]:(int)two;
1135 - (int)o^ne:(int)one [[two]]:(int)two;
1143 template <typename T> void f(T t) {
1148 {R
"cpp(// disallow rename on unrelated token.
1153 {R
"cpp(// disallow rename on unrelated token.
1154 temp^late<typename T>
1191 enum E { // transparent context.
1208 if (int Conflict = 42) {
1217 if (int Conflict = 42) {
1227 if (int V^ar = 42) {
1237 while (int V^ar = 10) {
1238 bool Conflict = true;
1246 for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
1256 for (int V^ar = 14, Conflict = 42;;) {
1263 void func(int Conflict) {
1272 void func(int V^ar) {
1278 {R
"cpp(// No conflict: only forward declaration's argument is renamed.
1279 void func(int [[V^ar]]);
1281 void func(int Var) {
1288 void func(int V^ar, int Conflict) {
1312 void func(int conflict) {
1328 void [[o^therFunc]](double);
1334 void [[o^therFunc]](double);
1342 "\"const\" is a keyword", !
HeaderFile,
"const"},
1344 {R
"cpp(// Trying to rename into the same name, SameName == SameName.
1349 "new name is the same", !
HeaderFile,
"SameName"},
1350 {R
"cpp(// Ensure it doesn't associate base specifier with base name.
1352 struct B : priv^ate A {};
1354 "Cannot rename symbol: there is no symbol at the given location",
false},
1355 {R
"cpp(// Ensure it doesn't associate base specifier with base name.
1358 A() : inva^lid(0) {}
1361 "no symbol",
false},
1363 {R
"cpp(// FIXME we probably want to rename both overloads here,
1364 // but renaming currently assumes there's only a
1365 // single canonical declaration.
1366 namespace ns { int foo(int); char foo(char); }
1369 "there are multiple symbols at the given location", !
HeaderFile},
1374 using namespace std;
1381 for (
const auto& Case : Cases) {
1382 SCOPED_TRACE(Case.Code);
1385 TU.ExtraArgs.push_back(
"-fno-delayed-template-parsing");
1386 if (Case.IsHeaderFile) {
1388 TU.Filename =
"test.h";
1390 TU.ExtraArgs.push_back(
"-xobjective-c++-header");
1392 auto AST = TU.build();
1393 llvm::StringRef NewName = Case.NewName;
1395 bool WantRename =
true;
1396 if (
T.ranges().empty())
1399 assert(Case.ErrorMessage &&
"Error message must be set!");
1400 EXPECT_FALSE(Results)
1401 <<
"expected rename returned an error: " <<
T.code();
1402 auto ActualMessage = llvm::toString(Results.takeError());
1403 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
1405 EXPECT_TRUE(
bool(Results)) <<
"rename returned an error: "
1406 << llvm::toString(Results.takeError());
1407 EXPECT_EQ(Results->LocalChanges,
T.ranges());
1412MATCHER_P(newText, T,
"") {
return arg.newText ==
T; }
1414TEST(RenameTest, IndexMergeMainFile) {
1417 TU.Filename =
"main.cc";
1418 auto AST = TU.build();
1421 auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
1422 InMemFS->addFile(
testPath(
"main.cc"), 0,
1423 llvm::MemoryBuffer::getMemBuffer(Code.code()));
1424 InMemFS->addFile(
testPath(
"other.cc"), 0,
1425 llvm::MemoryBuffer::getMemBuffer(Code.code()));
1432 Idx ? createOverlay(getVFSFromAST(
AST), InMemFS)
1436 auto Results =
rename(Inputs);
1437 EXPECT_TRUE(
bool(Results)) << llvm::toString(Results.takeError());
1438 return std::move(*Results);
1442 auto Results = Rename(TU.index().get());
1443 EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1444 EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1445 ElementsAre(newText(
"xPrime")));
1448 TU.Filename =
"other.cc";
1449 Results = Rename(TU.index().get());
1450 EXPECT_THAT(Results.GlobalChanges.keys(),
1451 UnorderedElementsAre(Main,
testPath(
"other.cc")));
1453#ifdef CLANGD_PATH_CASE_INSENSITIVE
1456 TU.Filename =
"MAIN.CC";
1457 Results = Rename(TU.index().get());
1458 EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1459 EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1460 ElementsAre(newText(
"xPrime")));
1464TEST(RenameTest, MainFileReferencesOnly) {
1466 llvm::StringRef Test =
1470 // rename references not from main file are not included.
1476 TU.AdditionalFiles[
"foo.inc"] = R
"cpp(
1481 auto AST = TU.build();
1482 llvm::StringRef NewName =
"abcde";
1489 expectedResult(Code, NewName));
1492TEST(RenameTest, NoRenameOnSymbolsFromSystemHeaders) {
1493 llvm::StringRef Test =
1500 void foo() { at^oi("9000"); }
1505 TU.AdditionalFiles[
"system"] = R
"cpp(
1506 class SystemSymbol {};
1508 TU.AdditionalFiles["cstdlib"] = R
"cpp(
1509 int atoi(const char *str);
1511 TU.ExtraArgs = {"-isystem",
testRoot()};
1512 auto AST = TU.build();
1513 llvm::StringRef NewName =
"abcde";
1517 for (
auto &Point : Code.points()) {
1519 EXPECT_FALSE(Results) <<
"expected rename returned an error: "
1521 auto ActualMessage = llvm::toString(Results.takeError());
1522 EXPECT_THAT(ActualMessage, testing::HasSubstr(
"not a supported kind"));
1526TEST(RenameTest, ProtobufSymbolIsExcluded) {
1530 R
"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1533 TU.HeaderFilename = "protobuf.pb.h";
1534 auto AST = TU.build();
1536 EXPECT_FALSE(Results);
1537 EXPECT_THAT(llvm::toString(Results.takeError()),
1538 testing::HasSubstr(
"not a supported kind"));
1541TEST(RenameTest, PrepareRename) {
1547 std::string FooHPath = testPath("foo.h");
1548 std::string FooCCPath =
testPath(
"foo.cc");
1550 FS.
Files[FooHPath] = std::string(FooH.code());
1551 FS.Files[FooCCPath] = std::string(FooCC.code());
1554 ServerOpts.BuildDynamicSymbolIndex =
true;
1565 ASSERT_TRUE(
bool(Results)) << Results.takeError();
1568 EXPECT_TRUE(Results->GlobalChanges.empty());
1569 EXPECT_THAT(FooCC.ranges(),
1570 testing::UnorderedElementsAreArray(Results->LocalChanges));
1574 std::string(
"int"), {});
1575 EXPECT_FALSE(Results);
1576 EXPECT_THAT(llvm::toString(Results.takeError()),
1577 testing::HasSubstr(
"keyword"));
1578 EXPECT_THAT(Tracer.takeMetric(
"rename_name_invalid",
"Keywords"),
1581 for (std::string BadIdent : {
"foo!bar",
"123foo",
"😀@"}) {
1584 EXPECT_FALSE(Results);
1585 EXPECT_THAT(llvm::toString(Results.takeError()),
1586 testing::HasSubstr(
"identifier"));
1587 EXPECT_THAT(Tracer.takeMetric(
"rename_name_invalid",
"BadIdentifier"),
1590 for (std::string GoodIdent : {
"fooBar",
"__foo$",
"😀"}) {
1593 EXPECT_TRUE(
bool(Results));
1597TEST(CrossFileRenameTests, DirtyBuffer) {
1599 std::string FooPath =
testPath(
"foo.cc");
1600 Annotations FooDirtyBuffer(
"class [[Foo]] {};\n// this is dirty buffer");
1602 std::string BarPath =
testPath(
"bar.cc");
1606 FSymbols.update(FooPath,
nullptr, buildRefSlab(FooCode,
"Foo", FooPath),
1608 FSymbols.update(BarPath,
nullptr, buildRefSlab(BarCode,
"Bar", BarPath),
1613 auto MainFilePath =
testPath(
"main.cc");
1614 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
1615 new llvm::vfs::InMemoryFileSystem;
1616 InMemFS->addFile(FooPath, 0,
1617 llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer.code()));
1622 auto AST = TU.build();
1623 llvm::StringRef NewName =
"newName";
1625 rename({MainCode.point(), NewName,
AST, MainFilePath,
1626 createOverlay(getVFSFromAST(
AST), InMemFS), Index.get()});
1627 ASSERT_TRUE(
bool(Results)) << Results.takeError();
1629 applyEdits(std::move(Results->GlobalChanges)),
1630 UnorderedElementsAre(
1631 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
1632 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1636 MainCode =
Annotations(
"void [[Bar]]() { [[B^ar]](); }");
1639 TU.AdditionalFiles[
"bar.cc"] = std::string(BarCode.code());
1641 Results =
rename({MainCode.point(), NewName,
AST, MainFilePath,
1642 createOverlay(getVFSFromAST(
AST), InMemFS), Index.get()});
1643 ASSERT_TRUE(
bool(Results)) << Results.takeError();
1645 applyEdits(std::move(Results->GlobalChanges)),
1646 UnorderedElementsAre(
1647 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1648 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1653 bool refs(
const RefsRequest &Req,
1654 llvm::function_ref<
void(
const Ref &)> Callback)
const override {
1658 bool containedRefs(
const ContainedRefsRequest &Req,
1659 llvm::function_ref<
void(
const ContainedRefsResult &)>
1660 Callback)
const override {
1665 const FuzzyFindRequest &Req,
1666 llvm::function_ref<
void(
const Symbol &)> Callback)
const override {
1670 lookup(
const LookupRequest &Req,
1671 llvm::function_ref<
void(
const Symbol &)> Callback)
const override {}
1673 void relations(
const RelationsRequest &Req,
1674 llvm::function_ref<
void(
const SymbolID &,
const Symbol &)>
1675 Callback)
const override {}
1678 indexedFiles()
const override {
1679 return [](llvm::StringRef) {
return IndexContents::None; };
1682 size_t estimateMemoryUsage()
const override {
return 0; }
1684 Results =
rename({MainCode.point(), NewName,
AST, MainFilePath,
1685 createOverlay(getVFSFromAST(
AST), InMemFS), &PIndex});
1686 EXPECT_FALSE(Results);
1687 EXPECT_THAT(llvm::toString(Results.takeError()),
1688 testing::HasSubstr(
"too many occurrences"));
1691TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1693 auto MainFilePath =
testPath(
"main.cc");
1698 TU.AdditionalFiles[
"bar.cc"] = std::string(BarCode.code());
1699 auto AST = TU.build();
1700 std::string BarPathURI =
URI::create(BarPath).toString();
1701 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
1706 DuplicatedXRefIndex(
const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1707 bool refs(
const RefsRequest &Req,
1708 llvm::function_ref<
void(
const Ref &)> Callback)
const override {
1715 bool containedRefs(
const ContainedRefsRequest &Req,
1716 llvm::function_ref<
void(
const ContainedRefsResult &)>
1717 Callback)
const override {
1721 bool fuzzyFind(
const FuzzyFindRequest &,
1722 llvm::function_ref<
void(
const Symbol &)>)
const override {
1725 void lookup(
const LookupRequest &,
1726 llvm::function_ref<
void(
const Symbol &)>)
const override {}
1728 void relations(
const RelationsRequest &,
1729 llvm::function_ref<
void(
const SymbolID &,
const Symbol &)>)
1733 indexedFiles()
const override {
1734 return [](llvm::StringRef) {
return IndexContents::None; };
1737 size_t estimateMemoryUsage()
const override {
return 0; }
1739 } DIndex(XRefInBarCC);
1740 llvm::StringRef NewName =
"newName";
1741 auto Results =
rename({MainCode.point(), NewName,
AST, MainFilePath,
1742 getVFSFromAST(
AST), &DIndex});
1743 ASSERT_TRUE(
bool(Results)) << Results.takeError();
1745 applyEdits(std::move(Results->GlobalChanges)),
1746 UnorderedElementsAre(
1747 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1748 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1751TEST(CrossFileRenameTests, WithUpToDateIndex) {
1757 llvm::StringRef FooH;
1758 llvm::StringRef FooCC;
1770 [[Foo]]::[[Foo]]() {}
1771 [[Foo]]::~[[Foo]]() {}
1781 template <typename T>
1783 // FIXME: explicit template specializations are not supported due the
1784 // clangd index limitations.
1786 class Foo<double> {};
1804 void Foo::[[foo]]() {}
1815 virtual void [[foo]]();
1817 class Derived1 : public Base {
1818 void [[f^oo]]() override;
1826 void Base::[[foo]]() {}
1827 void Derived1::[[foo]]() {}
1829 class Derived2 : public Derived1 {
1830 void [[foo]]() override {};
1833 void func(Base* b, Derived1* d1,
1834 Derived2* d2, NotDerived* nd) {
1844 template <typename> class Foo { virtual void [[m]](); };
1845 class Bar : Foo<int> { void [[^m]]() override; };
1850 template<typename T> void Foo<T>::[[m]]() {}
1851 // FIXME: not renamed as the index doesn't see this as an override of
1852 // the canonical Foo<T>::m().
1853 // https://github.com/clangd/clangd/issues/1325
1854 class Baz : Foo<float> { void m() override; };
1866 [[Foo]]::[[Foo]]() {}
1867 [[Foo]]::~[[Foo]]() {}
1891 typedef int [[IN^T]];
1902 using [[I^NT]] = int;
1913 static const int [[VA^R]] = 123;
1923 enum class [[K^ind]] { ABC };
1928 return [[Kind]]::ABC;
1935 enum class Kind { [[A^BC]] };
1940 return Kind::[[ABC]];
1968 @implementation [[Foo]]
1971 void func([[Foo]] *f) {}
1977 for (
const auto &T : Cases) {
1978 SCOPED_TRACE(
T.FooH);
1981 std::string FooHPath =
testPath(
"foo.h");
1982 std::string FooCCPath =
testPath(
"foo.cc");
1985 FS.
Files[FooHPath] = std::string(FooH.code());
1986 FS.Files[FooCCPath] = std::string(FooCC.code());
1989 ServerOpts.BuildDynamicSymbolIndex =
true;
1997 llvm::StringRef NewName =
"NewName";
1998 for (
const auto &RenamePos : FooH.points()) {
1999 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), SizeIs(0));
2000 auto FileEditsList =
2001 llvm::cantFail(
runRename(Server, FooHPath, RenamePos, NewName, {}));
2002 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), ElementsAre(2));
2004 applyEdits(std::move(FileEditsList.GlobalChanges)),
2005 UnorderedElementsAre(
2006 Pair(Eq(FooHPath), Eq(expectedResult(
T.FooH, NewName))),
2007 Pair(Eq(FooCCPath), Eq(expectedResult(
T.FooCC, NewName)))));
2012TEST(CrossFileRenameTests, ObjC) {
2017 llvm::StringRef FooH;
2018 llvm::StringRef FooM;
2019 llvm::StringRef NewName;
2020 llvm::StringRef ExpectedFooH;
2021 llvm::StringRef ExpectedFooM;
2028 - (int)performA^ction;
2033 - (int)performAction {
2034 [self performAction];
2043 - (int)performNewAction;
2048 - (int)performNewAction {
2049 [self performNewAction];
2059 - (int)performA^ction:(int)action;
2064 - (int)performAction:(int)action {
2065 [self performAction:action];
2070 "performNewAction:",
2074 - (int)performNewAction:(int)action;
2079 - (int)performNewAction:(int)action {
2080 [self performNewAction:action];
2090 - (int)performA^ction:(int)action with:(int)value;
2095 - (int)performAction:(int)action with:(int)value {
2096 [self performAction:action with:value];
2101 "performNewAction:by:",
2105 - (int)performNewAction:(int)action by:(int)value;
2110 - (int)performNewAction:(int)action by:(int)value {
2111 [self performNewAction:action by:value];
2118 for (
const auto &T : Cases) {
2119 SCOPED_TRACE(
T.FooH);
2122 std::string FooHPath =
testPath(
"foo.h");
2123 std::string FooMPath =
testPath(
"foo.m");
2126 FS.
Files[FooHPath] = std::string(FooH.code());
2127 FS.Files[FooMPath] = std::string(FooM.code());
2130 ServerOpts.BuildDynamicSymbolIndex =
true;
2138 for (
const auto &RenamePos : FooH.points()) {
2139 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), SizeIs(0));
2140 auto FileEditsList =
2141 llvm::cantFail(
runRename(Server, FooHPath, RenamePos,
T.NewName, {}));
2142 EXPECT_THAT(Tracer.takeMetric(
"rename_files"), ElementsAre(2));
2143 EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
2144 UnorderedElementsAre(Pair(Eq(FooHPath), Eq(
T.ExpectedFooH)),
2145 Pair(Eq(FooMPath), Eq(
T.ExpectedFooM))));
2150TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2153 Annotations Code(
"void f(int [[abc]]) { [[a^bc]] = 3; }");
2156 auto AST = TU.build();
2157 llvm::StringRef NewName =
"newName";
2159 ASSERT_TRUE(
bool(Results)) << Results.takeError();
2161 applyEdits(std::move(Results->GlobalChanges)),
2162 UnorderedElementsAre(Pair(Eq(
Path), Eq(expectedResult(Code, NewName)))));
2165TEST(CrossFileRenameTests, BuildRenameEdits) {
2167 auto LSPRange = Code.range();
2168 llvm::StringRef FilePath =
"/test/TestTU.cpp";
2169 llvm::SmallVector<llvm::StringRef, 2> NewNames = {
"abc"};
2171 ASSERT_TRUE(
bool(
Edit)) <<
Edit.takeError();
2177 LSPRange.end = {10, 0};
2180 EXPECT_THAT(llvm::toString(
Edit.takeError()),
2181 testing::HasSubstr(
"fail to convert"));
2191 ASSERT_TRUE(bool(
Edit)) <<
Edit.takeError();
2192 EXPECT_EQ(applyEdits(
FileEdits{{
T.code(), std::move(*
Edit)}}).front().second,
2193 expectedResult(T, NewNames[0]));
2201 llvm::StringRef IndexedCode;
2202 llvm::StringRef DraftCode;
2226 R
"cpp(int [[x]] = 0; void foo(int x);)cpp",
2227 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
2242 LangOptions LangOpts;
2243 LangOpts.CPlusPlus = true;
2244 for (
const auto &T : Tests) {
2245 SCOPED_TRACE(
T.DraftCode);
2251 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
2253 EXPECT_THAT(Draft.ranges(),
2254 testing::UnorderedElementsAreArray(*ActualRanges));
2258TEST(RangePatchingHeuristic, GetMappedRanges) {
2262 llvm::StringRef IndexedCode;
2263 llvm::StringRef LexedCode;
2309 ^[[]] ^[[]] // column is shifted.
2322 [[]] [[]] // not mapped (both line and column are changed).
2336 // insert a new line
2339 [[]] // additional range
2342 [[]] // additional range
2360 for (
const auto &T : Tests) {
2361 SCOPED_TRACE(
T.IndexedCode);
2363 auto LexedRanges = symbolRanges(Lexed.ranges());
2364 std::vector<SymbolRange> ExpectedMatches;
2365 for (
auto P : Lexed.points()) {
2366 auto Match = llvm::find_if(LexedRanges, [&P](
const SymbolRange &R) {
2367 return R.range().start ==
P;
2369 ASSERT_NE(Match, LexedRanges.end());
2370 ExpectedMatches.push_back(*Match);
2376 EXPECT_THAT(ExpectedMatches, IsEmpty());
2378 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2382TEST(CrossFileRenameTests, adjustmentCost) {
2384 llvm::StringRef RangeCode;
2385 size_t ExpectedCost;
2389 $idx[[]]$lex[[]] // diff: 0
2396 $lex[[]] // line diff: +1
2398 $lex[[]] // line diff: +1
2400 $lex[[]] // line diff: +1
2404 $lex[[]] // line diff: +2
2411 $lex[[]] // line diff: +1
2414 $lex[[]] // line diff: +2
2418 $lex[[]] // line diff: +3
2427 $lex[[]] // line diff: +3
2430 $lex[[]] // line diff: +2
2432 $lex[[]] // line diff: +1
2439 $lex[[]] // line diff: +1
2440 $lex[[]] // line diff: -2
2446 $lex[[]] // line diff: +3
2452 $idx[[]] $lex[[]] // column diff: +1
2453 $idx[[]]$lex[[]] // diff: 0
2460 $lex[[]] // diff: +1
2461 $idx[[]] $lex[[]] // column diff: +1
2462 $idx[[]]$lex[[]] // diff: 0
2468 $idx[[]] $lex[[]] // column diff: +1
2474 // column diffs: +1, +2, +3
2475 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2480 for (
const auto &T : Tests) {
2481 SCOPED_TRACE(
T.RangeCode);
2483 std::vector<size_t> MappedIndex;
2484 for (
size_t I = 0; I <
C.ranges(
"lex").size(); ++I)
2485 MappedIndex.push_back(I);
2487 C.ranges(
"idx"), symbolRanges(
C.ranges(
"lex")), MappedIndex),
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
std::vector< clangd::Range > ranges(llvm::StringRef Name="") const
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
static Options optsForTest()
A container of slabs associated with a key.
std::vector< std::string > ExtraClangFlags
llvm::StringMap< std::string > Files
Stores and provides access to parsed AST.
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
A name of a symbol that should be renamed.
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
A URI describes the location of a source file.
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
A RAII Tracer that can be used by tests.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
IndexContents
Describes what data is covered by an index.
std::optional< std::vector< SymbolRange > > adjustRenameRanges(llvm::StringRef DraftCode, const RenameSymbolName &Name, std::vector< Range > Indexed, const LangOptions &LangOpts)
Adjusts indexed occurrences to match the current state of the file.
llvm::Expected< RenameResult > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &RenameOpts)
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::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
llvm::Expected< RenameResult > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
std::string testPath(PathRef File, llvm::sys::path::Style Style)
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
TEST(BackgroundQueueTest, Priority)
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)
std::string Path
A typedef to represent a file path.
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.
const char ErrorMessage[]
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccess P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
tooling::Replacements Replacements
int line
Line position in a document (zero-based).
int character
Character offset on a line in a document (zero-based).
Position start
The range's start position.
Position end
The range's end position.
Represents a symbol occurrence in the source file.
Represents a symbol range where the symbol can potentially have multiple tokens.
SymbolID ID
The ID of the symbol.
static TestTU withCode(llvm::StringRef Code)