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