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 "support/TestTracer.h"
17 #include "clang/Tooling/Core/Replacement.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include <algorithm>
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23
24 namespace clang {
25 namespace clangd {
26 namespace {
27
28 using testing::ElementsAre;
29 using testing::Eq;
30 using testing::IsEmpty;
31 using testing::Pair;
32 using testing::SizeIs;
33 using testing::UnorderedElementsAre;
34 using testing::UnorderedElementsAreArray;
35
36 // Convert a Range to a Ref.
refWithRange(const clangd::Range & Range,const std::string & URI)37 Ref refWithRange(const clangd::Range &Range, const std::string &URI) {
38 Ref Result;
39 Result.Kind = RefKind::Reference | RefKind::Spelled;
40 Result.Location.Start.setLine(Range.start.line);
41 Result.Location.Start.setColumn(Range.start.character);
42 Result.Location.End.setLine(Range.end.line);
43 Result.Location.End.setColumn(Range.end.character);
44 Result.Location.FileURI = URI.c_str();
45 return Result;
46 }
47
48 // Build a RefSlab from all marked ranges in the annotation. The ranges are
49 // assumed to associate with the given SymbolName.
buildRefSlab(const Annotations & Code,llvm::StringRef SymbolName,llvm::StringRef Path)50 std::unique_ptr<RefSlab> buildRefSlab(const Annotations &Code,
51 llvm::StringRef SymbolName,
52 llvm::StringRef Path) {
53 RefSlab::Builder Builder;
54 TestTU TU;
55 TU.HeaderCode = std::string(Code.code());
56 auto Symbols = TU.headerSymbols();
57 const auto &SymbolID = findSymbol(Symbols, SymbolName).ID;
58 std::string PathURI = URI::create(Path).toString();
59 for (const auto &Range : Code.ranges())
60 Builder.insert(SymbolID, refWithRange(Range, PathURI));
61
62 return std::make_unique<RefSlab>(std::move(Builder).build());
63 }
64
65 std::vector<
66 std::pair</*FilePath*/ std::string, /*CodeAfterRename*/ std::string>>
applyEdits(FileEdits FE)67 applyEdits(FileEdits FE) {
68 std::vector<std::pair<std::string, std::string>> Results;
69 for (auto &It : FE)
70 Results.emplace_back(
71 It.first().str(),
72 llvm::cantFail(tooling::applyAllReplacements(
73 It.getValue().InitialCode, It.getValue().Replacements)));
74 return Results;
75 }
76
77 // Generates an expected rename result by replacing all ranges in the given
78 // annotation with the NewName.
expectedResult(Annotations Test,llvm::StringRef NewName)79 std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
80 std::string Result;
81 unsigned NextChar = 0;
82 llvm::StringRef Code = Test.code();
83 for (const auto &R : Test.llvm::Annotations::ranges()) {
84 assert(R.Begin <= R.End && NextChar <= R.Begin);
85 Result += Code.substr(NextChar, R.Begin - NextChar);
86 Result += NewName;
87 NextChar = R.End;
88 }
89 Result += Code.substr(NextChar);
90 return Result;
91 }
92
TEST(RenameTest,WithinFileRename)93 TEST(RenameTest, WithinFileRename) {
94 // For each "^" this test moves cursor to its location and applies renaming
95 // while checking that all identifiers in [[]] ranges are also renamed.
96 llvm::StringRef Tests[] = {
97 // Function.
98 R"cpp(
99 void [[foo^]]() {
100 [[fo^o]]();
101 }
102 )cpp",
103
104 // Type.
105 R"cpp(
106 struct [[foo^]] {};
107 [[foo]] test() {
108 [[f^oo]] x;
109 return x;
110 }
111 )cpp",
112
113 // Local variable.
114 R"cpp(
115 void bar() {
116 if (auto [[^foo]] = 5) {
117 [[foo]] = 3;
118 }
119 }
120 )cpp",
121
122 // Class, its constructor and destructor.
123 R"cpp(
124 class [[F^oo]] {
125 [[F^oo]]();
126 ~[[F^oo]]();
127 [[F^oo]] *foo(int x);
128
129 [[F^oo]] *Ptr;
130 };
131 [[F^oo]]::[[Fo^o]]() {}
132 [[F^oo]]::~[[Fo^o]]() {}
133 [[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
134 )cpp",
135
136 // Template class, its constructor and destructor.
137 R"cpp(
138 template <typename T>
139 class [[F^oo]] {
140 [[F^oo]]();
141 ~[[F^oo]]();
142 void f([[F^oo]] x);
143 };
144
145 template<typename T>
146 [[F^oo]]<T>::[[Fo^o]]() {}
147
148 template<typename T>
149 [[F^oo]]<T>::~[[Fo^o]]() {}
150 )cpp",
151
152 // Template class constructor.
153 R"cpp(
154 class [[F^oo]] {
155 template<typename T>
156 [[Fo^o]]();
157
158 template<typename T>
159 [[F^oo]](T t);
160 };
161
162 template<typename T>
163 [[F^oo]]::[[Fo^o]]() {}
164 )cpp",
165
166 // Class in template argument.
167 R"cpp(
168 class [[F^oo]] {};
169 template <typename T> void func();
170 template <typename T> class Baz {};
171 int main() {
172 func<[[F^oo]]>();
173 Baz<[[F^oo]]> obj;
174 return 0;
175 }
176 )cpp",
177
178 // Forward class declaration without definition.
179 R"cpp(
180 class [[F^oo]];
181 [[F^oo]] *f();
182 )cpp",
183
184 // Member function.
185 R"cpp(
186 struct X {
187 void [[F^oo]]() {}
188 void Baz() { [[F^oo]](); }
189 };
190 )cpp",
191
192 // Class methods overrides.
193 R"cpp(
194 struct A {
195 virtual void [[f^oo]]() {}
196 };
197 struct B : A {
198 void [[f^oo]]() override {}
199 };
200 struct C : B {
201 void [[f^oo]]() override {}
202 };
203
204 void func() {
205 A().[[f^oo]]();
206 B().[[f^oo]]();
207 C().[[f^oo]]();
208 }
209 )cpp",
210
211 // Templated method instantiation.
212 R"cpp(
213 template<typename T>
214 class Foo {
215 public:
216 static T [[f^oo]]() {}
217 };
218
219 void bar() {
220 Foo<int>::[[f^oo]]();
221 }
222 )cpp",
223 R"cpp(
224 template<typename T>
225 class Foo {
226 public:
227 T [[f^oo]]() {}
228 };
229
230 void bar() {
231 Foo<int>().[[f^oo]]();
232 }
233 )cpp",
234
235 // Template class (partial) specializations.
236 R"cpp(
237 template <typename T>
238 class [[F^oo]] {};
239
240 template<>
241 class [[F^oo]]<bool> {};
242 template <typename T>
243 class [[F^oo]]<T*> {};
244
245 void test() {
246 [[F^oo]]<int> x;
247 [[F^oo]]<bool> y;
248 [[F^oo]]<int*> z;
249 }
250 )cpp",
251
252 // Incomplete class specializations
253 R"cpp(
254 template <typename T>
255 class [[Fo^o]] {};
256 void func([[F^oo]]<int>);
257 )cpp",
258
259 // Template class instantiations.
260 R"cpp(
261 template <typename T>
262 class [[F^oo]] {
263 public:
264 T foo(T arg, T& ref, T* ptr) {
265 T value;
266 int number = 42;
267 value = (T)number;
268 value = static_cast<T>(number);
269 return value;
270 }
271 static void foo(T value) {}
272 T member;
273 };
274
275 template <typename T>
276 void func() {
277 [[F^oo]]<T> obj;
278 obj.member = T();
279 [[Foo]]<T>::foo();
280 }
281
282 void test() {
283 [[F^oo]]<int> i;
284 i.member = 0;
285 [[F^oo]]<int>::foo(0);
286
287 [[F^oo]]<bool> b;
288 b.member = false;
289 [[F^oo]]<bool>::foo(false);
290 }
291 )cpp",
292
293 // Template class methods.
294 R"cpp(
295 template <typename T>
296 class A {
297 public:
298 void [[f^oo]]() {}
299 };
300
301 void func() {
302 A<int>().[[f^oo]]();
303 A<double>().[[f^oo]]();
304 A<float>().[[f^oo]]();
305 }
306 )cpp",
307
308 // Templated class specialization.
309 R"cpp(
310 template<typename T, typename U=bool>
311 class [[Foo^]];
312
313 template<typename T, typename U>
314 class [[Foo^]] {};
315
316 template<typename T=int, typename U>
317 class [[Foo^]];
318 )cpp",
319 R"cpp(
320 template<typename T=float, typename U=int>
321 class [[Foo^]];
322
323 template<typename T, typename U>
324 class [[Foo^]] {};
325 )cpp",
326
327 // Function template specialization.
328 R"cpp(
329 template<typename T=int, typename U=bool>
330 U [[foo^]]();
331
332 template<typename T, typename U>
333 U [[foo^]]() {};
334 )cpp",
335 R"cpp(
336 template<typename T, typename U>
337 U [[foo^]]() {};
338
339 template<typename T=int, typename U=bool>
340 U [[foo^]]();
341 )cpp",
342 R"cpp(
343 template<typename T=int, typename U=bool>
344 U [[foo^]]();
345
346 template<typename T, typename U>
347 U [[foo^]]();
348 )cpp",
349 R"cpp(
350 template <typename T>
351 void [[f^oo]](T t);
352
353 template <>
354 void [[f^oo]](int a);
355
356 void test() {
357 [[f^oo]]<double>(1);
358 }
359 )cpp",
360
361 // Variable template.
362 R"cpp(
363 template <typename T, int U>
364 bool [[F^oo]] = true;
365
366 // Explicit template specialization
367 template <>
368 bool [[F^oo]]<int, 0> = false;
369
370 // Partial template specialization
371 template <typename T>
372 bool [[F^oo]]<T, 1> = false;
373
374 void foo() {
375 // Ref to the explicit template specialization
376 [[F^oo]]<int, 0>;
377 // Ref to the primary template.
378 [[F^oo]]<double, 2>;
379 }
380 )cpp",
381
382 // Complicated class type.
383 R"cpp(
384 // Forward declaration.
385 class [[Fo^o]];
386 class Baz {
387 virtual int getValue() const = 0;
388 };
389
390 class [[F^oo]] : public Baz {
391 public:
392 [[F^oo]](int value = 0) : x(value) {}
393
394 [[F^oo]] &operator++(int);
395
396 bool operator<([[Foo]] const &rhs);
397 int getValue() const;
398 private:
399 int x;
400 };
401
402 void func() {
403 [[F^oo]] *Pointer = 0;
404 [[F^oo]] Variable = [[Foo]](10);
405 for ([[F^oo]] it; it < Variable; it++);
406 const [[F^oo]] *C = new [[Foo]]();
407 const_cast<[[F^oo]] *>(C)->getValue();
408 [[F^oo]] foo;
409 const Baz &BazReference = foo;
410 const Baz *BazPointer = &foo;
411 reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
412 static_cast<const [[^Foo]] &>(BazReference).getValue();
413 static_cast<const [[^Foo]] *>(BazPointer)->getValue();
414 }
415 )cpp",
416
417 // Static class member.
418 R"cpp(
419 struct Foo {
420 static Foo *[[Static^Member]];
421 };
422
423 Foo* Foo::[[Static^Member]] = nullptr;
424
425 void foo() {
426 Foo* Pointer = Foo::[[Static^Member]];
427 }
428 )cpp",
429
430 // Reference in lambda parameters.
431 R"cpp(
432 template <class T>
433 class function;
434 template <class R, class... ArgTypes>
435 class function<R(ArgTypes...)> {
436 public:
437 template <typename Functor>
438 function(Functor f) {}
439
440 function() {}
441
442 R operator()(ArgTypes...) const {}
443 };
444
445 namespace ns {
446 class [[Old]] {};
447 void f() {
448 function<void([[Old]])> func;
449 }
450 } // namespace ns
451 )cpp",
452
453 // Destructor explicit call.
454 R"cpp(
455 class [[F^oo]] {
456 public:
457 ~[[^Foo]]();
458 };
459
460 [[Foo^]]::~[[^Foo]]() {}
461
462 int main() {
463 [[Fo^o]] f;
464 f.~/*something*/[[^Foo]]();
465 f.~[[^Foo]]();
466 }
467 )cpp",
468
469 // Derived destructor explicit call.
470 R"cpp(
471 class [[Bas^e]] {};
472 class Derived : public [[Bas^e]] {};
473
474 int main() {
475 [[Bas^e]] *foo = new Derived();
476 foo->[[^Base]]::~[[^Base]]();
477 }
478 )cpp",
479
480 // CXXConstructor initializer list.
481 R"cpp(
482 class Baz {};
483 class Qux {
484 Baz [[F^oo]];
485 public:
486 Qux();
487 };
488 Qux::Qux() : [[F^oo]]() {}
489 )cpp",
490
491 // DeclRefExpr.
492 R"cpp(
493 class C {
494 public:
495 static int [[F^oo]];
496 };
497
498 int foo(int x);
499 #define MACRO(a) foo(a)
500
501 void func() {
502 C::[[F^oo]] = 1;
503 MACRO(C::[[Foo]]);
504 int y = C::[[F^oo]];
505 }
506 )cpp",
507
508 // Macros.
509 R"cpp(
510 // no rename inside macro body.
511 #define M1 foo
512 #define M2(x) x
513 int [[fo^o]]();
514 void boo(int);
515
516 void qoo() {
517 [[f^oo]]();
518 boo([[f^oo]]());
519 M1();
520 boo(M1());
521 M2([[f^oo]]());
522 M2(M1()); // foo is inside the nested macro body.
523 }
524 )cpp",
525
526 // MemberExpr in macros
527 R"cpp(
528 class Baz {
529 public:
530 int [[F^oo]];
531 };
532 int qux(int x);
533 #define MACRO(a) qux(a)
534
535 int main() {
536 Baz baz;
537 baz.[[F^oo]] = 1;
538 MACRO(baz.[[F^oo]]);
539 int y = baz.[[F^oo]];
540 }
541 )cpp",
542
543 // Fields in classes & partial and full specialiations.
544 R"cpp(
545 template<typename T>
546 struct Foo {
547 T [[Vari^able]] = 42;
548 };
549
550 void foo() {
551 Foo<int> f;
552 f.[[Varia^ble]] = 9000;
553 }
554 )cpp",
555 R"cpp(
556 template<typename T, typename U>
557 struct Foo {
558 T Variable[42];
559 U Another;
560
561 void bar() {}
562 };
563
564 template<typename T>
565 struct Foo<T, bool> {
566 T [[Var^iable]];
567 void bar() { ++[[Var^iable]]; }
568 };
569
570 void foo() {
571 Foo<unsigned, bool> f;
572 f.[[Var^iable]] = 9000;
573 }
574 )cpp",
575 R"cpp(
576 template<typename T, typename U>
577 struct Foo {
578 T Variable[42];
579 U Another;
580
581 void bar() {}
582 };
583
584 template<typename T>
585 struct Foo<T, bool> {
586 T Variable;
587 void bar() { ++Variable; }
588 };
589
590 template<>
591 struct Foo<unsigned, bool> {
592 unsigned [[Var^iable]];
593 void bar() { ++[[Var^iable]]; }
594 };
595
596 void foo() {
597 Foo<unsigned, bool> f;
598 f.[[Var^iable]] = 9000;
599 }
600 )cpp",
601 // Static fields.
602 R"cpp(
603 struct Foo {
604 static int [[Var^iable]];
605 };
606
607 int Foo::[[Var^iable]] = 42;
608
609 void foo() {
610 int LocalInt = Foo::[[Var^iable]];
611 }
612 )cpp",
613 R"cpp(
614 template<typename T>
615 struct Foo {
616 static T [[Var^iable]];
617 };
618
619 template <>
620 int Foo<int>::[[Var^iable]] = 42;
621
622 template <>
623 bool Foo<bool>::[[Var^iable]] = true;
624
625 void foo() {
626 int LocalInt = Foo<int>::[[Var^iable]];
627 bool LocalBool = Foo<bool>::[[Var^iable]];
628 }
629 )cpp",
630
631 // Template parameters.
632 R"cpp(
633 template <typename [[^T]]>
634 class Foo {
635 [[T^]] foo([[T^]] arg, [[T^]]& ref, [[^T]]* ptr) {
636 [[T]] value;
637 int number = 42;
638 value = ([[T^]])number;
639 value = static_cast<[[^T]]>(number);
640 return value;
641 }
642 static void foo([[T^]] value) {}
643 [[T^]] member;
644 };
645 )cpp",
646
647 // Typedef.
648 R"cpp(
649 namespace ns {
650 class basic_string {};
651 typedef basic_string [[s^tring]];
652 } // namespace ns
653
654 ns::[[s^tring]] foo();
655 )cpp",
656
657 // Variable.
658 R"cpp(
659 namespace A {
660 int [[F^oo]];
661 }
662 int Foo;
663 int Qux = Foo;
664 int Baz = A::[[^Foo]];
665 void fun() {
666 struct {
667 int Foo;
668 } b = {100};
669 int Foo = 100;
670 Baz = Foo;
671 {
672 extern int Foo;
673 Baz = Foo;
674 Foo = A::[[F^oo]] + Baz;
675 A::[[Fo^o]] = b.Foo;
676 }
677 Foo = b.Foo;
678 }
679 )cpp",
680
681 // Namespace alias.
682 R"cpp(
683 namespace a { namespace b { void foo(); } }
684 namespace [[^x]] = a::b;
685 void bar() {
686 [[x^]]::foo();
687 }
688 )cpp",
689
690 // Enum.
691 R"cpp(
692 enum [[C^olor]] { Red, Green, Blue };
693 void foo() {
694 [[C^olor]] c;
695 c = [[C^olor]]::Blue;
696 }
697 )cpp",
698
699 // Scoped enum.
700 R"cpp(
701 enum class [[K^ind]] { ABC };
702 void ff() {
703 [[K^ind]] s;
704 s = [[K^ind]]::ABC;
705 }
706 )cpp",
707
708 // Template class in template argument list.
709 R"cpp(
710 template<typename T>
711 class [[Fo^o]] {};
712 template <template<typename> class Z> struct Bar { };
713 template <> struct Bar<[[F^oo]]> {};
714 )cpp",
715
716 // Designated initializer.
717 R"cpp(
718 struct Bar {
719 int [[Fo^o]];
720 };
721 Bar bar { .[[^Foo]] = 42 };
722 )cpp",
723
724 // Nested designated initializer.
725 R"cpp(
726 struct Baz {
727 int Field;
728 };
729 struct Bar {
730 Baz [[Fo^o]];
731 };
732 // FIXME: v selecting here results in renaming Field.
733 Bar bar { .[[Foo]].Field = 42 };
734 )cpp",
735 R"cpp(
736 struct Baz {
737 int [[Fiel^d]];
738 };
739 struct Bar {
740 Baz Foo;
741 };
742 Bar bar { .Foo.[[^Field]] = 42 };
743 )cpp",
744
745 // Templated alias.
746 R"cpp(
747 template <typename T>
748 class X { T t; };
749
750 template <typename T>
751 using [[Fo^o]] = X<T>;
752
753 void bar() {
754 [[Fo^o]]<int> Bar;
755 }
756 )cpp",
757
758 // Alias.
759 R"cpp(
760 class X {};
761 using [[F^oo]] = X;
762
763 void bar() {
764 [[Fo^o]] Bar;
765 }
766 )cpp",
767
768 // Alias within a namespace.
769 R"cpp(
770 namespace x { class X {}; }
771 namespace ns {
772 using [[Fo^o]] = x::X;
773 }
774
775 void bar() {
776 ns::[[Fo^o]] Bar;
777 }
778 )cpp",
779
780 // Alias within macros.
781 R"cpp(
782 namespace x { class Old {}; }
783 namespace ns {
784 #define REF(alias) alias alias_var;
785
786 #define ALIAS(old) \
787 using old##Alias = x::old; \
788 REF(old##Alias);
789
790 ALIAS(Old);
791
792 [[Old^Alias]] old_alias;
793 }
794
795 void bar() {
796 ns::[[Old^Alias]] Bar;
797 }
798 )cpp",
799
800 // User defined conversion.
801 R"cpp(
802 class [[F^oo]] {
803 public:
804 [[F^oo]]() {}
805 };
806
807 class Baz {
808 public:
809 operator [[F^oo]]() {
810 return [[F^oo]]();
811 }
812 };
813
814 int main() {
815 Baz boo;
816 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
817 }
818 )cpp",
819
820 // ObjC, should not crash.
821 R"cpp(
822 @interface ObjC {
823 char [[da^ta]];
824 } @end
825 )cpp",
826 };
827 llvm::StringRef NewName = "NewName";
828 for (llvm::StringRef T : Tests) {
829 SCOPED_TRACE(T);
830 Annotations Code(T);
831 auto TU = TestTU::withCode(Code.code());
832 TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
833 TU.ExtraArgs.push_back("-xobjective-c++");
834 auto AST = TU.build();
835 for (const auto &RenamePos : Code.points()) {
836 auto RenameResult =
837 rename({RenamePos, NewName, AST, testPath(TU.Filename)});
838 ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
839 ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
840 EXPECT_EQ(
841 applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
842 expectedResult(Code, NewName));
843 }
844 }
845 }
846
TEST(RenameTest,Renameable)847 TEST(RenameTest, Renameable) {
848 struct Case {
849 const char *Code;
850 const char* ErrorMessage; // null if no error
851 bool IsHeaderFile;
852 const SymbolIndex *Index;
853 llvm::StringRef NewName = "DummyName";
854 };
855 TestTU OtherFile = TestTU::withCode("Outside s; auto ss = &foo;");
856 const char *CommonHeader = R"cpp(
857 class Outside {};
858 void foo();
859 )cpp";
860 OtherFile.HeaderCode = CommonHeader;
861 OtherFile.Filename = "other.cc";
862 // The index has a "Outside" reference and a "foo" reference.
863 auto OtherFileIndex = OtherFile.index();
864 const SymbolIndex *Index = OtherFileIndex.get();
865
866 const bool HeaderFile = true;
867 Case Cases[] = {
868 {R"cpp(// allow -- function-local
869 void f(int [[Lo^cal]]) {
870 [[Local]] = 2;
871 }
872 )cpp",
873 nullptr, HeaderFile, Index},
874
875 {R"cpp(// allow -- symbol is indexable and has no refs in index.
876 void [[On^lyInThisFile]]();
877 )cpp",
878 nullptr, HeaderFile, Index},
879
880 {R"cpp(
881 void ^f();
882 )cpp",
883 "keyword", HeaderFile, Index, "return"},
884
885 {R"cpp(// disallow -- symbol is indexable and has other refs in index.
886 void f() {
887 Out^side s;
888 }
889 )cpp",
890 "used outside main file", HeaderFile, Index},
891
892 {R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
893 namespace {
894 class Unin^dexable {};
895 }
896 )cpp",
897 "not eligible for indexing", HeaderFile, Index},
898
899 {R"cpp(// allow -- symbol in anonymous namespace in non-header file is indexable.
900 namespace {
901 class [[F^oo]] {};
902 }
903 )cpp",
904 nullptr, !HeaderFile, Index},
905
906 {R"cpp(// disallow -- namespace symbol isn't supported
907 namespace n^s {}
908 )cpp",
909 "not a supported kind", HeaderFile, Index},
910
911 {
912 R"cpp(
913 #define MACRO 1
914 int s = MAC^RO;
915 )cpp",
916 "not a supported kind", HeaderFile, Index},
917
918 {
919 R"cpp(
920 struct X { X operator++(int); };
921 void f(X x) {x+^+;})cpp",
922 "no symbol", HeaderFile, Index},
923
924 {R"cpp(// foo is declared outside the file.
925 void fo^o() {}
926 )cpp",
927 "used outside main file", !HeaderFile /*cc file*/, Index},
928
929 {R"cpp(
930 // We should detect the symbol is used outside the file from the AST.
931 void fo^o() {})cpp",
932 "used outside main file", !HeaderFile, nullptr /*no index*/},
933
934 {R"cpp(// disallow rename on excluded symbols (e.g. std symbols)
935 namespace std {
936 class str^ing {};
937 }
938 )cpp",
939 "not a supported kind", !HeaderFile, Index},
940 {R"cpp(// disallow rename on excluded symbols (e.g. std symbols)
941 namespace std {
942 inline namespace __u {
943 class str^ing {};
944 }
945 }
946 )cpp",
947 "not a supported kind", !HeaderFile, Index},
948
949 {R"cpp(// disallow rename on non-normal identifiers.
950 @interface Foo {}
951 -(int) fo^o:(int)x; // Token is an identifier, but declaration name isn't a simple identifier.
952 @end
953 )cpp",
954 "not a supported kind", HeaderFile, Index},
955
956 {R"cpp(
957 void foo(int);
958 void foo(char);
959 template <typename T> void f(T t) {
960 fo^o(t);
961 })cpp",
962 "multiple symbols", !HeaderFile, nullptr /*no index*/},
963
964 {R"cpp(// disallow rename on unrelated token.
965 cl^ass Foo {};
966 )cpp",
967 "no symbol", !HeaderFile, nullptr},
968
969 {R"cpp(// disallow rename on unrelated token.
970 temp^late<typename T>
971 class Foo {};
972 )cpp",
973 "no symbol", !HeaderFile, nullptr},
974
975 {R"cpp(
976 namespace {
977 int Conflict;
978 int Va^r;
979 }
980 )cpp",
981 "conflict", !HeaderFile, nullptr, "Conflict"},
982
983 {R"cpp(
984 int Conflict;
985 int Va^r;
986 )cpp",
987 "conflict", !HeaderFile, nullptr, "Conflict"},
988
989 {R"cpp(
990 class Foo {
991 int Conflict;
992 int Va^r;
993 };
994 )cpp",
995 "conflict", !HeaderFile, nullptr, "Conflict"},
996
997 {R"cpp(
998 enum E {
999 Conflict,
1000 Fo^o,
1001 };
1002 )cpp",
1003 "conflict", !HeaderFile, nullptr, "Conflict"},
1004
1005 {R"cpp(
1006 int Conflict;
1007 enum E { // transparent context.
1008 F^oo,
1009 };
1010 )cpp",
1011 "conflict", !HeaderFile, nullptr, "Conflict"},
1012
1013 {R"cpp(// FIXME: detecting local variables is not supported yet.
1014 void func() {
1015 int Conflict;
1016 int [[V^ar]];
1017 }
1018 )cpp",
1019 nullptr, !HeaderFile, nullptr, "Conflict"},
1020
1021 {R"cpp(// Trying to rename into the same name, SameName == SameName.
1022 void func() {
1023 int S^ameName;
1024 }
1025 )cpp",
1026 "new name is the same", !HeaderFile, nullptr, "SameName"},
1027 };
1028
1029 for (const auto& Case : Cases) {
1030 SCOPED_TRACE(Case.Code);
1031 Annotations T(Case.Code);
1032 TestTU TU = TestTU::withCode(T.code());
1033 TU.HeaderCode = CommonHeader;
1034 TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
1035 if (Case.IsHeaderFile) {
1036 // We open the .h file as the main file.
1037 TU.Filename = "test.h";
1038 // Parsing the .h file as C++ include.
1039 TU.ExtraArgs.push_back("-xobjective-c++-header");
1040 }
1041 auto AST = TU.build();
1042 llvm::StringRef NewName = Case.NewName;
1043 auto Results =
1044 rename({T.point(), NewName, AST, testPath(TU.Filename), Case.Index});
1045 bool WantRename = true;
1046 if (T.ranges().empty())
1047 WantRename = false;
1048 if (!WantRename) {
1049 assert(Case.ErrorMessage && "Error message must be set!");
1050 EXPECT_FALSE(Results)
1051 << "expected rename returned an error: " << T.code();
1052 auto ActualMessage = llvm::toString(Results.takeError());
1053 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
1054 } else {
1055 EXPECT_TRUE(bool(Results)) << "rename returned an error: "
1056 << llvm::toString(Results.takeError());
1057 ASSERT_EQ(1u, Results->GlobalChanges.size());
1058 EXPECT_EQ(applyEdits(std::move(Results->GlobalChanges)).front().second,
1059 expectedResult(T, NewName));
1060 }
1061 }
1062 }
1063
TEST(RenameTest,MainFileReferencesOnly)1064 TEST(RenameTest, MainFileReferencesOnly) {
1065 // filter out references not from main file.
1066 llvm::StringRef Test =
1067 R"cpp(
1068 void test() {
1069 int [[fo^o]] = 1;
1070 // rename references not from main file are not included.
1071 #include "foo.inc"
1072 })cpp";
1073
1074 Annotations Code(Test);
1075 auto TU = TestTU::withCode(Code.code());
1076 TU.AdditionalFiles["foo.inc"] = R"cpp(
1077 #define Macro(X) X
1078 &Macro(foo);
1079 &foo;
1080 )cpp";
1081 auto AST = TU.build();
1082 llvm::StringRef NewName = "abcde";
1083
1084 auto RenameResult =
1085 rename({Code.point(), NewName, AST, testPath(TU.Filename)});
1086 ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << Code.point();
1087 ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
1088 EXPECT_EQ(applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
1089 expectedResult(Code, NewName));
1090 }
1091
TEST(RenameTest,ProtobufSymbolIsExcluded)1092 TEST(RenameTest, ProtobufSymbolIsExcluded) {
1093 Annotations Code("Prot^obuf buf;");
1094 auto TU = TestTU::withCode(Code.code());
1095 TU.HeaderCode =
1096 R"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1097 class Protobuf {};
1098 )cpp";
1099 TU.HeaderFilename = "protobuf.pb.h";
1100 auto AST = TU.build();
1101 auto Results = rename({Code.point(), "newName", AST, testPath(TU.Filename)});
1102 EXPECT_FALSE(Results);
1103 EXPECT_THAT(llvm::toString(Results.takeError()),
1104 testing::HasSubstr("not a supported kind"));
1105 }
1106
TEST(RenameTest,PrepareRename)1107 TEST(RenameTest, PrepareRename) {
1108 Annotations FooH("void func();");
1109 Annotations FooCC(R"cpp(
1110 #include "foo.h"
1111 void [[fu^nc]]() {}
1112 )cpp");
1113 std::string FooHPath = testPath("foo.h");
1114 std::string FooCCPath = testPath("foo.cc");
1115 MockFS FS;
1116 FS.Files[FooHPath] = std::string(FooH.code());
1117 FS.Files[FooCCPath] = std::string(FooCC.code());
1118
1119 auto ServerOpts = ClangdServer::optsForTest();
1120 ServerOpts.BuildDynamicSymbolIndex = true;
1121
1122 trace::TestTracer Tracer;
1123 MockCompilationDatabase CDB;
1124 ClangdServer Server(CDB, FS, ServerOpts);
1125 runAddDocument(Server, FooHPath, FooH.code());
1126 runAddDocument(Server, FooCCPath, FooCC.code());
1127
1128 auto Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1129 /*NewName=*/llvm::None, {/*CrossFile=*/true});
1130 // Verify that for multi-file rename, we only return main-file occurrences.
1131 ASSERT_TRUE(bool(Results)) << Results.takeError();
1132 // We don't know the result is complete in prepareRename (passing a nullptr
1133 // index internally), so GlobalChanges should be empty.
1134 EXPECT_TRUE(Results->GlobalChanges.empty());
1135 EXPECT_THAT(FooCC.ranges(),
1136 testing::UnorderedElementsAreArray(Results->LocalChanges));
1137
1138 // Name validation.
1139 Results =
1140 runPrepareRename(Server, FooCCPath, FooCC.point(),
1141 /*NewName=*/std::string("int"), {/*CrossFile=*/true});
1142 EXPECT_FALSE(Results);
1143 EXPECT_THAT(llvm::toString(Results.takeError()),
1144 testing::HasSubstr("keyword"));
1145 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "Keywords"),
1146 ElementsAre(1));
1147
1148 // Single-file rename on global symbols, we should report an error.
1149 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1150 /*NewName=*/llvm::None, {/*CrossFile=*/false});
1151 EXPECT_FALSE(Results);
1152 EXPECT_THAT(llvm::toString(Results.takeError()),
1153 testing::HasSubstr("is used outside"));
1154 }
1155
TEST(CrossFileRenameTests,DirtyBuffer)1156 TEST(CrossFileRenameTests, DirtyBuffer) {
1157 Annotations FooCode("class [[Foo]] {};");
1158 std::string FooPath = testPath("foo.cc");
1159 Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1160 Annotations BarCode("void [[Bar]]() {}");
1161 std::string BarPath = testPath("bar.cc");
1162 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1163 // references from bar.cc.
1164 FileSymbols FSymbols;
1165 FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
1166 nullptr, false);
1167 FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
1168 nullptr, false);
1169 auto Index = FSymbols.buildIndex(IndexType::Light);
1170
1171 Annotations MainCode("class [[Fo^o]] {};");
1172 auto MainFilePath = testPath("main.cc");
1173 // Dirty buffer for foo.cc.
1174 auto GetDirtyBuffer = [&](PathRef Path) -> llvm::Optional<std::string> {
1175 if (Path == FooPath)
1176 return FooDirtyBuffer.code().str();
1177 return llvm::None;
1178 };
1179
1180 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1181 // respect the dirty buffer.
1182 TestTU TU = TestTU::withCode(MainCode.code());
1183 auto AST = TU.build();
1184 llvm::StringRef NewName = "newName";
1185 auto Results = rename({MainCode.point(),
1186 NewName,
1187 AST,
1188 MainFilePath,
1189 Index.get(),
1190 {/*CrossFile=*/true},
1191 GetDirtyBuffer});
1192 ASSERT_TRUE(bool(Results)) << Results.takeError();
1193 EXPECT_THAT(
1194 applyEdits(std::move(Results->GlobalChanges)),
1195 UnorderedElementsAre(
1196 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
1197 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1198
1199 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1200 // so we should read file content from VFS.
1201 MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
1202 TU = TestTU::withCode(MainCode.code());
1203 // Set a file "bar.cc" on disk.
1204 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1205 AST = TU.build();
1206 Results = rename({MainCode.point(),
1207 NewName,
1208 AST,
1209 MainFilePath,
1210 Index.get(),
1211 {/*CrossFile=*/true},
1212 GetDirtyBuffer});
1213 ASSERT_TRUE(bool(Results)) << Results.takeError();
1214 EXPECT_THAT(
1215 applyEdits(std::move(Results->GlobalChanges)),
1216 UnorderedElementsAre(
1217 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1218 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1219
1220 // Run rename on a pagination index which couldn't return all refs in one
1221 // request, we reject rename on this case.
1222 class PaginationIndex : public SymbolIndex {
1223 bool refs(const RefsRequest &Req,
1224 llvm::function_ref<void(const Ref &)> Callback) const override {
1225 return true; // has more references
1226 }
1227
1228 bool fuzzyFind(
1229 const FuzzyFindRequest &Req,
1230 llvm::function_ref<void(const Symbol &)> Callback) const override {
1231 return false;
1232 }
1233 void
1234 lookup(const LookupRequest &Req,
1235 llvm::function_ref<void(const Symbol &)> Callback) const override {}
1236
1237 void relations(const RelationsRequest &Req,
1238 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1239 Callback) const override {}
1240 size_t estimateMemoryUsage() const override { return 0; }
1241 } PIndex;
1242 Results = rename({MainCode.point(),
1243 NewName,
1244 AST,
1245 MainFilePath,
1246 &PIndex,
1247 {/*CrossFile=*/true},
1248 GetDirtyBuffer});
1249 EXPECT_FALSE(Results);
1250 EXPECT_THAT(llvm::toString(Results.takeError()),
1251 testing::HasSubstr("too many occurrences"));
1252 }
1253
TEST(CrossFileRenameTests,DeduplicateRefsFromIndex)1254 TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1255 auto MainCode = Annotations("int [[^x]] = 2;");
1256 auto MainFilePath = testPath("main.cc");
1257 auto BarCode = Annotations("int [[x]];");
1258 auto BarPath = testPath("bar.cc");
1259 auto TU = TestTU::withCode(MainCode.code());
1260 // Set a file "bar.cc" on disk.
1261 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1262 auto AST = TU.build();
1263 std::string BarPathURI = URI::create(BarPath).toString();
1264 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
1265 // The index will return duplicated refs, our code should be robost to handle
1266 // it.
1267 class DuplicatedXRefIndex : public SymbolIndex {
1268 public:
1269 DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1270 bool refs(const RefsRequest &Req,
1271 llvm::function_ref<void(const Ref &)> Callback) const override {
1272 // Return two duplicated refs.
1273 Callback(ReturnedRef);
1274 Callback(ReturnedRef);
1275 return false;
1276 }
1277
1278 bool fuzzyFind(const FuzzyFindRequest &,
1279 llvm::function_ref<void(const Symbol &)>) const override {
1280 return false;
1281 }
1282 void lookup(const LookupRequest &,
1283 llvm::function_ref<void(const Symbol &)>) const override {}
1284
1285 void relations(const RelationsRequest &,
1286 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1287 const override {}
1288 size_t estimateMemoryUsage() const override { return 0; }
1289 Ref ReturnedRef;
1290 } DIndex(XRefInBarCC);
1291 llvm::StringRef NewName = "newName";
1292 auto Results = rename({MainCode.point(),
1293 NewName,
1294 AST,
1295 MainFilePath,
1296 &DIndex,
1297 {/*CrossFile=*/true}});
1298 ASSERT_TRUE(bool(Results)) << Results.takeError();
1299 EXPECT_THAT(
1300 applyEdits(std::move(Results->GlobalChanges)),
1301 UnorderedElementsAre(
1302 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1303 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1304 }
1305
TEST(CrossFileRenameTests,WithUpToDateIndex)1306 TEST(CrossFileRenameTests, WithUpToDateIndex) {
1307 MockCompilationDatabase CDB;
1308 CDB.ExtraClangFlags = {"-xc++"};
1309 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1310 // expected rename occurrences.
1311 struct Case {
1312 llvm::StringRef FooH;
1313 llvm::StringRef FooCC;
1314 } Cases[] = {
1315 {
1316 // classes.
1317 R"cpp(
1318 class [[Fo^o]] {
1319 [[Foo]]();
1320 ~[[Foo]]();
1321 };
1322 )cpp",
1323 R"cpp(
1324 #include "foo.h"
1325 [[Foo]]::[[Foo]]() {}
1326 [[Foo]]::~[[Foo]]() {}
1327
1328 void func() {
1329 [[Foo]] foo;
1330 }
1331 )cpp",
1332 },
1333 {
1334 // class templates.
1335 R"cpp(
1336 template <typename T>
1337 class [[Foo]] {};
1338 // FIXME: explicit template specializations are not supported due the
1339 // clangd index limitations.
1340 template <>
1341 class Foo<double> {};
1342 )cpp",
1343 R"cpp(
1344 #include "foo.h"
1345 void func() {
1346 [[F^oo]]<int> foo;
1347 }
1348 )cpp",
1349 },
1350 {
1351 // class methods.
1352 R"cpp(
1353 class Foo {
1354 void [[f^oo]]();
1355 };
1356 )cpp",
1357 R"cpp(
1358 #include "foo.h"
1359 void Foo::[[foo]]() {}
1360
1361 void func(Foo* p) {
1362 p->[[foo]]();
1363 }
1364 )cpp",
1365 },
1366 {
1367 // rename on constructor and destructor.
1368 R"cpp(
1369 class [[Foo]] {
1370 [[^Foo]]();
1371 ~[[Foo^]]();
1372 };
1373 )cpp",
1374 R"cpp(
1375 #include "foo.h"
1376 [[Foo]]::[[Foo]]() {}
1377 [[Foo]]::~[[Foo]]() {}
1378
1379 void func() {
1380 [[Foo]] foo;
1381 }
1382 )cpp",
1383 },
1384 {
1385 // functions.
1386 R"cpp(
1387 void [[f^oo]]();
1388 )cpp",
1389 R"cpp(
1390 #include "foo.h"
1391 void [[foo]]() {}
1392
1393 void func() {
1394 [[foo]]();
1395 }
1396 )cpp",
1397 },
1398 {
1399 // typedefs.
1400 R"cpp(
1401 typedef int [[IN^T]];
1402 [[INT]] foo();
1403 )cpp",
1404 R"cpp(
1405 #include "foo.h"
1406 [[INT]] foo() {}
1407 )cpp",
1408 },
1409 {
1410 // usings.
1411 R"cpp(
1412 using [[I^NT]] = int;
1413 [[INT]] foo();
1414 )cpp",
1415 R"cpp(
1416 #include "foo.h"
1417 [[INT]] foo() {}
1418 )cpp",
1419 },
1420 {
1421 // variables.
1422 R"cpp(
1423 static const int [[VA^R]] = 123;
1424 )cpp",
1425 R"cpp(
1426 #include "foo.h"
1427 int s = [[VAR]];
1428 )cpp",
1429 },
1430 {
1431 // scope enums.
1432 R"cpp(
1433 enum class [[K^ind]] { ABC };
1434 )cpp",
1435 R"cpp(
1436 #include "foo.h"
1437 [[Kind]] ff() {
1438 return [[Kind]]::ABC;
1439 }
1440 )cpp",
1441 },
1442 {
1443 // enum constants.
1444 R"cpp(
1445 enum class Kind { [[A^BC]] };
1446 )cpp",
1447 R"cpp(
1448 #include "foo.h"
1449 Kind ff() {
1450 return Kind::[[ABC]];
1451 }
1452 )cpp",
1453 },
1454 {
1455 // Implicit references in macro expansions.
1456 R"cpp(
1457 class [[Fo^o]] {};
1458 #define FooFoo Foo
1459 #define FOO Foo
1460 )cpp",
1461 R"cpp(
1462 #include "foo.h"
1463 void bar() {
1464 [[Foo]] x;
1465 FOO y;
1466 FooFoo z;
1467 }
1468 )cpp",
1469 },
1470 };
1471
1472 trace::TestTracer Tracer;
1473 for (const auto &T : Cases) {
1474 SCOPED_TRACE(T.FooH);
1475 Annotations FooH(T.FooH);
1476 Annotations FooCC(T.FooCC);
1477 std::string FooHPath = testPath("foo.h");
1478 std::string FooCCPath = testPath("foo.cc");
1479
1480 MockFS FS;
1481 FS.Files[FooHPath] = std::string(FooH.code());
1482 FS.Files[FooCCPath] = std::string(FooCC.code());
1483
1484 auto ServerOpts = ClangdServer::optsForTest();
1485 ServerOpts.BuildDynamicSymbolIndex = true;
1486 ClangdServer Server(CDB, FS, ServerOpts);
1487
1488 // Add all files to clangd server to make sure the dynamic index has been
1489 // built.
1490 runAddDocument(Server, FooHPath, FooH.code());
1491 runAddDocument(Server, FooCCPath, FooCC.code());
1492
1493 llvm::StringRef NewName = "NewName";
1494 for (const auto &RenamePos : FooH.points()) {
1495 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
1496 auto FileEditsList = llvm::cantFail(runRename(
1497 Server, FooHPath, RenamePos, NewName, {/*CrossFile=*/true}));
1498 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
1499 EXPECT_THAT(
1500 applyEdits(std::move(FileEditsList.GlobalChanges)),
1501 UnorderedElementsAre(
1502 Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
1503 Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
1504 }
1505 }
1506 }
1507
TEST(CrossFileRenameTests,CrossFileOnLocalSymbol)1508 TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
1509 // cross-file rename should work for function-local symbols, even there is no
1510 // index provided.
1511 Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
1512 auto TU = TestTU::withCode(Code.code());
1513 auto Path = testPath(TU.Filename);
1514 auto AST = TU.build();
1515 llvm::StringRef NewName = "newName";
1516 auto Results = rename({Code.point(), NewName, AST, Path});
1517 ASSERT_TRUE(bool(Results)) << Results.takeError();
1518 EXPECT_THAT(
1519 applyEdits(std::move(Results->GlobalChanges)),
1520 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
1521 }
1522
TEST(CrossFileRenameTests,BuildRenameEdits)1523 TEST(CrossFileRenameTests, BuildRenameEdits) {
1524 Annotations Code("[[]]");
1525 auto LSPRange = Code.range();
1526 llvm::StringRef FilePath = "/test/TestTU.cpp";
1527 llvm::StringRef NewName = "abc";
1528 auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
1529 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
1530 ASSERT_EQ(1UL, Edit->Replacements.size());
1531 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
1532 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
1533
1534 // Test invalid range.
1535 LSPRange.end = {10, 0}; // out of range
1536 Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
1537 EXPECT_FALSE(Edit);
1538 EXPECT_THAT(llvm::toString(Edit.takeError()),
1539 testing::HasSubstr("fail to convert"));
1540
1541 // Normal ascii characters.
1542 Annotations T(R"cpp(
1543 [[range]]
1544 [[range]]
1545 [[range]]
1546 )cpp");
1547 Edit = buildRenameEdit(FilePath, T.code(), T.ranges(), NewName);
1548 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
1549 EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
1550 expectedResult(T, NewName));
1551 }
1552
TEST(CrossFileRenameTests,adjustRenameRanges)1553 TEST(CrossFileRenameTests, adjustRenameRanges) {
1554 // Ranges in IndexedCode indicate the indexed occurrences;
1555 // ranges in DraftCode indicate the expected mapped result, empty indicates
1556 // we expect no matched result found.
1557 struct {
1558 llvm::StringRef IndexedCode;
1559 llvm::StringRef DraftCode;
1560 } Tests[] = {
1561 {
1562 // both line and column are changed, not a near miss.
1563 R"cpp(
1564 int [[x]] = 0;
1565 )cpp",
1566 R"cpp(
1567 // insert a line.
1568 double x = 0;
1569 )cpp",
1570 },
1571 {
1572 // subset.
1573 R"cpp(
1574 int [[x]] = 0;
1575 )cpp",
1576 R"cpp(
1577 int [[x]] = 0;
1578 {int x = 0; }
1579 )cpp",
1580 },
1581 {
1582 // shift columns.
1583 R"cpp(int [[x]] = 0; void foo(int x);)cpp",
1584 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
1585 },
1586 {
1587 // shift lines.
1588 R"cpp(
1589 int [[x]] = 0;
1590 void foo(int x);
1591 )cpp",
1592 R"cpp(
1593 // insert a line.
1594 int [[x]] = 0;
1595 void foo(int x);
1596 )cpp",
1597 },
1598 };
1599 LangOptions LangOpts;
1600 LangOpts.CPlusPlus = true;
1601 for (const auto &T : Tests) {
1602 SCOPED_TRACE(T.DraftCode);
1603 Annotations Draft(T.DraftCode);
1604 auto ActualRanges = adjustRenameRanges(
1605 Draft.code(), "x", Annotations(T.IndexedCode).ranges(), LangOpts);
1606 if (!ActualRanges)
1607 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
1608 else
1609 EXPECT_THAT(Draft.ranges(),
1610 testing::UnorderedElementsAreArray(*ActualRanges));
1611 }
1612 }
1613
TEST(RangePatchingHeuristic,GetMappedRanges)1614 TEST(RangePatchingHeuristic, GetMappedRanges) {
1615 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
1616 // there are no mapped ranges.
1617 struct {
1618 llvm::StringRef IndexedCode;
1619 llvm::StringRef LexedCode;
1620 } Tests[] = {
1621 {
1622 // no lexed ranges.
1623 "[[]]",
1624 "",
1625 },
1626 {
1627 // both line and column are changed, not a near miss.
1628 R"([[]])",
1629 R"(
1630 [[]]
1631 )",
1632 },
1633 {
1634 // subset.
1635 "[[]]",
1636 "^[[]] [[]]"
1637 },
1638 {
1639 // shift columns.
1640 "[[]] [[]]",
1641 " ^[[]] ^[[]] [[]]"
1642 },
1643 {
1644 R"(
1645 [[]]
1646
1647 [[]] [[]]
1648 )",
1649 R"(
1650 // insert a line
1651 ^[[]]
1652
1653 ^[[]] ^[[]]
1654 )",
1655 },
1656 {
1657 R"(
1658 [[]]
1659
1660 [[]] [[]]
1661 )",
1662 R"(
1663 // insert a line
1664 ^[[]]
1665 ^[[]] ^[[]] // column is shifted.
1666 )",
1667 },
1668 {
1669 R"(
1670 [[]]
1671
1672 [[]] [[]]
1673 )",
1674 R"(
1675 // insert a line
1676 [[]]
1677
1678 [[]] [[]] // not mapped (both line and column are changed).
1679 )",
1680 },
1681 {
1682 R"(
1683 [[]]
1684 [[]]
1685
1686 [[]]
1687 [[]]
1688
1689 }
1690 )",
1691 R"(
1692 // insert a new line
1693 ^[[]]
1694 ^[[]]
1695 [[]] // additional range
1696 ^[[]]
1697 ^[[]]
1698 [[]] // additional range
1699 )",
1700 },
1701 {
1702 // non-distinct result (two best results), not a near miss
1703 R"(
1704 [[]]
1705 [[]]
1706 [[]]
1707 )",
1708 R"(
1709 [[]]
1710 [[]]
1711 [[]]
1712 [[]]
1713 )",
1714 }
1715 };
1716 for (const auto &T : Tests) {
1717 SCOPED_TRACE(T.IndexedCode);
1718 auto Lexed = Annotations(T.LexedCode);
1719 auto LexedRanges = Lexed.ranges();
1720 std::vector<Range> ExpectedMatches;
1721 for (auto P : Lexed.points()) {
1722 auto Match = llvm::find_if(LexedRanges, [&P](const Range& R) {
1723 return R.start == P;
1724 });
1725 ASSERT_NE(Match, LexedRanges.end());
1726 ExpectedMatches.push_back(*Match);
1727 }
1728
1729 auto Mapped =
1730 getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
1731 if (!Mapped)
1732 EXPECT_THAT(ExpectedMatches, IsEmpty());
1733 else
1734 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
1735 }
1736 }
1737
TEST(CrossFileRenameTests,adjustmentCost)1738 TEST(CrossFileRenameTests, adjustmentCost) {
1739 struct {
1740 llvm::StringRef RangeCode;
1741 size_t ExpectedCost;
1742 } Tests[] = {
1743 {
1744 R"(
1745 $idx[[]]$lex[[]] // diff: 0
1746 )",
1747 0,
1748 },
1749 {
1750 R"(
1751 $idx[[]]
1752 $lex[[]] // line diff: +1
1753 $idx[[]]
1754 $lex[[]] // line diff: +1
1755 $idx[[]]
1756 $lex[[]] // line diff: +1
1757
1758 $idx[[]]
1759
1760 $lex[[]] // line diff: +2
1761 )",
1762 1 + 1
1763 },
1764 {
1765 R"(
1766 $idx[[]]
1767 $lex[[]] // line diff: +1
1768 $idx[[]]
1769
1770 $lex[[]] // line diff: +2
1771 $idx[[]]
1772
1773
1774 $lex[[]] // line diff: +3
1775 )",
1776 1 + 1 + 1
1777 },
1778 {
1779 R"(
1780 $idx[[]]
1781
1782
1783 $lex[[]] // line diff: +3
1784 $idx[[]]
1785
1786 $lex[[]] // line diff: +2
1787 $idx[[]]
1788 $lex[[]] // line diff: +1
1789 )",
1790 3 + 1 + 1
1791 },
1792 {
1793 R"(
1794 $idx[[]]
1795 $lex[[]] // line diff: +1
1796 $lex[[]] // line diff: -2
1797
1798 $idx[[]]
1799 $idx[[]]
1800
1801
1802 $lex[[]] // line diff: +3
1803 )",
1804 1 + 3 + 5
1805 },
1806 {
1807 R"(
1808 $idx[[]] $lex[[]] // column diff: +1
1809 $idx[[]]$lex[[]] // diff: 0
1810 )",
1811 1
1812 },
1813 {
1814 R"(
1815 $idx[[]]
1816 $lex[[]] // diff: +1
1817 $idx[[]] $lex[[]] // column diff: +1
1818 $idx[[]]$lex[[]] // diff: 0
1819 )",
1820 1 + 1 + 1
1821 },
1822 {
1823 R"(
1824 $idx[[]] $lex[[]] // column diff: +1
1825 )",
1826 1
1827 },
1828 {
1829 R"(
1830 // column diffs: +1, +2, +3
1831 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
1832 )",
1833 1 + 1 + 1,
1834 },
1835 };
1836 for (const auto &T : Tests) {
1837 SCOPED_TRACE(T.RangeCode);
1838 Annotations C(T.RangeCode);
1839 std::vector<size_t> MappedIndex;
1840 for (size_t I = 0; I < C.ranges("lex").size(); ++I)
1841 MappedIndex.push_back(I);
1842 EXPECT_EQ(renameRangeAdjustmentCost(C.ranges("idx"), C.ranges("lex"),
1843 MappedIndex),
1844 T.ExpectedCost);
1845 }
1846 }
1847
1848 } // namespace
1849 } // namespace clangd
1850 } // namespace clang
1851