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