clang-tools 10.0.0

RenameTests.cpp
Go to the documentation of this file.
1 //===-- RenameTests.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "Annotations.h"
10 #include "ClangdServer.h"
11 #include "SyncAPI.h"
12 #include "TestFS.h"
13 #include "TestTU.h"
14 #include "index/Ref.h"
15 #include "refactor/Rename.h"
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"
21 #include <algorithm>
22
23 namespace clang {
24 namespace clangd {
25 namespace {
26
27 using testing::Eq;
28 using testing::Pair;
29 using testing::IsEmpty;
30 using testing::UnorderedElementsAre;
31 using testing::UnorderedElementsAreArray;
32
33 // Convert a Range to a Ref.
34 Ref refWithRange(const clangd::Range &Range, const std::string &URI) {
35 Ref Result;
36 Result.Kind = RefKind::Reference;
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();
42 return Result;
43 }
44
45 // Build a RefSlab from all marked ranges in the annotation. The ranges are
46 // assumed to associate with the given SymbolName.
47 std::unique_ptr<RefSlab> buildRefSlab(const Annotations &Code,
48 llvm::StringRef SymbolName,
49 llvm::StringRef Path) {
50 RefSlab::Builder Builder;
51 TestTU TU;
52 TU.HeaderCode = Code.code();
53 auto Symbols = TU.headerSymbols();
54 const auto &SymbolID = findSymbol(Symbols, SymbolName).ID;
55 std::string PathURI = URI::create(Path).toString();
56 for (const auto &Range : Code.ranges())
57 Builder.insert(SymbolID, refWithRange(Range, PathURI));
58
59 return std::make_unique<RefSlab>(std::move(Builder).build());
60 }
61
62 std::vector<
63 std::pair</*FilePath*/ std::string, /*CodeAfterRename*/ std::string>>
64 applyEdits(FileEdits FE) {
65 std::vector<std::pair<std::string, std::string>> Results;
66 for (auto &It : FE)
67 Results.emplace_back(
68 It.first().str(),
69 llvm::cantFail(tooling::applyAllReplacements(
70 It.getValue().InitialCode, It.getValue().Replacements)));
71 return Results;
72 }
73
74 // Generates an expected rename result by replacing all ranges in the given
75 // annotation with the NewName.
76 std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
77 std::string Result;
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);
83 Result += NewName;
84 NextChar = R.End;
85 }
86 Result += Code.substr(NextChar);
87 return Result;
88 }
89
90 TEST(RenameTest, WithinFileRename) {
91 // rename is runnning on all "^" points, and "[[]]" ranges point to the
92 // identifier that is being renamed.
93 llvm::StringRef Tests[] = {
94 // Function.
95 R"cpp(
96 void [[foo^]]() {
97 [[fo^o]]();
98 }
99 )cpp",
100
101 // Type.
102 R"cpp(
103 struct [[foo^]] {};
104 [[foo]] test() {
105 [[f^oo]] x;
106 return x;
107 }
108 )cpp",
109
110 // Local variable.
111 R"cpp(
112 void bar() {
113 if (auto [[^foo]] = 5) {
114 [[foo]] = 3;
115 }
116 }
117 )cpp",
118
119 // Rename class, including constructor/destructor.
120 R"cpp(
121 class [[F^oo]] {
122 [[F^oo]]();
123 ~[[Foo]]();
124 void foo(int x);
125 };
126 [[Foo]]::[[Fo^o]]() {}
127 void [[Foo]]::foo(int x) {}
128 )cpp",
129
130 // Class in template argument.
131 R"cpp(
132 class [[F^oo]] {};
133 template <typename T> void func();
134 template <typename T> class Baz {};
135 int main() {
136 func<[[F^oo]]>();
137 Baz<[[F^oo]]> obj;
138 return 0;
139 }
140 )cpp",
141
142 // Forward class declaration without definition.
143 R"cpp(
144 class [[F^oo]];
145 [[Foo]] *f();
146 )cpp",
147
148 // Class methods overrides.
149 R"cpp(
150 struct A {
151 virtual void [[f^oo]]() {}
152 };
153 struct B : A {
154 void [[f^oo]]() override {}
155 };
156 struct C : B {
157 void [[f^oo]]() override {}
158 };
159
160 void func() {
161 A().[[f^oo]]();
162 B().[[f^oo]]();
163 C().[[f^oo]]();
164 }
165 )cpp",
166
167 // Template class (partial) specializations.
168 R"cpp(
169 template <typename T>
170 class [[F^oo]] {};
171
172 template<>
173 class [[F^oo]]<bool> {};
174 template <typename T>
175 class [[F^oo]]<T*> {};
176
177 void test() {
178 [[Foo]]<int> x;
179 [[Foo]]<bool> y;
180 [[Foo]]<int*> z;
181 }
182 )cpp",
183
184 // Template class instantiations.
185 R"cpp(
186 template <typename T>
187 class [[F^oo]] {
188 public:
189 T foo(T arg, T& ref, T* ptr) {
190 T value;
191 int number = 42;
192 value = (T)number;
193 value = static_cast<T>(number);
194 return value;
195 }
196 static void foo(T value) {}
197 T member;
198 };
199
200 template <typename T>
201 void func() {
202 [[F^oo]]<T> obj;
203 obj.member = T();
204 [[Foo]]<T>::foo();
205 }
206
207 void test() {
208 [[F^oo]]<int> i;
209 i.member = 0;
210 [[F^oo]]<int>::foo(0);
211
212 [[F^oo]]<bool> b;
213 b.member = false;
214 [[Foo]]<bool>::foo(false);
215 }
216 )cpp",
217
218 // Template class methods.
219 R"cpp(
220 template <typename T>
221 class A {
222 public:
223 void [[f^oo]]() {}
224 };
225
226 void func() {
227 A<int>().[[f^oo]]();
228 A<double>().[[f^oo]]();
229 A<float>().[[f^oo]]();
230 }
231 )cpp",
232
233 // Complicated class type.
234 R"cpp(
235 // Forward declaration.
236 class [[Fo^o]];
237 class Baz {
238 virtual int getValue() const = 0;
239 };
240
241 class [[F^oo]] : public Baz {
242 public:
243 [[Foo]](int value = 0) : x(value) {}
244
245 [[Foo]] &operator++(int);
246
247 bool operator<([[Foo]] const &rhs);
248 int getValue() const;
249 private:
250 int x;
251 };
252
253 void func() {
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();
259 [[Foo]] foo;
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();
265 }
266 )cpp",
267
268 // CXXConstructor initializer list.
269 R"cpp(
270 class Baz {};
271 class Qux {
272 Baz [[F^oo]];
273 public:
274 Qux();
275 };
276 Qux::Qux() : [[F^oo]]() {}
277 )cpp",
278
279 // DeclRefExpr.
280 R"cpp(
281 class C {
282 public:
283 static int [[F^oo]];
284 };
285
286 int foo(int x);
287 #define MACRO(a) foo(a)
288
289 void func() {
290 C::[[F^oo]] = 1;
291 MACRO(C::[[Foo]]);
292 int y = C::[[F^oo]];
293 }
294 )cpp",
295
296 // Macros.
297 R"cpp(
298 // no rename inside macro body.
299 #define M1 foo
300 #define M2(x) x
301 int [[fo^o]]();
302 void boo(int);
303
304 void qoo() {
305 [[foo]]();
306 boo([[foo]]());
307 M1();
308 boo(M1());
309 M2([[foo]]());
310 M2(M1()); // foo is inside the nested macro body.
311 }
312 )cpp",
313
314 // MemberExpr in macros
315 R"cpp(
316 class Baz {
317 public:
318 int [[F^oo]];
319 };
320 int qux(int x);
321 #define MACRO(a) qux(a)
322
323 int main() {
324 Baz baz;
325 baz.[[Foo]] = 1;
326 MACRO(baz.[[Foo]]);
327 int y = baz.[[Foo]];
328 }
329 )cpp",
330
331 // Template parameters.
332 R"cpp(
333 template <typename [[^T]]>
334 class Foo {
335 [[T]] foo([[T]] arg, [[T]]& ref, [[^T]]* ptr) {
336 [[T]] value;
337 int number = 42;
338 value = ([[T]])number;
339 value = static_cast<[[^T]]>(number);
340 return value;
341 }
342 static void foo([[T]] value) {}
343 [[T]] member;
344 };
345 )cpp",
346
347 // Typedef.
348 R"cpp(
349 namespace std {
350 class basic_string {};
351 typedef basic_string [[s^tring]];
352 } // namespace std
353
354 std::[[s^tring]] foo();
355 )cpp",
356
357 // Variable.
358 R"cpp(
359 namespace A {
360 int [[F^oo]];
361 }
362 int Foo;
363 int Qux = Foo;
364 int Baz = A::[[^Foo]];
365 void fun() {
366 struct {
367 int Foo;
368 } b = {100};
369 int Foo = 100;
370 Baz = Foo;
371 {
372 extern int Foo;
373 Baz = Foo;
374 Foo = A::[[F^oo]] + Baz;
375 A::[[Fo^o]] = b.Foo;
376 }
377 Foo = b.Foo;
378 }
379 )cpp",
380
381 // Namespace alias.
382 R"cpp(
383 namespace a { namespace b { void foo(); } }
384 namespace [[^x]] = a::b;
385 void bar() {
386 [[x]]::foo();
387 }
388 )cpp",
389
390 // Scope enums.
391 R"cpp(
392 enum class [[K^ind]] { ABC };
393 void ff() {
394 [[K^ind]] s;
395 s = [[Kind]]::ABC;
396 }
397 )cpp",
398
399 // template class in template argument list.
400 R"cpp(
401 template<typename T>
402 class [[Fo^o]] {};
403 template <template<typename> class Z> struct Bar { };
404 template <> struct Bar<[[Foo]]> {};
405 )cpp",
406 };
407 for (llvm::StringRef T : Tests) {
408 Annotations Code(T);
409 auto TU = TestTU::withCode(Code.code());
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()) {
414 auto RenameResult =
415 rename({RenamePos, NewName, AST, testPath(TU.Filename)});
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));
420 }
421 }
422 }
423
424 TEST(RenameTest, Renameable) {
425 struct Case {
426 const char *Code;
427 const char* ErrorMessage; // null if no error
428 bool IsHeaderFile;
429 const SymbolIndex *Index;
430 };
431 TestTU OtherFile = TestTU::withCode("Outside s; auto ss = &foo;");
432 const char *CommonHeader = R"cpp(
433 class Outside {};
434 void foo();
435 )cpp";
436 OtherFile.HeaderCode = CommonHeader;
437 OtherFile.Filename = "other.cc";
438 // The index has a "Outside" reference and a "foo" reference.
439 auto OtherFileIndex = OtherFile.index();
440 const SymbolIndex *Index = OtherFileIndex.get();
441
442 const bool HeaderFile = true;
443 Case Cases[] = {
444 {R"cpp(// allow -- function-local
445 void f(int [[Lo^cal]]) {
446 [[Local]] = 2;
447 }
448 )cpp",
449 nullptr, HeaderFile, Index},
450
451 {R"cpp(// allow -- symbol is indexable and has no refs in index.
452 void [[On^lyInThisFile]]();
453 )cpp",
454 nullptr, HeaderFile, Index},
455
456 {R"cpp(// disallow -- symbol is indexable and has other refs in index.
457 void f() {
458 Out^side s;
459 }
460 )cpp",
461 "used outside main file", HeaderFile, Index},
462
463 {R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
464 namespace {
465 class Unin^dexable {};
466 }
467 )cpp",
468 "not eligible for indexing", HeaderFile, Index},
469
470 {R"cpp(// allow -- symbol in anonymous namespace in non-header file is indexable.
471 namespace {
472 class [[F^oo]] {};
473 }
474 )cpp",
475 nullptr, !HeaderFile, Index},
476
477 {R"cpp(// disallow -- namespace symbol isn't supported
478 namespace n^s {}
479 )cpp",
480 "not a supported kind", HeaderFile, Index},
481
482 {
483 R"cpp(
484 #define MACRO 1
485 int s = MAC^RO;
486 )cpp",
487 "not a supported kind", HeaderFile, Index},
488
489 {
490 R"cpp(
491 struct X { X operator++(int); };
492 void f(X x) {x+^+;})cpp",
493 "no symbol", HeaderFile, Index},
494
495 {R"cpp(// foo is declared outside the file.
496 void fo^o() {}
497 )cpp",
498 "used outside main file", !HeaderFile /*cc file*/, Index},
499
500 {R"cpp(
501 // We should detect the symbol is used outside the file from the AST.
502 void fo^o() {})cpp",
503 "used outside main file", !HeaderFile, nullptr /*no index*/},
504
505 {R"cpp(
506 void foo(int);
507 void foo(char);
508 template <typename T> void f(T t) {
509 fo^o(t);
510 })cpp",
511 "multiple symbols", !HeaderFile, nullptr /*no index*/},
512
513 {R"cpp(// disallow rename on unrelated token.
514 cl^ass Foo {};
515 )cpp",
516 "no symbol", !HeaderFile, nullptr},
517
518 {R"cpp(// disallow rename on unrelated token.
519 temp^late<typename T>
520 class Foo {};
521 )cpp",
522 "no symbol", !HeaderFile, nullptr},
523 };
524
525 for (const auto& Case : Cases) {
526 Annotations T(Case.Code);
527 TestTU TU = TestTU::withCode(T.code());
528 TU.HeaderCode = CommonHeader;
529 TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
530 if (Case.IsHeaderFile) {
531 // We open the .h file as the main file.
532 TU.Filename = "test.h";
533 // Parsing the .h file as C++ include.
534 TU.ExtraArgs.push_back("-xobjective-c++-header");
535 }
536 auto AST = TU.build();
537 llvm::StringRef NewName = "dummyNewName";
538 auto Results =
539 rename({T.point(), NewName, AST, testPath(TU.Filename), Case.Index});
540 bool WantRename = true;
541 if (T.ranges().empty())
542 WantRename = false;
543 if (!WantRename) {
544 assert(Case.ErrorMessage && "Error message must be set!");
545 EXPECT_FALSE(Results)
546 << "expected rename returned an error: " << T.code();
547 auto ActualMessage = llvm::toString(Results.takeError());
548 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
549 } else {
550 EXPECT_TRUE(bool(Results)) << "rename returned an error: "
551 << llvm::toString(Results.takeError());
552 ASSERT_EQ(1u, Results->size());
553 EXPECT_EQ(applyEdits(std::move(*Results)).front().second,
554 expectedResult(T, NewName));
555 }
556 }
557 }
558
559 TEST(RenameTest, MainFileReferencesOnly) {
560 // filter out references not from main file.
561 llvm::StringRef Test =
562 R"cpp(
563 void test() {
564 int [[fo^o]] = 1;
565 // rename references not from main file are not included.
566 #include "foo.inc"
567 })cpp";
568
569 Annotations Code(Test);
570 auto TU = TestTU::withCode(Code.code());
571 TU.AdditionalFiles["foo.inc"] = R"cpp(
572 #define Macro(X) X
573 &Macro(foo);
574 &foo;
575 )cpp";
576 auto AST = TU.build();
577 llvm::StringRef NewName = "abcde";
578
579 auto RenameResult =
580 rename({Code.point(), NewName, AST, testPath(TU.Filename)});
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));
585 }
586
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");
593 // Build the index, the index has "Foo" references from foo.cc and "Bar"
594 // references from bar.cc.
595 FileSymbols FSymbols;
596 FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
597 nullptr, false);
598 FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
599 nullptr, false);
600 auto Index = FSymbols.buildIndex(IndexType::Light);
601
602 Annotations MainCode("class [[Fo^o]] {};");
603 auto MainFilePath = testPath("main.cc");
604 // Dirty buffer for foo.cc.
605 auto GetDirtyBuffer = [&](PathRef Path) -> llvm::Optional<std::string> {
606 if (Path == FooPath)
607 return FooDirtyBuffer.code().str();
608 return llvm::None;
609 };
610
611 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
612 // respect the dirty buffer.
613 TestTU TU = TestTU::withCode(MainCode.code());
614 auto AST = TU.build();
615 llvm::StringRef NewName = "newName";
616 auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
617 Index.get(), /*CrossFile=*/true, GetDirtyBuffer});
618 ASSERT_TRUE(bool(Results)) << Results.takeError();
619 EXPECT_THAT(
620 applyEdits(std::move(*Results)),
621 UnorderedElementsAre(
622 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
623 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
624
625 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
626 // so we should read file content from VFS.
627 MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
628 TU = TestTU::withCode(MainCode.code());
629 // Set a file "bar.cc" on disk.
630 TU.AdditionalFiles["bar.cc"] = BarCode.code();
631 AST = TU.build();
632 Results = rename({MainCode.point(), NewName, AST, MainFilePath, Index.get(),
633 /*CrossFile=*/true, GetDirtyBuffer});
634 ASSERT_TRUE(bool(Results)) << Results.takeError();
635 EXPECT_THAT(
636 applyEdits(std::move(*Results)),
637 UnorderedElementsAre(
638 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
639 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
640
641 // Run rename on a pagination index which couldn't return all refs in one
642 // request, we reject rename on this case.
643 class PaginationIndex : public SymbolIndex {
644 bool refs(const RefsRequest &Req,
645 llvm::function_ref<void(const Ref &)> Callback) const override {
646 return true; // has more references
647 }
648
649 bool fuzzyFind(
650 const FuzzyFindRequest &Req,
651 llvm::function_ref<void(const Symbol &)> Callback) const override {
652 return false;
653 }
654 void
655 lookup(const LookupRequest &Req,
656 llvm::function_ref<void(const Symbol &)> Callback) const override {}
657
658 void relations(const RelationsRequest &Req,
659 llvm::function_ref<void(const SymbolID &, const Symbol &)>
660 Callback) const override {}
661 size_t estimateMemoryUsage() const override { return 0; }
662 } PIndex;
663 Results = rename({MainCode.point(), NewName, AST, MainFilePath, &PIndex,
664 /*CrossFile=*/true, GetDirtyBuffer});
665 EXPECT_FALSE(Results);
666 EXPECT_THAT(llvm::toString(Results.takeError()),
667 testing::HasSubstr("too many occurrences"));
668 }
669
670 TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
671 auto MainCode = Annotations("int [[^x]] = 2;");
672 auto MainFilePath = testPath("main.cc");
673 auto BarCode = Annotations("int [[x]];");
674 auto BarPath = testPath("bar.cc");
675 auto TU = TestTU::withCode(MainCode.code());
676 // Set a file "bar.cc" on disk.
677 TU.AdditionalFiles["bar.cc"] = BarCode.code();
678 auto AST = TU.build();
679 std::string BarPathURI = URI::create(BarPath).toString();
680 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
681 // The index will return duplicated refs, our code should be robost to handle
682 // it.
683 class DuplicatedXRefIndex : public SymbolIndex {
684 public:
685 DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
686 bool refs(const RefsRequest &Req,
687 llvm::function_ref<void(const Ref &)> Callback) const override {
688 // Return two duplicated refs.
689 Callback(ReturnedRef);
690 Callback(ReturnedRef);
691 return false;
692 }
693
694 bool fuzzyFind(const FuzzyFindRequest &,
695 llvm::function_ref<void(const Symbol &)>) const override {
696 return false;
697 }
698 void lookup(const LookupRequest &,
699 llvm::function_ref<void(const Symbol &)>) const override {}
700
701 void relations(const RelationsRequest &,
702 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
703 const override {}
704 size_t estimateMemoryUsage() const override { return 0; }
705 Ref ReturnedRef;
706 } DIndex(XRefInBarCC);
707 llvm::StringRef NewName = "newName";
708 auto Results = rename({MainCode.point(), NewName, AST, MainFilePath, &DIndex,
709 /*CrossFile=*/true});
710 ASSERT_TRUE(bool(Results)) << Results.takeError();
711 EXPECT_THAT(
712 applyEdits(std::move(*Results)),
713 UnorderedElementsAre(
714 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
715 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
716 }
717
718 TEST(CrossFileRenameTests, WithUpToDateIndex) {
719 MockCompilationDatabase CDB;
720 CDB.ExtraClangFlags = {"-xc++"};
721 class IgnoreDiagnostics : public DiagnosticsConsumer {
722 void onDiagnosticsReady(PathRef File,
723 std::vector<Diag> Diagnostics) override {}
724 } DiagConsumer;
725 // rename is runnning on the "^" point in FooH, and "[[]]" ranges are the
726 // expected rename occurrences.
727 struct Case {
728 llvm::StringRef FooH;
729 llvm::StringRef FooCC;
730 } Cases[] = {
731 {
732 // classes.
733 R"cpp(
734 class [[Fo^o]] {
735 [[Foo]]();
736 ~[[Foo]]();
737 };
738 )cpp",
739 R"cpp(
740 #include "foo.h"
741 [[Foo]]::[[Foo]]() {}
742 [[Foo]]::~[[Foo]]() {}
743
744 void func() {
745 [[Foo]] foo;
746 }
747 )cpp",
748 },
749 {
750 // class methods.
751 R"cpp(
752 class Foo {
753 void [[f^oo]]();
754 };
755 )cpp",
756 R"cpp(
757 #include "foo.h"
758 void Foo::[[foo]]() {}
759
760 void func(Foo* p) {
761 p->[[foo]]();
762 }
763 )cpp",
764 },
765 {
766 // Constructor.
767 R"cpp(
768 class [[Foo]] {
769 [[^Foo]]();
770 ~[[Foo]]();
771 };
772 )cpp",
773 R"cpp(
774 #include "foo.h"
775 [[Foo]]::[[Foo]]() {}
776 [[Foo]]::~[[Foo]]() {}
777
778 void func() {
779 [[Foo]] foo;
780 }
781 )cpp",
782 },
783 {
784 // Destructor (selecting before the identifier).
785 R"cpp(
786 class [[Foo]] {
787 [[Foo]]();
788 ~[[Foo^]]();
789 };
790 )cpp",
791 R"cpp(
792 #include "foo.h"
793 [[Foo]]::[[Foo]]() {}
794 [[Foo]]::~[[Foo]]() {}
795
796 void func() {
797 [[Foo]] foo;
798 }
799 )cpp",
800 },
801 {
802 // functions.
803 R"cpp(
804 void [[f^oo]]();
805 )cpp",
806 R"cpp(
807 #include "foo.h"
808 void [[foo]]() {}
809
810 void func() {
811 [[foo]]();
812 }
813 )cpp",
814 },
815 {
816 // typedefs.
817 R"cpp(
818 typedef int [[IN^T]];
819 [[INT]] foo();
820 )cpp",
821 R"cpp(
822 #include "foo.h"
823 [[INT]] foo() {}
824 )cpp",
825 },
826 {
827 // usings.
828 R"cpp(
829 using [[I^NT]] = int;
830 [[INT]] foo();
831 )cpp",
832 R"cpp(
833 #include "foo.h"
834 [[INT]] foo() {}
835 )cpp",
836 },
837 {
838 // variables.
839 R"cpp(
840 static const int [[VA^R]] = 123;
841 )cpp",
842 R"cpp(
843 #include "foo.h"
844 int s = [[VAR]];
845 )cpp",
846 },
847 {
848 // scope enums.
849 R"cpp(
850 enum class [[K^ind]] { ABC };
851 )cpp",
852 R"cpp(
853 #include "foo.h"
854 [[Kind]] ff() {
855 return [[Kind]]::ABC;
856 }
857 )cpp",
858 },
859 {
860 // enum constants.
861 R"cpp(
862 enum class Kind { [[A^BC]] };
863 )cpp",
864 R"cpp(
865 #include "foo.h"
866 Kind ff() {
867 return Kind::[[ABC]];
868 }
869 )cpp",
870 },
871 };
872
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");
878
879 MockFSProvider FS;
880 FS.Files[FooHPath] = FooH.code();
881 FS.Files[FooCCPath] = FooCC.code();
882
883 auto ServerOpts = ClangdServer::optsForTest();
884 ServerOpts.CrossFileRename = true;
885 ServerOpts.BuildDynamicSymbolIndex = true;
886 ClangdServer Server(CDB, FS, DiagConsumer, ServerOpts);
887
888 // Add all files to clangd server to make sure the dynamic index has been
889 // built.
890 runAddDocument(Server, FooHPath, FooH.code());
891 runAddDocument(Server, FooCCPath, FooCC.code());
892
893 llvm::StringRef NewName = "NewName";
894 auto FileEditsList =
895 llvm::cantFail(runRename(Server, FooHPath, FooH.point(), 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)))));
900 }
901 }
902
903 TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
904 // cross-file rename should work for function-local symbols, even there is no
905 // index provided.
906 Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
907 auto TU = TestTU::withCode(Code.code());
908 auto Path = testPath(TU.Filename);
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();
913 EXPECT_THAT(
914 applyEdits(std::move(*Results)),
915 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
916 }
917
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());
928
929 // Test invalid range.
930 LSPRange.end = {10, 0}; // out of range
931 Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
932 EXPECT_FALSE(Edit);
933 EXPECT_THAT(llvm::toString(Edit.takeError()),
934 testing::HasSubstr("fail to convert"));
935
936 // Normal ascii characters.
937 Annotations T(R"cpp(
938 [[range]]
939 [[range]]
940 [[range]]
941 )cpp");
942 Edit = buildRenameEdit(FilePath, T.code(), T.ranges(), NewName);
943 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
944 EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
945 expectedResult(T, NewName));
946 }
947
948 TEST(CrossFileRenameTests, adjustRenameRanges) {
949 // Ranges in IndexedCode indicate the indexed occurrences;
950 // ranges in DraftCode indicate the expected mapped result, empty indicates
951 // we expect no matched result found.
952 struct {
953 llvm::StringRef IndexedCode;
954 llvm::StringRef DraftCode;
955 } Tests[] = {
956 {
957 // both line and column are changed, not a near miss.
958 R"cpp(
959 int [[x]] = 0;
960 )cpp",
961 R"cpp(
962 // insert a line.
963 double x = 0;
964 )cpp",
965 },
966 {
967 // subset.
968 R"cpp(
969 int [[x]] = 0;
970 )cpp",
971 R"cpp(
972 int [[x]] = 0;
973 {int x = 0; }
974 )cpp",
975 },
976 {
977 // shift columns.
978 R"cpp(int [[x]] = 0; void foo(int x);)cpp",
979 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
980 },
981 {
982 // shift lines.
983 R"cpp(
984 int [[x]] = 0;
985 void foo(int x);
986 )cpp",
987 R"cpp(
988 // insert a line.
989 int [[x]] = 0;
990 void foo(int x);
991 )cpp",
992 },
993 };
994 LangOptions LangOpts;
995 LangOpts.CPlusPlus = true;
996 for (const auto &T : Tests) {
997 Annotations Draft(T.DraftCode);
998 auto ActualRanges = adjustRenameRanges(
999 Draft.code(), "x", Annotations(T.IndexedCode).ranges(), LangOpts);
1000 if (!ActualRanges)
1001 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
1002 else
1003 EXPECT_THAT(Draft.ranges(),
1004 testing::UnorderedElementsAreArray(*ActualRanges))
1005 << T.DraftCode;
1006 }
1007 }
1008
1009 TEST(RangePatchingHeuristic, GetMappedRanges) {
1010 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
1011 // there are no mapped ranges.
1012 struct {
1013 llvm::StringRef IndexedCode;
1014 llvm::StringRef LexedCode;
1015 } Tests[] = {
1016 {
1017 // no lexed ranges.
1018 "[[]]",
1019 "",
1020 },
1021 {
1022 // both line and column are changed, not a near miss.
1023 R"([[]])",
1024 R"(
1025 [[]]
1026 )",
1027 },
1028 {
1029 // subset.
1030 "[[]]",
1031 "^[[]] [[]]"
1032 },
1033 {
1034 // shift columns.
1035 "[[]] [[]]",
1036 " ^[[]] ^[[]] [[]]"
1037 },
1038 {
1039 R"(
1040 [[]]
1041
1042 [[]] [[]]
1043 )",
1044 R"(
1045 // insert a line
1046 ^[[]]
1047
1048 ^[[]] ^[[]]
1049 )",
1050 },
1051 {
1052 R"(
1053 [[]]
1054
1055 [[]] [[]]
1056 )",
1057 R"(
1058 // insert a line
1059 ^[[]]
1060 ^[[]] ^[[]] // column is shifted.
1061 )",
1062 },
1063 {
1064 R"(
1065 [[]]
1066
1067 [[]] [[]]
1068 )",
1069 R"(
1070 // insert a line
1071 [[]]
1072
1073 [[]] [[]] // not mapped (both line and column are changed).
1074 )",
1075 },
1076 {
1077 R"(
1078 [[]]
1079 [[]]
1080
1081 [[]]
1082 [[]]
1083
1084 }
1085 )",
1086 R"(
1087 // insert a new line
1088 ^[[]]
1089 ^[[]]
1090 [[]] // additional range
1091 ^[[]]
1092 ^[[]]
1093 [[]] // additional range
1094 )",
1095 },
1096 {
1097 // non-distinct result (two best results), not a near miss
1098 R"(
1099 [[]]
1100 [[]]
1101 [[]]
1102 )",
1103 R"(
1104 [[]]
1105 [[]]
1106 [[]]
1107 [[]]
1108 )",
1109 }
1110 };
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;
1118 });
1119 ASSERT_NE(Match, LexedRanges.end());
1120 ExpectedMatches.push_back(*Match);
1121 }
1122
1123 auto Mapped =
1124 getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
1125 if (!Mapped)
1126 EXPECT_THAT(ExpectedMatches, IsEmpty());
1127 else
1128 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped))
1129 << T.IndexedCode;
1130 }
1131 }
1132
1133 TEST(CrossFileRenameTests, adjustmentCost) {
1134 struct {
1135 llvm::StringRef RangeCode;
1136 size_t ExpectedCost;
1137 } Tests[] = {
1138 {
1139 R"(
1140 $idx[[]]$lex[[]] // diff: 0
1141 )",
1142 0,
1143 },
1144 {
1145 R"(
1146 $idx[[]]
1147 $lex[[]] // line diff: +1
1148 $idx[[]]
1149 $lex[[]] // line diff: +1
1150 $idx[[]]
1151 $lex[[]] // line diff: +1
1152
1153 $idx[[]]
1154
1155 $lex[[]] // line diff: +2
1156 )",
1157 1 + 1
1158 },
1159 {
1160 R"(
1161 $idx[[]]
1162 $lex[[]] // line diff: +1
1163 $idx[[]]
1164
1165 $lex[[]] // line diff: +2
1166 $idx[[]]
1167
1168
1169 $lex[[]] // line diff: +3
1170 )",
1171 1 + 1 + 1
1172 },
1173 {
1174 R"(
1175 $idx[[]]
1176
1177
1178 $lex[[]] // line diff: +3
1179 $idx[[]]
1180
1181 $lex[[]] // line diff: +2
1182 $idx[[]]
1183 $lex[[]] // line diff: +1
1184 )",
1185 3 + 1 + 1
1186 },
1187 {
1188 R"(
1189 $idx[[]]
1190 $lex[[]] // line diff: +1
1191 $lex[[]] // line diff: -2
1192
1193 $idx[[]]
1194 $idx[[]]
1195
1196
1197 $lex[[]] // line diff: +3
1198 )",
1199 1 + 3 + 5
1200 },
1201 {
1202 R"(
1203 $idx[[]] $lex[[]] // column diff: +1
1204 $idx[[]]$lex[[]] // diff: 0
1205 )",
1206 1
1207 },
1208 {
1209 R"(
1210 $idx[[]]
1211 $lex[[]] // diff: +1
1212 $idx[[]] $lex[[]] // column diff: +1
1213 $idx[[]]$lex[[]] // diff: 0
1214 )",
1215 1 + 1 + 1
1216 },
1217 {
1218 R"(
1219 $idx[[]] $lex[[]] // column diff: +1
1220 )",
1221 1
1222 },
1223 {
1224 R"(
1225 // column diffs: +1, +2, +3
1226 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
1227 )",
1228 1 + 1 + 1,
1229 },
1230 };
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);
1236 EXPECT_EQ(renameRangeAdjustmentCost(C.ranges("idx"), C.ranges("lex"),
1237 MappedIndex),
1238 T.ExpectedCost) << T.RangeCode;
1239 }
1240 }
1241
1242 } // namespace
1243 } // namespace clangd
1244 } // namespace clang
Code
std::string Code
Definition: FindTargetTests.cpp:67
clang::clangd::TestTU::HeaderCode
std::string HeaderCode
Definition: TestTU.h:50
clang::clangd::getMappedRanges
llvm::Optional< std::vector< Range > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
Definition: Rename.cpp:594
ClangdServer.h
clang::clangd::IndexType::Light
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:58
Results
std::vector< CodeCompletionResult > Results
Definition: CodeComplete.cpp:689
clang::clangd::Callback
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28
FS
MockFSProvider FS
Definition: HeadersTests.cpp:119
clang::clangd::ClangdServer::optsForTest
static Options optsForTest()
Definition: ClangdServer.cpp:103
clang::clangd::TextDocumentSyncKind::None
Documents should not be synced at all.
TestFS.h
TestTU.h
clang::clangd::Symbol::ID
SymbolID ID
The ID of the symbol.
Definition: Symbol.h:38
clang::clangd::TestTU::ExtraArgs
std::vector< const char * > ExtraArgs
Definition: TestTU.h:57
clang::clangd::SymbolOrigin::AST
Annotations.h
clang::clangd::lookup
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
Definition: TestIndex.cpp:106
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:647
clang::clangd::testPath
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
Symbols
SymbolSlab Symbols
Definition: SymbolCollectorTests.cpp:295
clang::clangd::FileEdits
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits...
Definition: SourceCode.h:222
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:35
Ref.h
clang::clangd::RefKind::Reference
clang::clangd::URI::create
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:197
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Server
ClangdServer Server
Definition: FindSymbolsTests.cpp:70
clang::clangd::findSymbol
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:97
clang::clangd::renameRangeAdjustmentCost
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
Definition: Rename.cpp:655
clang::clangd::runRename
llvm::Expected< FileEdits > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName)
Definition: SyncAPI.cpp:99
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
SyncAPI.h
DiagConsumer
IgnoreDiagnostics DiagConsumer
Definition: FindSymbolsTests.cpp:69
clang::clangd::adjustRenameRanges
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.
Definition: Rename.cpp:584
Rename.h
clang::clangd::rename
llvm::Expected< FileEdits > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
Definition: Rename.cpp:427
clang::clangd::ParsedAST::build
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.
Definition: ParsedAST.cpp:218
clang::clangd::CompletionItemKind::File
clang::doc::SymbolID
std::array< uint8_t, 20 > SymbolID
Definition: Representation.h:30
clang::clangd::TestTU::AdditionalFiles
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:54
clang::clangd::buildRenameEdit
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.
Definition: Rename.cpp:519
clang::clangd::runAddDocument
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags)
Definition: SyncAPI.cpp:15
Index
const SymbolIndex * Index
Definition: Dexp.cpp:84