clang-tools 22.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, PrepareRename) {
1542 Annotations FooH("void func();");
1543 Annotations FooCC(R"cpp(
1544 #include "foo.h"
1545 void [[fu^nc]]() {}
1546 )cpp");
1547 std::string FooHPath = testPath("foo.h");
1548 std::string FooCCPath = testPath("foo.cc");
1549 MockFS FS;
1550 FS.Files[FooHPath] = std::string(FooH.code());
1551 FS.Files[FooCCPath] = std::string(FooCC.code());
1552
1553 auto ServerOpts = ClangdServer::optsForTest();
1554 ServerOpts.BuildDynamicSymbolIndex = true;
1555
1556 trace::TestTracer Tracer;
1558 ClangdServer Server(CDB, FS, ServerOpts);
1559 runAddDocument(Server, FooHPath, FooH.code());
1560 runAddDocument(Server, FooCCPath, FooCC.code());
1561
1562 auto Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1563 /*NewName=*/std::nullopt, {});
1564 // Verify that for multi-file rename, we only return main-file occurrences.
1565 ASSERT_TRUE(bool(Results)) << Results.takeError();
1566 // We don't know the result is complete in prepareRename (passing a nullptr
1567 // index internally), so GlobalChanges should be empty.
1568 EXPECT_TRUE(Results->GlobalChanges.empty());
1569 EXPECT_THAT(FooCC.ranges(),
1570 testing::UnorderedElementsAreArray(Results->LocalChanges));
1571
1572 // Name validation.
1573 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1574 /*NewName=*/std::string("int"), {});
1575 EXPECT_FALSE(Results);
1576 EXPECT_THAT(llvm::toString(Results.takeError()),
1577 testing::HasSubstr("keyword"));
1578 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "Keywords"),
1579 ElementsAre(1));
1580
1581 for (std::string BadIdent : {"foo!bar", "123foo", "😀@"}) {
1582 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1583 /*NewName=*/BadIdent, {});
1584 EXPECT_FALSE(Results);
1585 EXPECT_THAT(llvm::toString(Results.takeError()),
1586 testing::HasSubstr("identifier"));
1587 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "BadIdentifier"),
1588 ElementsAre(1));
1589 }
1590 for (std::string GoodIdent : {"fooBar", "__foo$", "😀"}) {
1591 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1592 /*NewName=*/GoodIdent, {});
1593 EXPECT_TRUE(bool(Results));
1594 }
1595}
1596
1597TEST(CrossFileRenameTests, DirtyBuffer) {
1598 Annotations FooCode("class [[Foo]] {};");
1599 std::string FooPath = testPath("foo.cc");
1600 Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1601 Annotations BarCode("void [[Bar]]() {}");
1602 std::string BarPath = testPath("bar.cc");
1603 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1604 // references from bar.cc.
1605 FileSymbols FSymbols(IndexContents::All, true);
1606 FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
1607 nullptr, false);
1608 FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
1609 nullptr, false);
1610 auto Index = FSymbols.buildIndex(IndexType::Light);
1611
1612 Annotations MainCode("class [[Fo^o]] {};");
1613 auto MainFilePath = testPath("main.cc");
1614 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
1615 new llvm::vfs::InMemoryFileSystem;
1616 InMemFS->addFile(FooPath, 0,
1617 llvm::MemoryBuffer::getMemBuffer(FooDirtyBuffer.code()));
1618
1619 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1620 // respect the dirty buffer.
1621 TestTU TU = TestTU::withCode(MainCode.code());
1622 auto AST = TU.build();
1623 llvm::StringRef NewName = "newName";
1624 auto Results =
1625 rename({MainCode.point(), NewName, AST, MainFilePath,
1626 createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
1627 ASSERT_TRUE(bool(Results)) << Results.takeError();
1628 EXPECT_THAT(
1629 applyEdits(std::move(Results->GlobalChanges)),
1630 UnorderedElementsAre(
1631 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
1632 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1633
1634 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1635 // so we should read file content from VFS.
1636 MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
1637 TU = TestTU::withCode(MainCode.code());
1638 // Set a file "bar.cc" on disk.
1639 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1640 AST = TU.build();
1641 Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1642 createOverlay(getVFSFromAST(AST), InMemFS), Index.get()});
1643 ASSERT_TRUE(bool(Results)) << Results.takeError();
1644 EXPECT_THAT(
1645 applyEdits(std::move(Results->GlobalChanges)),
1646 UnorderedElementsAre(
1647 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1648 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1649
1650 // Run rename on a pagination index which couldn't return all refs in one
1651 // request, we reject rename on this case.
1652 class PaginationIndex : public SymbolIndex {
1653 bool refs(const RefsRequest &Req,
1654 llvm::function_ref<void(const Ref &)> Callback) const override {
1655 return true; // has more references
1656 }
1657
1658 bool containedRefs(const ContainedRefsRequest &Req,
1659 llvm::function_ref<void(const ContainedRefsResult &)>
1660 Callback) const override {
1661 return false;
1662 }
1663
1664 bool fuzzyFind(
1665 const FuzzyFindRequest &Req,
1666 llvm::function_ref<void(const Symbol &)> Callback) const override {
1667 return false;
1668 }
1669 void
1670 lookup(const LookupRequest &Req,
1671 llvm::function_ref<void(const Symbol &)> Callback) const override {}
1672
1673 void relations(const RelationsRequest &Req,
1674 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1675 Callback) const override {}
1676
1677 llvm::unique_function<IndexContents(llvm::StringRef) const>
1678 indexedFiles() const override {
1679 return [](llvm::StringRef) { return IndexContents::None; };
1680 }
1681
1682 size_t estimateMemoryUsage() const override { return 0; }
1683 } PIndex;
1684 Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1685 createOverlay(getVFSFromAST(AST), InMemFS), &PIndex});
1686 EXPECT_FALSE(Results);
1687 EXPECT_THAT(llvm::toString(Results.takeError()),
1688 testing::HasSubstr("too many occurrences"));
1689}
1690
1691TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1692 auto MainCode = Annotations("int [[^x]] = 2;");
1693 auto MainFilePath = testPath("main.cc");
1694 auto BarCode = Annotations("int [[x]];");
1695 auto BarPath = testPath("bar.cc");
1696 auto TU = TestTU::withCode(MainCode.code());
1697 // Set a file "bar.cc" on disk.
1698 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1699 auto AST = TU.build();
1700 std::string BarPathURI = URI::create(BarPath).toString();
1701 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
1702 // The index will return duplicated refs, our code should be robost to handle
1703 // it.
1704 class DuplicatedXRefIndex : public SymbolIndex {
1705 public:
1706 DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1707 bool refs(const RefsRequest &Req,
1708 llvm::function_ref<void(const Ref &)> Callback) const override {
1709 // Return two duplicated refs.
1710 Callback(ReturnedRef);
1711 Callback(ReturnedRef);
1712 return false;
1713 }
1714
1715 bool containedRefs(const ContainedRefsRequest &Req,
1716 llvm::function_ref<void(const ContainedRefsResult &)>
1717 Callback) const override {
1718 return false;
1719 }
1720
1721 bool fuzzyFind(const FuzzyFindRequest &,
1722 llvm::function_ref<void(const Symbol &)>) const override {
1723 return false;
1724 }
1725 void lookup(const LookupRequest &,
1726 llvm::function_ref<void(const Symbol &)>) const override {}
1727
1728 void relations(const RelationsRequest &,
1729 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1730 const override {}
1731
1732 llvm::unique_function<IndexContents(llvm::StringRef) const>
1733 indexedFiles() const override {
1734 return [](llvm::StringRef) { return IndexContents::None; };
1735 }
1736
1737 size_t estimateMemoryUsage() const override { return 0; }
1738 Ref ReturnedRef;
1739 } DIndex(XRefInBarCC);
1740 llvm::StringRef NewName = "newName";
1741 auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1742 getVFSFromAST(AST), &DIndex});
1743 ASSERT_TRUE(bool(Results)) << Results.takeError();
1744 EXPECT_THAT(
1745 applyEdits(std::move(Results->GlobalChanges)),
1746 UnorderedElementsAre(
1747 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1748 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1749}
1750
1751TEST(CrossFileRenameTests, WithUpToDateIndex) {
1753 CDB.ExtraClangFlags = {"-xobjective-c++"};
1754 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1755 // expected rename occurrences.
1756 struct Case {
1757 llvm::StringRef FooH;
1758 llvm::StringRef FooCC;
1759 } Cases[] = {
1760 {
1761 // classes.
1762 R"cpp(
1763 class [[Fo^o]] {
1764 [[Foo]]();
1765 ~[[Foo]]();
1766 };
1767 )cpp",
1768 R"cpp(
1769 #include "foo.h"
1770 [[Foo]]::[[Foo]]() {}
1771 [[Foo]]::~[[Foo]]() {}
1772
1773 void func() {
1774 [[Foo]] foo;
1775 }
1776 )cpp",
1777 },
1778 {
1779 // class templates.
1780 R"cpp(
1781 template <typename T>
1782 class [[Foo]] {};
1783 // FIXME: explicit template specializations are not supported due the
1784 // clangd index limitations.
1785 template <>
1786 class Foo<double> {};
1787 )cpp",
1788 R"cpp(
1789 #include "foo.h"
1790 void func() {
1791 [[F^oo]]<int> foo;
1792 }
1793 )cpp",
1794 },
1795 {
1796 // class methods.
1797 R"cpp(
1798 class Foo {
1799 void [[f^oo]]();
1800 };
1801 )cpp",
1802 R"cpp(
1803 #include "foo.h"
1804 void Foo::[[foo]]() {}
1805
1806 void func(Foo* p) {
1807 p->[[foo]]();
1808 }
1809 )cpp",
1810 },
1811 {
1812 // virtual methods.
1813 R"cpp(
1814 class Base {
1815 virtual void [[foo]]();
1816 };
1817 class Derived1 : public Base {
1818 void [[f^oo]]() override;
1819 };
1820 class NotDerived {
1821 void foo() {};
1822 }
1823 )cpp",
1824 R"cpp(
1825 #include "foo.h"
1826 void Base::[[foo]]() {}
1827 void Derived1::[[foo]]() {}
1828
1829 class Derived2 : public Derived1 {
1830 void [[foo]]() override {};
1831 };
1832
1833 void func(Base* b, Derived1* d1,
1834 Derived2* d2, NotDerived* nd) {
1835 b->[[foo]]();
1836 d1->[[foo]]();
1837 d2->[[foo]]();
1838 nd->foo();
1839 }
1840 )cpp",
1841 },
1842 {// virtual templated method
1843 R"cpp(
1844 template <typename> class Foo { virtual void [[m]](); };
1845 class Bar : Foo<int> { void [[^m]]() override; };
1846 )cpp",
1847 R"cpp(
1848 #include "foo.h"
1849
1850 template<typename T> void Foo<T>::[[m]]() {}
1851 // FIXME: not renamed as the index doesn't see this as an override of
1852 // the canonical Foo<T>::m().
1853 // https://github.com/clangd/clangd/issues/1325
1854 class Baz : Foo<float> { void m() override; };
1855 )cpp"},
1856 {
1857 // rename on constructor and destructor.
1858 R"cpp(
1859 class [[Foo]] {
1860 [[^Foo]]();
1861 ~[[Foo^]]();
1862 };
1863 )cpp",
1864 R"cpp(
1865 #include "foo.h"
1866 [[Foo]]::[[Foo]]() {}
1867 [[Foo]]::~[[Foo]]() {}
1868
1869 void func() {
1870 [[Foo]] foo;
1871 }
1872 )cpp",
1873 },
1874 {
1875 // functions.
1876 R"cpp(
1877 void [[f^oo]]();
1878 )cpp",
1879 R"cpp(
1880 #include "foo.h"
1881 void [[foo]]() {}
1882
1883 void func() {
1884 [[foo]]();
1885 }
1886 )cpp",
1887 },
1888 {
1889 // typedefs.
1890 R"cpp(
1891 typedef int [[IN^T]];
1892 [[INT]] foo();
1893 )cpp",
1894 R"cpp(
1895 #include "foo.h"
1896 [[INT]] foo() {}
1897 )cpp",
1898 },
1899 {
1900 // usings.
1901 R"cpp(
1902 using [[I^NT]] = int;
1903 [[INT]] foo();
1904 )cpp",
1905 R"cpp(
1906 #include "foo.h"
1907 [[INT]] foo() {}
1908 )cpp",
1909 },
1910 {
1911 // variables.
1912 R"cpp(
1913 static const int [[VA^R]] = 123;
1914 )cpp",
1915 R"cpp(
1916 #include "foo.h"
1917 int s = [[VAR]];
1918 )cpp",
1919 },
1920 {
1921 // scope enums.
1922 R"cpp(
1923 enum class [[K^ind]] { ABC };
1924 )cpp",
1925 R"cpp(
1926 #include "foo.h"
1927 [[Kind]] ff() {
1928 return [[Kind]]::ABC;
1929 }
1930 )cpp",
1931 },
1932 {
1933 // enum constants.
1934 R"cpp(
1935 enum class Kind { [[A^BC]] };
1936 )cpp",
1937 R"cpp(
1938 #include "foo.h"
1939 Kind ff() {
1940 return Kind::[[ABC]];
1941 }
1942 )cpp",
1943 },
1944 {
1945 // Implicit references in macro expansions.
1946 R"cpp(
1947 class [[Fo^o]] {};
1948 #define FooFoo Foo
1949 #define FOO Foo
1950 )cpp",
1951 R"cpp(
1952 #include "foo.h"
1953 void bar() {
1954 [[Foo]] x;
1955 FOO y;
1956 FooFoo z;
1957 }
1958 )cpp",
1959 },
1960 {
1961 // Objective-C classes.
1962 R"cpp(
1963 @interface [[Fo^o]]
1964 @end
1965 )cpp",
1966 R"cpp(
1967 #include "foo.h"
1968 @implementation [[Foo]]
1969 @end
1970
1971 void func([[Foo]] *f) {}
1972 )cpp",
1973 },
1974 };
1975
1976 trace::TestTracer Tracer;
1977 for (const auto &T : Cases) {
1978 SCOPED_TRACE(T.FooH);
1979 Annotations FooH(T.FooH);
1980 Annotations FooCC(T.FooCC);
1981 std::string FooHPath = testPath("foo.h");
1982 std::string FooCCPath = testPath("foo.cc");
1983
1984 MockFS FS;
1985 FS.Files[FooHPath] = std::string(FooH.code());
1986 FS.Files[FooCCPath] = std::string(FooCC.code());
1987
1988 auto ServerOpts = ClangdServer::optsForTest();
1989 ServerOpts.BuildDynamicSymbolIndex = true;
1990 ClangdServer Server(CDB, FS, ServerOpts);
1991
1992 // Add all files to clangd server to make sure the dynamic index has been
1993 // built.
1994 runAddDocument(Server, FooHPath, FooH.code());
1995 runAddDocument(Server, FooCCPath, FooCC.code());
1996
1997 llvm::StringRef NewName = "NewName";
1998 for (const auto &RenamePos : FooH.points()) {
1999 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
2000 auto FileEditsList =
2001 llvm::cantFail(runRename(Server, FooHPath, RenamePos, NewName, {}));
2002 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
2003 EXPECT_THAT(
2004 applyEdits(std::move(FileEditsList.GlobalChanges)),
2005 UnorderedElementsAre(
2006 Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
2007 Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
2008 }
2009 }
2010}
2011
2012TEST(CrossFileRenameTests, ObjC) {
2014 CDB.ExtraClangFlags = {"-xobjective-c"};
2015 // rename is runnning on all "^" points in FooH.
2016 struct Case {
2017 llvm::StringRef FooH;
2018 llvm::StringRef FooM;
2019 llvm::StringRef NewName;
2020 llvm::StringRef ExpectedFooH;
2021 llvm::StringRef ExpectedFooM;
2022 };
2023 Case Cases[] = {// --- Zero arg selector
2024 {
2025 // Input
2026 R"cpp(
2027 @interface Foo
2028 - (int)performA^ction;
2029 @end
2030 )cpp",
2031 R"cpp(
2032 @implementation Foo
2033 - (int)performAction {
2034 [self performAction];
2035 }
2036 @end
2037 )cpp",
2038 // New name
2039 "performNewAction",
2040 // Expected
2041 R"cpp(
2042 @interface Foo
2043 - (int)performNewAction;
2044 @end
2045 )cpp",
2046 R"cpp(
2047 @implementation Foo
2048 - (int)performNewAction {
2049 [self performNewAction];
2050 }
2051 @end
2052 )cpp",
2053 },
2054 // --- Single arg selector
2055 {
2056 // Input
2057 R"cpp(
2058 @interface Foo
2059 - (int)performA^ction:(int)action;
2060 @end
2061 )cpp",
2062 R"cpp(
2063 @implementation Foo
2064 - (int)performAction:(int)action {
2065 [self performAction:action];
2066 }
2067 @end
2068 )cpp",
2069 // New name
2070 "performNewAction:",
2071 // Expected
2072 R"cpp(
2073 @interface Foo
2074 - (int)performNewAction:(int)action;
2075 @end
2076 )cpp",
2077 R"cpp(
2078 @implementation Foo
2079 - (int)performNewAction:(int)action {
2080 [self performNewAction:action];
2081 }
2082 @end
2083 )cpp",
2084 },
2085 // --- Multi arg selector
2086 {
2087 // Input
2088 R"cpp(
2089 @interface Foo
2090 - (int)performA^ction:(int)action with:(int)value;
2091 @end
2092 )cpp",
2093 R"cpp(
2094 @implementation Foo
2095 - (int)performAction:(int)action with:(int)value {
2096 [self performAction:action with:value];
2097 }
2098 @end
2099 )cpp",
2100 // New name
2101 "performNewAction:by:",
2102 // Expected
2103 R"cpp(
2104 @interface Foo
2105 - (int)performNewAction:(int)action by:(int)value;
2106 @end
2107 )cpp",
2108 R"cpp(
2109 @implementation Foo
2110 - (int)performNewAction:(int)action by:(int)value {
2111 [self performNewAction:action by:value];
2112 }
2113 @end
2114 )cpp",
2115 }};
2116
2117 trace::TestTracer Tracer;
2118 for (const auto &T : Cases) {
2119 SCOPED_TRACE(T.FooH);
2120 Annotations FooH(T.FooH);
2121 Annotations FooM(T.FooM);
2122 std::string FooHPath = testPath("foo.h");
2123 std::string FooMPath = testPath("foo.m");
2124
2125 MockFS FS;
2126 FS.Files[FooHPath] = std::string(FooH.code());
2127 FS.Files[FooMPath] = std::string(FooM.code());
2128
2129 auto ServerOpts = ClangdServer::optsForTest();
2130 ServerOpts.BuildDynamicSymbolIndex = true;
2131 ClangdServer Server(CDB, FS, ServerOpts);
2132
2133 // Add all files to clangd server to make sure the dynamic index has been
2134 // built.
2135 runAddDocument(Server, FooHPath, FooH.code());
2136 runAddDocument(Server, FooMPath, FooM.code());
2137
2138 for (const auto &RenamePos : FooH.points()) {
2139 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
2140 auto FileEditsList =
2141 llvm::cantFail(runRename(Server, FooHPath, RenamePos, T.NewName, {}));
2142 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
2143 EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
2144 UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)),
2145 Pair(Eq(FooMPath), Eq(T.ExpectedFooM))));
2146 }
2147 }
2148}
2149
2150TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2151 // cross-file rename should work for function-local symbols, even there is no
2152 // index provided.
2153 Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
2154 auto TU = TestTU::withCode(Code.code());
2155 auto Path = testPath(TU.Filename);
2156 auto AST = TU.build();
2157 llvm::StringRef NewName = "newName";
2158 auto Results = rename({Code.point(), NewName, AST, Path});
2159 ASSERT_TRUE(bool(Results)) << Results.takeError();
2160 EXPECT_THAT(
2161 applyEdits(std::move(Results->GlobalChanges)),
2162 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
2163}
2164
2165TEST(CrossFileRenameTests, BuildRenameEdits) {
2166 Annotations Code("[[😂]]");
2167 auto LSPRange = Code.range();
2168 llvm::StringRef FilePath = "/test/TestTU.cpp";
2169 llvm::SmallVector<llvm::StringRef, 2> NewNames = {"abc"};
2170 auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
2171 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2172 ASSERT_EQ(1UL, Edit->Replacements.size());
2173 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
2174 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
2175
2176 // Test invalid range.
2177 LSPRange.end = {10, 0}; // out of range
2178 Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
2179 EXPECT_FALSE(Edit);
2180 EXPECT_THAT(llvm::toString(Edit.takeError()),
2181 testing::HasSubstr("fail to convert"));
2182
2183 // Normal ascii characters.
2184 Annotations T(R"cpp(
2185 [[range]]
2186 [[range]]
2187 [[range]]
2188 )cpp");
2189 Edit =
2190 buildRenameEdit(FilePath, T.code(), symbolRanges(T.ranges()), NewNames);
2191 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2192 EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
2193 expectedResult(T, NewNames[0]));
2194}
2195
2196TEST(CrossFileRenameTests, adjustRenameRanges) {
2197 // Ranges in IndexedCode indicate the indexed occurrences;
2198 // ranges in DraftCode indicate the expected mapped result, empty indicates
2199 // we expect no matched result found.
2200 struct {
2201 llvm::StringRef IndexedCode;
2202 llvm::StringRef DraftCode;
2203 } Tests[] = {
2204 {
2205 // both line and column are changed, not a near miss.
2206 R"cpp(
2207 int [[x]] = 0;
2208 )cpp",
2209 R"cpp(
2210 // insert a line.
2211 double x = 0;
2212 )cpp",
2213 },
2214 {
2215 // subset.
2216 R"cpp(
2217 int [[x]] = 0;
2218 )cpp",
2219 R"cpp(
2220 int [[x]] = 0;
2221 {int x = 0; }
2222 )cpp",
2223 },
2224 {
2225 // shift columns.
2226 R"cpp(int [[x]] = 0; void foo(int x);)cpp",
2227 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
2228 },
2229 {
2230 // shift lines.
2231 R"cpp(
2232 int [[x]] = 0;
2233 void foo(int x);
2234 )cpp",
2235 R"cpp(
2236 // insert a line.
2237 int [[x]] = 0;
2238 void foo(int x);
2239 )cpp",
2240 },
2241 };
2242 LangOptions LangOpts;
2243 LangOpts.CPlusPlus = true;
2244 for (const auto &T : Tests) {
2245 SCOPED_TRACE(T.DraftCode);
2246 Annotations Draft(T.DraftCode);
2247 auto ActualRanges = adjustRenameRanges(
2248 Draft.code(), RenameSymbolName(ArrayRef<std::string>{"x"}),
2249 Annotations(T.IndexedCode).ranges(), LangOpts);
2250 if (!ActualRanges)
2251 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
2252 else
2253 EXPECT_THAT(Draft.ranges(),
2254 testing::UnorderedElementsAreArray(*ActualRanges));
2255 }
2256}
2257
2258TEST(RangePatchingHeuristic, GetMappedRanges) {
2259 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
2260 // there are no mapped ranges.
2261 struct {
2262 llvm::StringRef IndexedCode;
2263 llvm::StringRef LexedCode;
2264 } Tests[] = {
2265 {
2266 // no lexed ranges.
2267 "[[]]",
2268 "",
2269 },
2270 {
2271 // both line and column are changed, not a near miss.
2272 R"([[]])",
2273 R"(
2274 [[]]
2275 )",
2276 },
2277 {
2278 // subset.
2279 "[[]]",
2280 "^[[]] [[]]"
2281 },
2282 {
2283 // shift columns.
2284 "[[]] [[]]",
2285 " ^[[]] ^[[]] [[]]"
2286 },
2287 {
2288 R"(
2289 [[]]
2290
2291 [[]] [[]]
2292 )",
2293 R"(
2294 // insert a line
2295 ^[[]]
2296
2297 ^[[]] ^[[]]
2298 )",
2299 },
2300 {
2301 R"(
2302 [[]]
2303
2304 [[]] [[]]
2305 )",
2306 R"(
2307 // insert a line
2308 ^[[]]
2309 ^[[]] ^[[]] // column is shifted.
2310 )",
2311 },
2312 {
2313 R"(
2314 [[]]
2315
2316 [[]] [[]]
2317 )",
2318 R"(
2319 // insert a line
2320 [[]]
2321
2322 [[]] [[]] // not mapped (both line and column are changed).
2323 )",
2324 },
2325 {
2326 R"(
2327 [[]]
2328 [[]]
2329
2330 [[]]
2331 [[]]
2332
2333 }
2334 )",
2335 R"(
2336 // insert a new line
2337 ^[[]]
2338 ^[[]]
2339 [[]] // additional range
2340 ^[[]]
2341 ^[[]]
2342 [[]] // additional range
2343 )",
2344 },
2345 {
2346 // non-distinct result (two best results), not a near miss
2347 R"(
2348 [[]]
2349 [[]]
2350 [[]]
2351 )",
2352 R"(
2353 [[]]
2354 [[]]
2355 [[]]
2356 [[]]
2357 )",
2358 }
2359 };
2360 for (const auto &T : Tests) {
2361 SCOPED_TRACE(T.IndexedCode);
2362 auto Lexed = Annotations(T.LexedCode);
2363 auto LexedRanges = symbolRanges(Lexed.ranges());
2364 std::vector<SymbolRange> ExpectedMatches;
2365 for (auto P : Lexed.points()) {
2366 auto Match = llvm::find_if(LexedRanges, [&P](const SymbolRange &R) {
2367 return R.range().start == P;
2368 });
2369 ASSERT_NE(Match, LexedRanges.end());
2370 ExpectedMatches.push_back(*Match);
2371 }
2372
2373 auto Mapped =
2374 getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
2375 if (!Mapped)
2376 EXPECT_THAT(ExpectedMatches, IsEmpty());
2377 else
2378 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2379 }
2380}
2381
2382TEST(CrossFileRenameTests, adjustmentCost) {
2383 struct {
2384 llvm::StringRef RangeCode;
2385 size_t ExpectedCost;
2386 } Tests[] = {
2387 {
2388 R"(
2389 $idx[[]]$lex[[]] // diff: 0
2390 )",
2391 0,
2392 },
2393 {
2394 R"(
2395 $idx[[]]
2396 $lex[[]] // line diff: +1
2397 $idx[[]]
2398 $lex[[]] // line diff: +1
2399 $idx[[]]
2400 $lex[[]] // line diff: +1
2401
2402 $idx[[]]
2403
2404 $lex[[]] // line diff: +2
2405 )",
2406 1 + 1
2407 },
2408 {
2409 R"(
2410 $idx[[]]
2411 $lex[[]] // line diff: +1
2412 $idx[[]]
2413
2414 $lex[[]] // line diff: +2
2415 $idx[[]]
2416
2417
2418 $lex[[]] // line diff: +3
2419 )",
2420 1 + 1 + 1
2421 },
2422 {
2423 R"(
2424 $idx[[]]
2425
2426
2427 $lex[[]] // line diff: +3
2428 $idx[[]]
2429
2430 $lex[[]] // line diff: +2
2431 $idx[[]]
2432 $lex[[]] // line diff: +1
2433 )",
2434 3 + 1 + 1
2435 },
2436 {
2437 R"(
2438 $idx[[]]
2439 $lex[[]] // line diff: +1
2440 $lex[[]] // line diff: -2
2441
2442 $idx[[]]
2443 $idx[[]]
2444
2445
2446 $lex[[]] // line diff: +3
2447 )",
2448 1 + 3 + 5
2449 },
2450 {
2451 R"(
2452 $idx[[]] $lex[[]] // column diff: +1
2453 $idx[[]]$lex[[]] // diff: 0
2454 )",
2455 1
2456 },
2457 {
2458 R"(
2459 $idx[[]]
2460 $lex[[]] // diff: +1
2461 $idx[[]] $lex[[]] // column diff: +1
2462 $idx[[]]$lex[[]] // diff: 0
2463 )",
2464 1 + 1 + 1
2465 },
2466 {
2467 R"(
2468 $idx[[]] $lex[[]] // column diff: +1
2469 )",
2470 1
2471 },
2472 {
2473 R"(
2474 // column diffs: +1, +2, +3
2475 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2476 )",
2477 1 + 1 + 1,
2478 },
2479 };
2480 for (const auto &T : Tests) {
2481 SCOPED_TRACE(T.RangeCode);
2482 Annotations C(T.RangeCode);
2483 std::vector<size_t> MappedIndex;
2484 for (size_t I = 0; I < C.ranges("lex").size(); ++I)
2485 MappedIndex.push_back(I);
2486 EXPECT_EQ(renameRangeAdjustmentCost(
2487 C.ranges("idx"), symbolRanges(C.ranges("lex")), MappedIndex),
2488 T.ExpectedCost);
2489 }
2490}
2491
2492} // namespace
2493} // namespace clangd
2494} // 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:46
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:45
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:93
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:186
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::ProBoundsAvoidUncheckedContainerAccess 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:158
int character
Character offset on a line in a document (zero-based).
Definition Protocol.h:163
Position start
The range's start position.
Definition Protocol.h:187
Position end
The range's end position.
Definition Protocol.h:190
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:41
static TestTU withCode(llvm::StringRef Code)
Definition TestTU.h:36
std::string HeaderCode
Definition TestTU.h:53