16 #include "clang/Tooling/Core/Replacement.h" 17 #include "llvm/ADT/STLExtras.h" 18 #include "llvm/Support/MemoryBuffer.h" 19 #include "gmock/gmock.h" 20 #include "gtest/gtest.h" 29 using testing::IsEmpty;
30 using testing::UnorderedElementsAre;
31 using testing::UnorderedElementsAreArray;
37 Result.Location.Start.setLine(Range.start.line);
38 Result.Location.Start.setColumn(Range.start.character);
39 Result.Location.End.setLine(Range.end.line);
40 Result.Location.End.setColumn(Range.end.character);
41 Result.Location.FileURI = URI.c_str();
47 std::unique_ptr<RefSlab> buildRefSlab(
const Annotations &
Code,
48 llvm::StringRef SymbolName,
49 llvm::StringRef
Path) {
52 TU.HeaderCode = Code.code();
53 auto Symbols = TU.headerSymbols();
56 for (
const auto &Range : Code.ranges())
57 Builder.insert(
SymbolID, refWithRange(Range, PathURI));
59 return std::make_unique<RefSlab>(std::move(Builder).build());
63 std::pair< std::string, std::string>>
65 std::vector<std::pair<std::string, std::string>>
Results;
69 llvm::cantFail(tooling::applyAllReplacements(
70 It.getValue().InitialCode, It.getValue().Replacements)));
76 std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
78 unsigned NextChar = 0;
79 llvm::StringRef Code = Test.code();
80 for (
const auto &R : Test.llvm::Annotations::ranges()) {
81 assert(R.Begin <= R.End && NextChar <= R.Begin);
82 Result += Code.substr(NextChar, R.Begin - NextChar);
86 Result += Code.substr(NextChar);
90 TEST(RenameTest, WithinFileRename) {
93 llvm::StringRef Tests[] = {
113 if (auto [[^foo]] = 5) { 126 [[Foo]]::[[Fo^o]]() {} 127 void [[Foo]]::foo(int x) {} 133 template <typename T> void func(); 134 template <typename T> class Baz {}; 151 virtual void [[f^oo]]() {} 154 void [[f^oo]]() override {} 157 void [[f^oo]]() override {} 169 template <typename T> 173 class [[F^oo]]<bool> {}; 174 template <typename T> 175 class [[F^oo]]<T*> {}; 186 template <typename T> 189 T foo(T arg, T& ref, T* ptr) { 193 value = static_cast<T>(number); 196 static void foo(T value) {} 200 template <typename T> 210 [[F^oo]]<int>::foo(0); 214 [[Foo]]<bool>::foo(false); 220 template <typename T> 228 A<double>().[[f^oo]](); 229 A<float>().[[f^oo]](); 235 // Forward declaration. 238 virtual int getValue() const = 0; 241 class [[F^oo]] : public Baz { 243 [[Foo]](int value = 0) : x(value) {} 245 [[Foo]] &operator++(int); 247 bool operator<([[Foo]] const &rhs); 248 int getValue() const; 254 [[Foo]] *Pointer = 0; 255 [[Foo]] Variable = [[Foo]](10); 256 for ([[Foo]] it; it < Variable; it++); 257 const [[Foo]] *C = new [[Foo]](); 258 const_cast<[[Foo]] *>(C)->getValue(); 260 const Baz &BazReference = foo; 261 const Baz *BazPointer = &foo; 262 reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue(); 263 static_cast<const [[^Foo]] &>(BazReference).getValue(); 264 static_cast<const [[^Foo]] *>(BazPointer)->getValue(); 276 Qux::Qux() : [[F^oo]]() {} 287 #define MACRO(a) foo(a) 298 // no rename inside macro body. 310 M2(M1()); // foo is inside the nested macro body. 321 #define MACRO(a) qux(a) 333 template <typename [[^T]]> 335 [[T]] foo([[T]] arg, [[T]]& ref, [[^T]]* ptr) { 338 value = ([[T]])number; 339 value = static_cast<[[^T]]>(number); 342 static void foo([[T]] value) {} 350 class basic_string {}; 351 typedef basic_string [[s^tring]]; 354 std::[[s^tring]] foo(); 364 int Baz = A::[[^Foo]]; 374 Foo = A::[[F^oo]] + Baz; 383 namespace a { namespace b { void foo(); } } 384 namespace [[^x]] = a::b; 392 enum class [[K^ind]] { ABC }; 403 template <template<typename> class Z> struct Bar { }; 404 template <> struct Bar<[[Foo]]> {}; 407 for (llvm::StringRef T : Tests) {
410 TU.
ExtraArgs.push_back(
"-fno-delayed-template-parsing");
411 auto AST = TU.
build();
412 llvm::StringRef NewName =
"abcde";
413 for (
const auto &RenamePos : Code.points()) {
416 ASSERT_TRUE(
bool(RenameResult)) << RenameResult.takeError();
417 ASSERT_EQ(1u, RenameResult->size());
418 EXPECT_EQ(applyEdits(std::move(*RenameResult)).front().second,
419 expectedResult(Code, NewName));
424 TEST(RenameTest, Renameable) {
427 const char* ErrorMessage;
429 const SymbolIndex *
Index;
432 const char *CommonHeader = R
"cpp( 436 OtherFile.HeaderCode = CommonHeader; 437 OtherFile.Filename = "other.cc";
439 auto OtherFileIndex = OtherFile.index();
440 const SymbolIndex *
Index = OtherFileIndex.get();
442 const bool HeaderFile =
true;
444 {R
"cpp(// allow -- function-local 445 void f(int [[Lo^cal]]) { 449 nullptr, HeaderFile, Index},
451 {R
"cpp(// allow -- symbol is indexable and has no refs in index. 452 void [[On^lyInThisFile]](); 454 nullptr, HeaderFile, Index},
456 {R
"cpp(// disallow -- symbol is indexable and has other refs in index. 461 "used outside main file", HeaderFile, Index},
463 {R
"cpp(// disallow -- symbol in anonymous namespace in header is not indexable. 465 class Unin^dexable {}; 468 "not eligible for indexing", HeaderFile, Index},
470 {R
"cpp(// allow -- symbol in anonymous namespace in non-header file is indexable. 475 nullptr, !HeaderFile, Index},
477 {R
"cpp(// disallow -- namespace symbol isn't supported 480 "not a supported kind", HeaderFile, Index},
487 "not a supported kind", HeaderFile, Index},
491 struct X { X operator++(int); }; 492 void f(X x) {x+^+;})cpp", 493 "no symbol", HeaderFile, Index},
495 {R
"cpp(// foo is declared outside the file. 498 "used outside main file", !HeaderFile , Index},
501 // We should detect the symbol is used outside the file from the AST. 503 "used outside main file", !HeaderFile,
nullptr },
508 template <typename T> void f(T t) { 511 "multiple symbols", !HeaderFile,
nullptr },
513 {R
"cpp(// disallow rename on unrelated token. 516 "no symbol", !HeaderFile,
nullptr},
518 {R
"cpp(// disallow rename on unrelated token. 519 temp^late<typename T> 522 "no symbol", !HeaderFile,
nullptr},
525 for (
const auto& Case : Cases) {
526 Annotations T(Case.Code);
529 TU.ExtraArgs.push_back(
"-fno-delayed-template-parsing");
530 if (Case.IsHeaderFile) {
532 TU.Filename =
"test.h";
534 TU.ExtraArgs.push_back(
"-xobjective-c++-header");
536 auto AST = TU.build();
537 llvm::StringRef NewName =
"dummyNewName";
540 bool WantRename =
true;
541 if (T.ranges().empty())
544 assert(Case.ErrorMessage &&
"Error message must be set!");
545 EXPECT_FALSE(Results)
546 <<
"expected rename returned an error: " << T.code();
548 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
550 EXPECT_TRUE(
bool(Results)) <<
"rename returned an error: " 552 ASSERT_EQ(1u, Results->size());
553 EXPECT_EQ(applyEdits(std::move(*Results)).front().second,
554 expectedResult(T, NewName));
559 TEST(RenameTest, MainFileReferencesOnly) {
561 llvm::StringRef Test =
565 // rename references not from main file are not included. 569 Annotations Code(Test); 576 auto AST = TU.build();
577 llvm::StringRef NewName =
"abcde";
581 ASSERT_TRUE(
bool(RenameResult)) << RenameResult.takeError() << Code.point();
582 ASSERT_EQ(1u, RenameResult->size());
583 EXPECT_EQ(applyEdits(std::move(*RenameResult)).front().second,
584 expectedResult(Code, NewName));
587 TEST(CrossFileRenameTests, DirtyBuffer) {
588 Annotations FooCode(
"class [[Foo]] {};");
589 std::string FooPath =
testPath(
"foo.cc");
590 Annotations FooDirtyBuffer(
"class [[Foo]] {};\n// this is dirty buffer");
591 Annotations BarCode(
"void [[Bar]]() {}");
592 std::string BarPath =
testPath(
"bar.cc");
595 FileSymbols FSymbols;
596 FSymbols.update(FooPath,
nullptr, buildRefSlab(FooCode,
"Foo", FooPath),
598 FSymbols.update(BarPath,
nullptr, buildRefSlab(BarCode,
"Bar", BarPath),
602 Annotations MainCode(
"class [[Fo^o]] {};");
603 auto MainFilePath =
testPath(
"main.cc");
605 auto GetDirtyBuffer = [&](
PathRef Path) -> llvm::Optional<std::string> {
607 return FooDirtyBuffer.code().str();
614 auto AST = TU.build();
615 llvm::StringRef NewName =
"newName";
616 auto Results =
rename({MainCode.point(), NewName,
AST, MainFilePath,
617 Index.get(),
true, GetDirtyBuffer});
618 ASSERT_TRUE(
bool(Results)) << Results.takeError();
620 applyEdits(std::move(*Results)),
621 UnorderedElementsAre(
622 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
623 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
627 MainCode = Annotations(
"void [[Bar]]() { [[B^ar]](); }");
632 Results =
rename({MainCode.point(), NewName,
AST, MainFilePath, Index.get(),
633 true, GetDirtyBuffer});
634 ASSERT_TRUE(
bool(Results)) << Results.takeError();
636 applyEdits(std::move(*Results)),
637 UnorderedElementsAre(
638 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
639 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
643 class PaginationIndex :
public SymbolIndex {
644 bool refs(
const RefsRequest &Req,
645 llvm::function_ref<
void(
const Ref &)>
Callback)
const override {
650 const FuzzyFindRequest &Req,
651 llvm::function_ref<
void(
const Symbol &)>
Callback)
const override {
655 lookup(
const LookupRequest &Req,
656 llvm::function_ref<
void(
const Symbol &)>
Callback)
const override {}
658 void relations(
const RelationsRequest &Req,
659 llvm::function_ref<
void(
const SymbolID &,
const Symbol &)>
661 size_t estimateMemoryUsage()
const override {
return 0; }
663 Results =
rename({MainCode.point(), NewName,
AST, MainFilePath, &PIndex,
664 true, GetDirtyBuffer});
665 EXPECT_FALSE(Results);
667 testing::HasSubstr(
"too many occurrences"));
670 TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
671 auto MainCode = Annotations(
"int [[^x]] = 2;");
672 auto MainFilePath =
testPath(
"main.cc");
673 auto BarCode = Annotations(
"int [[x]];");
678 auto AST = TU.build();
679 std::string BarPathURI =
URI::create(BarPath).toString();
680 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
683 class DuplicatedXRefIndex :
public SymbolIndex {
685 DuplicatedXRefIndex(
const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
686 bool refs(
const RefsRequest &Req,
687 llvm::function_ref<
void(
const Ref &)>
Callback)
const override {
694 bool fuzzyFind(
const FuzzyFindRequest &,
695 llvm::function_ref<
void(
const Symbol &)>)
const override {
698 void lookup(
const LookupRequest &,
699 llvm::function_ref<
void(
const Symbol &)>)
const override {}
701 void relations(
const RelationsRequest &,
702 llvm::function_ref<
void(
const SymbolID &,
const Symbol &)>)
704 size_t estimateMemoryUsage()
const override {
return 0; }
706 } DIndex(XRefInBarCC);
707 llvm::StringRef NewName =
"newName";
708 auto Results =
rename({MainCode.point(), NewName,
AST, MainFilePath, &DIndex,
710 ASSERT_TRUE(
bool(Results)) << Results.takeError();
712 applyEdits(std::move(*Results)),
713 UnorderedElementsAre(
714 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
715 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
718 TEST(CrossFileRenameTests, WithUpToDateIndex) {
719 MockCompilationDatabase CDB;
720 CDB.ExtraClangFlags = {
"-xc++"};
721 class IgnoreDiagnostics :
public DiagnosticsConsumer {
723 std::vector<Diag> Diagnostics)
override {}
728 llvm::StringRef FooH;
729 llvm::StringRef FooCC;
741 [[Foo]]::[[Foo]]() {} 742 [[Foo]]::~[[Foo]]() {} 758 void Foo::[[foo]]() {} 775 [[Foo]]::[[Foo]]() {} 776 [[Foo]]::~[[Foo]]() {} 793 [[Foo]]::[[Foo]]() {} 794 [[Foo]]::~[[Foo]]() {} 818 typedef int [[IN^T]]; 829 using [[I^NT]] = int; 840 static const int [[VA^R]] = 123; 850 enum class [[K^ind]] { ABC }; 855 return [[Kind]]::ABC; 862 enum class Kind { [[A^BC]] }; 867 return Kind::[[ABC]]; 873 for (
const auto& T : Cases) {
874 Annotations FooH(T.FooH);
875 Annotations FooCC(T.FooCC);
876 std::string FooHPath =
testPath(
"foo.h");
877 std::string FooCCPath =
testPath(
"foo.cc");
880 FS.Files[FooHPath] = FooH.code();
881 FS.Files[FooCCPath] = FooCC.code();
884 ServerOpts.CrossFileRename =
true;
885 ServerOpts.BuildDynamicSymbolIndex =
true;
893 llvm::StringRef NewName =
"NewName";
896 EXPECT_THAT(applyEdits(std::move(FileEditsList)),
897 UnorderedElementsAre(
898 Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
899 Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
903 TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
906 Annotations
Code(
"void f(int [[abc]]) { [[a^bc]] = 3; }");
909 auto AST = TU.build();
910 llvm::StringRef NewName =
"newName";
911 auto Results =
rename({Code.point(), NewName,
AST, Path});
912 ASSERT_TRUE(
bool(Results)) << Results.takeError();
914 applyEdits(std::move(*Results)),
915 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
918 TEST(CrossFileRenameTests, BuildRenameEdits) {
919 Annotations
Code(
"[[😂]]");
920 auto LSPRange = Code.range();
921 llvm::StringRef FilePath =
"/test/TestTU.cpp";
922 llvm::StringRef NewName =
"abc";
923 auto Edit =
buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
924 ASSERT_TRUE(
bool(Edit)) << Edit.takeError();
925 ASSERT_EQ(1UL, Edit->Replacements.size());
926 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
927 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
930 LSPRange.end = {10, 0};
934 testing::HasSubstr(
"fail to convert"));
943 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
944 EXPECT_EQ(applyEdits(
FileEdits{{T.code(), std::move(*Edit)}}).front().second,
945 expectedResult(T, NewName));
953 llvm::StringRef IndexedCode;
954 llvm::StringRef DraftCode;
978 R
"cpp(int [[x]] = 0; void foo(int x);)cpp", 979 R"cpp(double [[x]] = 0; void foo(double x);)cpp", 994 LangOptions LangOpts; 995 LangOpts.CPlusPlus = true;
996 for (
const auto &T : Tests) {
997 Annotations Draft(T.DraftCode);
999 Draft.code(),
"x", Annotations(T.IndexedCode).ranges(), LangOpts);
1001 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
1003 EXPECT_THAT(Draft.ranges(),
1004 testing::UnorderedElementsAreArray(*ActualRanges))
1009 TEST(RangePatchingHeuristic, GetMappedRanges) {
1013 llvm::StringRef IndexedCode;
1014 llvm::StringRef LexedCode;
1060 ^[[]] ^[[]] // column is shifted. 1073 [[]] [[]] // not mapped (both line and column are changed). 1087 // insert a new line 1090 [[]] // additional range 1093 [[]] // additional range 1111 for (
const auto &T : Tests) {
1112 auto Lexed = Annotations(T.LexedCode);
1113 auto LexedRanges = Lexed.ranges();
1114 std::vector<Range> ExpectedMatches;
1115 for (
auto P : Lexed.points()) {
1116 auto Match = llvm::find_if(LexedRanges, [&P](
const Range& R) {
1117 return R.start == P;
1119 ASSERT_NE(Match, LexedRanges.end());
1120 ExpectedMatches.push_back(*Match);
1126 EXPECT_THAT(ExpectedMatches, IsEmpty());
1128 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped))
1133 TEST(CrossFileRenameTests, adjustmentCost) {
1135 llvm::StringRef RangeCode;
1136 size_t ExpectedCost;
1140 $idx[[]]$lex[[]] // diff: 0 1147 $lex[[]] // line diff: +1 1149 $lex[[]] // line diff: +1 1151 $lex[[]] // line diff: +1 1155 $lex[[]] // line diff: +2 1162 $lex[[]] // line diff: +1 1165 $lex[[]] // line diff: +2 1169 $lex[[]] // line diff: +3 1178 $lex[[]] // line diff: +3 1181 $lex[[]] // line diff: +2 1183 $lex[[]] // line diff: +1 1190 $lex[[]] // line diff: +1 1191 $lex[[]] // line diff: -2 1197 $lex[[]] // line diff: +3 1203 $idx[[]] $lex[[]] // column diff: +1 1204 $idx[[]]$lex[[]] // diff: 0 1211 $lex[[]] // diff: +1 1212 $idx[[]] $lex[[]] // column diff: +1 1213 $idx[[]]$lex[[]] // diff: 0 1219 $idx[[]] $lex[[]] // column diff: +1 1225 // column diffs: +1, +2, +3 1226 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]] 1231 for (
const auto &T : Tests) {
1232 Annotations C(T.RangeCode);
1233 std::vector<size_t> MappedIndex;
1234 for (
size_t I = 0; I < C.ranges(
"lex").size(); ++I)
1235 MappedIndex.push_back(I);
1238 T.ExpectedCost) << T.RangeCode;
llvm::Optional< std::vector< Range > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
llvm::StringRef PathRef
A typedef to represent a ref to file path.
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
std::vector< CodeCompletionResult > Results
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
static Options optsForTest()
Documents should not be synced at all.
SymbolID ID
The ID of the symbol.
std::vector< const char * > ExtraArgs
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
std::string Path
A typedef to represent a file path.
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits...
static TestTU withCode(llvm::StringRef Code)
CodeCompletionBuilder Builder
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
llvm::Expected< FileEdits > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName)
CharSourceRange Range
SourceRange for the file name.
IgnoreDiagnostics DiagConsumer
llvm::Optional< std::vector< Range > > adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, std::vector< Range > Indexed, const LangOptions &LangOpts)
Adjusts indexed occurrences to match the current state of the file.
llvm::Expected< FileEdits > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
std::array< uint8_t, 20 > SymbolID
llvm::StringMap< std::string > AdditionalFiles
llvm::Expected< Edit > buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode, std::vector< Range > Occurrences, llvm::StringRef NewName)
Generates rename edits that replaces all given occurrences with the NewName.
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags)
const SymbolIndex * Index