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

source code of clang-tools-extra/clangd/unittests/RenameTests.cpp