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]]() { return T(); }
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]]() { return T(); }
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 struct conflict {};
1274 enum v^ar {};
1275 )cpp",
1276 .ErrorMessage: "conflict", .IsHeaderFile: !HeaderFile, .NewName: "conflict"},
1277
1278 {.Code: R"cpp(
1279 struct conflict {};
1280 int [[v^ar]];
1281 )cpp",
1282 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile, .NewName: "conflict"},
1283
1284 {.Code: R"cpp(
1285 enum conflict {};
1286 int [[v^ar]];
1287 )cpp",
1288 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile, .NewName: "conflict"},
1289
1290 {.Code: R"cpp(
1291 void func(int conflict) {
1292 struct [[t^ag]] {};
1293 }
1294 )cpp",
1295 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile, .NewName: "conflict"},
1296
1297 {.Code: R"cpp(
1298 void func(void) {
1299 struct conflict {};
1300 int [[v^ar]];
1301 }
1302 )cpp",
1303 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile, .NewName: "conflict"},
1304
1305 {.Code: R"cpp(
1306 void func(int);
1307 void [[o^therFunc]](double);
1308 )cpp",
1309 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile, .NewName: "func"},
1310 {.Code: R"cpp(
1311 struct S {
1312 void func(int);
1313 void [[o^therFunc]](double);
1314 };
1315 )cpp",
1316 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile, .NewName: "func"},
1317
1318 {.Code: R"cpp(
1319 int V^ar;
1320 )cpp",
1321 .ErrorMessage: "\"const\" is a keyword", .IsHeaderFile: !HeaderFile, .NewName: "const"},
1322
1323 {.Code: R"cpp(// Trying to rename into the same name, SameName == SameName.
1324 void func() {
1325 int S^ameName;
1326 }
1327 )cpp",
1328 .ErrorMessage: "new name is the same", .IsHeaderFile: !HeaderFile, .NewName: "SameName"},
1329 {.Code: R"cpp(// Ensure it doesn't associate base specifier with base name.
1330 struct A {};
1331 struct B : priv^ate A {};
1332 )cpp",
1333 .ErrorMessage: "Cannot rename symbol: there is no symbol at the given location", .IsHeaderFile: false},
1334 {.Code: R"cpp(// Ensure it doesn't associate base specifier with base name.
1335 /*error-ok*/
1336 struct A {
1337 A() : inva^lid(0) {}
1338 };
1339 )cpp",
1340 .ErrorMessage: "no symbol", .IsHeaderFile: false},
1341
1342 {.Code: R"cpp(// FIXME we probably want to rename both overloads here,
1343 // but renaming currently assumes there's only a
1344 // single canonical declaration.
1345 namespace ns { int foo(int); char foo(char); }
1346 using ns::^foo;
1347 )cpp",
1348 .ErrorMessage: "there are multiple symbols at the given location", .IsHeaderFile: !HeaderFile},
1349
1350 {.Code: R"cpp(
1351 void test() {
1352 // no crash
1353 using namespace std;
1354 int [[V^ar]];
1355 }
1356 )cpp",
1357 .ErrorMessage: nullptr, .IsHeaderFile: !HeaderFile},
1358 };
1359
1360 for (const auto& Case : Cases) {
1361 SCOPED_TRACE(Case.Code);
1362 Annotations T(Case.Code);
1363 TestTU TU = TestTU::withCode(Code: T.code());
1364 TU.ExtraArgs.push_back(x: "-fno-delayed-template-parsing");
1365 if (Case.IsHeaderFile) {
1366 // We open the .h file as the main file.
1367 TU.Filename = "test.h";
1368 // Parsing the .h file as C++ include.
1369 TU.ExtraArgs.push_back(x: "-xobjective-c++-header");
1370 }
1371 auto AST = TU.build();
1372 llvm::StringRef NewName = Case.NewName;
1373 auto Results = rename(RInputs: {.Pos: T.point(), .NewName: NewName, .AST: AST, .MainFilePath: testPath(File: TU.Filename)});
1374 bool WantRename = true;
1375 if (T.ranges().empty())
1376 WantRename = false;
1377 if (!WantRename) {
1378 assert(Case.ErrorMessage && "Error message must be set!");
1379 EXPECT_FALSE(Results)
1380 << "expected rename returned an error: " << T.code();
1381 auto ActualMessage = llvm::toString(E: Results.takeError());
1382 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
1383 } else {
1384 EXPECT_TRUE(bool(Results)) << "rename returned an error: "
1385 << llvm::toString(E: Results.takeError());
1386 EXPECT_EQ(Results->LocalChanges, T.ranges());
1387 }
1388 }
1389}
1390
1391MATCHER_P(newText, T, "") { return arg.newText == T; }
1392
1393TEST(RenameTest, IndexMergeMainFile) {
1394 Annotations Code("int ^x();");
1395 TestTU TU = TestTU::withCode(Code: Code.code());
1396 TU.Filename = "main.cc";
1397 auto AST = TU.build();
1398
1399 auto Main = testPath(File: "main.cc");
1400 auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
1401 InMemFS->addFile(Path: testPath(File: "main.cc"), ModificationTime: 0,
1402 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: Code.code()));
1403 InMemFS->addFile(Path: testPath(File: "other.cc"), ModificationTime: 0,
1404 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: Code.code()));
1405
1406 auto Rename = [&](const SymbolIndex *Idx) {
1407 RenameInputs Inputs{.Pos: Code.point(),
1408 .NewName: "xPrime",
1409 .AST: AST,
1410 .MainFilePath: Main,
1411 .FS: Idx ? createOverlay(Base: getVFSFromAST(AST), Overlay: InMemFS)
1412 : nullptr,
1413 .Index: Idx,
1414 .Opts: RenameOptions()};
1415 auto Results = rename(RInputs: Inputs);
1416 EXPECT_TRUE(bool(Results)) << llvm::toString(E: Results.takeError());
1417 return std::move(*Results);
1418 };
1419
1420 // We do not expect to see duplicated edits from AST vs index.
1421 auto Results = Rename(TU.index().get());
1422 EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1423 EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1424 ElementsAre(newText("xPrime")));
1425
1426 // Sanity check: we do expect to see index results!
1427 TU.Filename = "other.cc";
1428 Results = Rename(TU.index().get());
1429 EXPECT_THAT(Results.GlobalChanges.keys(),
1430 UnorderedElementsAre(Main, testPath("other.cc")));
1431
1432#ifdef CLANGD_PATH_CASE_INSENSITIVE
1433 // On case-insensitive systems, no duplicates if AST vs index case differs.
1434 // https://github.com/clangd/clangd/issues/665
1435 TU.Filename = "MAIN.CC";
1436 Results = Rename(TU.index().get());
1437 EXPECT_THAT(Results.GlobalChanges.keys(), ElementsAre(Main));
1438 EXPECT_THAT(Results.GlobalChanges[Main].asTextEdits(),
1439 ElementsAre(newText("xPrime")));
1440#endif
1441}
1442
1443TEST(RenameTest, MainFileReferencesOnly) {
1444 // filter out references not from main file.
1445 llvm::StringRef Test =
1446 R"cpp(
1447 void test() {
1448 int [[fo^o]] = 1;
1449 // rename references not from main file are not included.
1450 #include "foo.inc"
1451 })cpp";
1452
1453 Annotations Code(Test);
1454 auto TU = TestTU::withCode(Code: Code.code());
1455 TU.AdditionalFiles["foo.inc"] = R"cpp(
1456 #define Macro(X) X
1457 &Macro(foo);
1458 &foo;
1459 )cpp";
1460 auto AST = TU.build();
1461 llvm::StringRef NewName = "abcde";
1462
1463 auto RenameResult =
1464 rename(RInputs: {.Pos: Code.point(), .NewName: NewName, .AST: AST, .MainFilePath: testPath(File: TU.Filename)});
1465 ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << Code.point();
1466 ASSERT_EQ(1u, RenameResult->GlobalChanges.size());
1467 EXPECT_EQ(applyEdits(std::move(RenameResult->GlobalChanges)).front().second,
1468 expectedResult(Code, NewName));
1469}
1470
1471TEST(RenameTest, NoRenameOnSymbolsFromSystemHeaders) {
1472 llvm::StringRef Test =
1473 R"cpp(
1474 #include <cstdlib>
1475 #include <system>
1476
1477 SystemSym^bol abc;
1478
1479 void foo() { at^oi("9000"); }
1480 )cpp";
1481
1482 Annotations Code(Test);
1483 auto TU = TestTU::withCode(Code: Code.code());
1484 TU.AdditionalFiles["system"] = R"cpp(
1485 class SystemSymbol {};
1486 )cpp";
1487 TU.AdditionalFiles["cstdlib"] = R"cpp(
1488 int atoi(const char *str);
1489 )cpp";
1490 TU.ExtraArgs = {"-isystem", testRoot()};
1491 auto AST = TU.build();
1492 llvm::StringRef NewName = "abcde";
1493
1494 // Clangd will not allow renaming symbols from the system headers for
1495 // correctness.
1496 for (auto &Point : Code.points()) {
1497 auto Results = rename(RInputs: {.Pos: Point, .NewName: NewName, .AST: AST, .MainFilePath: testPath(File: TU.Filename)});
1498 EXPECT_FALSE(Results) << "expected rename returned an error: "
1499 << Code.code();
1500 auto ActualMessage = llvm::toString(E: Results.takeError());
1501 EXPECT_THAT(ActualMessage, testing::HasSubstr("not a supported kind"));
1502 }
1503}
1504
1505TEST(RenameTest, ProtobufSymbolIsExcluded) {
1506 Annotations Code("Prot^obuf buf;");
1507 auto TU = TestTU::withCode(Code: Code.code());
1508 TU.HeaderCode =
1509 R"cpp(// Generated by the protocol buffer compiler. DO NOT EDIT!
1510 class Protobuf {};
1511 )cpp";
1512 TU.HeaderFilename = "protobuf.pb.h";
1513 auto AST = TU.build();
1514 auto Results = rename(RInputs: {.Pos: Code.point(), .NewName: "newName", .AST: AST, .MainFilePath: testPath(File: TU.Filename)});
1515 EXPECT_FALSE(Results);
1516 EXPECT_THAT(llvm::toString(Results.takeError()),
1517 testing::HasSubstr("not a supported kind"));
1518}
1519
1520TEST(RenameTest, PrepareRename) {
1521 Annotations FooH("void func();");
1522 Annotations FooCC(R"cpp(
1523 #include "foo.h"
1524 void [[fu^nc]]() {}
1525 )cpp");
1526 std::string FooHPath = testPath(File: "foo.h");
1527 std::string FooCCPath = testPath(File: "foo.cc");
1528 MockFS FS;
1529 FS.Files[FooHPath] = std::string(FooH.code());
1530 FS.Files[FooCCPath] = std::string(FooCC.code());
1531
1532 auto ServerOpts = ClangdServer::optsForTest();
1533 ServerOpts.BuildDynamicSymbolIndex = true;
1534
1535 trace::TestTracer Tracer;
1536 MockCompilationDatabase CDB;
1537 ClangdServer Server(CDB, FS, ServerOpts);
1538 runAddDocument(Server, File: FooHPath, Contents: FooH.code());
1539 runAddDocument(Server, File: FooCCPath, Contents: FooCC.code());
1540
1541 auto Results = runPrepareRename(Server, File: FooCCPath, Pos: FooCC.point(),
1542 /*NewName=*/std::nullopt, RenameOpts: {});
1543 // Verify that for multi-file rename, we only return main-file occurrences.
1544 ASSERT_TRUE(bool(Results)) << Results.takeError();
1545 // We don't know the result is complete in prepareRename (passing a nullptr
1546 // index internally), so GlobalChanges should be empty.
1547 EXPECT_TRUE(Results->GlobalChanges.empty());
1548 EXPECT_THAT(FooCC.ranges(),
1549 testing::UnorderedElementsAreArray(Results->LocalChanges));
1550
1551 // Name validation.
1552 Results = runPrepareRename(Server, File: FooCCPath, Pos: FooCC.point(),
1553 /*NewName=*/std::string("int"), RenameOpts: {});
1554 EXPECT_FALSE(Results);
1555 EXPECT_THAT(llvm::toString(Results.takeError()),
1556 testing::HasSubstr("keyword"));
1557 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "Keywords"),
1558 ElementsAre(1));
1559
1560 for (std::string BadIdent : {"foo!bar", "123foo", "😀@"}) {
1561 Results = runPrepareRename(Server, File: FooCCPath, Pos: FooCC.point(),
1562 /*NewName=*/BadIdent, RenameOpts: {});
1563 EXPECT_FALSE(Results);
1564 EXPECT_THAT(llvm::toString(Results.takeError()),
1565 testing::HasSubstr("identifier"));
1566 EXPECT_THAT(Tracer.takeMetric("rename_name_invalid", "BadIdentifier"),
1567 ElementsAre(1));
1568 }
1569 for (std::string GoodIdent : {"fooBar", "__foo$", "😀"}) {
1570 Results = runPrepareRename(Server, File: FooCCPath, Pos: FooCC.point(),
1571 /*NewName=*/GoodIdent, RenameOpts: {});
1572 EXPECT_TRUE(bool(Results));
1573 }
1574}
1575
1576TEST(CrossFileRenameTests, DirtyBuffer) {
1577 Annotations FooCode("class [[Foo]] {};");
1578 std::string FooPath = testPath(File: "foo.cc");
1579 Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1580 Annotations BarCode("void [[Bar]]() {}");
1581 std::string BarPath = testPath(File: "bar.cc");
1582 // Build the index, the index has "Foo" references from foo.cc and "Bar"
1583 // references from bar.cc.
1584 FileSymbols FSymbols(IndexContents::All, true);
1585 FSymbols.update(Key: FooPath, Symbols: nullptr, Refs: buildRefSlab(Code: FooCode, SymbolName: "Foo", Path: FooPath),
1586 Relations: nullptr, CountReferences: false);
1587 FSymbols.update(Key: BarPath, Symbols: nullptr, Refs: buildRefSlab(Code: BarCode, SymbolName: "Bar", Path: BarPath),
1588 Relations: nullptr, CountReferences: false);
1589 auto Index = FSymbols.buildIndex(IndexType::Light);
1590
1591 Annotations MainCode("class [[Fo^o]] {};");
1592 auto MainFilePath = testPath(File: "main.cc");
1593 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
1594 new llvm::vfs::InMemoryFileSystem;
1595 InMemFS->addFile(Path: FooPath, ModificationTime: 0,
1596 Buffer: llvm::MemoryBuffer::getMemBuffer(InputData: FooDirtyBuffer.code()));
1597
1598 // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
1599 // respect the dirty buffer.
1600 TestTU TU = TestTU::withCode(Code: MainCode.code());
1601 auto AST = TU.build();
1602 llvm::StringRef NewName = "newName";
1603 auto Results =
1604 rename(RInputs: {.Pos: MainCode.point(), .NewName: NewName, .AST: AST, .MainFilePath: MainFilePath,
1605 .FS: createOverlay(Base: getVFSFromAST(AST), Overlay: InMemFS), .Index: Index.get()});
1606 ASSERT_TRUE(bool(Results)) << Results.takeError();
1607 EXPECT_THAT(
1608 applyEdits(std::move(Results->GlobalChanges)),
1609 UnorderedElementsAre(
1610 Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
1611 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1612
1613 // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
1614 // so we should read file content from VFS.
1615 MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
1616 TU = TestTU::withCode(Code: MainCode.code());
1617 // Set a file "bar.cc" on disk.
1618 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1619 AST = TU.build();
1620 Results = rename(RInputs: {.Pos: MainCode.point(), .NewName: NewName, .AST: AST, .MainFilePath: MainFilePath,
1621 .FS: createOverlay(Base: getVFSFromAST(AST), Overlay: InMemFS), .Index: Index.get()});
1622 ASSERT_TRUE(bool(Results)) << Results.takeError();
1623 EXPECT_THAT(
1624 applyEdits(std::move(Results->GlobalChanges)),
1625 UnorderedElementsAre(
1626 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1627 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1628
1629 // Run rename on a pagination index which couldn't return all refs in one
1630 // request, we reject rename on this case.
1631 class PaginationIndex : public SymbolIndex {
1632 bool refs(const RefsRequest &Req,
1633 llvm::function_ref<void(const Ref &)> Callback) const override {
1634 return true; // has more references
1635 }
1636
1637 bool containedRefs(const ContainedRefsRequest &Req,
1638 llvm::function_ref<void(const ContainedRefsResult &)>
1639 Callback) const override {
1640 return false;
1641 }
1642
1643 bool fuzzyFind(
1644 const FuzzyFindRequest &Req,
1645 llvm::function_ref<void(const Symbol &)> Callback) const override {
1646 return false;
1647 }
1648 void
1649 lookup(const LookupRequest &Req,
1650 llvm::function_ref<void(const Symbol &)> Callback) const override {}
1651
1652 void relations(const RelationsRequest &Req,
1653 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1654 Callback) const override {}
1655
1656 llvm::unique_function<IndexContents(llvm::StringRef) const>
1657 indexedFiles() const override {
1658 return [](llvm::StringRef) { return IndexContents::None; };
1659 }
1660
1661 size_t estimateMemoryUsage() const override { return 0; }
1662 } PIndex;
1663 Results = rename(RInputs: {.Pos: MainCode.point(), .NewName: NewName, .AST: AST, .MainFilePath: MainFilePath,
1664 .FS: createOverlay(Base: getVFSFromAST(AST), Overlay: InMemFS), .Index: &PIndex});
1665 EXPECT_FALSE(Results);
1666 EXPECT_THAT(llvm::toString(Results.takeError()),
1667 testing::HasSubstr("too many occurrences"));
1668}
1669
1670TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1671 auto MainCode = Annotations("int [[^x]] = 2;");
1672 auto MainFilePath = testPath(File: "main.cc");
1673 auto BarCode = Annotations("int [[x]];");
1674 auto BarPath = testPath(File: "bar.cc");
1675 auto TU = TestTU::withCode(Code: MainCode.code());
1676 // Set a file "bar.cc" on disk.
1677 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1678 auto AST = TU.build();
1679 std::string BarPathURI = URI::create(AbsolutePath: BarPath).toString();
1680 Ref XRefInBarCC = refWithRange(Range: BarCode.range(), URI: BarPathURI);
1681 // The index will return duplicated refs, our code should be robost to handle
1682 // it.
1683 class DuplicatedXRefIndex : public SymbolIndex {
1684 public:
1685 DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1686 bool refs(const RefsRequest &Req,
1687 llvm::function_ref<void(const Ref &)> Callback) const override {
1688 // Return two duplicated refs.
1689 Callback(ReturnedRef);
1690 Callback(ReturnedRef);
1691 return false;
1692 }
1693
1694 bool containedRefs(const ContainedRefsRequest &Req,
1695 llvm::function_ref<void(const ContainedRefsResult &)>
1696 Callback) const override {
1697 return false;
1698 }
1699
1700 bool fuzzyFind(const FuzzyFindRequest &,
1701 llvm::function_ref<void(const Symbol &)>) const override {
1702 return false;
1703 }
1704 void lookup(const LookupRequest &,
1705 llvm::function_ref<void(const Symbol &)>) const override {}
1706
1707 void relations(const RelationsRequest &,
1708 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1709 const override {}
1710
1711 llvm::unique_function<IndexContents(llvm::StringRef) const>
1712 indexedFiles() const override {
1713 return [](llvm::StringRef) { return IndexContents::None; };
1714 }
1715
1716 size_t estimateMemoryUsage() const override { return 0; }
1717 Ref ReturnedRef;
1718 } DIndex(XRefInBarCC);
1719 llvm::StringRef NewName = "newName";
1720 auto Results = rename(RInputs: {.Pos: MainCode.point(), .NewName: NewName, .AST: AST, .MainFilePath: MainFilePath,
1721 .FS: getVFSFromAST(AST), .Index: &DIndex});
1722 ASSERT_TRUE(bool(Results)) << Results.takeError();
1723 EXPECT_THAT(
1724 applyEdits(std::move(Results->GlobalChanges)),
1725 UnorderedElementsAre(
1726 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1727 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1728}
1729
1730TEST(CrossFileRenameTests, WithUpToDateIndex) {
1731 MockCompilationDatabase CDB;
1732 CDB.ExtraClangFlags = {"-xobjective-c++"};
1733 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1734 // expected rename occurrences.
1735 struct Case {
1736 llvm::StringRef FooH;
1737 llvm::StringRef FooCC;
1738 } Cases[] = {
1739 {
1740 // classes.
1741 .FooH: R"cpp(
1742 class [[Fo^o]] {
1743 [[Foo]]();
1744 ~[[Foo]]();
1745 };
1746 )cpp",
1747 .FooCC: R"cpp(
1748 #include "foo.h"
1749 [[Foo]]::[[Foo]]() {}
1750 [[Foo]]::~[[Foo]]() {}
1751
1752 void func() {
1753 [[Foo]] foo;
1754 }
1755 )cpp",
1756 },
1757 {
1758 // class templates.
1759 .FooH: R"cpp(
1760 template <typename T>
1761 class [[Foo]] {};
1762 // FIXME: explicit template specializations are not supported due the
1763 // clangd index limitations.
1764 template <>
1765 class Foo<double> {};
1766 )cpp",
1767 .FooCC: R"cpp(
1768 #include "foo.h"
1769 void func() {
1770 [[F^oo]]<int> foo;
1771 }
1772 )cpp",
1773 },
1774 {
1775 // class methods.
1776 .FooH: R"cpp(
1777 class Foo {
1778 void [[f^oo]]();
1779 };
1780 )cpp",
1781 .FooCC: R"cpp(
1782 #include "foo.h"
1783 void Foo::[[foo]]() {}
1784
1785 void func(Foo* p) {
1786 p->[[foo]]();
1787 }
1788 )cpp",
1789 },
1790 {
1791 // virtual methods.
1792 .FooH: R"cpp(
1793 class Base {
1794 virtual void [[foo]]();
1795 };
1796 class Derived1 : public Base {
1797 void [[f^oo]]() override;
1798 };
1799 class NotDerived {
1800 void foo() {};
1801 }
1802 )cpp",
1803 .FooCC: R"cpp(
1804 #include "foo.h"
1805 void Base::[[foo]]() {}
1806 void Derived1::[[foo]]() {}
1807
1808 class Derived2 : public Derived1 {
1809 void [[foo]]() override {};
1810 };
1811
1812 void func(Base* b, Derived1* d1,
1813 Derived2* d2, NotDerived* nd) {
1814 b->[[foo]]();
1815 d1->[[foo]]();
1816 d2->[[foo]]();
1817 nd->foo();
1818 }
1819 )cpp",
1820 },
1821 {// virtual templated method
1822 .FooH: R"cpp(
1823 template <typename> class Foo { virtual void [[m]](); };
1824 class Bar : Foo<int> { void [[^m]]() override; };
1825 )cpp",
1826 .FooCC: R"cpp(
1827 #include "foo.h"
1828
1829 template<typename T> void Foo<T>::[[m]]() {}
1830 // FIXME: not renamed as the index doesn't see this as an override of
1831 // the canonical Foo<T>::m().
1832 // https://github.com/clangd/clangd/issues/1325
1833 class Baz : Foo<float> { void m() override; };
1834 )cpp"},
1835 {
1836 // rename on constructor and destructor.
1837 .FooH: R"cpp(
1838 class [[Foo]] {
1839 [[^Foo]]();
1840 ~[[Foo^]]();
1841 };
1842 )cpp",
1843 .FooCC: R"cpp(
1844 #include "foo.h"
1845 [[Foo]]::[[Foo]]() {}
1846 [[Foo]]::~[[Foo]]() {}
1847
1848 void func() {
1849 [[Foo]] foo;
1850 }
1851 )cpp",
1852 },
1853 {
1854 // functions.
1855 .FooH: R"cpp(
1856 void [[f^oo]]();
1857 )cpp",
1858 .FooCC: R"cpp(
1859 #include "foo.h"
1860 void [[foo]]() {}
1861
1862 void func() {
1863 [[foo]]();
1864 }
1865 )cpp",
1866 },
1867 {
1868 // typedefs.
1869 .FooH: R"cpp(
1870 typedef int [[IN^T]];
1871 [[INT]] foo();
1872 )cpp",
1873 .FooCC: R"cpp(
1874 #include "foo.h"
1875 [[INT]] foo() {}
1876 )cpp",
1877 },
1878 {
1879 // usings.
1880 .FooH: R"cpp(
1881 using [[I^NT]] = int;
1882 [[INT]] foo();
1883 )cpp",
1884 .FooCC: R"cpp(
1885 #include "foo.h"
1886 [[INT]] foo() {}
1887 )cpp",
1888 },
1889 {
1890 // variables.
1891 .FooH: R"cpp(
1892 static const int [[VA^R]] = 123;
1893 )cpp",
1894 .FooCC: R"cpp(
1895 #include "foo.h"
1896 int s = [[VAR]];
1897 )cpp",
1898 },
1899 {
1900 // scope enums.
1901 .FooH: R"cpp(
1902 enum class [[K^ind]] { ABC };
1903 )cpp",
1904 .FooCC: R"cpp(
1905 #include "foo.h"
1906 [[Kind]] ff() {
1907 return [[Kind]]::ABC;
1908 }
1909 )cpp",
1910 },
1911 {
1912 // enum constants.
1913 .FooH: R"cpp(
1914 enum class Kind { [[A^BC]] };
1915 )cpp",
1916 .FooCC: R"cpp(
1917 #include "foo.h"
1918 Kind ff() {
1919 return Kind::[[ABC]];
1920 }
1921 )cpp",
1922 },
1923 {
1924 // Implicit references in macro expansions.
1925 .FooH: R"cpp(
1926 class [[Fo^o]] {};
1927 #define FooFoo Foo
1928 #define FOO Foo
1929 )cpp",
1930 .FooCC: R"cpp(
1931 #include "foo.h"
1932 void bar() {
1933 [[Foo]] x;
1934 FOO y;
1935 FooFoo z;
1936 }
1937 )cpp",
1938 },
1939 {
1940 // Objective-C classes.
1941 .FooH: R"cpp(
1942 @interface [[Fo^o]]
1943 @end
1944 )cpp",
1945 .FooCC: R"cpp(
1946 #include "foo.h"
1947 @implementation [[Foo]]
1948 @end
1949
1950 void func([[Foo]] *f) {}
1951 )cpp",
1952 },
1953 };
1954
1955 trace::TestTracer Tracer;
1956 for (const auto &T : Cases) {
1957 SCOPED_TRACE(T.FooH);
1958 Annotations FooH(T.FooH);
1959 Annotations FooCC(T.FooCC);
1960 std::string FooHPath = testPath(File: "foo.h");
1961 std::string FooCCPath = testPath(File: "foo.cc");
1962
1963 MockFS FS;
1964 FS.Files[FooHPath] = std::string(FooH.code());
1965 FS.Files[FooCCPath] = std::string(FooCC.code());
1966
1967 auto ServerOpts = ClangdServer::optsForTest();
1968 ServerOpts.BuildDynamicSymbolIndex = true;
1969 ClangdServer Server(CDB, FS, ServerOpts);
1970
1971 // Add all files to clangd server to make sure the dynamic index has been
1972 // built.
1973 runAddDocument(Server, File: FooHPath, Contents: FooH.code());
1974 runAddDocument(Server, File: FooCCPath, Contents: FooCC.code());
1975
1976 llvm::StringRef NewName = "NewName";
1977 for (const auto &RenamePos : FooH.points()) {
1978 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
1979 auto FileEditsList =
1980 llvm::cantFail(ValOrErr: runRename(Server, File: FooHPath, Pos: RenamePos, NewName, RenameOpts: {}));
1981 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
1982 EXPECT_THAT(
1983 applyEdits(std::move(FileEditsList.GlobalChanges)),
1984 UnorderedElementsAre(
1985 Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
1986 Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
1987 }
1988 }
1989}
1990
1991TEST(CrossFileRenameTests, ObjC) {
1992 MockCompilationDatabase CDB;
1993 CDB.ExtraClangFlags = {"-xobjective-c"};
1994 // rename is runnning on all "^" points in FooH.
1995 struct Case {
1996 llvm::StringRef FooH;
1997 llvm::StringRef FooM;
1998 llvm::StringRef NewName;
1999 llvm::StringRef ExpectedFooH;
2000 llvm::StringRef ExpectedFooM;
2001 };
2002 Case Cases[] = {// --- Zero arg selector
2003 {
2004 // Input
2005 .FooH: R"cpp(
2006 @interface Foo
2007 - (int)performA^ction;
2008 @end
2009 )cpp",
2010 .FooM: R"cpp(
2011 @implementation Foo
2012 - (int)performAction {
2013 [self performAction];
2014 }
2015 @end
2016 )cpp",
2017 // New name
2018 .NewName: "performNewAction",
2019 // Expected
2020 .ExpectedFooH: R"cpp(
2021 @interface Foo
2022 - (int)performNewAction;
2023 @end
2024 )cpp",
2025 .ExpectedFooM: R"cpp(
2026 @implementation Foo
2027 - (int)performNewAction {
2028 [self performNewAction];
2029 }
2030 @end
2031 )cpp",
2032 },
2033 // --- Single arg selector
2034 {
2035 // Input
2036 .FooH: R"cpp(
2037 @interface Foo
2038 - (int)performA^ction:(int)action;
2039 @end
2040 )cpp",
2041 .FooM: R"cpp(
2042 @implementation Foo
2043 - (int)performAction:(int)action {
2044 [self performAction:action];
2045 }
2046 @end
2047 )cpp",
2048 // New name
2049 .NewName: "performNewAction:",
2050 // Expected
2051 .ExpectedFooH: R"cpp(
2052 @interface Foo
2053 - (int)performNewAction:(int)action;
2054 @end
2055 )cpp",
2056 .ExpectedFooM: R"cpp(
2057 @implementation Foo
2058 - (int)performNewAction:(int)action {
2059 [self performNewAction:action];
2060 }
2061 @end
2062 )cpp",
2063 },
2064 // --- Multi arg selector
2065 {
2066 // Input
2067 .FooH: R"cpp(
2068 @interface Foo
2069 - (int)performA^ction:(int)action with:(int)value;
2070 @end
2071 )cpp",
2072 .FooM: R"cpp(
2073 @implementation Foo
2074 - (int)performAction:(int)action with:(int)value {
2075 [self performAction:action with:value];
2076 }
2077 @end
2078 )cpp",
2079 // New name
2080 .NewName: "performNewAction:by:",
2081 // Expected
2082 .ExpectedFooH: R"cpp(
2083 @interface Foo
2084 - (int)performNewAction:(int)action by:(int)value;
2085 @end
2086 )cpp",
2087 .ExpectedFooM: R"cpp(
2088 @implementation Foo
2089 - (int)performNewAction:(int)action by:(int)value {
2090 [self performNewAction:action by:value];
2091 }
2092 @end
2093 )cpp",
2094 }};
2095
2096 trace::TestTracer Tracer;
2097 for (const auto &T : Cases) {
2098 SCOPED_TRACE(T.FooH);
2099 Annotations FooH(T.FooH);
2100 Annotations FooM(T.FooM);
2101 std::string FooHPath = testPath(File: "foo.h");
2102 std::string FooMPath = testPath(File: "foo.m");
2103
2104 MockFS FS;
2105 FS.Files[FooHPath] = std::string(FooH.code());
2106 FS.Files[FooMPath] = std::string(FooM.code());
2107
2108 auto ServerOpts = ClangdServer::optsForTest();
2109 ServerOpts.BuildDynamicSymbolIndex = true;
2110 ClangdServer Server(CDB, FS, ServerOpts);
2111
2112 // Add all files to clangd server to make sure the dynamic index has been
2113 // built.
2114 runAddDocument(Server, File: FooHPath, Contents: FooH.code());
2115 runAddDocument(Server, File: FooMPath, Contents: FooM.code());
2116
2117 for (const auto &RenamePos : FooH.points()) {
2118 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
2119 auto FileEditsList =
2120 llvm::cantFail(ValOrErr: runRename(Server, File: FooHPath, Pos: RenamePos, NewName: T.NewName, RenameOpts: {}));
2121 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
2122 EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
2123 UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)),
2124 Pair(Eq(FooMPath), Eq(T.ExpectedFooM))));
2125 }
2126 }
2127}
2128
2129TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2130 // cross-file rename should work for function-local symbols, even there is no
2131 // index provided.
2132 Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
2133 auto TU = TestTU::withCode(Code: Code.code());
2134 auto Path = testPath(File: TU.Filename);
2135 auto AST = TU.build();
2136 llvm::StringRef NewName = "newName";
2137 auto Results = rename(RInputs: {.Pos: Code.point(), .NewName: NewName, .AST: AST, .MainFilePath: Path});
2138 ASSERT_TRUE(bool(Results)) << Results.takeError();
2139 EXPECT_THAT(
2140 applyEdits(std::move(Results->GlobalChanges)),
2141 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
2142}
2143
2144TEST(CrossFileRenameTests, BuildRenameEdits) {
2145 Annotations Code("[[😂]]");
2146 auto LSPRange = Code.range();
2147 llvm::StringRef FilePath = "/test/TestTU.cpp";
2148 llvm::SmallVector<llvm::StringRef, 2> NewNames = {"abc"};
2149 auto Edit = buildRenameEdit(AbsFilePath: FilePath, InitialCode: Code.code(), Occurrences: {LSPRange}, NewNames);
2150 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2151 ASSERT_EQ(1UL, Edit->Replacements.size());
2152 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
2153 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
2154
2155 // Test invalid range.
2156 LSPRange.end = {.line: 10, .character: 0}; // out of range
2157 Edit = buildRenameEdit(AbsFilePath: FilePath, InitialCode: Code.code(), Occurrences: {LSPRange}, NewNames);
2158 EXPECT_FALSE(Edit);
2159 EXPECT_THAT(llvm::toString(Edit.takeError()),
2160 testing::HasSubstr("fail to convert"));
2161
2162 // Normal ascii characters.
2163 Annotations T(R"cpp(
2164 [[range]]
2165 [[range]]
2166 [[range]]
2167 )cpp");
2168 Edit =
2169 buildRenameEdit(AbsFilePath: FilePath, InitialCode: T.code(), Occurrences: symbolRanges(Ranges: T.ranges()), NewNames);
2170 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2171 EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
2172 expectedResult(T, NewNames[0]));
2173}
2174
2175TEST(CrossFileRenameTests, adjustRenameRanges) {
2176 // Ranges in IndexedCode indicate the indexed occurrences;
2177 // ranges in DraftCode indicate the expected mapped result, empty indicates
2178 // we expect no matched result found.
2179 struct {
2180 llvm::StringRef IndexedCode;
2181 llvm::StringRef DraftCode;
2182 } Tests[] = {
2183 {
2184 // both line and column are changed, not a near miss.
2185 .IndexedCode: R"cpp(
2186 int [[x]] = 0;
2187 )cpp",
2188 .DraftCode: R"cpp(
2189 // insert a line.
2190 double x = 0;
2191 )cpp",
2192 },
2193 {
2194 // subset.
2195 .IndexedCode: R"cpp(
2196 int [[x]] = 0;
2197 )cpp",
2198 .DraftCode: R"cpp(
2199 int [[x]] = 0;
2200 {int x = 0; }
2201 )cpp",
2202 },
2203 {
2204 // shift columns.
2205 .IndexedCode: R"cpp(int [[x]] = 0; void foo(int x);)cpp",
2206 .DraftCode: R"cpp(double [[x]] = 0; void foo(double x);)cpp",
2207 },
2208 {
2209 // shift lines.
2210 .IndexedCode: R"cpp(
2211 int [[x]] = 0;
2212 void foo(int x);
2213 )cpp",
2214 .DraftCode: R"cpp(
2215 // insert a line.
2216 int [[x]] = 0;
2217 void foo(int x);
2218 )cpp",
2219 },
2220 };
2221 LangOptions LangOpts;
2222 LangOpts.CPlusPlus = true;
2223 for (const auto &T : Tests) {
2224 SCOPED_TRACE(T.DraftCode);
2225 Annotations Draft(T.DraftCode);
2226 auto ActualRanges = adjustRenameRanges(
2227 DraftCode: Draft.code(), Name: RenameSymbolName(ArrayRef<std::string>{"x"}),
2228 Indexed: Annotations(T.IndexedCode).ranges(), LangOpts);
2229 if (!ActualRanges)
2230 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
2231 else
2232 EXPECT_THAT(Draft.ranges(),
2233 testing::UnorderedElementsAreArray(*ActualRanges));
2234 }
2235}
2236
2237TEST(RangePatchingHeuristic, GetMappedRanges) {
2238 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
2239 // there are no mapped ranges.
2240 struct {
2241 llvm::StringRef IndexedCode;
2242 llvm::StringRef LexedCode;
2243 } Tests[] = {
2244 {
2245 // no lexed ranges.
2246 .IndexedCode: "[[]]",
2247 .LexedCode: "",
2248 },
2249 {
2250 // both line and column are changed, not a near miss.
2251 .IndexedCode: R"([[]])",
2252 .LexedCode: R"(
2253 [[]]
2254 )",
2255 },
2256 {
2257 // subset.
2258 .IndexedCode: "[[]]",
2259 .LexedCode: "^[[]] [[]]"
2260 },
2261 {
2262 // shift columns.
2263 .IndexedCode: "[[]] [[]]",
2264 .LexedCode: " ^[[]] ^[[]] [[]]"
2265 },
2266 {
2267 .IndexedCode: R"(
2268 [[]]
2269
2270 [[]] [[]]
2271 )",
2272 .LexedCode: R"(
2273 // insert a line
2274 ^[[]]
2275
2276 ^[[]] ^[[]]
2277 )",
2278 },
2279 {
2280 .IndexedCode: R"(
2281 [[]]
2282
2283 [[]] [[]]
2284 )",
2285 .LexedCode: R"(
2286 // insert a line
2287 ^[[]]
2288 ^[[]] ^[[]] // column is shifted.
2289 )",
2290 },
2291 {
2292 .IndexedCode: R"(
2293 [[]]
2294
2295 [[]] [[]]
2296 )",
2297 .LexedCode: R"(
2298 // insert a line
2299 [[]]
2300
2301 [[]] [[]] // not mapped (both line and column are changed).
2302 )",
2303 },
2304 {
2305 .IndexedCode: R"(
2306 [[]]
2307 [[]]
2308
2309 [[]]
2310 [[]]
2311
2312 }
2313 )",
2314 .LexedCode: R"(
2315 // insert a new line
2316 ^[[]]
2317 ^[[]]
2318 [[]] // additional range
2319 ^[[]]
2320 ^[[]]
2321 [[]] // additional range
2322 )",
2323 },
2324 {
2325 // non-distinct result (two best results), not a near miss
2326 .IndexedCode: R"(
2327 [[]]
2328 [[]]
2329 [[]]
2330 )",
2331 .LexedCode: R"(
2332 [[]]
2333 [[]]
2334 [[]]
2335 [[]]
2336 )",
2337 }
2338 };
2339 for (const auto &T : Tests) {
2340 SCOPED_TRACE(T.IndexedCode);
2341 auto Lexed = Annotations(T.LexedCode);
2342 auto LexedRanges = symbolRanges(Ranges: Lexed.ranges());
2343 std::vector<SymbolRange> ExpectedMatches;
2344 for (auto P : Lexed.points()) {
2345 auto Match = llvm::find_if(Range&: LexedRanges, P: [&P](const SymbolRange &R) {
2346 return R.range().start == P;
2347 });
2348 ASSERT_NE(Match, LexedRanges.end());
2349 ExpectedMatches.push_back(x: *Match);
2350 }
2351
2352 auto Mapped =
2353 getMappedRanges(Indexed: Annotations(T.IndexedCode).ranges(), Lexed: LexedRanges);
2354 if (!Mapped)
2355 EXPECT_THAT(ExpectedMatches, IsEmpty());
2356 else
2357 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2358 }
2359}
2360
2361TEST(CrossFileRenameTests, adjustmentCost) {
2362 struct {
2363 llvm::StringRef RangeCode;
2364 size_t ExpectedCost;
2365 } Tests[] = {
2366 {
2367 .RangeCode: R"(
2368 $idx[[]]$lex[[]] // diff: 0
2369 )",
2370 .ExpectedCost: 0,
2371 },
2372 {
2373 .RangeCode: R"(
2374 $idx[[]]
2375 $lex[[]] // line diff: +1
2376 $idx[[]]
2377 $lex[[]] // line diff: +1
2378 $idx[[]]
2379 $lex[[]] // line diff: +1
2380
2381 $idx[[]]
2382
2383 $lex[[]] // line diff: +2
2384 )",
2385 .ExpectedCost: 1 + 1
2386 },
2387 {
2388 .RangeCode: R"(
2389 $idx[[]]
2390 $lex[[]] // line diff: +1
2391 $idx[[]]
2392
2393 $lex[[]] // line diff: +2
2394 $idx[[]]
2395
2396
2397 $lex[[]] // line diff: +3
2398 )",
2399 .ExpectedCost: 1 + 1 + 1
2400 },
2401 {
2402 .RangeCode: R"(
2403 $idx[[]]
2404
2405
2406 $lex[[]] // line diff: +3
2407 $idx[[]]
2408
2409 $lex[[]] // line diff: +2
2410 $idx[[]]
2411 $lex[[]] // line diff: +1
2412 )",
2413 .ExpectedCost: 3 + 1 + 1
2414 },
2415 {
2416 .RangeCode: R"(
2417 $idx[[]]
2418 $lex[[]] // line diff: +1
2419 $lex[[]] // line diff: -2
2420
2421 $idx[[]]
2422 $idx[[]]
2423
2424
2425 $lex[[]] // line diff: +3
2426 )",
2427 .ExpectedCost: 1 + 3 + 5
2428 },
2429 {
2430 .RangeCode: R"(
2431 $idx[[]] $lex[[]] // column diff: +1
2432 $idx[[]]$lex[[]] // diff: 0
2433 )",
2434 .ExpectedCost: 1
2435 },
2436 {
2437 .RangeCode: R"(
2438 $idx[[]]
2439 $lex[[]] // diff: +1
2440 $idx[[]] $lex[[]] // column diff: +1
2441 $idx[[]]$lex[[]] // diff: 0
2442 )",
2443 .ExpectedCost: 1 + 1 + 1
2444 },
2445 {
2446 .RangeCode: R"(
2447 $idx[[]] $lex[[]] // column diff: +1
2448 )",
2449 .ExpectedCost: 1
2450 },
2451 {
2452 .RangeCode: R"(
2453 // column diffs: +1, +2, +3
2454 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2455 )",
2456 .ExpectedCost: 1 + 1 + 1,
2457 },
2458 };
2459 for (const auto &T : Tests) {
2460 SCOPED_TRACE(T.RangeCode);
2461 Annotations C(T.RangeCode);
2462 std::vector<size_t> MappedIndex;
2463 for (size_t I = 0; I < C.ranges(Name: "lex").size(); ++I)
2464 MappedIndex.push_back(x: I);
2465 EXPECT_EQ(renameRangeAdjustmentCost(
2466 C.ranges("idx"), symbolRanges(C.ranges("lex")), MappedIndex),
2467 T.ExpectedCost);
2468 }
2469}
2470
2471} // namespace
2472} // namespace clangd
2473} // namespace clang
2474

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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