clang-tools 20.0.0git
RenameTests.cpp
Go to the documentation of this file.
1//===-- RenameTests.cpp -----------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "Annotations.h"
10#include "ClangdServer.h"
11#include "SyncAPI.h"
12#include "TestFS.h"
13#include "TestTU.h"
14#include "index/Ref.h"
15#include "refactor/Rename.h"
16#include "support/TestTracer.h"
17#include "clang/Tooling/Core/Replacement.h"
18#include "llvm/ADT/STLExtras.h"
19#include "llvm/Support/MemoryBuffer.h"
20#include <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>(std::move(Base));
41 OFS->pushOverlay(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;
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, SymbolName).ID;
71 std::string PathURI = URI::create(Path).toString();
72 for (const auto &Range : Code.ranges())
73 Builder.insert(SymbolID, refWithRange(Range, PathURI));
74
75 return std::make_unique<RefSlab>(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 It.first().str(),
85 llvm::cantFail(tooling::applyAllReplacements(
86 It.getValue().InitialCode, 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(NextChar, R.Begin - NextChar);
99 Result += NewName;
100 NextChar = R.End;
101 }
102 Result += Code.substr(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(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());
870 TU.ExtraArgs.push_back("-xobjective-c++");
871 auto AST = TU.build();
872 auto Index = TU.index();
873 for (const auto &RenamePos : Code.points()) {
874 auto RenameResult =
875 rename({RenamePos, NewName, AST, testPath(TU.Filename),
876 getVFSFromAST(AST), 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 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 "performNewAction:by:",
912 // Expected
913 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 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 "performNewAction:by:",
940 // Expected error
941 std::nullopt,
942 },
943 // Rename selector in macro definition
944 {
945 // Input
946 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 "performNewAction:by:",
959 // Expected error
960 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 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 "performNewAction:by:",
982 // Expected
983 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 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 "performNewAction:by:",
1009 // Expected
1010 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());
1016 TU.ExtraArgs.push_back("-xobjective-c");
1017 auto AST = TU.build();
1018 auto Index = TU.index();
1019 for (const auto &RenamePos : Code.points()) {
1020 auto RenameResult =
1021 rename({RenamePos, T.NewName, AST, testPath(TU.Filename),
1022 getVFSFromAST(AST), 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(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 {R"cpp(// allow -- function-local
1047 void f(int [[Lo^cal]]) {
1048 [[Local]] = 2;
1049 }
1050 )cpp",
1051 nullptr, HeaderFile},
1052
1053 {R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
1054 namespace {
1055 class Unin^dexable {};
1056 }
1057 )cpp",
1058 "not eligible for indexing", HeaderFile},
1059
1060 {R"cpp(// disallow -- namespace symbol isn't supported
1061 namespace n^s {}
1062 )cpp",
1063 "not a supported kind", HeaderFile},
1064
1065 {R"cpp(// disallow - category rename.
1066 @interface Foo
1067 @end
1068 @interface Foo (Cate^gory)
1069 @end
1070 )cpp",
1071 "Cannot rename symbol: there is no symbol at the given location",
1072 HeaderFile},
1073
1074 {
1075 R"cpp(
1076 #define MACRO 1
1077 int s = MAC^RO;
1078 )cpp",
1079 "not a supported kind", HeaderFile},
1080
1081 {
1082 R"cpp(
1083 struct X { X operator++(int); };
1084 void f(X x) {x+^+;})cpp",
1085 "no symbol", HeaderFile},
1086
1087 {R"cpp(
1088 @interface Foo {}
1089 - (int)[[fo^o]]:(int)x;
1090 @end
1091 )cpp",
1092 nullptr, HeaderFile, "newName:"},
1093 {R"cpp(//disallow as : count must match
1094 @interface Foo {}
1095 - (int)fo^o:(int)x;
1096 @end
1097 )cpp",
1098 "invalid name: the chosen name \"MockName\" is not a valid identifier",
1099 HeaderFile},
1100 {R"cpp(
1101 @interface Foo {}
1102 - (int)[[o^ne]]:(int)one two:(int)two;
1103 @end
1104 )cpp",
1105 nullptr, HeaderFile, "a:two:"},
1106 {R"cpp(
1107 @interface Foo {}
1108 - (int)[[o^ne]]:(int)one [[two]]:(int)two;
1109 @end
1110 )cpp",
1111 nullptr, HeaderFile, "a:b:"},
1112 {R"cpp(
1113 @interface Foo {}
1114 - (int)o^ne:(int)one [[two]]:(int)two;
1115 @end
1116 )cpp",
1117 nullptr, HeaderFile, "one:three:"},
1118
1119 {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 "multiple symbols", !HeaderFile},
1126
1127 {R"cpp(// disallow rename on unrelated token.
1128 cl^ass Foo {};
1129 )cpp",
1130 "no symbol", !HeaderFile},
1131
1132 {R"cpp(// disallow rename on unrelated token.
1133 temp^late<typename T>
1134 class Foo {};
1135 )cpp",
1136 "no symbol", !HeaderFile},
1137
1138 {R"cpp(
1139 namespace {
1140 int Conflict;
1141 int Va^r;
1142 }
1143 )cpp",
1144 "conflict", !HeaderFile, "Conflict"},
1145
1146 {R"cpp(
1147 int Conflict;
1148 int Va^r;
1149 )cpp",
1150 "conflict", !HeaderFile, "Conflict"},
1151
1152 {R"cpp(
1153 class Foo {
1154 int Conflict;
1155 int Va^r;
1156 };
1157 )cpp",
1158 "conflict", !HeaderFile, "Conflict"},
1159
1160 {R"cpp(
1161 enum E {
1162 Conflict,
1163 Fo^o,
1164 };
1165 )cpp",
1166 "conflict", !HeaderFile, "Conflict"},
1167
1168 {R"cpp(
1169 int Conflict;
1170 enum E { // transparent context.
1171 F^oo,
1172 };
1173 )cpp",
1174 "conflict", !HeaderFile, "Conflict"},
1175
1176 {R"cpp(
1177 void func() {
1178 bool Whatever;
1179 int V^ar;
1180 char Conflict;
1181 }
1182 )cpp",
1183 "conflict", !HeaderFile, "Conflict"},
1184
1185 {R"cpp(
1186 void func() {
1187 if (int Conflict = 42) {
1188 int V^ar;
1189 }
1190 }
1191 )cpp",
1192 "conflict", !HeaderFile, "Conflict"},
1193
1194 {R"cpp(
1195 void func() {
1196 if (int Conflict = 42) {
1197 } else {
1198 bool V^ar;
1199 }
1200 }
1201 )cpp",
1202 "conflict", !HeaderFile, "Conflict"},
1203
1204 {R"cpp(
1205 void func() {
1206 if (int V^ar = 42) {
1207 } else {
1208 bool Conflict;
1209 }
1210 }
1211 )cpp",
1212 "conflict", !HeaderFile, "Conflict"},
1213
1214 {R"cpp(
1215 void func() {
1216 while (int V^ar = 10) {
1217 bool Conflict = true;
1218 }
1219 }
1220 )cpp",
1221 "conflict", !HeaderFile, "Conflict"},
1222
1223 {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 "conflict", !HeaderFile, "Conflict"},
1232
1233 {R"cpp(
1234 void func() {
1235 for (int V^ar = 14, Conflict = 42;;) {
1236 }
1237 }
1238 )cpp",
1239 "conflict", !HeaderFile, "Conflict"},
1240
1241 {R"cpp(
1242 void func(int Conflict) {
1243 bool V^ar;
1244 }
1245 )cpp",
1246 "conflict", !HeaderFile, "Conflict"},
1247
1248 {R"cpp(
1249 void func(int Var);
1250
1251 void func(int V^ar) {
1252 bool Conflict;
1253 }
1254 )cpp",
1255 "conflict", !HeaderFile, "Conflict"},
1256
1257 {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 nullptr, !HeaderFile, "Conflict"},
1265
1266 {R"cpp(
1267 void func(int V^ar, int Conflict) {
1268 }
1269 )cpp",
1270 "conflict", !HeaderFile, "Conflict"},
1271
1272 {R"cpp(
1273 void func(int);
1274 void [[o^therFunc]](double);
1275 )cpp",
1276 nullptr, !HeaderFile, "func"},
1277 {R"cpp(
1278 struct S {
1279 void func(int);
1280 void [[o^therFunc]](double);
1281 };
1282 )cpp",
1283 nullptr, !HeaderFile, "func"},
1284
1285 {R"cpp(
1286 int V^ar;
1287 )cpp",
1288 "\"const\" is a keyword", !HeaderFile, "const"},
1289
1290 {R"cpp(// Trying to rename into the same name, SameName == SameName.
1291 void func() {
1292 int S^ameName;
1293 }
1294 )cpp",
1295 "new name is the same", !HeaderFile, "SameName"},
1296 {R"cpp(// Ensure it doesn't associate base specifier with base name.
1297 struct A {};
1298 struct B : priv^ate A {};
1299 )cpp",
1300 "Cannot rename symbol: there is no symbol at the given location", false},
1301 {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 "no symbol", false},
1308
1309 {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 "there are multiple symbols at the given location", !HeaderFile},
1316
1317 {R"cpp(
1318 void test() {
1319 // no crash
1320 using namespace std;
1321 int [[V^ar]];
1322 }
1323 )cpp",
1324 nullptr, !HeaderFile},
1325 };
1326
1327 for (const auto& Case : Cases) {
1328 SCOPED_TRACE(Case.Code);
1329 Annotations T(Case.Code);
1330 TestTU TU = TestTU::withCode(T.code());
1331 TU.ExtraArgs.push_back("-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("-xobjective-c++-header");
1337 }
1338 auto AST = TU.build();
1339 llvm::StringRef NewName = Case.NewName;
1340 auto Results = rename({T.point(), NewName, AST, testPath(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(Results.takeError());
1349 EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
1350 } else {
1351 EXPECT_TRUE(bool(Results)) << "rename returned an error: "
1352 << llvm::toString(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());
1363 TU.Filename = "main.cc";
1364 auto AST = TU.build();
1365
1366 auto Main = testPath("main.cc");
1367 auto InMemFS = llvm::makeIntrusiveRefCnt<llvm::vfs::InMemoryFileSystem>();
1368 InMemFS->addFile(testPath("main.cc"), 0,
1369 llvm::MemoryBuffer::getMemBuffer(Code.code()));
1370 InMemFS->addFile(testPath("other.cc"), 0,
1371 llvm::MemoryBuffer::getMemBuffer(Code.code()));
1372
1373 auto Rename = [&](const SymbolIndex *Idx) {
1374 RenameInputs Inputs{Code.point(),
1375 "xPrime",
1376 AST,
1377 Main,
1378 Idx ? createOverlay(getVFSFromAST(AST), InMemFS)
1379 : nullptr,
1380 Idx,
1381 RenameOptions()};
1382 auto Results = rename(Inputs);
1383 EXPECT_TRUE(bool(Results)) << llvm::toString(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());
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({Code.point(), NewName, AST, testPath(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());
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({Point, NewName, AST, testPath(TU.Filename)});
1465 EXPECT_FALSE(Results) << "expected rename returned an error: "
1466 << Code.code();
1467 auto ActualMessage = llvm::toString(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());
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({Code.point(), "newName", AST, testPath(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("foo.h");
1494 std::string FooCCPath = testPath("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, FooHPath, FooH.code());
1506 runAddDocument(Server, FooCCPath, FooCC.code());
1507
1508 auto Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1509 /*NewName=*/std::nullopt, {});
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, FooCCPath, FooCC.point(),
1520 /*NewName=*/std::string("int"), {});
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, FooCCPath, FooCC.point(),
1529 /*NewName=*/BadIdent, {});
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, FooCCPath, FooCC.point(),
1538 /*NewName=*/GoodIdent, {});
1539 EXPECT_TRUE(bool(Results));
1540 }
1541}
1542
1543TEST(CrossFileRenameTests, DirtyBuffer) {
1544 Annotations FooCode("class [[Foo]] {};");
1545 std::string FooPath = testPath("foo.cc");
1546 Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
1547 Annotations BarCode("void [[Bar]]() {}");
1548 std::string BarPath = testPath("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, true);
1552 FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
1553 nullptr, false);
1554 FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
1555 nullptr, false);
1556 auto Index = FSymbols.buildIndex(IndexType::Light);
1557
1558 Annotations MainCode("class [[Fo^o]] {};");
1559 auto MainFilePath = testPath("main.cc");
1560 llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemFS =
1561 new llvm::vfs::InMemoryFileSystem;
1562 InMemFS->addFile(FooPath, 0,
1563 llvm::MemoryBuffer::getMemBuffer(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(MainCode.code());
1568 auto AST = TU.build();
1569 llvm::StringRef NewName = "newName";
1570 auto Results =
1571 rename({MainCode.point(), NewName, AST, MainFilePath,
1572 createOverlay(getVFSFromAST(AST), InMemFS), 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(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({MainCode.point(), NewName, AST, MainFilePath,
1588 createOverlay(getVFSFromAST(AST), InMemFS), 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 containedRefs(const ContainedRefsRequest &Req,
1605 llvm::function_ref<void(const ContainedRefsResult &)>
1606 Callback) const override {
1607 return false;
1608 }
1609
1610 bool fuzzyFind(
1611 const FuzzyFindRequest &Req,
1612 llvm::function_ref<void(const Symbol &)> Callback) const override {
1613 return false;
1614 }
1615 void
1616 lookup(const LookupRequest &Req,
1617 llvm::function_ref<void(const Symbol &)> Callback) const override {}
1618
1619 void relations(const RelationsRequest &Req,
1620 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1621 Callback) const override {}
1622
1623 llvm::unique_function<IndexContents(llvm::StringRef) const>
1624 indexedFiles() const override {
1625 return [](llvm::StringRef) { return IndexContents::None; };
1626 }
1627
1628 size_t estimateMemoryUsage() const override { return 0; }
1629 } PIndex;
1630 Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1631 createOverlay(getVFSFromAST(AST), InMemFS), &PIndex});
1632 EXPECT_FALSE(Results);
1633 EXPECT_THAT(llvm::toString(Results.takeError()),
1634 testing::HasSubstr("too many occurrences"));
1635}
1636
1637TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
1638 auto MainCode = Annotations("int [[^x]] = 2;");
1639 auto MainFilePath = testPath("main.cc");
1640 auto BarCode = Annotations("int [[x]];");
1641 auto BarPath = testPath("bar.cc");
1642 auto TU = TestTU::withCode(MainCode.code());
1643 // Set a file "bar.cc" on disk.
1644 TU.AdditionalFiles["bar.cc"] = std::string(BarCode.code());
1645 auto AST = TU.build();
1646 std::string BarPathURI = URI::create(BarPath).toString();
1647 Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
1648 // The index will return duplicated refs, our code should be robost to handle
1649 // it.
1650 class DuplicatedXRefIndex : public SymbolIndex {
1651 public:
1652 DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
1653 bool refs(const RefsRequest &Req,
1654 llvm::function_ref<void(const Ref &)> Callback) const override {
1655 // Return two duplicated refs.
1656 Callback(ReturnedRef);
1657 Callback(ReturnedRef);
1658 return false;
1659 }
1660
1661 bool containedRefs(const ContainedRefsRequest &Req,
1662 llvm::function_ref<void(const ContainedRefsResult &)>
1663 Callback) const override {
1664 return false;
1665 }
1666
1667 bool fuzzyFind(const FuzzyFindRequest &,
1668 llvm::function_ref<void(const Symbol &)>) const override {
1669 return false;
1670 }
1671 void lookup(const LookupRequest &,
1672 llvm::function_ref<void(const Symbol &)>) const override {}
1673
1674 void relations(const RelationsRequest &,
1675 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1676 const override {}
1677
1678 llvm::unique_function<IndexContents(llvm::StringRef) const>
1679 indexedFiles() const override {
1680 return [](llvm::StringRef) { return IndexContents::None; };
1681 }
1682
1683 size_t estimateMemoryUsage() const override { return 0; }
1684 Ref ReturnedRef;
1685 } DIndex(XRefInBarCC);
1686 llvm::StringRef NewName = "newName";
1687 auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
1688 getVFSFromAST(AST), &DIndex});
1689 ASSERT_TRUE(bool(Results)) << Results.takeError();
1690 EXPECT_THAT(
1691 applyEdits(std::move(Results->GlobalChanges)),
1692 UnorderedElementsAre(
1693 Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
1694 Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
1695}
1696
1697TEST(CrossFileRenameTests, WithUpToDateIndex) {
1698 MockCompilationDatabase CDB;
1699 CDB.ExtraClangFlags = {"-xobjective-c++"};
1700 // rename is runnning on all "^" points in FooH, and "[[]]" ranges are the
1701 // expected rename occurrences.
1702 struct Case {
1703 llvm::StringRef FooH;
1704 llvm::StringRef FooCC;
1705 } Cases[] = {
1706 {
1707 // classes.
1708 R"cpp(
1709 class [[Fo^o]] {
1710 [[Foo]]();
1711 ~[[Foo]]();
1712 };
1713 )cpp",
1714 R"cpp(
1715 #include "foo.h"
1716 [[Foo]]::[[Foo]]() {}
1717 [[Foo]]::~[[Foo]]() {}
1718
1719 void func() {
1720 [[Foo]] foo;
1721 }
1722 )cpp",
1723 },
1724 {
1725 // class templates.
1726 R"cpp(
1727 template <typename T>
1728 class [[Foo]] {};
1729 // FIXME: explicit template specializations are not supported due the
1730 // clangd index limitations.
1731 template <>
1732 class Foo<double> {};
1733 )cpp",
1734 R"cpp(
1735 #include "foo.h"
1736 void func() {
1737 [[F^oo]]<int> foo;
1738 }
1739 )cpp",
1740 },
1741 {
1742 // class methods.
1743 R"cpp(
1744 class Foo {
1745 void [[f^oo]]();
1746 };
1747 )cpp",
1748 R"cpp(
1749 #include "foo.h"
1750 void Foo::[[foo]]() {}
1751
1752 void func(Foo* p) {
1753 p->[[foo]]();
1754 }
1755 )cpp",
1756 },
1757 {
1758 // virtual methods.
1759 R"cpp(
1760 class Base {
1761 virtual void [[foo]]();
1762 };
1763 class Derived1 : public Base {
1764 void [[f^oo]]() override;
1765 };
1766 class NotDerived {
1767 void foo() {};
1768 }
1769 )cpp",
1770 R"cpp(
1771 #include "foo.h"
1772 void Base::[[foo]]() {}
1773 void Derived1::[[foo]]() {}
1774
1775 class Derived2 : public Derived1 {
1776 void [[foo]]() override {};
1777 };
1778
1779 void func(Base* b, Derived1* d1,
1780 Derived2* d2, NotDerived* nd) {
1781 b->[[foo]]();
1782 d1->[[foo]]();
1783 d2->[[foo]]();
1784 nd->foo();
1785 }
1786 )cpp",
1787 },
1788 {// virtual templated method
1789 R"cpp(
1790 template <typename> class Foo { virtual void [[m]](); };
1791 class Bar : Foo<int> { void [[^m]]() override; };
1792 )cpp",
1793 R"cpp(
1794 #include "foo.h"
1795
1796 template<typename T> void Foo<T>::[[m]]() {}
1797 // FIXME: not renamed as the index doesn't see this as an override of
1798 // the canonical Foo<T>::m().
1799 // https://github.com/clangd/clangd/issues/1325
1800 class Baz : Foo<float> { void m() override; };
1801 )cpp"},
1802 {
1803 // rename on constructor and destructor.
1804 R"cpp(
1805 class [[Foo]] {
1806 [[^Foo]]();
1807 ~[[Foo^]]();
1808 };
1809 )cpp",
1810 R"cpp(
1811 #include "foo.h"
1812 [[Foo]]::[[Foo]]() {}
1813 [[Foo]]::~[[Foo]]() {}
1814
1815 void func() {
1816 [[Foo]] foo;
1817 }
1818 )cpp",
1819 },
1820 {
1821 // functions.
1822 R"cpp(
1823 void [[f^oo]]();
1824 )cpp",
1825 R"cpp(
1826 #include "foo.h"
1827 void [[foo]]() {}
1828
1829 void func() {
1830 [[foo]]();
1831 }
1832 )cpp",
1833 },
1834 {
1835 // typedefs.
1836 R"cpp(
1837 typedef int [[IN^T]];
1838 [[INT]] foo();
1839 )cpp",
1840 R"cpp(
1841 #include "foo.h"
1842 [[INT]] foo() {}
1843 )cpp",
1844 },
1845 {
1846 // usings.
1847 R"cpp(
1848 using [[I^NT]] = int;
1849 [[INT]] foo();
1850 )cpp",
1851 R"cpp(
1852 #include "foo.h"
1853 [[INT]] foo() {}
1854 )cpp",
1855 },
1856 {
1857 // variables.
1858 R"cpp(
1859 static const int [[VA^R]] = 123;
1860 )cpp",
1861 R"cpp(
1862 #include "foo.h"
1863 int s = [[VAR]];
1864 )cpp",
1865 },
1866 {
1867 // scope enums.
1868 R"cpp(
1869 enum class [[K^ind]] { ABC };
1870 )cpp",
1871 R"cpp(
1872 #include "foo.h"
1873 [[Kind]] ff() {
1874 return [[Kind]]::ABC;
1875 }
1876 )cpp",
1877 },
1878 {
1879 // enum constants.
1880 R"cpp(
1881 enum class Kind { [[A^BC]] };
1882 )cpp",
1883 R"cpp(
1884 #include "foo.h"
1885 Kind ff() {
1886 return Kind::[[ABC]];
1887 }
1888 )cpp",
1889 },
1890 {
1891 // Implicit references in macro expansions.
1892 R"cpp(
1893 class [[Fo^o]] {};
1894 #define FooFoo Foo
1895 #define FOO Foo
1896 )cpp",
1897 R"cpp(
1898 #include "foo.h"
1899 void bar() {
1900 [[Foo]] x;
1901 FOO y;
1902 FooFoo z;
1903 }
1904 )cpp",
1905 },
1906 {
1907 // Objective-C classes.
1908 R"cpp(
1909 @interface [[Fo^o]]
1910 @end
1911 )cpp",
1912 R"cpp(
1913 #include "foo.h"
1914 @implementation [[Foo]]
1915 @end
1916
1917 void func([[Foo]] *f) {}
1918 )cpp",
1919 },
1920 };
1921
1922 trace::TestTracer Tracer;
1923 for (const auto &T : Cases) {
1924 SCOPED_TRACE(T.FooH);
1925 Annotations FooH(T.FooH);
1926 Annotations FooCC(T.FooCC);
1927 std::string FooHPath = testPath("foo.h");
1928 std::string FooCCPath = testPath("foo.cc");
1929
1930 MockFS FS;
1931 FS.Files[FooHPath] = std::string(FooH.code());
1932 FS.Files[FooCCPath] = std::string(FooCC.code());
1933
1934 auto ServerOpts = ClangdServer::optsForTest();
1935 ServerOpts.BuildDynamicSymbolIndex = true;
1936 ClangdServer Server(CDB, FS, ServerOpts);
1937
1938 // Add all files to clangd server to make sure the dynamic index has been
1939 // built.
1940 runAddDocument(Server, FooHPath, FooH.code());
1941 runAddDocument(Server, FooCCPath, FooCC.code());
1942
1943 llvm::StringRef NewName = "NewName";
1944 for (const auto &RenamePos : FooH.points()) {
1945 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
1946 auto FileEditsList =
1947 llvm::cantFail(runRename(Server, FooHPath, RenamePos, NewName, {}));
1948 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
1949 EXPECT_THAT(
1950 applyEdits(std::move(FileEditsList.GlobalChanges)),
1951 UnorderedElementsAre(
1952 Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
1953 Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
1954 }
1955 }
1956}
1957
1958TEST(CrossFileRenameTests, ObjC) {
1959 MockCompilationDatabase CDB;
1960 CDB.ExtraClangFlags = {"-xobjective-c"};
1961 // rename is runnning on all "^" points in FooH.
1962 struct Case {
1963 llvm::StringRef FooH;
1964 llvm::StringRef FooM;
1965 llvm::StringRef NewName;
1966 llvm::StringRef ExpectedFooH;
1967 llvm::StringRef ExpectedFooM;
1968 };
1969 Case Cases[] = {// --- Zero arg selector
1970 {
1971 // Input
1972 R"cpp(
1973 @interface Foo
1974 - (int)performA^ction;
1975 @end
1976 )cpp",
1977 R"cpp(
1978 @implementation Foo
1979 - (int)performAction {
1980 [self performAction];
1981 }
1982 @end
1983 )cpp",
1984 // New name
1985 "performNewAction",
1986 // Expected
1987 R"cpp(
1988 @interface Foo
1989 - (int)performNewAction;
1990 @end
1991 )cpp",
1992 R"cpp(
1993 @implementation Foo
1994 - (int)performNewAction {
1995 [self performNewAction];
1996 }
1997 @end
1998 )cpp",
1999 },
2000 // --- Single arg selector
2001 {
2002 // Input
2003 R"cpp(
2004 @interface Foo
2005 - (int)performA^ction:(int)action;
2006 @end
2007 )cpp",
2008 R"cpp(
2009 @implementation Foo
2010 - (int)performAction:(int)action {
2011 [self performAction:action];
2012 }
2013 @end
2014 )cpp",
2015 // New name
2016 "performNewAction:",
2017 // Expected
2018 R"cpp(
2019 @interface Foo
2020 - (int)performNewAction:(int)action;
2021 @end
2022 )cpp",
2023 R"cpp(
2024 @implementation Foo
2025 - (int)performNewAction:(int)action {
2026 [self performNewAction:action];
2027 }
2028 @end
2029 )cpp",
2030 },
2031 // --- Multi arg selector
2032 {
2033 // Input
2034 R"cpp(
2035 @interface Foo
2036 - (int)performA^ction:(int)action with:(int)value;
2037 @end
2038 )cpp",
2039 R"cpp(
2040 @implementation Foo
2041 - (int)performAction:(int)action with:(int)value {
2042 [self performAction:action with:value];
2043 }
2044 @end
2045 )cpp",
2046 // New name
2047 "performNewAction:by:",
2048 // Expected
2049 R"cpp(
2050 @interface Foo
2051 - (int)performNewAction:(int)action by:(int)value;
2052 @end
2053 )cpp",
2054 R"cpp(
2055 @implementation Foo
2056 - (int)performNewAction:(int)action by:(int)value {
2057 [self performNewAction:action by:value];
2058 }
2059 @end
2060 )cpp",
2061 }};
2062
2063 trace::TestTracer Tracer;
2064 for (const auto &T : Cases) {
2065 SCOPED_TRACE(T.FooH);
2066 Annotations FooH(T.FooH);
2067 Annotations FooM(T.FooM);
2068 std::string FooHPath = testPath("foo.h");
2069 std::string FooMPath = testPath("foo.m");
2070
2071 MockFS FS;
2072 FS.Files[FooHPath] = std::string(FooH.code());
2073 FS.Files[FooMPath] = std::string(FooM.code());
2074
2075 auto ServerOpts = ClangdServer::optsForTest();
2076 ServerOpts.BuildDynamicSymbolIndex = true;
2077 ClangdServer Server(CDB, FS, ServerOpts);
2078
2079 // Add all files to clangd server to make sure the dynamic index has been
2080 // built.
2081 runAddDocument(Server, FooHPath, FooH.code());
2082 runAddDocument(Server, FooMPath, FooM.code());
2083
2084 for (const auto &RenamePos : FooH.points()) {
2085 EXPECT_THAT(Tracer.takeMetric("rename_files"), SizeIs(0));
2086 auto FileEditsList =
2087 llvm::cantFail(runRename(Server, FooHPath, RenamePos, T.NewName, {}));
2088 EXPECT_THAT(Tracer.takeMetric("rename_files"), ElementsAre(2));
2089 EXPECT_THAT(applyEdits(std::move(FileEditsList.GlobalChanges)),
2090 UnorderedElementsAre(Pair(Eq(FooHPath), Eq(T.ExpectedFooH)),
2091 Pair(Eq(FooMPath), Eq(T.ExpectedFooM))));
2092 }
2093 }
2094}
2095
2096TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
2097 // cross-file rename should work for function-local symbols, even there is no
2098 // index provided.
2099 Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
2100 auto TU = TestTU::withCode(Code.code());
2101 auto Path = testPath(TU.Filename);
2102 auto AST = TU.build();
2103 llvm::StringRef NewName = "newName";
2104 auto Results = rename({Code.point(), NewName, AST, Path});
2105 ASSERT_TRUE(bool(Results)) << Results.takeError();
2106 EXPECT_THAT(
2107 applyEdits(std::move(Results->GlobalChanges)),
2108 UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
2109}
2110
2111TEST(CrossFileRenameTests, BuildRenameEdits) {
2112 Annotations Code("[[😂]]");
2113 auto LSPRange = Code.range();
2114 llvm::StringRef FilePath = "/test/TestTU.cpp";
2115 llvm::SmallVector<llvm::StringRef, 2> NewNames = {"abc"};
2116 auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
2117 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2118 ASSERT_EQ(1UL, Edit->Replacements.size());
2119 EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
2120 EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
2121
2122 // Test invalid range.
2123 LSPRange.end = {10, 0}; // out of range
2124 Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewNames);
2125 EXPECT_FALSE(Edit);
2126 EXPECT_THAT(llvm::toString(Edit.takeError()),
2127 testing::HasSubstr("fail to convert"));
2128
2129 // Normal ascii characters.
2130 Annotations T(R"cpp(
2131 [[range]]
2132 [[range]]
2133 [[range]]
2134 )cpp");
2135 Edit =
2136 buildRenameEdit(FilePath, T.code(), symbolRanges(T.ranges()), NewNames);
2137 ASSERT_TRUE(bool(Edit)) << Edit.takeError();
2138 EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
2139 expectedResult(T, NewNames[0]));
2140}
2141
2142TEST(CrossFileRenameTests, adjustRenameRanges) {
2143 // Ranges in IndexedCode indicate the indexed occurrences;
2144 // ranges in DraftCode indicate the expected mapped result, empty indicates
2145 // we expect no matched result found.
2146 struct {
2147 llvm::StringRef IndexedCode;
2148 llvm::StringRef DraftCode;
2149 } Tests[] = {
2150 {
2151 // both line and column are changed, not a near miss.
2152 R"cpp(
2153 int [[x]] = 0;
2154 )cpp",
2155 R"cpp(
2156 // insert a line.
2157 double x = 0;
2158 )cpp",
2159 },
2160 {
2161 // subset.
2162 R"cpp(
2163 int [[x]] = 0;
2164 )cpp",
2165 R"cpp(
2166 int [[x]] = 0;
2167 {int x = 0; }
2168 )cpp",
2169 },
2170 {
2171 // shift columns.
2172 R"cpp(int [[x]] = 0; void foo(int x);)cpp",
2173 R"cpp(double [[x]] = 0; void foo(double x);)cpp",
2174 },
2175 {
2176 // shift lines.
2177 R"cpp(
2178 int [[x]] = 0;
2179 void foo(int x);
2180 )cpp",
2181 R"cpp(
2182 // insert a line.
2183 int [[x]] = 0;
2184 void foo(int x);
2185 )cpp",
2186 },
2187 };
2188 LangOptions LangOpts;
2189 LangOpts.CPlusPlus = true;
2190 for (const auto &T : Tests) {
2191 SCOPED_TRACE(T.DraftCode);
2192 Annotations Draft(T.DraftCode);
2193 auto ActualRanges = adjustRenameRanges(Draft.code(), "x",
2194 Annotations(T.IndexedCode).ranges(),
2195 LangOpts, std::nullopt);
2196 if (!ActualRanges)
2197 EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
2198 else
2199 EXPECT_THAT(Draft.ranges(),
2200 testing::UnorderedElementsAreArray(*ActualRanges));
2201 }
2202}
2203
2204TEST(RangePatchingHeuristic, GetMappedRanges) {
2205 // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
2206 // there are no mapped ranges.
2207 struct {
2208 llvm::StringRef IndexedCode;
2209 llvm::StringRef LexedCode;
2210 } Tests[] = {
2211 {
2212 // no lexed ranges.
2213 "[[]]",
2214 "",
2215 },
2216 {
2217 // both line and column are changed, not a near miss.
2218 R"([[]])",
2219 R"(
2220 [[]]
2221 )",
2222 },
2223 {
2224 // subset.
2225 "[[]]",
2226 "^[[]] [[]]"
2227 },
2228 {
2229 // shift columns.
2230 "[[]] [[]]",
2231 " ^[[]] ^[[]] [[]]"
2232 },
2233 {
2234 R"(
2235 [[]]
2236
2237 [[]] [[]]
2238 )",
2239 R"(
2240 // insert a line
2241 ^[[]]
2242
2243 ^[[]] ^[[]]
2244 )",
2245 },
2246 {
2247 R"(
2248 [[]]
2249
2250 [[]] [[]]
2251 )",
2252 R"(
2253 // insert a line
2254 ^[[]]
2255 ^[[]] ^[[]] // column is shifted.
2256 )",
2257 },
2258 {
2259 R"(
2260 [[]]
2261
2262 [[]] [[]]
2263 )",
2264 R"(
2265 // insert a line
2266 [[]]
2267
2268 [[]] [[]] // not mapped (both line and column are changed).
2269 )",
2270 },
2271 {
2272 R"(
2273 [[]]
2274 [[]]
2275
2276 [[]]
2277 [[]]
2278
2279 }
2280 )",
2281 R"(
2282 // insert a new line
2283 ^[[]]
2284 ^[[]]
2285 [[]] // additional range
2286 ^[[]]
2287 ^[[]]
2288 [[]] // additional range
2289 )",
2290 },
2291 {
2292 // non-distinct result (two best results), not a near miss
2293 R"(
2294 [[]]
2295 [[]]
2296 [[]]
2297 )",
2298 R"(
2299 [[]]
2300 [[]]
2301 [[]]
2302 [[]]
2303 )",
2304 }
2305 };
2306 for (const auto &T : Tests) {
2307 SCOPED_TRACE(T.IndexedCode);
2308 auto Lexed = Annotations(T.LexedCode);
2309 auto LexedRanges = symbolRanges(Lexed.ranges());
2310 std::vector<SymbolRange> ExpectedMatches;
2311 for (auto P : Lexed.points()) {
2312 auto Match = llvm::find_if(LexedRanges, [&P](const SymbolRange &R) {
2313 return R.range().start == P;
2314 });
2315 ASSERT_NE(Match, LexedRanges.end());
2316 ExpectedMatches.push_back(*Match);
2317 }
2318
2319 auto Mapped =
2320 getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
2321 if (!Mapped)
2322 EXPECT_THAT(ExpectedMatches, IsEmpty());
2323 else
2324 EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped));
2325 }
2326}
2327
2328TEST(CrossFileRenameTests, adjustmentCost) {
2329 struct {
2330 llvm::StringRef RangeCode;
2331 size_t ExpectedCost;
2332 } Tests[] = {
2333 {
2334 R"(
2335 $idx[[]]$lex[[]] // diff: 0
2336 )",
2337 0,
2338 },
2339 {
2340 R"(
2341 $idx[[]]
2342 $lex[[]] // line diff: +1
2343 $idx[[]]
2344 $lex[[]] // line diff: +1
2345 $idx[[]]
2346 $lex[[]] // line diff: +1
2347
2348 $idx[[]]
2349
2350 $lex[[]] // line diff: +2
2351 )",
2352 1 + 1
2353 },
2354 {
2355 R"(
2356 $idx[[]]
2357 $lex[[]] // line diff: +1
2358 $idx[[]]
2359
2360 $lex[[]] // line diff: +2
2361 $idx[[]]
2362
2363
2364 $lex[[]] // line diff: +3
2365 )",
2366 1 + 1 + 1
2367 },
2368 {
2369 R"(
2370 $idx[[]]
2371
2372
2373 $lex[[]] // line diff: +3
2374 $idx[[]]
2375
2376 $lex[[]] // line diff: +2
2377 $idx[[]]
2378 $lex[[]] // line diff: +1
2379 )",
2380 3 + 1 + 1
2381 },
2382 {
2383 R"(
2384 $idx[[]]
2385 $lex[[]] // line diff: +1
2386 $lex[[]] // line diff: -2
2387
2388 $idx[[]]
2389 $idx[[]]
2390
2391
2392 $lex[[]] // line diff: +3
2393 )",
2394 1 + 3 + 5
2395 },
2396 {
2397 R"(
2398 $idx[[]] $lex[[]] // column diff: +1
2399 $idx[[]]$lex[[]] // diff: 0
2400 )",
2401 1
2402 },
2403 {
2404 R"(
2405 $idx[[]]
2406 $lex[[]] // diff: +1
2407 $idx[[]] $lex[[]] // column diff: +1
2408 $idx[[]]$lex[[]] // diff: 0
2409 )",
2410 1 + 1 + 1
2411 },
2412 {
2413 R"(
2414 $idx[[]] $lex[[]] // column diff: +1
2415 )",
2416 1
2417 },
2418 {
2419 R"(
2420 // column diffs: +1, +2, +3
2421 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2422 )",
2423 1 + 1 + 1,
2424 },
2425 };
2426 for (const auto &T : Tests) {
2427 SCOPED_TRACE(T.RangeCode);
2428 Annotations C(T.RangeCode);
2429 std::vector<size_t> MappedIndex;
2430 for (size_t I = 0; I < C.ranges("lex").size(); ++I)
2431 MappedIndex.push_back(I);
2432 EXPECT_EQ(renameRangeAdjustmentCost(
2433 C.ranges("idx"), symbolRanges(C.ranges("lex")), MappedIndex),
2434 T.ExpectedCost);
2435 }
2436}
2437
2438} // namespace
2439} // namespace clangd
2440} // namespace clang
Bracket::Index Pair
Definition: Bracket.cpp:80
std::vector< CodeCompletionResult > Results
CodeCompletionBuilder Builder
const Criteria C
CharSourceRange Range
SourceRange for the file name.
std::vector< const char * > Expected
static Options optsForTest()
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:208
IndexContents
Describes what data is covered by an index.
Definition: Index.h:114
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
llvm::Expected< RenameResult > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName, const RenameOptions &RenameOpts)
Definition: SyncAPI.cpp:101
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits.
Definition: SourceCode.h:209
llvm::Expected< Edit > buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode, std::vector< SymbolRange > Occurrences, llvm::ArrayRef< llvm::StringRef > NewNames)
Generates rename edits that replaces all given occurrences with the NewName.
Definition: Rename.cpp:1157
llvm::Expected< RenameResult > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
Definition: Rename.cpp:1031
MATCHER_P(named, N, "")
std::string testPath(PathRef File, llvm::sys::path::Style Style)
Definition: TestFS.cpp:93
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28
TEST(BackgroundQueueTest, Priority)
std::optional< std::vector< SymbolRange > > adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, std::vector< Range > Indexed, const LangOptions &LangOpts, std::optional< Selector > Selector)
Adjusts indexed occurrences to match the current state of the file.
Definition: Rename.cpp:1241
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, llvm::StringRef Version, WantDiagnostics WantDiags, bool ForceRebuild)
Definition: SyncAPI.cpp:17
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:185
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< SymbolRange > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
Definition: Rename.cpp:1321
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
Definition: TestIndex.cpp:151
const char * testRoot()
Definition: TestFS.cpp:85
llvm::Expected< RenameResult > runPrepareRename(ClangdServer &Server, PathRef File, Position Pos, std::optional< std::string > NewName, const RenameOptions &RenameOpts)
Definition: SyncAPI.cpp:110
std::optional< std::vector< SymbolRange > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< SymbolRange > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
Definition: Rename.cpp:1254
std::array< uint8_t, 20 > SymbolID
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
SymbolID ID
The ID of the symbol.
Definition: Symbol.h:41
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:36