clang-tools 24.0.0git
RenameTests.cpp
Go to the documentation of this file.
1//===-- RenameTests.cpp -----------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Annotations.h"
10#include "ClangdServer.h"
11#include "SyncAPI.h"
12#include "TestFS.h"
13#include "TestTU.h"
14#include "index/Ref.h"
15#include "refactor/Rename.h"
16#include "support/TestTracer.h"
17#include "clang/Tooling/Core/Replacement.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include "gmock/gmock.h"
21#include "gtest/gtest.h"
22
23#include <algorithm>
24
25namespace clang {
26namespace clangd {
27namespace {
28
29using testing::ElementsAre;
30using testing::Eq;
31using testing::IsEmpty;
32using testing::Pair;
33using testing::SizeIs;
34using testing::UnorderedElementsAre;
35using testing::UnorderedElementsAreArray;
36
37llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
38createOverlay(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Base,
39 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> Overlay) {
40 auto OFS =
41 llvm::makeIntrusiveRefCnt<llvm::vfs::OverlayFileSystem>(std::move(Base));
42 OFS->pushOverlay(std::move(Overlay));
43 return OFS;
44}
45
46llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getVFSFromAST(ParsedAST &AST) {
47 return &AST.getSourceManager().getFileManager().getVirtualFileSystem();
48}
49
50// Convert a Range to a Ref.
51Ref refWithRange(const clangd::Range &Range, const std::string &URI) {
52 Ref Result;
54 Result.Location.Start.setLine(Range.start.line);
55 Result.Location.Start.setColumn(Range.start.character);
56 Result.Location.End.setLine(Range.end.line);
57 Result.Location.End.setColumn(Range.end.character);
58 Result.Location.FileURI = URI.c_str();
59 return Result;
60}
61
62// Build a RefSlab from all marked ranges in the annotation. The ranges are
63// assumed to associate with the given SymbolName.
64std::unique_ptr<RefSlab> buildRefSlab(const Annotations &Code,
65 llvm::StringRef SymbolName,
66 llvm::StringRef Path) {
67 RefSlab::Builder Builder;
68 TestTU TU;
69 TU.HeaderCode = std::string(Code.code());
70 auto Symbols = TU.headerSymbols();
71 const auto &SymbolID = findSymbol(Symbols, SymbolName).ID;
72 std::string PathURI = URI::create(Path).toString();
73 for (const auto &Range : Code.ranges())
74 Builder.insert(SymbolID, refWithRange(Range, PathURI));
75
76 return std::make_unique<RefSlab>(std::move(Builder).build());
77}
78
79std::vector<
80 std::pair</*FilePath*/ std::string, /*CodeAfterRename*/ std::string>>
81applyEdits(FileEdits FE) {
82 std::vector<std::pair<std::string, std::string>> Results;
83 for (auto &It : FE)
84 Results.emplace_back(
85 It.first().str(),
86 llvm::cantFail(tooling::applyAllReplacements(
87 It.getValue().InitialCode, It.getValue().Replacements)));
88 return Results;
89}
90
91// Generates an expected rename result by replacing all ranges in the given
92// annotation with the NewName.
93std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
94 std::string Result;
95 unsigned NextChar = 0;
96 llvm::StringRef Code = Test.code();
97 for (const auto &R : Test.llvm::Annotations::ranges()) {
98 assert(R.Begin <= R.End && NextChar <= R.Begin);
99 Result += Code.substr(NextChar, R.Begin - NextChar);
100 Result += NewName;
101 NextChar = R.End;
102 }
103 Result += Code.substr(NextChar);
104 return Result;
105}
106
107std::vector<SymbolRange> symbolRanges(llvm::ArrayRef<Range> Ranges) {
108 std::vector<SymbolRange> Result;
109 for (const auto &R : Ranges)
110 Result.emplace_back(R);
111 return Result;
112}
113
114TEST(RenameTest, WithinFileRename) {
115 // For each "^" this test moves cursor to its location and applies renaming
116 // while checking that all identifiers in [[]] ranges are also renamed.
117 llvm::StringRef Tests[] = {
118 // Function.
119 R"cpp(
120 void [[foo^]]() {
121 [[fo^o]]();
122 }
123 )cpp",
124
125 // Type.
126 R"cpp(
127 struct [[foo^]] {};
128 [[foo]] test() {
129 [[f^oo]] x;
130 return x;
131 }
132 )cpp",
133
134 // Local variable.
135 R"cpp(
136 void bar() {
137 if (auto [[^foo]] = 5) {
138 [[foo]] = 3;
139 }
140 }
141 )cpp",
142
143 // Class, its constructor and destructor.
144 R"cpp(
145 class [[F^oo]] {
146 [[F^oo]]();
147 ~[[F^oo]]();
148 [[F^oo]] *foo(int x);
149
150 [[F^oo]] *Ptr;
151 };
152 [[F^oo]]::[[Fo^o]]() {}
153 [[F^oo]]::~[[Fo^o]]() {}
154 [[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
155 )cpp",
156
157 // Template class, its constructor and destructor.
158 R"cpp(
159 template <typename T>
160 class [[F^oo]] {
161 [[F^oo]]();
162 ~[[F^oo]]();
163 void f([[F^oo]] x);
164 };
165
166 template<typename T>
167 [[F^oo]]<T>::[[Fo^o]]() {}
168
169 template<typename T>
170 [[F^oo]]<T>::~[[Fo^o]]() {}
171 )cpp",
172
173 // Template class constructor.
174 R"cpp(
175 class [[F^oo]] {
176 template<typename T>
177 [[Fo^o]]();
178
179 template<typename T>
180 [[F^oo]](T t);
181 };
182
183 template<typename T>
184 [[F^oo]]::[[Fo^o]]() {}
185 )cpp",
186
187 // Class in template argument.
188 R"cpp(
189 class [[F^oo]] {};
190 template <typename T> void func();
191 template <typename T> class Baz {};
192 int main() {
193 func<[[F^oo]]>();
194 Baz<[[F^oo]]> obj;
195 return 0;
196 }
197 )cpp",
198
199 // Forward class declaration without definition.
200 R"cpp(
201 class [[F^oo]];
202 [[F^oo]] *f();
203 )cpp",
204
205 // Member function.
206 R"cpp(
207 struct X {
208 void [[F^oo]]() {}
209 void Baz() { [[F^oo]](); }
210 };
211 )cpp",
212
213 // Templated method instantiation.
214 R"cpp(
215 template<typename T>
216 class Foo {
217 public:
218 static T [[f^oo]]() { return T(); }
219 };
220
221 void bar() {
222 Foo<int>::[[f^oo]]();
223 }
224 )cpp",
225 R"cpp(
226 template<typename T>
227 class Foo {
228 public:
229 T [[f^oo]]() { return T(); }
230 };
231
232 void bar() {
233 Foo<int>().[[f^oo]]();
234 }
235 )cpp",
236
237 // Template class (partial) specializations.
238 R"cpp(
239 template <typename T>
240 class [[F^oo]] {};
241
242 template<>
243 class [[F^oo]]<bool> {};
244 template <typename T>
245 class [[F^oo]]<T*> {};
246
247 void test() {
248 [[F^oo]]<int> x;
249 [[F^oo]]<bool> y;
250 [[F^oo]]<int*> z;
251 }
252 )cpp",
253
254 // Incomplete class specializations
255 R"cpp(
256 template <typename T>
257 class [[Fo^o]] {};
258 void func([[F^oo]]<int>);
259 )cpp",
260
261 // Template class instantiations.
262 R"cpp(
263 template <typename T>
264 class [[F^oo]] {
265 public:
266 T foo(T arg, T& ref, T* ptr) {
267 T value;
268 int number = 42;
269 value = (T)number;
270 value = static_cast<T>(number);
271 return value;
272 }
273 static void foo(T value) {}
274 T member;
275 };
276
277 template <typename T>
278 void func() {
279 [[F^oo]]<T> obj;
280 obj.member = T();
281 [[Foo]]<T>::foo();
282 }
283
284 void test() {
285 [[F^oo]]<int> i;
286 i.member = 0;
287 [[F^oo]]<int>::foo(0);
288
289 [[F^oo]]<bool> b;
290 b.member = false;
291 [[F^oo]]<bool>::foo(false);
292 }
293 )cpp",
294
295 // Template class methods.
296 R"cpp(
297 template <typename T>
298 class A {
299 public:
300 void [[f^oo]]() {}
301 };
302
303 void func() {
304 A<int>().[[f^oo]]();
305 A<double>().[[f^oo]]();
306 A<float>().[[f^oo]]();
307 }
308 )cpp",
309
310 // Templated class specialization.
311 R"cpp(
312 template<typename T, typename U=bool>
313 class [[Foo^]];
314
315 template<typename T, typename U>
316 class [[Foo^]] {};
317
318 template<typename T=int, typename U>
319 class [[Foo^]];
320 )cpp",
321 R"cpp(
322 template<typename T=float, typename U=int>
323 class [[Foo^]];
324
325 template<typename T, typename U>
326 class [[Foo^]] {};
327 )cpp",
328
329 // Function template specialization.
330 R"cpp(
331 template<typename T=int, typename U=bool>
332 U [[foo^]]();
333
334 template<typename T, typename U>
335 U [[foo^]]() {};
336 )cpp",
337 R"cpp(
338 template<typename T, typename U>
339 U [[foo^]]() {};
340
341 template<typename T=int, typename U=bool>
342 U [[foo^]]();
343 )cpp",
344 R"cpp(
345 template<typename T=int, typename U=bool>
346 U [[foo^]]();
347
348 template<typename T, typename U>
349 U [[foo^]]();
350 )cpp",
351 R"cpp(
352 template <typename T>
353 void [[f^oo]](T t);
354
355 template <>
356 void [[f^oo]](int a);
357
358 void test() {
359 [[f^oo]]<double>(1);
360 }
361 )cpp",
362
363 // Variable template.
364 R"cpp(
365 template <typename T, int U>
366 bool [[F^oo]] = true;
367
368 // Explicit template specialization
369 template <>
370 bool [[F^oo]]<int, 0> = false;
371
372 // Partial template specialization
373 template <typename T>
374 bool [[F^oo]]<T, 1> = false;
375
376 void foo() {
377 // Ref to the explicit template specialization
378 [[F^oo]]<int, 0>;
379 // Ref to the primary template.
380 [[F^oo]]<double, 2>;
381 }
382 )cpp",
383
384 // Complicated class type.
385 R"cpp(
386 // Forward declaration.
387 class [[Fo^o]];
388 class Baz {
389 virtual int getValue() const = 0;
390 };
391
392 class [[F^oo]] : public Baz {
393 public:
394 [[F^oo]](int value = 0) : x(value) {}
395
396 [[F^oo]] &operator++(int);
397
398 bool operator<([[Foo]] const &rhs);
399 int getValue() const;
400 private:
401 int x;
402 };
403
404 void func() {
405 [[F^oo]] *Pointer = 0;
406 [[F^oo]] Variable = [[Foo]](10);
407 for ([[F^oo]] it; it < Variable; it++);
408 const [[F^oo]] *C = new [[Foo]]();
409 const_cast<[[F^oo]] *>(C)->getValue();
410 [[F^oo]] foo;
411 const Baz &BazReference = foo;
412 const Baz *BazPointer = &foo;
413 reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
414 static_cast<const [[^Foo]] &>(BazReference).getValue();
415 static_cast<const [[^Foo]] *>(BazPointer)->getValue();
416 }
417 )cpp",
418
419 // Static class member.
420 R"cpp(
421 struct Foo {
422 static Foo *[[Static^Member]];
423 };
424
425 Foo* Foo::[[Static^Member]] = nullptr;
426
427 void foo() {
428 Foo* Pointer = Foo::[[Static^Member]];
429 }
430 )cpp",
431
432 // Reference in lambda parameters.
433 R"cpp(
434 template <class T>
435 class function;
436 template <class R, class... ArgTypes>
437 class function<R(ArgTypes...)> {
438 public:
439 template <typename Functor>
440 function(Functor f) {}
441
442 function() {}
443
444 R operator()(ArgTypes...) const {}
445 };
446
447 namespace ns {
448 class [[Old]] {};
449 void f() {
450 function<void([[Old]])> func;
451 }
452 } // namespace ns
453 )cpp",
454
455 // Destructor explicit call.
456 R"cpp(
457 class [[F^oo]] {
458 public:
459 ~[[^Foo]]();
460 };
461
462 [[Foo^]]::~[[^Foo]]() {}
463
464 int main() {
465 [[Fo^o]] f;
466 f.~/*something*/[[^Foo]]();
467 f.~[[^Foo]]();
468 }
469 )cpp",
470
471 // Derived destructor explicit call.
472 R"cpp(
473 class [[Bas^e]] {};
474 class Derived : public [[Bas^e]] {};
475
476 int main() {
477 [[Bas^e]] *foo = new Derived();
478 foo->[[^Base]]::~[[^Base]]();
479 }
480 )cpp",
481
482 // CXXConstructor initializer list.
483 R"cpp(
484 class Baz {};
485 class Qux {
486 Baz [[F^oo]];
487 public:
488 Qux();
489 };
490 Qux::Qux() : [[F^oo]]() {}
491 )cpp",
492
493 // DeclRefExpr.
494 R"cpp(
495 class C {
496 public:
497 static int [[F^oo]];
498 };
499
500 int foo(int x);
501 #define MACRO(a) foo(a)
502
503 void func() {
504 C::[[F^oo]] = 1;
505 MACRO(C::[[Foo]]);
506 int y = C::[[F^oo]];
507 }
508 )cpp",
509
510 // Macros.
511 R"cpp(
512 // no rename inside macro body.
513 #define M1 foo
514 #define M2(x) x
515 int [[fo^o]]();
516 void boo(int);
517
518 void qoo() {
519 [[f^oo]]();
520 boo([[f^oo]]());
521 M1();
522 boo(M1());
523 M2([[f^oo]]());
524 M2(M1()); // foo is inside the nested macro body.
525 }
526 )cpp",
527
528 // MemberExpr in macros
529 R"cpp(
530 class Baz {
531 public:
532 int [[F^oo]];
533 };
534 int qux(int x);
535 #define MACRO(a) qux(a)
536
537 int main() {
538 Baz baz;
539 baz.[[F^oo]] = 1;
540 MACRO(baz.[[F^oo]]);
541 int y = baz.[[F^oo]];
542 }
543 )cpp",
544
545 // Fields in classes & partial and full specialiations.
546 R"cpp(
547 template<typename T>
548 struct Foo {
549 T [[Vari^able]] = 42;
550 };
551
552 void foo() {
553 Foo<int> f;
554 f.[[Varia^ble]] = 9000;
555 }
556 )cpp",
557 R"cpp(
558 template<typename T, typename U>
559 struct Foo {
560 T Variable[42];
561 U Another;
562
563 void bar() {}
564 };
565
566 template<typename T>
567 struct Foo<T, bool> {
568 T [[Var^iable]];
569 void bar() { ++[[Var^iable]]; }
570 };
571
572 void foo() {
573 Foo<unsigned, bool> f;
574 f.[[Var^iable]] = 9000;
575 }
576 )cpp",
577 R"cpp(
578 template<typename T, typename U>
579 struct Foo {
580 T Variable[42];
581 U Another;
582
583 void bar() {}
584 };
585
586 template<typename T>
587 struct Foo<T, bool> {
588 T Variable;
589 void bar() { ++Variable; }
590 };
591
592 template<>
593 struct Foo<unsigned, bool> {
594 unsigned [[Var^iable]];
595 void bar() { ++[[Var^iable]]; }
596 };
597
598 void foo() {
599 Foo<unsigned, bool> f;
600 f.[[Var^iable]] = 9000;
601 }
602 )cpp",
603 // Static fields.
604 R"cpp(
605 struct Foo {
606 static int [[Var^iable]];
607 };
608
609 int Foo::[[Var^iable]] = 42;
610
611 void foo() {
612 int LocalInt = Foo::[[Var^iable]];
613 }
614 )cpp",
615 R"cpp(
616 template<typename T>
617 struct Foo {
618 static T [[Var^iable]];
619 };
620
621 template <>
622 int Foo<int>::[[Var^iable]] = 42;
623
624 template <>
625 bool Foo<bool>::[[Var^iable]] = true;
626
627 void foo() {
628 int LocalInt = Foo<int>::[[Var^iable]];
629 bool LocalBool = Foo<bool>::[[Var^iable]];
630 }
631 )cpp",
632
633 // Template parameters.
634 R"cpp(
635 template <typename [[^T]]>
636 class Foo {
637 [[T^]] foo([[T^]] arg, [[T^]]& ref, [[^T]]* ptr) {
638 [[T]] value;
639 int number = 42;
640 value = ([[T^]])number;
641 value = static_cast<[[^T]]>(number);
642 return value;
643 }
644 static void foo([[T^]] value) {}
645 [[T^]] member;
646 };
647 )cpp",
648
649 // Typedef.
650 R"cpp(
651 namespace ns {
652 class basic_string {};
653 typedef basic_string [[s^tring]];
654 } // namespace ns
655
656 ns::[[s^tring]] foo();
657 )cpp",
658
659 // Variable.
660 R"cpp(
661 namespace A {
662 int [[F^oo]];
663 }
664 int Foo;
665 int Qux = Foo;
666 int Baz = A::[[^Foo]];
667 void fun() {
668 struct {
669 int Foo;
670 } b = {100};
671 int Foo = 100;
672 Baz = Foo;
673 {
674 extern int Foo;
675 Baz = Foo;
676 Foo = A::[[F^oo]] + Baz;
677 A::[[Fo^o]] = b.Foo;
678 }
679 Foo = b.Foo;
680 }
681 )cpp",
682
683 // Namespace alias.
684 R"cpp(
685 namespace a { namespace b { void foo(); } }
686 namespace [[^x]] = a::b;
687 void bar() {
688 [[x^]]::foo();
689 }
690 )cpp",
691
692 // Enum.
693 R"cpp(
694 enum [[C^olor]] { Red, Green, Blue };
695 void foo() {
696 [[C^olor]] c;
697 c = [[C^olor]]::Blue;
698 }
699 )cpp",
700
701 // Scoped enum.
702 R"cpp(
703 enum class [[K^ind]] { ABC };
704 void ff() {
705 [[K^ind]] s;
706 s = [[K^ind]]::ABC;
707 }
708 )cpp",
709
710 // Template class in template argument list.
711 R"cpp(
712 template<typename T>
713 class [[Fo^o]] {};
714 template <template<typename> class Z> struct Bar { };
715 template <> struct Bar<[[F^oo]]> {};
716 )cpp",
717
718 // Designated initializer.
719 R"cpp(
720 struct Bar {
721 int [[Fo^o]];
722 };
723 Bar bar { .[[^Foo]] = 42 };
724 )cpp",
725
726 // Nested designated initializer.
727 R"cpp(
728 struct Baz {
729 int Field;
730 };
731 struct Bar {
732 Baz [[Fo^o]];
733 };
734 // FIXME: v selecting here results in renaming Field.
735 Bar bar { .[[Foo]].Field = 42 };
736 )cpp",
737 R"cpp(
738 struct Baz {
739 int [[Fiel^d]];
740 };
741 struct Bar {
742 Baz Foo;
743 };
744 Bar bar { .Foo.[[^Field]] = 42 };
745 )cpp",
746
747 // Templated alias.
748 R"cpp(
749 template <typename T>
750 class X { T t; };
751
752 template <typename T>
753 using [[Fo^o]] = X<T>;
754
755 void bar() {
756 [[Fo^o]]<int> Bar;
757 }
758 )cpp",
759
760 // Alias.
761 R"cpp(
762 class X {};
763 using [[F^oo]] = X;
764
765 void bar() {
766 [[Fo^o]] Bar;
767 }
768 )cpp",
769
770 // Alias within a namespace.
771 R"cpp(
772 namespace x { class X {}; }
773 namespace ns {
774 using [[Fo^o]] = x::X;
775 }
776
777 void bar() {
778 ns::[[Fo^o]] Bar;
779 }
780 )cpp",
781
782 // Alias within macros.
783 R"cpp(
784 namespace x { class Old {}; }
785 namespace ns {
786 #define REF(alias) alias alias_var;
787
788 #define ALIAS(old) \
789 using old##Alias = x::old; \
790 REF(old##Alias);
791
792 ALIAS(Old);
793
794 [[Old^Alias]] old_alias;
795 }
796
797 void bar() {
798 ns::[[Old^Alias]] Bar;
799 }
800 )cpp",
801
802 // User defined conversion.
803 R"cpp(
804 class [[F^oo]] {
805 public:
806 [[F^oo]]() {}
807 };
808
809 class Baz {
810 public:
811 operator [[F^oo]]() {
812 return [[F^oo]]();
813 }
814 };
815
816 int main() {
817 Baz boo;
818 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
819 }
820 )cpp",
821
822 // ObjC, should not crash.
823 R"cpp(
824 @interface ObjC {
825 char [[da^ta]];
826 } @end
827 )cpp",
828
829 // Issue 170: Rename symbol introduced by UsingDecl
830 R"cpp(
831 namespace ns { void [[f^oo]](); }
832
833 using ns::[[f^oo]];
834
835 void f() {
836 [[f^oo]]();
837 auto p = &[[f^oo]];
838 }
839 )cpp",
840
841 // Issue 170: using decl that imports multiple overloads
842 // -> Only the overload under the cursor is renamed
843 R"cpp(
844 namespace ns { int [[^foo]](int); char foo(char); }
845 using ns::[[foo]];
846 void f() {
847 [[^foo]](42);
848 foo('x');
849 }
850 )cpp",
851
852 // ObjC class with a category.
853 R"cpp(
854 @interface [[Fo^o]]
855 @end
856 @implementation [[F^oo]]
857 @end
858 @interface [[Fo^o]] (Category)
859 @end
860 @implementation [[F^oo]] (Category)
861 @end
862
863 void func([[Fo^o]] *f) {}
864 )cpp",
865
866 // rename with explicit object parameter
867 R"cpp(
868 struct Foo {
869 int [[memb^er]] {};
870 auto&& getter1(this auto&& self) {
871 auto local = [&] {
872 return self.[[memb^er]];
873 }();
874 return local + self.[[memb^er]];
875 }
876 auto&& getter2(this Foo&& self) {
877 return self.[[memb^er]];
878 }
879 int normal() {
880 return this->[[mem^ber]] + [[memb^er]];
881 }
882 };
883 )cpp",
884 };
885 llvm::StringRef NewName = "NewName";
886 for (llvm::StringRef T : Tests) {
887 SCOPED_TRACE(T);
888 Annotations Code(T);
889 auto TU = TestTU::withCode(Code.code());
890 TU.ExtraArgs.push_back("-xobjective-c++");
891 TU.ExtraArgs.push_back("-std=c++23");
892 auto AST = TU.build();
893 auto Index = TU.index();
894 for (const auto &RenamePos : Code.points()) {
895 auto RenameResult =
896 rename({RenamePos, NewName, AST, testPath(TU.Filename),
897 getVFSFromAST(AST), Index.get()});
898 ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
899 ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
900 EXPECT_EQ(
901 applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
902 expectedResult(Code, NewName));
903 }
904 }
905}
906
907TEST(RenameTest, ObjCWithinFileRename) {
908 struct TestCase {
909 /// Annotated source code that should be renamed. Every point (indicated by
910 /// `^`) will be used as a rename location.
911 llvm::StringRef Input;
912 /// The new name that should be given to the rename locaitons.
913 llvm::StringRef NewName;
914 /// The expected rename source code or `nullopt` if we expect rename to
915 /// fail.
916 std::optional<llvm::StringRef> Expected;
917 };
918 TestCase Tests[] = {// Simple rename
919 {
920 // Input
921 R"cpp(
922 @interface Foo
923 - (int)performA^ction:(int)action w^ith:(int)value;
924 @end
925 @implementation Foo
926 - (int)performAc^tion:(int)action w^ith:(int)value {
927 return [self performAction:action with:value];
928 }
929 @end
930 )cpp",
931 // New name
932 "performNewAction:by:",
933 // Expected
934 R"cpp(
935 @interface Foo
936 - (int)performNewAction:(int)action by:(int)value;
937 @end
938 @implementation Foo
939 - (int)performNewAction:(int)action by:(int)value {
940 return [self performNewAction:action by:value];
941 }
942 @end
943 )cpp",
944 },
945 // Rename selector with macro
946 {
947 // Input
948 R"cpp(
949 #define mySelector - (int)performAction:(int)action with:(int)value
950 @interface Foo
951 ^mySelector;
952 @end
953 @implementation Foo
954 mySelector {
955 return [self performAction:action with:value];
956 }
957 @end
958 )cpp",
959 // New name
960 "performNewAction:by:",
961 // Expected error
962 std::nullopt,
963 },
964 // Rename selector in macro definition
965 {
966 // Input
967 R"cpp(
968 #define mySelector - (int)perform^Action:(int)action with:(int)value
969 @interface Foo
970 mySelector;
971 @end
972 @implementation Foo
973 mySelector {
974 return [self performAction:action with:value];
975 }
976 @end
977 )cpp",
978 // New name
979 "performNewAction:by:",
980 // Expected error
981 std::nullopt,
982 },
983 // Don't rename `@selector`
984 // `@selector` is not tied to a single selector. Eg. there
985 // might be multiple
986 // classes in the codebase that implement that selector.
987 // It's thus more like
988 // a string literal and we shouldn't rename it.
989 {
990 // Input
991 R"cpp(
992 @interface Foo
993 - (void)performA^ction:(int)action with:(int)value;
994 @end
995 @implementation Foo
996 - (void)performAction:(int)action with:(int)value {
997 SEL mySelector = @selector(performAction:with:);
998 }
999 @end
1000 )cpp",
1001 // New name
1002 "performNewAction:by:",
1003 // Expected
1004 R"cpp(
1005 @interface Foo
1006 - (void)performNewAction:(int)action by:(int)value;
1007 @end
1008 @implementation Foo
1009 - (void)performNewAction:(int)action by:(int)value {
1010 SEL mySelector = @selector(performAction:with:);
1011 }
1012 @end
1013 )cpp",
1014 },
1015 // Fail if rename initiated inside @selector
1016 {
1017 // Input
1018 R"cpp(
1019 @interface Foo
1020 - (void)performAction:(int)action with:(int)value;
1021 @end
1022 @implementation Foo
1023 - (void)performAction:(int)action with:(int)value {
1024 SEL mySelector = @selector(perfo^rmAction:with:);
1025 }
1026 @end
1027 )cpp",
1028 // New name
1029 "performNewAction:by:",
1030 // Expected
1031 std::nullopt,
1032 }};
1033 for (TestCase T : Tests) {
1034 SCOPED_TRACE(T.Input);
1035 Annotations Code(T.Input);
1036 auto TU = TestTU::withCode(Code.code());
1037 TU.ExtraArgs.push_back("-xobjective-c");
1038 auto AST = TU.build();
1039 auto Index = TU.index();
1040 for (const auto &RenamePos : Code.points()) {
1041 auto RenameResult =
1042 rename({RenamePos, T.NewName, AST, testPath(TU.Filename),
1043 getVFSFromAST(AST), Index.get()});
1044 if (std::optional<StringRef> Expected = T.Expected) {
1045 ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
1046 ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
1047 EXPECT_EQ(
1048 applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
1049 *Expected);
1050 } else {
1051 ASSERT_FALSE(bool(RenameResult));
1052 consumeError(RenameResult.takeError());
1053 }
1054 }
1055 }
1056}
1057
1058TEST(RenameTest, Renameable) {
1059 struct Case {
1060 const char *Code;
1061 const char* ErrorMessage; // null if no error
1062 bool IsHeaderFile;
1063 llvm::StringRef NewName = "MockName";
1064 };
1065 const bool HeaderFile = true;
1066 Case Cases[] = {
1067 {R"cpp(// allow -- function-local
1068 void f(int [[Lo^cal]]) {
1069 [[Local]] = 2;
1070 }
1071 )cpp",
1072 nullptr, HeaderFile},
1073
1074 {R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
1075 namespace {
1076 class Unin^dexable {};
1077 }
1078 )cpp",
1079 "not eligible for indexing", HeaderFile},
1080
1081 {R"cpp(// disallow -- namespace symbol isn't supported
1082 namespace n^s {}
1083 )cpp",
1084 "not a supported kind", HeaderFile},
1085
1086 {R"cpp(// disallow - category rename.
1087 @interface Foo
1088 @end
1089 @interface Foo (Cate^gory)
1090 @end
1091 )cpp",
1092 "Cannot rename symbol: there is no symbol at the given location",
1093 HeaderFile},
1094
1095 {
1096 R"cpp(
1097 #define MACRO 1
1098 int s = MAC^RO;
1099 )cpp",
1100 "not a supported kind", HeaderFile},
1101
1102 {
1103 R"cpp(
1104 struct X { X operator++(int); };
1105 void f(X x) {x+^+;})cpp",
1106 "no symbol", HeaderFile},
1107
1108 {R"cpp(
1109 @interface Foo {}
1110 - (int)[[fo^o]]:(int)x;
1111 @end
1112 )cpp",
1113 nullptr, HeaderFile, "newName:"},
1114 {R"cpp(//disallow as : count must match
1115 @interface Foo {}
1116 - (int)fo^o:(int)x;
1117 @end
1118 )cpp",
1119 "invalid name: the chosen name \"MockName\" is not a valid identifier",
1120 HeaderFile},
1121 {R"cpp(
1122 @interface Foo {}
1123 - (int)[[o^ne]]:(int)one two:(int)two;
1124 @end
1125 )cpp",
1126 nullptr, HeaderFile, "a:two:"},
1127 {R"cpp(
1128 @interface Foo {}
1129 - (int)[[o^ne]]:(int)one [[two]]:(int)two;
1130 @end
1131 )cpp",
1132 nullptr, HeaderFile, "a:b:"},
1133 {R"cpp(
1134 @interface Foo {}
1135 - (int)o^ne:(int)one [[two]]:(int)two;
1136 @end
1137 )cpp",
1138 nullptr, HeaderFile, "one:three:"},
1139
1140 {R"cpp(
1141 void foo(int);
1142 void foo(char);
1143 template <typename T> void f(T t) {
1144 fo^o(t);
1145 })cpp",
1146 "multiple symbols", !HeaderFile},
1147
1148 {R"cpp(// disallow rename on unrelated token.
1149 cl^ass Foo {};
1150 )cpp",
1151 "no symbol", !HeaderFile},
1152
1153 {R"cpp(// disallow rename on unrelated token.
1154 temp^late<typename T>
1155 class Foo {};
1156 )cpp",
1157 "no symbol", !HeaderFile},
1158
1159 {R"cpp(
1160 namespace {
1161 int Conflict;
1162 int Va^r;
1163 }
1164 )cpp",
1165 "conflict", !HeaderFile, "Conflict"},
1166
1167 {R"cpp(
1168 int Conflict;
1169 int Va^r;
1170 )cpp",
1171 "conflict", !HeaderFile, "Conflict"},
1172
1173 {R"cpp(
1174 class Foo {
1175 int Conflict;
1176 int Va^r;
1177 };
1178 )cpp",
1179 "conflict", !HeaderFile, "Conflict"},
1180
1181 {R"cpp(
1182 enum E {
1183 Conflict,
1184 Fo^o,
1185 };
1186 )cpp",
1187 "conflict", !HeaderFile, "Conflict"},
1188
1189 {R"cpp(
1190 int Conflict;
1191 enum E { // transparent context.
1192 F^oo,
1193 };
1194 )cpp",
1195 "conflict", !HeaderFile, "Conflict"},
1196
1197 {R"cpp(
1198 void func() {
1199 bool Whatever;
1200 int V^ar;
1201 char Conflict;
1202 }
1203 )cpp",
1204 "conflict", !HeaderFile, "Conflict"},
1205
1206 {R"cpp(
1207 void func() {
1208 if (int Conflict = 42) {
1209 int V^ar;
1210 }
1211 }
1212 )cpp",
1213 "conflict", !HeaderFile, "Conflict"},
1214
1215 {R"cpp(
1216 void func() {
1217 if (int Conflict = 42) {
1218 } else {
1219 bool V^ar;
1220 }
1221 }
1222 )cpp",
1223 "conflict", !HeaderFile, "Conflict"},
1224
1225 {R"cpp(
1226 void func() {
1227 if (int V^ar = 42) {
1228 } else {
1229 bool Conflict;
1230 }
1231 }
1232 )cpp",
1233 "conflict", !HeaderFile, "Conflict"},
1234
1235 {R"cpp(
1236 void func() {
1237 while (int V^ar = 10) {
1238 bool Conflict = true;
1239 }
1240 }
1241 )cpp",
1242 "conflict", !HeaderFile, "Conflict"},
1243
1244 {R"cpp(
1245 void func() {
1246 for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
1247 ++Something) {
1248 int V^ar;
1249 }
1250 }
1251 )cpp",
1252 "conflict", !HeaderFile, "Conflict"},
1253
1254 {R"cpp(
1255 void func() {
1256 for (int V^ar = 14, Conflict = 42;;) {
1257 }
1258 }
1259 )cpp",
1260 "conflict", !HeaderFile, "Conflict"},
1261
1262 {R"cpp(
1263 void func(int Conflict) {
1264 bool V^ar;
1265 }
1266 )cpp",
1267 "conflict", !HeaderFile, "Conflict"},
1268
1269 {R"cpp(
1270 void func(int Var);
1271
1272 void func(int V^ar) {
1273 bool Conflict;
1274 }
1275 )cpp",
1276 "conflict", !HeaderFile, "Conflict"},
1277
1278 {R"cpp(// No conflict: only forward declaration's argument is renamed.
1279 void func(int [[V^ar]]);
1280
1281 void func(int Var) {
1282 bool Conflict;
1283 }
1284 )cpp",
1285 nullptr, !HeaderFile, "Conflict"},
1286
1287 {R"cpp(
1288 void func(int V^ar, int Conflict) {
1289 }
1290 )cpp",
1291 "conflict", !HeaderFile, "Conflict"},
1292
1293 {R"cpp(
1294 struct conflict {};
1295 enum v^ar {};
1296 )cpp",
1297 "conflict", !HeaderFile, "conflict"},
1298
1299 {R"cpp(
1300 struct conflict {};
1301 int [[v^ar]];
1302 )cpp",
1303 nullptr, !HeaderFile, "conflict"},
1304
1305 {R"cpp(
1306 enum conflict {};
1307 int [[v^ar]];
1308 )cpp",
1309 nullptr, !HeaderFile, "conflict"},
1310
1311 {R"cpp(
1312 void func(int conflict) {
1313 struct [[t^ag]] {};
1314 }
1315 )cpp",
1316 nullptr, !HeaderFile, "conflict"},
1317
1318 {R"cpp(
1319 void func(void) {
1320 struct conflict {};
1321 int [[v^ar]];
1322 }
1323 )cpp",
1324 nullptr, !HeaderFile, "conflict"},
1325
1326 {R"cpp(
1327 void func(int);
1328 void [[o^therFunc]](double);
1329 )cpp",
1330 nullptr, !HeaderFile, "func"},
1331 {R"cpp(
1332 struct S {
1333 void func(int);
1334 void [[o^therFunc]](double);
1335 };
1336 )cpp",
1337 nullptr, !HeaderFile, "func"},
1338
1339 {R"cpp(
1340 int V^ar;
1341 )cpp",
1342 "\"const\" is a keyword", !HeaderFile, "const"},
1343
1344 {R"cpp(// Trying to rename into the same name, SameName == SameName.
1345 void func() {
1346 int S^ameName;
1347 }
1348 )cpp",
1349 "new name is the same", !HeaderFile, "SameName"},
1350 {R"cpp(// Ensure it doesn't associate base specifier with base name.
1351 struct A {};
1352 struct B : priv^ate A {};
1353 )cpp",
1354 "Cannot rename symbol: there is no symbol at the given location", false},
1355 {R"cpp(// Ensure it doesn't associate base specifier with base name.
1356 /*error-ok*/
1357 struct A {
1358 A() : inva^lid(0) {}
1359 };
1360 )cpp",
1361 "no symbol", false},
1362
1363 {R"cpp(// FIXME we probably want to rename both overloads here,
1364 // but renaming currently assumes there's only a
1365 // single canonical declaration.
1366 namespace ns { int foo(int); char foo(char); }
1367 using ns::^foo;
1368 )cpp",
1369 "there are multiple symbols at the given location", !HeaderFile},
1370
1371 {R"cpp(
1372 void test() {
1373 // no crash
1374 using namespace std;
1375 int [[V^ar]];
1376 }
1377 )cpp",
1378 nullptr, !HeaderFile},
1379 };
1380
1381 for (const auto& Case : Cases) {
1382 SCOPED_TRACE(Case.Code);
1383 Annotations T(Case.Code);
1384 TestTU TU = TestTU::withCode(T.code());
1385 TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
1386 if (Case.IsHeaderFile) {
1387 // We open the .h file as the main file.
1388 TU.Filename = "test.h";
1389 // Parsing the .h file as C++ include.
1390 TU.ExtraArgs.push_back("-xobjective-c++-header");
1391 }
1392 auto AST = TU.build();
1393 llvm::StringRef NewName = Case.NewName;
1394 auto Results = rename({T.point(), NewName, AST, testPath(TU.Filename)});
1395 bool WantRename = true;
1396 if (T.ranges().empty())
1397 WantRename = false;
1398 if (!WantRename) {
1399 assert(Case.ErrorMessage && "Error message must be set!");
1400 EXPECT_FALSE(Results)
1401 << "expected rename returned an error: " << T.code();
1402 auto ActualMessage = llvm::toString(Results.takeError());
1403 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
1404 } else {
1405 EXPECT_TRUE(bool(Results)) << "rename returned an error: "
1406 << llvm::toString(Results.takeError());
1407 EXPECT_EQ(Results->LocalChanges, T.ranges());
1408 }
1409 }
1410}
1411
1412MATCHER_P(newText, T, "") { return arg.newText == T; }
1413
1414TEST(RenameTest, IndexMergeMainFile) {
1415 Annotations Code("int ^x();");
1416 TestTU TU = TestTU::withCode(Code.code());
1417 TU.Filename = "main.cc";
1418 auto AST = TU.build();
1419
1420 auto Main = testPath("main.cc");
1421 auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
1422 InMemFS->addFile(testPath("main.cc"), 0,
1423 llvm::MemoryBuffer::getMemBuffer(Code.code()));
1424 InMemFS->addFile(testPath("other.cc"), 0,
1425 llvm::MemoryBuffer::getMemBuffer(Code.code()));
1426
1427 auto Rename = [&](const SymbolIndex *Idx) {
1428 RenameInputs Inputs{Code.point(),
1429 "xPrime",
1430 AST,
1431 Main,
1432 Idx ? createOverlay(getVFSFromAST(AST), InMemFS)
1433 : nullptr,
1434 Idx,
1435 RenameOptions()};
1436 auto Results = rename(Inputs);
1437 EXPECT_TRUE(bool(Results)) << llvm::toString(Results.takeError());
1438 return std::move(*Results);
1439 };
1440
1441 // We do not expect to see duplicated edits from AST vs index.
1442 auto Results = Rename(TU.index().get());
1443 EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1444 EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1445 ElementsAre(newText("xPrime")));
1446
1447 // Sanity check: we do expect to see index results!
1448 TU.Filename = "other.cc";
1449 Results = Rename(TU.index().get());
1450 EXPECT_THAT(Results.GlobalChanges.keys(),
1451 UnorderedElementsAre(Main, testPath("other.cc")));
1452
1453#ifdef CLANGD_PATH_CASE_INSENSITIVE
1454 // On case-insensitive systems, no duplicates if AST vs index case differs.
1455 // https://github.com/clangd/clangd/issues/665
1456 TU.Filename = "MAIN.CC";
1457 Results = Rename(TU.index().get());
1458 EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1459 EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1460 ElementsAre(newText("xPrime")));
1461#endif
1462}
1463
1464TEST(RenameTest, MainFileReferencesOnly) {
1465 // filter out references not from main file.
1466 llvm::StringRef Test =
1467 R"cpp(
1468 void test() {
1469 int [[fo^o]] = 1;
1470 // rename references not from main file are not included.
1471 #include "foo.inc"
1472 })cpp";
1473
1474 Annotations Code(Test);
1475 auto TU = TestTU::withCode(Code.code());
1476 TU.AdditionalFiles["foo.inc"] = R"cpp(
1477 #define Macro(X) X
1478 &Macro(foo);
1479 &foo;
1480 )cpp";
1481 auto AST = TU.build();
1482 llvm::StringRef NewName = "abcde";
1483
1484 auto RenameResult =
1485 rename({Code.point(), NewName, AST, testPath(TU.Filename)});
1486 ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << Code.point();
1487 ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
1488 EXPECT_EQ(applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
1489 expectedResult(Code, NewName));
1490}
1491
1492TEST(RenameTest, NoRenameOnSymbolsFromSystemHeaders) {
1493 llvm::StringRef Test =
1494 R"cpp(
1495 #include <cstdlib>
1496 #include <system>
1497
1498 SystemSym^bol abc;
1499
1500 void foo() { at^oi("9000"); }
1501 )cpp";
1502
1503 Annotations Code(Test);
1504 auto TU = TestTU::withCode(Code.code());
1505 TU.AdditionalFiles["system"] = R"cpp(
1506 class SystemSymbol {};
1507 )cpp";
1508 TU.AdditionalFiles["cstdlib"] = R"cpp(
1509 int atoi(const char *str);
1510 )cpp";
1511 TU.ExtraArgs = {"-isystem", testRoot()};
1512 auto AST = TU.build();
1513 llvm::StringRef NewName = "abcde";
1514
1515 // Clangd will not allow renaming symbols from the system headers for
1516 // correctness.
1517 for (auto &Point : Code.points()) {
1518 auto Results = rename({Point, NewName, AST, testPath(TU.Filename)});
1519 EXPECT_FALSE(Results) << "expected rename returned an error: "
1520 << Code.code();
1521 auto ActualMessage = llvm::toString(Results.takeError());
1522 EXPECT_THAT(ActualMessage, testing::HasSubstr("not a supported kind"));
1523 }
1524}
1525
1526TEST(RenameTest, ProtobufSymbolIsExcluded) {
1527 Annotations Code("Prot^obuf buf;");
1528 auto TU = TestTU::withCode(Code.code());
1529 TU.HeaderCode =
1530 R"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1531 class Protobuf {};
1532 )cpp";
1533 TU.HeaderFilename = "protobuf.pb.h";
1534 auto AST = TU.build();
1535 auto Results = rename({Code.point(), "newName", AST, testPath(TU.Filename)});
1536 EXPECT_FALSE(Results);
1537 EXPECT_THAT(llvm::toString(Results.takeError()),
1538 testing::HasSubstr("not a supported kind"));
1539}
1540
1541TEST(RenameTest, ExportedSymbols) {
1542 // Symbols whose parent is an ExportDecl must be indexable, or rename rejects
1543 // them as "not eligible for indexing".
1544 const char *Tests[] = {
1545 R"cpp(
1546 export module m;
1547 export int [[f^un]]() { return 32; }
1548 int x = [[fun]]();
1549 )cpp",
1550 R"cpp(
1551 export module m;
1552 export {
1553 struct [[S^truct]] {};
1554 }
1555 [[Struct]] s;
1556 )cpp",
1557 R"cpp(
1558 export module m;
1559 namespace ns {
1560 export int [[V^ar]];
1561 }
1562 int y = ns::[[Var]];
1563 )cpp",
1564 };
1565
1566 for (const char *Test : Tests) {
1567 SCOPED_TRACE(Test);
1568 Annotations Code(Test);
1569 TestTU TU = TestTU::withCode(Code.code());
1570 TU.Filename = "test.cppm";
1571 TU.ExtraArgs = {"-std=c++20", "-xc++-module"};
1572 auto AST = TU.build();
1573 auto Results =
1574 rename({Code.point(), "newName", AST, testPath(TU.Filename)});
1575
1576 ASSERT_TRUE(bool(Results)) << llvm::toString(Results.takeError());
1577 EXPECT_EQ(Results->LocalChanges, Code.ranges());
1578 }
1579}
1580
1581TEST(RenameTest, PrepareRename) {
1582 Annotations FooH("void func();");
1583 Annotations FooCC(R"cpp(
1584 #include "foo.h"
1585 void [[fu^nc]]() {}
1586 )cpp");
1587 std::string FooHPath = testPath("foo.h");
1588 std::string FooCCPath = testPath("foo.cc");
1589 MockFS FS;
1590 FS.Files[FooHPath] = std::string(FooH.code());
1591 FS.Files[FooCCPath] = std::string(FooCC.code());
1592
1593 auto ServerOpts = ClangdServer::optsForTest();
1594 ServerOpts.BuildDynamicSymbolIndex = true;
1595
1596 trace::TestTracer Tracer;
1598 ClangdServer Server(CDB, FS, ServerOpts);
1599 runAddDocument(Server, FooHPath, FooH.code());
1600 runAddDocument(Server, FooCCPath, FooCC.code());
1601
1602 auto Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1603 /*NewName=*/std::nullopt, {});
1604 // Verify that for multi-file rename, we only return main-file occurrences.
1605 ASSERT_TRUE(bool(Results)) << Results.takeError();
1606 // We don't know the result is complete in prepareRename (passing a nullptr
1607 // index internally), so GlobalChanges should be empty.
1608 EXPECT_TRUE(Results->GlobalChanges.empty());
1609 EXPECT_THAT(FooCC.ranges(),
1610 testing::UnorderedElementsAreArray(Results->LocalChanges));
1611
1612 // Name validation.
1613 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1614 /*NewName=*/std::string("int"), {});
1615 EXPECT_FALSE(Results);
1616 EXPECT_THAT(llvm::toString(Results.takeError()),
1617 testing::HasSubstr("keyword"));
1618 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "Keywords"),
1619 ElementsAre(1));
1620
1621 for (std::string BadIdent : {"foo!bar", "123foo", "😀@"}) {
1622 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1623 /*NewName=*/BadIdent, {});
1624 EXPECT_FALSE(Results);
1625 EXPECT_THAT(llvm::toString(Results.takeError()),
1626 testing::HasSubstr("identifier"));
1627 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "BadIdentifier"),
1628 ElementsAre(1));
1629 }
1630 for (std::string GoodIdent : {"fooBar", "__foo$", "😀"}) {
1631 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1632 /*NewName=*/GoodIdent, {});
1633 EXPECT_TRUE(bool(Results));
1634 }
1635}
1636
1637TEST(CrossFileRenameTests, DirtyBuffer) {
1638 Annotations FooCode("class [[Foo]] {};");
1639 std::string FooPath = testPath("foo.cc");
1640 Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1641 Annotations BarCode("void [[Bar]]() {}");
1642 std::string BarPath = testPath("bar.cc");
1643 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1644 // references from bar.cc.
1645 FileSymbols FSymbols(IndexContents::All, true);
1646 FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
1647 nullptr, false);
1648 FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
1649 nullptr, false);
1650 auto Index = FSymbols.buildIndex(IndexType::Light);
1651
1652 Annotations MainCode("class [[Fo^o]] {};");
1653 auto MainFilePath = testPath("main.cc");
1654 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
1655 new llvm::vfs::InMemoryFileSystem;
1656 InMemFS->addFile(FooPath, 0,
1657 llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer.code()));
1658
1659 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1660 // respect the dirty buffer.
1661 TestTU TU = TestTU::withCode(MainCode.code());
1662 auto AST = TU.build();
1663 llvm::StringRef NewName = "newName";
1664 auto Results =
1665 rename({MainCode.point(), NewName, AST, MainFilePath,
1666 createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
1667 ASSERT_TRUE(bool(Results)) << Results.takeError();
1668 EXPECT_THAT(
1669 applyEdits(std::move(Results->GlobalChanges)),
1670 UnorderedElementsAre(
1671 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
1672 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1673
1674 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1675 // so we should read file content from VFS.
1676 MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
1677 TU = TestTU::withCode(MainCode.code());
1678 // Set a file "bar.cc" on disk.
1679 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1680 AST = TU.build();
1681 Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1682 createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
1683 ASSERT_TRUE(bool(Results)) << Results.takeError();
1684 EXPECT_THAT(
1685 applyEdits(std::move(Results->GlobalChanges)),
1686 UnorderedElementsAre(
1687 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1688 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1689
1690 // Run rename on a pagination index which couldn't return all refs in one
1691 // request, we reject rename on this case.
1692 class PaginationIndex : public SymbolIndex {
1693 bool refs(const RefsRequest &Req,
1694 llvm::function_ref<void(const Ref &)> Callback) const override {
1695 return true; // has more references
1696 }
1697
1698 bool containedRefs(const ContainedRefsRequest &Req,
1699 llvm::function_ref<void(const ContainedRefsResult &)>
1700 Callback) const override {
1701 return false;
1702 }
1703
1704 bool fuzzyFind(
1705 const FuzzyFindRequest &Req,
1706 llvm::function_ref<void(const Symbol &)> Callback) const override {
1707 return false;
1708 }
1709 void
1710 lookup(const LookupRequest &Req,
1711 llvm::function_ref<void(const Symbol &)> Callback) const override {}
1712
1713 void relations(const RelationsRequest &Req,
1714 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1715 Callback) const override {}
1716
1717 void
1718 reverseRelations(const RelationsRequest &Req,
1719 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1720 Callback) const override {}
1721
1722 llvm::unique_function<IndexContents(llvm::StringRef) const>
1723 indexedFiles() const override {
1724 return [](llvm::StringRef) { return IndexContents::None; };
1725 }
1726
1727 size_t estimateMemoryUsage() const override { return 0; }
1728 } PIndex;
1729 Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1730 createOverlay(getVFSFromAST(AST), InMemFS), &PIndex});
1731 EXPECT_FALSE(Results);
1732 EXPECT_THAT(llvm::toString(Results.takeError()),
1733 testing::HasSubstr("too many occurrences"));
1734}
1735
1736TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1737 auto MainCode = Annotations("int [[^x]] = 2;");
1738 auto MainFilePath = testPath("main.cc");
1739 auto BarCode = Annotations("int [[x]];");
1740 auto BarPath = testPath("bar.cc");
1741 auto TU = TestTU::withCode(MainCode.code());
1742 // Set a file "bar.cc" on disk.
1743 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1744 auto AST = TU.build();
1745 std::string BarPathURI = URI::create(BarPath).toString();
1746 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
1747 // The index will return duplicated refs, our code should be robost to handle
1748 // it.
1749 class DuplicatedXRefIndex : public SymbolIndex {
1750 public:
1751 DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1752 bool refs(const RefsRequest &Req,
1753 llvm::function_ref<void(const Ref &)> Callback) const override {
1754 // Return two duplicated refs.
1755 Callback(ReturnedRef);
1756 Callback(ReturnedRef);
1757 return false;
1758 }
1759
1760 bool containedRefs(const ContainedRefsRequest &Req,
1761 llvm::function_ref<void(const ContainedRefsResult &)>
1762 Callback) const override {
1763 return false;
1764 }
1765
1766 bool fuzzyFind(const FuzzyFindRequest &,
1767 llvm::function_ref<void(const Symbol &)>) const override {
1768 return false;
1769 }
1770 void lookup(const LookupRequest &,
1771 llvm::function_ref<void(const Symbol &)>) const override {}
1772
1773 void relations(const RelationsRequest &,
1774 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1775 const override {}
1776
1777 void
1778 reverseRelations(const RelationsRequest &,
1779 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1780 const override {}
1781
1782 llvm::unique_function<IndexContents(llvm::StringRef) const>
1783 indexedFiles() const override {
1784 return [](llvm::StringRef) { return IndexContents::None; };
1785 }
1786
1787 size_t estimateMemoryUsage() const override { return 0; }
1788 Ref ReturnedRef;
1789 } DIndex(XRefInBarCC);
1790 llvm::StringRef NewName = "newName";
1791 auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1792 getVFSFromAST(AST), &DIndex});
1793 ASSERT_TRUE(bool(Results)) << Results.takeError();
1794 EXPECT_THAT(
1795 applyEdits(std::move(Results->GlobalChanges)),
1796 UnorderedElementsAre(
1797 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1798 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1799}
1800
1801TEST(CrossFileRenameTests, WithUpToDateIndex) {
1803 CDB.ExtraClangFlags = {"-xobjective-c++"};
1804 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1805 // expected rename occurrences.
1806 struct Case {
1807 llvm::StringRef FooH;
1808 llvm::StringRef FooCC;
1809 } Cases[] = {
1810 {
1811 // classes.
1812 R"cpp(
1813 class [[Fo^o]] {
1814 [[Foo]]();
1815 ~[[Foo]]();
1816 };
1817 )cpp",
1818 R"cpp(
1819 #include "foo.h"
1820 [[Foo]]::[[Foo]]() {}
1821 [[Foo]]::~[[Foo]]() {}
1822
1823 void func() {
1824 [[Foo]] foo;
1825 }
1826 )cpp",
1827 },
1828 {
1829 // class templates.
1830 R"cpp(
1831 template <typename T>
1832 class [[Foo]] {};
1833 // FIXME: explicit template specializations are not supported due the
1834 // clangd index limitations.
1835 template <>
1836 class Foo<double> {};
1837 )cpp",
1838 R"cpp(
1839 #include "foo.h"
1840 void func() {
1841 [[F^oo]]<int> foo;
1842 }
1843 )cpp",
1844 },
1845 {
1846 // class methods.
1847 R"cpp(
1848 class Foo {
1849 void [[f^oo]]();
1850 };
1851 )cpp",
1852 R"cpp(
1853 #include "foo.h"
1854 void Foo::[[foo]]() {}
1855
1856 void func(Foo* p) {
1857 p->[[foo]]();
1858 }
1859 )cpp",
1860 },
1861 {
1862 // virtual methods.
1863 R"cpp(
1864 class Base {
1865 virtual void [[foo]]();
1866 };
1867 class Derived1 : public Base {
1868 void [[f^oo]]() override;
1869 };
1870 class NotDerived {
1871 void foo() {};
1872 }
1873 )cpp",
1874 R"cpp(
1875 #include "foo.h"
1876 void Base::[[foo]]() {}
1877 void Derived1::[[foo]]() {}
1878
1879 class Derived2 : public Derived1 {
1880 void [[foo]]() override {};
1881 };
1882
1883 void func(Base* b, Derived1* d1,
1884 Derived2* d2, NotDerived* nd) {
1885 b->[[foo]]();
1886 d1->[[foo]]();
1887 d2->[[foo]]();
1888 nd->foo();
1889 }
1890 )cpp",
1891 },
1892 {// virtual templated method
1893 R"cpp(
1894 template <typename> class Foo { virtual void [[m]](); };
1895 class Bar : Foo<int> { void [[^m]]() override; };
1896 )cpp",
1897 R"cpp(
1898 #include "foo.h"
1899
1900 template<typename T> void Foo<T>::[[m]]() {}
1901 // FIXME: not renamed as the index doesn't see this as an override of
1902 // the canonical Foo<T>::m().
1903 // https://github.com/clangd/clangd/issues/1325
1904 class Baz : Foo<float> { void m() override; };
1905 )cpp"},
1906 {
1907 // rename on constructor and destructor.
1908 R"cpp(
1909 class [[Foo]] {
1910 [[^Foo]]();
1911 ~[[Foo^]]();
1912 };
1913 )cpp",
1914 R"cpp(
1915 #include "foo.h"
1916 [[Foo]]::[[Foo]]() {}
1917 [[Foo]]::~[[Foo]]() {}
1918
1919 void func() {
1920 [[Foo]] foo;
1921 }
1922 )cpp",
1923 },
1924 {
1925 // functions.
1926 R"cpp(
1927 void [[f^oo]]();
1928 )cpp",
1929 R"cpp(
1930 #include "foo.h"
1931 void [[foo]]() {}
1932
1933 void func() {
1934 [[foo]]();
1935 }
1936 )cpp",
1937 },
1938 {
1939 // typedefs.
1940 R"cpp(
1941 typedef int [[IN^T]];
1942 [[INT]] foo();
1943 )cpp",
1944 R"cpp(
1945 #include "foo.h"
1946 [[INT]] foo() {}
1947 )cpp",
1948 },
1949 {
1950 // usings.
1951 R"cpp(
1952 using [[I^NT]] = int;
1953 [[INT]] foo();
1954 )cpp",
1955 R"cpp(
1956 #include "foo.h"
1957 [[INT]] foo() {}
1958 )cpp",
1959 },
1960 {
1961 // variables.
1962 R"cpp(
1963 static const int [[VA^R]] = 123;
1964 )cpp",
1965 R"cpp(
1966 #include "foo.h"
1967 int s = [[VAR]];
1968 )cpp",
1969 },
1970 {
1971 // scope enums.
1972 R"cpp(
1973 enum class [[K^ind]] { ABC };
1974 )cpp",
1975 R"cpp(
1976 #include "foo.h"
1977 [[Kind]] ff() {
1978 return [[Kind]]::ABC;
1979 }
1980 )cpp",
1981 },
1982 {
1983 // enum constants.
1984 R"cpp(
1985 enum class Kind { [[A^BC]] };
1986 )cpp",
1987 R"cpp(
1988 #include "foo.h"
1989 Kind ff() {
1990 return Kind::[[ABC]];
1991 }
1992 )cpp",
1993 },
1994 {
1995 // Implicit references in macro expansions.
1996 R"cpp(
1997 class [[Fo^o]] {};
1998 #define FooFoo Foo
1999 #define FOO Foo
2000 )cpp",
2001 R"cpp(
2002 #include "foo.h"
2003 void bar() {
2004 [[Foo]] x;
2005 FOO y;
2006 FooFoo z;
2007 }
2008 )cpp",
2009 },
2010 {
2011 // Objective-C classes.
2012 R"cpp(
2013 @interface [[Fo^o]]
2014 @end
2015 )cpp",
2016 R"cpp(
2017 #include "foo.h"
2018 @implementation [[Foo]]
2019 @end
2020
2021 void func([[Foo]] *f) {}
2022 )cpp",
2023 },
2024 };
2025
2026 trace::TestTracer Tracer;
2027 for (const auto &T : Cases) {
2028 SCOPED_TRACE(T.FooH);
2029 Annotations FooH(T.FooH);
2030 Annotations FooCC(T.FooCC);
2031 std::string FooHPath = testPath("foo.h");
2032 std::string FooCCPath = testPath("foo.cc");
2033
2034 MockFS FS;
2035 FS.Files[FooHPath] = std::string(FooH.code());
2036 FS.Files[FooCCPath] = std::string(FooCC.code());
2037
2038 auto ServerOpts = ClangdServer::optsForTest();
2039 ServerOpts.BuildDynamicSymbolIndex = true;
2040 ClangdServer Server(CDB, FS, ServerOpts);
2041
2042 // Add all files to clangd server to make sure the dynamic index has been
2043 // built.
2044 runAddDocument(Server, FooHPath, FooH.code());
2045 runAddDocument(Server, FooCCPath, FooCC.code());
2046
2047 llvm::StringRef NewName = "NewName";
2048 for (const auto &RenamePos : FooH.points()) {
2049 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
2050 auto FileEditsList =
2051 llvm::cantFail(runRename(Server, FooHPath, RenamePos, NewName, {}));
2052 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
2053 EXPECT_THAT(
2054 applyEdits(std::move(FileEditsList.GlobalChanges)),
2055 UnorderedElementsAre(
2056 Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
2057 Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
2058 }
2059 }
2060}
2061
2062TEST(CrossFileRenameTests, ObjC) {
2064 CDB.ExtraClangFlags = {"-xobjective-c"};
2065 // rename is runnning on all "^" points in FooH.
2066 struct Case {
2067 llvm::StringRef FooH;
2068 llvm::StringRef FooM;
2069 llvm::StringRef NewName;
2070 llvm::StringRef ExpectedFooH;
2071 llvm::StringRef ExpectedFooM;
2072 };
2073 Case Cases[] = {// --- Zero arg selector
2074 {
2075 // Input
2076 R"cpp(
2077 @interface Foo
2078 - (int)performA^ction;
2079 @end
2080 )cpp",
2081 R"cpp(
2082 @implementation Foo
2083 - (int)performAction {
2084 [self performAction];
2085 }
2086 @end
2087 )cpp",
2088 // New name
2089 "performNewAction",
2090 // Expected
2091 R"cpp(
2092 @interface Foo
2093 - (int)performNewAction;
2094 @end
2095 )cpp",
2096 R"cpp(
2097 @implementation Foo
2098 - (int)performNewAction {
2099 [self performNewAction];
2100 }
2101 @end
2102 )cpp",
2103 },
2104 // --- Single arg selector
2105 {
2106 // Input
2107 R"cpp(
2108 @interface Foo
2109 - (int)performA^ction:(int)action;
2110 @end
2111 )cpp",
2112 R"cpp(
2113 @implementation Foo
2114 - (int)performAction:(int)action {
2115 [self performAction:action];
2116 }
2117 @end
2118 )cpp",
2119 // New name
2120 "performNewAction:",
2121 // Expected
2122 R"cpp(
2123 @interface Foo
2124 - (int)performNewAction:(int)action;
2125 @end
2126 )cpp",
2127 R"cpp(
2128 @implementation Foo
2129 - (int)performNewAction:(int)action {
2130 [self performNewAction:action];
2131 }
2132 @end
2133 )cpp",
2134 },
2135 // --- Multi arg selector
2136 {
2137 // Input
2138 R"cpp(
2139 @interface Foo
2140 - (int)performA^ction:(int)action with:(int)value;
2141 @end
2142 )cpp",
2143 R"cpp(
2144 @implementation Foo
2145 - (int)performAction:(int)action with:(int)value {
2146 [self performAction:action with:value];
2147 }
2148 @end
2149 )cpp",
2150 // New name
2151 "performNewAction:by:",
2152 // Expected
2153 R"cpp(
2154 @interface Foo
2155 - (int)performNewAction:(int)action by:(int)value;
2156 @end
2157 )cpp",
2158 R"cpp(
2159 @implementation Foo
2160 - (int)performNewAction:(int)action by:(int)value {
2161 [self performNewAction:action by:value];
2162 }
2163 @end
2164 )cpp",
2165 }};
2166
2167 trace::TestTracer Tracer;
2168 for (const auto &T : Cases) {
2169 SCOPED_TRACE(T.FooH);
2170 Annotations FooH(T.FooH);
2171 Annotations FooM(T.FooM);
2172 std::string FooHPath = testPath("foo.h");
2173 std::string FooMPath = testPath("foo.m");
2174
2175 MockFS FS;
2176 FS.Files[FooHPath] = std::string(FooH.code());
2177 FS.Files[FooMPath] = std::string(FooM.code());
2178
2179 auto ServerOpts = ClangdServer::optsForTest();
2180 ServerOpts.BuildDynamicSymbolIndex = true;
2181 ClangdServer Server(CDB, FS, ServerOpts);
2182
2183 // Add all files to clangd server to make sure the dynamic index has been
2184 // built.
2185 runAddDocument(Server, FooHPath, FooH.code());
2186 runAddDocument(Server, FooMPath, FooM.code());
2187
2188 for (const auto &RenamePos : FooH.points()) {
2189 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
2190 auto FileEditsList =
2191 llvm::cantFail(runRename(Server, FooHPath, RenamePos, T.NewName, {}));
2192 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
2193 EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
2194 UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)),
2195 Pair(Eq(FooMPath), Eq(T.ExpectedFooM))));
2196 }
2197 }
2198}
2199
2200TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2201 // cross-file rename should work for function-local symbols, even there is no
2202 // index provided.
2203 Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
2204 auto TU = TestTU::withCode(Code.code());
2205 auto Path = testPath(TU.Filename);
2206 auto AST = TU.build();
2207 llvm::StringRef NewName = "newName";
2208 auto Results = rename({Code.point(), NewName, AST, Path});
2209 ASSERT_TRUE(bool(Results)) << Results.takeError();
2210 EXPECT_THAT(
2211 applyEdits(std::move(Results->GlobalChanges)),
2212 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
2213}
2214
2215TEST(CrossFileRenameTests, BuildRenameEdits) {
2216 Annotations Code("[[😂]]");
2217 auto LSPRange = Code.range();
2218 llvm::StringRef FilePath = "/test/TestTU.cpp";
2219 llvm::SmallVector<llvm::StringRef, 2> NewNames = {"abc"};
2220 auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
2221 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2222 ASSERT_EQ(1UL, Edit->Replacements.size());
2223 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
2224 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
2225
2226 // Test invalid range.
2227 LSPRange.end = {10, 0}; // out of range
2228 Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
2229 EXPECT_FALSE(Edit);
2230 EXPECT_THAT(llvm::toString(Edit.takeError()),
2231 testing::HasSubstr("fail to convert"));
2232
2233 // Normal ascii characters.
2234 Annotations T(R"cpp(
2235 [[range]]
2236 [[range]]
2237 [[range]]
2238 )cpp");
2239 Edit =
2240 buildRenameEdit(FilePath, T.code(), symbolRanges(T.ranges()), NewNames);
2241 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2242 EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
2243 expectedResult(T, NewNames[0]));
2244}
2245
2246TEST(CrossFileRenameTests, adjustRenameRanges) {
2247 // Ranges in IndexedCode indicate the indexed occurrences;
2248 // ranges in DraftCode indicate the expected mapped result, empty indicates
2249 // we expect no matched result found.
2250 struct {
2251 llvm::StringRef IndexedCode;
2252 llvm::StringRef DraftCode;
2253 } Tests[] = {
2254 {
2255 // both line and column are changed, not a near miss.
2256 R"cpp(
2257 int [[x]] = 0;
2258 )cpp",
2259 R"cpp(
2260 // insert a line.
2261 double x = 0;
2262 )cpp",
2263 },
2264 {
2265 // subset.
2266 R"cpp(
2267 int [[x]] = 0;
2268 )cpp",
2269 R"cpp(
2270 int [[x]] = 0;
2271 {int x = 0; }
2272 )cpp",
2273 },
2274 {
2275 // shift columns.
2276 R"cpp(int [[x]] = 0; void foo(int x);)cpp",
2277 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
2278 },
2279 {
2280 // shift lines.
2281 R"cpp(
2282 int [[x]] = 0;
2283 void foo(int x);
2284 )cpp",
2285 R"cpp(
2286 // insert a line.
2287 int [[x]] = 0;
2288 void foo(int x);
2289 )cpp",
2290 },
2291 };
2292 LangOptions LangOpts;
2293 LangOpts.CPlusPlus = true;
2294 for (const auto &T : Tests) {
2295 SCOPED_TRACE(T.DraftCode);
2296 Annotations Draft(T.DraftCode);
2297 auto ActualRanges = adjustRenameRanges(
2298 Draft.code(), RenameSymbolName(ArrayRef<std::string>{"x"}),
2299 Annotations(T.IndexedCode).ranges(), LangOpts);
2300 if (!ActualRanges)
2301 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
2302 else
2303 EXPECT_THAT(Draft.ranges(),
2304 testing::UnorderedElementsAreArray(*ActualRanges));
2305 }
2306}
2307
2308TEST(RangePatchingHeuristic, GetMappedRanges) {
2309 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
2310 // there are no mapped ranges.
2311 struct {
2312 llvm::StringRef IndexedCode;
2313 llvm::StringRef LexedCode;
2314 } Tests[] = {
2315 {
2316 // no lexed ranges.
2317 "[[]]",
2318 "",
2319 },
2320 {
2321 // both line and column are changed, not a near miss.
2322 R"([[]])",
2323 R"(
2324 [[]]
2325 )",
2326 },
2327 {
2328 // subset.
2329 "[[]]",
2330 "^[[]] [[]]"
2331 },
2332 {
2333 // shift columns.
2334 "[[]] [[]]",
2335 " ^[[]] ^[[]] [[]]"
2336 },
2337 {
2338 R"(
2339 [[]]
2340
2341 [[]] [[]]
2342 )",
2343 R"(
2344 // insert a line
2345 ^[[]]
2346
2347 ^[[]] ^[[]]
2348 )",
2349 },
2350 {
2351 R"(
2352 [[]]
2353
2354 [[]] [[]]
2355 )",
2356 R"(
2357 // insert a line
2358 ^[[]]
2359 ^[[]] ^[[]] // column is shifted.
2360 )",
2361 },
2362 {
2363 R"(
2364 [[]]
2365
2366 [[]] [[]]
2367 )",
2368 R"(
2369 // insert a line
2370 [[]]
2371
2372 [[]] [[]] // not mapped (both line and column are changed).
2373 )",
2374 },
2375 {
2376 R"(
2377 [[]]
2378 [[]]
2379
2380 [[]]
2381 [[]]
2382
2383 }
2384 )",
2385 R"(
2386 // insert a new line
2387 ^[[]]
2388 ^[[]]
2389 [[]] // additional range
2390 ^[[]]
2391 ^[[]]
2392 [[]] // additional range
2393 )",
2394 },
2395 {
2396 // non-distinct result (two best results), not a near miss
2397 R"(
2398 [[]]
2399 [[]]
2400 [[]]
2401 )",
2402 R"(
2403 [[]]
2404 [[]]
2405 [[]]
2406 [[]]
2407 )",
2408 }
2409 };
2410 for (const auto &T : Tests) {
2411 SCOPED_TRACE(T.IndexedCode);
2412 auto Lexed = Annotations(T.LexedCode);
2413 auto LexedRanges = symbolRanges(Lexed.ranges());
2414 std::vector<SymbolRange> ExpectedMatches;
2415 for (auto P : Lexed.points()) {
2416 auto Match = llvm::find_if(LexedRanges, [&P](const SymbolRange &R) {
2417 return R.range().start == P;
2418 });
2419 ASSERT_NE(Match, LexedRanges.end());
2420 ExpectedMatches.push_back(*Match);
2421 }
2422
2423 auto Mapped =
2424 getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
2425 if (!Mapped)
2426 EXPECT_THAT(ExpectedMatches, IsEmpty());
2427 else
2428 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2429 }
2430}
2431
2432TEST(CrossFileRenameTests, adjustmentCost) {
2433 struct {
2434 llvm::StringRef RangeCode;
2435 size_t ExpectedCost;
2436 } Tests[] = {
2437 {
2438 R"(
2439 $idx[[]]$lex[[]] // diff: 0
2440 )",
2441 0,
2442 },
2443 {
2444 R"(
2445 $idx[[]]
2446 $lex[[]] // line diff: +1
2447 $idx[[]]
2448 $lex[[]] // line diff: +1
2449 $idx[[]]
2450 $lex[[]] // line diff: +1
2451
2452 $idx[[]]
2453
2454 $lex[[]] // line diff: +2
2455 )",
2456 1 + 1
2457 },
2458 {
2459 R"(
2460 $idx[[]]
2461 $lex[[]] // line diff: +1
2462 $idx[[]]
2463
2464 $lex[[]] // line diff: +2
2465 $idx[[]]
2466
2467
2468 $lex[[]] // line diff: +3
2469 )",
2470 1 + 1 + 1
2471 },
2472 {
2473 R"(
2474 $idx[[]]
2475
2476
2477 $lex[[]] // line diff: +3
2478 $idx[[]]
2479
2480 $lex[[]] // line diff: +2
2481 $idx[[]]
2482 $lex[[]] // line diff: +1
2483 )",
2484 3 + 1 + 1
2485 },
2486 {
2487 R"(
2488 $idx[[]]
2489 $lex[[]] // line diff: +1
2490 $lex[[]] // line diff: -2
2491
2492 $idx[[]]
2493 $idx[[]]
2494
2495
2496 $lex[[]] // line diff: +3
2497 )",
2498 1 + 3 + 5
2499 },
2500 {
2501 R"(
2502 $idx[[]] $lex[[]] // column diff: +1
2503 $idx[[]]$lex[[]] // diff: 0
2504 )",
2505 1
2506 },
2507 {
2508 R"(
2509 $idx[[]]
2510 $lex[[]] // diff: +1
2511 $idx[[]] $lex[[]] // column diff: +1
2512 $idx[[]]$lex[[]] // diff: 0
2513 )",
2514 1 + 1 + 1
2515 },
2516 {
2517 R"(
2518 $idx[[]] $lex[[]] // column diff: +1
2519 )",
2520 1
2521 },
2522 {
2523 R"(
2524 // column diffs: +1, +2, +3
2525 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2526 )",
2527 1 + 1 + 1,
2528 },
2529 };
2530 for (const auto &T : Tests) {
2531 SCOPED_TRACE(T.RangeCode);
2532 Annotations C(T.RangeCode);
2533 std::vector<size_t> MappedIndex;
2534 for (size_t I = 0; I < C.ranges("lex").size(); ++I)
2535 MappedIndex.push_back(I);
2536 EXPECT_EQ(renameRangeAdjustmentCost(
2537 C.ranges("idx"), symbolRanges(C.ranges("lex")), MappedIndex),
2538 T.ExpectedCost);
2539 }
2540}
2541
2542} // namespace
2543} // namespace clangd
2544} // namespace clang
Same as llvm::Annotations, but adjusts functions to LSP-specific types for positions and ranges.
Definition Annotations.h:23
std::vector< clangd::Range > ranges(llvm::StringRef Name="") const
Manages a collection of source files and derived data (ASTs, indexes), and provides language-aware fe...
A container of slabs associated with a key.
Definition FileIndex.h:70
std::vector< std::string > ExtraClangFlags
Definition TestFS.h:68
llvm::StringMap< std::string > Files
Definition TestFS.h:45
Stores and provides access to parsed AST.
Definition ParsedAST.h:47
RefSlab::Builder is a mutable container that can 'freeze' to RefSlab.
Definition Ref.h:135
A name of a symbol that should be renamed.
Definition Rename.h:47
Interface for symbol indexes that can be used for searching or matching symbols among a set of symbol...
Definition Index.h:134
A URI describes the location of a source file.
Definition URI.h:28
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition URI.cpp:208
A RAII Tracer that can be used by tests.
Definition TestTracer.h:28
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
IndexContents
Describes what data is covered by an index.
Definition Index.h:114
std::optional< std::vector< SymbolRange > > adjustRenameRanges(llvm::StringRef DraftCode, const RenameSymbolName &Name, std::vector< Range > Indexed, const LangOptions &LangOpts)
Adjusts indexed occurrences to match the current state of the file.
Definition Rename.cpp:1285
llvm::Expected< RenameResult > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &RenameOpts)
Definition SyncAPI.cpp:101
llvm::Expected< Edit > buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode, std::vector< SymbolRange > Occurrences, llvm::ArrayRef< llvm::StringRef > NewNames)
Generates rename edits that replaces all given occurrences with the NewName.
Definition Rename.cpp:1202
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition Function.h:28
llvm::Expected< RenameResult > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
Definition Rename.cpp:1076
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition TestFS.cpp:94
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
Definition SourceCode.h:209
TEST(BackgroundQueueTest, Priority)
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, llvm::StringRef Version, WantDiagnostics WantDiags, bool ForceRebuild)
Definition SyncAPI.cpp:17
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition TestTU.cpp:197
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< SymbolRange > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
Definition Rename.cpp:1364
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
std::string Path
A typedef to represent a file path.
Definition Path.h:26
const char * testRoot()
Definition TestFS.cpp:85
llvm::Expected< RenameResult > runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional< std::string > NewName, const RenameOptions &RenameOpts)
Definition SyncAPI.cpp:110
std::optional< std::vector< SymbolRange > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< SymbolRange > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
Definition Rename.cpp:1297
cppcoreguidelines::ProBoundsAvoidUncheckedContainerAccessCheck P
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
A set of edits generated for a single file.
Definition SourceCode.h:189
tooling::Replacements Replacements
Definition SourceCode.h:190
Represents a header file to be include'd.
Definition Headers.h:42
int line
Line position in a document (zero-based).
Definition Protocol.h:159
int character
Character offset on a line in a document (zero-based).
Definition Protocol.h:164
Position start
The range's start position.
Definition Protocol.h:188
Position end
The range's end position.
Definition Protocol.h:191
Represents a symbol occurrence in the source file.
Definition Ref.h:88
RefKind Kind
Definition Ref.h:91
Represents a symbol range where the symbol can potentially have multiple tokens.
Definition Rename.h:112
SymbolID ID
The ID of the symbol.
Definition Symbol.h:51
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36
std::string HeaderCode
Definition TestTU.h:53