[libc++abi] Build cxxabi with sanitizers (#119612)
[llvm-project.git] / clang-tools-extra / clangd / unittests / RenameTests.cpp
blob142ed171d1a1cb50bd621faf5f352d3574793c7f
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 //===----------------------------------------------------------------------===//
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"
24 namespace clang {
25 namespace clangd {
26 namespace {
28 using testing::ElementsAre;
29 using testing::Eq;
30 using testing::IsEmpty;
31 using testing::Pair;
32 using testing::SizeIs;
33 using testing::UnorderedElementsAre;
34 using testing::UnorderedElementsAreArray;
36 llvm::IntrusiveRefCntPtr<llvm::vfs::OverlayFileSystem>
37 createOverlay(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;
45 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> getVFSFromAST(ParsedAST &AST) {
46 return &AST.getSourceManager().getFileManager().getVirtualFileSystem();
49 // Convert a Range to a Ref.
50 Ref 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;
61 // Build a RefSlab from all marked ranges in the annotation. The ranges are
62 // assumed to associate with the given SymbolName.
63 std::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));
75 return std::make_unique<RefSlab>(std::move(Builder).build());
78 std::vector<
79 std::pair</*FilePath*/ std::string, /*CodeAfterRename*/ std::string>>
80 applyEdits(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;
90 // Generates an expected rename result by replacing all ranges in the given
91 // annotation with the NewName.
92 std::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;
102 Result += Code.substr(NextChar);
103 return Result;
106 std::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;
113 TEST(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]]();
122 )cpp",
124 // Type.
125 R"cpp(
126 struct [[foo^]] {};
127 [[foo]] test() {
128 [[f^oo]] x;
129 return x;
131 )cpp",
133 // Local variable.
134 R"cpp(
135 void bar() {
136 if (auto [[^foo]] = 5) {
137 [[foo]] = 3;
140 )cpp",
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);
149 [[F^oo]] *Ptr;
151 [[F^oo]]::[[Fo^o]]() {}
152 [[F^oo]]::~[[Fo^o]]() {}
153 [[F^oo]] *[[F^oo]]::foo(int x) { return Ptr; }
154 )cpp",
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);
165 template<typename T>
166 [[F^oo]]<T>::[[Fo^o]]() {}
168 template<typename T>
169 [[F^oo]]<T>::~[[Fo^o]]() {}
170 )cpp",
172 // Template class constructor.
173 R"cpp(
174 class [[F^oo]] {
175 template<typename T>
176 [[Fo^o]]();
178 template<typename T>
179 [[F^oo]](T t);
182 template<typename T>
183 [[F^oo]]::[[Fo^o]]() {}
184 )cpp",
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;
196 )cpp",
198 // Forward class declaration without definition.
199 R"cpp(
200 class [[F^oo]];
201 [[F^oo]] *f();
202 )cpp",
204 // Member function.
205 R"cpp(
206 struct X {
207 void [[F^oo]]() {}
208 void Baz() { [[F^oo]](); }
210 )cpp",
212 // Templated method instantiation.
213 R"cpp(
214 template<typename T>
215 class Foo {
216 public:
217 static T [[f^oo]]() {}
220 void bar() {
221 Foo<int>::[[f^oo]]();
223 )cpp",
224 R"cpp(
225 template<typename T>
226 class Foo {
227 public:
228 T [[f^oo]]() {}
231 void bar() {
232 Foo<int>().[[f^oo]]();
234 )cpp",
236 // Template class (partial) specializations.
237 R"cpp(
238 template <typename T>
239 class [[F^oo]] {};
241 template<>
242 class [[F^oo]]<bool> {};
243 template <typename T>
244 class [[F^oo]]<T*> {};
246 void test() {
247 [[F^oo]]<int> x;
248 [[F^oo]]<bool> y;
249 [[F^oo]]<int*> z;
251 )cpp",
253 // Incomplete class specializations
254 R"cpp(
255 template <typename T>
256 class [[Fo^o]] {};
257 void func([[F^oo]]<int>);
258 )cpp",
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;
272 static void foo(T value) {}
273 T member;
276 template <typename T>
277 void func() {
278 [[F^oo]]<T> obj;
279 obj.member = T();
280 [[Foo]]<T>::foo();
283 void test() {
284 [[F^oo]]<int> i;
285 i.member = 0;
286 [[F^oo]]<int>::foo(0);
288 [[F^oo]]<bool> b;
289 b.member = false;
290 [[F^oo]]<bool>::foo(false);
292 )cpp",
294 // Template class methods.
295 R"cpp(
296 template <typename T>
297 class A {
298 public:
299 void [[f^oo]]() {}
302 void func() {
303 A<int>().[[f^oo]]();
304 A<double>().[[f^oo]]();
305 A<float>().[[f^oo]]();
307 )cpp",
309 // Templated class specialization.
310 R"cpp(
311 template<typename T, typename U=bool>
312 class [[Foo^]];
314 template<typename T, typename U>
315 class [[Foo^]] {};
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^]];
324 template<typename T, typename U>
325 class [[Foo^]] {};
326 )cpp",
328 // Function template specialization.
329 R"cpp(
330 template<typename T=int, typename U=bool>
331 U [[foo^]]();
333 template<typename T, typename U>
334 U [[foo^]]() {};
335 )cpp",
336 R"cpp(
337 template<typename T, typename U>
338 U [[foo^]]() {};
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^]]();
347 template<typename T, typename U>
348 U [[foo^]]();
349 )cpp",
350 R"cpp(
351 template <typename T>
352 void [[f^oo]](T t);
354 template <>
355 void [[f^oo]](int a);
357 void test() {
358 [[f^oo]]<double>(1);
360 )cpp",
362 // Variable template.
363 R"cpp(
364 template <typename T, int U>
365 bool [[F^oo]] = true;
367 // Explicit template specialization
368 template <>
369 bool [[F^oo]]<int, 0> = false;
371 // Partial template specialization
372 template <typename T>
373 bool [[F^oo]]<T, 1> = false;
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>;
381 )cpp",
383 // Complicated class type.
384 R"cpp(
385 // Forward declaration.
386 class [[Fo^o]];
387 class Baz {
388 virtual int getValue() const = 0;
391 class [[F^oo]] : public Baz {
392 public:
393 [[F^oo]](int value = 0) : x(value) {}
395 [[F^oo]] &operator++(int);
397 bool operator<([[Foo]] const &rhs);
398 int getValue() const;
399 private:
400 int x;
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();
416 )cpp",
418 // Static class member.
419 R"cpp(
420 struct Foo {
421 static Foo *[[Static^Member]];
424 Foo* Foo::[[Static^Member]] = nullptr;
426 void foo() {
427 Foo* Pointer = Foo::[[Static^Member]];
429 )cpp",
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) {}
441 function() {}
443 R operator()(ArgTypes...) const {}
446 namespace ns {
447 class [[Old]] {};
448 void f() {
449 function<void([[Old]])> func;
451 } // namespace ns
452 )cpp",
454 // Destructor explicit call.
455 R"cpp(
456 class [[F^oo]] {
457 public:
458 ~[[^Foo]]();
461 [[Foo^]]::~[[^Foo]]() {}
463 int main() {
464 [[Fo^o]] f;
465 f.~/*something*/[[^Foo]]();
466 f.~[[^Foo]]();
468 )cpp",
470 // Derived destructor explicit call.
471 R"cpp(
472 class [[Bas^e]] {};
473 class Derived : public [[Bas^e]] {};
475 int main() {
476 [[Bas^e]] *foo = new Derived();
477 foo->[[^Base]]::~[[^Base]]();
479 )cpp",
481 // CXXConstructor initializer list.
482 R"cpp(
483 class Baz {};
484 class Qux {
485 Baz [[F^oo]];
486 public:
487 Qux();
489 Qux::Qux() : [[F^oo]]() {}
490 )cpp",
492 // DeclRefExpr.
493 R"cpp(
494 class C {
495 public:
496 static int [[F^oo]];
499 int foo(int x);
500 #define MACRO(a) foo(a)
502 void func() {
503 C::[[F^oo]] = 1;
504 MACRO(C::[[Foo]]);
505 int y = C::[[F^oo]];
507 )cpp",
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);
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.
525 )cpp",
527 // MemberExpr in macros
528 R"cpp(
529 class Baz {
530 public:
531 int [[F^oo]];
533 int qux(int x);
534 #define MACRO(a) qux(a)
536 int main() {
537 Baz baz;
538 baz.[[F^oo]] = 1;
539 MACRO(baz.[[F^oo]]);
540 int y = baz.[[F^oo]];
542 )cpp",
544 // Fields in classes & partial and full specialiations.
545 R"cpp(
546 template<typename T>
547 struct Foo {
548 T [[Vari^able]] = 42;
551 void foo() {
552 Foo<int> f;
553 f.[[Varia^ble]] = 9000;
555 )cpp",
556 R"cpp(
557 template<typename T, typename U>
558 struct Foo {
559 T Variable[42];
560 U Another;
562 void bar() {}
565 template<typename T>
566 struct Foo<T, bool> {
567 T [[Var^iable]];
568 void bar() { ++[[Var^iable]]; }
571 void foo() {
572 Foo<unsigned, bool> f;
573 f.[[Var^iable]] = 9000;
575 )cpp",
576 R"cpp(
577 template<typename T, typename U>
578 struct Foo {
579 T Variable[42];
580 U Another;
582 void bar() {}
585 template<typename T>
586 struct Foo<T, bool> {
587 T Variable;
588 void bar() { ++Variable; }
591 template<>
592 struct Foo<unsigned, bool> {
593 unsigned [[Var^iable]];
594 void bar() { ++[[Var^iable]]; }
597 void foo() {
598 Foo<unsigned, bool> f;
599 f.[[Var^iable]] = 9000;
601 )cpp",
602 // Static fields.
603 R"cpp(
604 struct Foo {
605 static int [[Var^iable]];
608 int Foo::[[Var^iable]] = 42;
610 void foo() {
611 int LocalInt = Foo::[[Var^iable]];
613 )cpp",
614 R"cpp(
615 template<typename T>
616 struct Foo {
617 static T [[Var^iable]];
620 template <>
621 int Foo<int>::[[Var^iable]] = 42;
623 template <>
624 bool Foo<bool>::[[Var^iable]] = true;
626 void foo() {
627 int LocalInt = Foo<int>::[[Var^iable]];
628 bool LocalBool = Foo<bool>::[[Var^iable]];
630 )cpp",
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;
643 static void foo([[T^]] value) {}
644 [[T^]] member;
646 )cpp",
648 // Typedef.
649 R"cpp(
650 namespace ns {
651 class basic_string {};
652 typedef basic_string [[s^tring]];
653 } // namespace ns
655 ns::[[s^tring]] foo();
656 )cpp",
658 // Variable.
659 R"cpp(
660 namespace A {
661 int [[F^oo]];
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;
673 extern int Foo;
674 Baz = Foo;
675 Foo = A::[[F^oo]] + Baz;
676 A::[[Fo^o]] = b.Foo;
678 Foo = b.Foo;
680 )cpp",
682 // Namespace alias.
683 R"cpp(
684 namespace a { namespace b { void foo(); } }
685 namespace [[^x]] = a::b;
686 void bar() {
687 [[x^]]::foo();
689 )cpp",
691 // Enum.
692 R"cpp(
693 enum [[C^olor]] { Red, Green, Blue };
694 void foo() {
695 [[C^olor]] c;
696 c = [[C^olor]]::Blue;
698 )cpp",
700 // Scoped enum.
701 R"cpp(
702 enum class [[K^ind]] { ABC };
703 void ff() {
704 [[K^ind]] s;
705 s = [[K^ind]]::ABC;
707 )cpp",
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",
717 // Designated initializer.
718 R"cpp(
719 struct Bar {
720 int [[Fo^o]];
722 Bar bar { .[[^Foo]] = 42 };
723 )cpp",
725 // Nested designated initializer.
726 R"cpp(
727 struct Baz {
728 int Field;
730 struct Bar {
731 Baz [[Fo^o]];
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]];
740 struct Bar {
741 Baz Foo;
743 Bar bar { .Foo.[[^Field]] = 42 };
744 )cpp",
746 // Templated alias.
747 R"cpp(
748 template <typename T>
749 class X { T t; };
751 template <typename T>
752 using [[Fo^o]] = X<T>;
754 void bar() {
755 [[Fo^o]]<int> Bar;
757 )cpp",
759 // Alias.
760 R"cpp(
761 class X {};
762 using [[F^oo]] = X;
764 void bar() {
765 [[Fo^o]] Bar;
767 )cpp",
769 // Alias within a namespace.
770 R"cpp(
771 namespace x { class X {}; }
772 namespace ns {
773 using [[Fo^o]] = x::X;
776 void bar() {
777 ns::[[Fo^o]] Bar;
779 )cpp",
781 // Alias within macros.
782 R"cpp(
783 namespace x { class Old {}; }
784 namespace ns {
785 #define REF(alias) alias alias_var;
787 #define ALIAS(old) \
788 using old##Alias = x::old; \
789 REF(old##Alias);
791 ALIAS(Old);
793 [[Old^Alias]] old_alias;
796 void bar() {
797 ns::[[Old^Alias]] Bar;
799 )cpp",
801 // User defined conversion.
802 R"cpp(
803 class [[F^oo]] {
804 public:
805 [[F^oo]]() {}
808 class Baz {
809 public:
810 operator [[F^oo]]() {
811 return [[F^oo]]();
815 int main() {
816 Baz boo;
817 [[F^oo]] foo = static_cast<[[F^oo]]>(boo);
819 )cpp",
821 // ObjC, should not crash.
822 R"cpp(
823 @interface ObjC {
824 char [[da^ta]];
825 } @end
826 )cpp",
828 // Issue 170: Rename symbol introduced by UsingDecl
829 R"cpp(
830 namespace ns { void [[f^oo]](); }
832 using ns::[[f^oo]];
834 void f() {
835 [[f^oo]]();
836 auto p = &[[f^oo]];
838 )cpp",
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');
849 )cpp",
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
862 void func([[Fo^o]] *f) {}
863 )cpp",
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));
886 TEST(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;
897 TestCase Tests[] = {// Simple rename
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];
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];
921 @end
922 )cpp",
924 // Rename selector with macro
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];
936 @end
937 )cpp",
938 // New name
939 "performNewAction:by:",
940 // Expected error
941 std::nullopt,
943 // Rename selector in macro definition
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];
955 @end
956 )cpp",
957 // New name
958 "performNewAction:by:",
959 // Expected error
960 std::nullopt,
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.
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:);
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:);
991 @end
992 )cpp",
994 // Fail if rename initiated inside @selector
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:);
1005 @end
1006 )cpp",
1007 // New name
1008 "performNewAction:by:",
1009 // Expected
1010 std::nullopt,
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());
1037 TEST(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";
1044 const bool HeaderFile = true;
1045 Case Cases[] = {
1046 {R"cpp(// allow -- function-local
1047 void f(int [[Lo^cal]]) {
1048 [[Local]] = 2;
1050 )cpp",
1051 nullptr, HeaderFile},
1053 {R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
1054 namespace {
1055 class Unin^dexable {};
1057 )cpp",
1058 "not eligible for indexing", HeaderFile},
1060 {R"cpp(// disallow -- namespace symbol isn't supported
1061 namespace n^s {}
1062 )cpp",
1063 "not a supported kind", HeaderFile},
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},
1075 R"cpp(
1076 #define MACRO 1
1077 int s = MAC^RO;
1078 )cpp",
1079 "not a supported kind", HeaderFile},
1082 R"cpp(
1083 struct X { X operator++(int); };
1084 void f(X x) {x+^+;})cpp",
1085 "no symbol", HeaderFile},
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:"},
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},
1127 {R"cpp(// disallow rename on unrelated token.
1128 cl^ass Foo {};
1129 )cpp",
1130 "no symbol", !HeaderFile},
1132 {R"cpp(// disallow rename on unrelated token.
1133 temp^late<typename T>
1134 class Foo {};
1135 )cpp",
1136 "no symbol", !HeaderFile},
1138 {R"cpp(
1139 namespace {
1140 int Conflict;
1141 int Va^r;
1143 )cpp",
1144 "conflict", !HeaderFile, "Conflict"},
1146 {R"cpp(
1147 int Conflict;
1148 int Va^r;
1149 )cpp",
1150 "conflict", !HeaderFile, "Conflict"},
1152 {R"cpp(
1153 class Foo {
1154 int Conflict;
1155 int Va^r;
1157 )cpp",
1158 "conflict", !HeaderFile, "Conflict"},
1160 {R"cpp(
1161 enum E {
1162 Conflict,
1163 Fo^o,
1165 )cpp",
1166 "conflict", !HeaderFile, "Conflict"},
1168 {R"cpp(
1169 int Conflict;
1170 enum E { // transparent context.
1171 F^oo,
1173 )cpp",
1174 "conflict", !HeaderFile, "Conflict"},
1176 {R"cpp(
1177 void func() {
1178 bool Whatever;
1179 int V^ar;
1180 char Conflict;
1182 )cpp",
1183 "conflict", !HeaderFile, "Conflict"},
1185 {R"cpp(
1186 void func() {
1187 if (int Conflict = 42) {
1188 int V^ar;
1191 )cpp",
1192 "conflict", !HeaderFile, "Conflict"},
1194 {R"cpp(
1195 void func() {
1196 if (int Conflict = 42) {
1197 } else {
1198 bool V^ar;
1201 )cpp",
1202 "conflict", !HeaderFile, "Conflict"},
1204 {R"cpp(
1205 void func() {
1206 if (int V^ar = 42) {
1207 } else {
1208 bool Conflict;
1211 )cpp",
1212 "conflict", !HeaderFile, "Conflict"},
1214 {R"cpp(
1215 void func() {
1216 while (int V^ar = 10) {
1217 bool Conflict = true;
1220 )cpp",
1221 "conflict", !HeaderFile, "Conflict"},
1223 {R"cpp(
1224 void func() {
1225 for (int Something = 9000, Anything = 14, Conflict = 42; Anything > 9;
1226 ++Something) {
1227 int V^ar;
1230 )cpp",
1231 "conflict", !HeaderFile, "Conflict"},
1233 {R"cpp(
1234 void func() {
1235 for (int V^ar = 14, Conflict = 42;;) {
1238 )cpp",
1239 "conflict", !HeaderFile, "Conflict"},
1241 {R"cpp(
1242 void func(int Conflict) {
1243 bool V^ar;
1245 )cpp",
1246 "conflict", !HeaderFile, "Conflict"},
1248 {R"cpp(
1249 void func(int Var);
1251 void func(int V^ar) {
1252 bool Conflict;
1254 )cpp",
1255 "conflict", !HeaderFile, "Conflict"},
1257 {R"cpp(// No conflict: only forward declaration's argument is renamed.
1258 void func(int [[V^ar]]);
1260 void func(int Var) {
1261 bool Conflict;
1263 )cpp",
1264 nullptr, !HeaderFile, "Conflict"},
1266 {R"cpp(
1267 void func(int V^ar, int Conflict) {
1269 )cpp",
1270 "conflict", !HeaderFile, "Conflict"},
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);
1282 )cpp",
1283 nullptr, !HeaderFile, "func"},
1285 {R"cpp(
1286 int V^ar;
1287 )cpp",
1288 "\"const\" is a keyword", !HeaderFile, "const"},
1290 {R"cpp(// Trying to rename into the same name, SameName == SameName.
1291 void func() {
1292 int S^ameName;
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) {}
1306 )cpp",
1307 "no symbol", false},
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},
1317 {R"cpp(
1318 void test() {
1319 // no crash
1320 using namespace std;
1321 int [[V^ar]];
1323 )cpp",
1324 nullptr, !HeaderFile},
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");
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());
1358 MATCHER_P(newText, T, "") { return arg.newText == T; }
1360 TEST(RenameTest, IndexMergeMainFile) {
1361 Annotations Code("int ^x();");
1362 TestTU TU = TestTU::withCode(Code.code());
1363 TU.Filename = "main.cc";
1364 auto AST = TU.build();
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()));
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);
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")));
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")));
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
1410 TEST(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";
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";
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));
1438 TEST(RenameTest, NoRenameOnSymbolsFromSystemHeaders) {
1439 llvm::StringRef Test =
1440 R"cpp(
1441 #include <cstdlib>
1442 #include <system>
1444 SystemSym^bol abc;
1446 void foo() { at^oi("9000"); }
1447 )cpp";
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";
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"));
1472 TEST(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"));
1487 TEST(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());
1499 auto ServerOpts = ClangdServer::optsForTest();
1500 ServerOpts.BuildDynamicSymbolIndex = true;
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());
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));
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));
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));
1536 for (std::string GoodIdent : {"fooBar", "__foo$", "😀"}) {
1537 Results = runPrepareRename(Server, FooCCPath, FooCC.point(),
1538 /*NewName=*/GoodIdent, {});
1539 EXPECT_TRUE(bool(Results));
1543 TEST(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);
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()));
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)))));
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)))));
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
1604 bool containedRefs(const ContainedRefsRequest &Req,
1605 llvm::function_ref<void(const ContainedRefsResult &)>
1606 Callback) const override {
1607 return false;
1610 bool fuzzyFind(
1611 const FuzzyFindRequest &Req,
1612 llvm::function_ref<void(const Symbol &)> Callback) const override {
1613 return false;
1615 void
1616 lookup(const LookupRequest &Req,
1617 llvm::function_ref<void(const Symbol &)> Callback) const override {}
1619 void relations(const RelationsRequest &Req,
1620 llvm::function_ref<void(const SymbolID &, const Symbol &)>
1621 Callback) const override {}
1623 llvm::unique_function<IndexContents(llvm::StringRef) const>
1624 indexedFiles() const override {
1625 return [](llvm::StringRef) { return IndexContents::None; };
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"));
1637 TEST(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;
1661 bool containedRefs(const ContainedRefsRequest &Req,
1662 llvm::function_ref<void(const ContainedRefsResult &)>
1663 Callback) const override {
1664 return false;
1667 bool fuzzyFind(const FuzzyFindRequest &,
1668 llvm::function_ref<void(const Symbol &)>) const override {
1669 return false;
1671 void lookup(const LookupRequest &,
1672 llvm::function_ref<void(const Symbol &)>) const override {}
1674 void relations(const RelationsRequest &,
1675 llvm::function_ref<void(const SymbolID &, const Symbol &)>)
1676 const override {}
1678 llvm::unique_function<IndexContents(llvm::StringRef) const>
1679 indexedFiles() const override {
1680 return [](llvm::StringRef) { return IndexContents::None; };
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)))));
1697 TEST(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[] = {
1707 // classes.
1708 R"cpp(
1709 class [[Fo^o]] {
1710 [[Foo]]();
1711 ~[[Foo]]();
1713 )cpp",
1714 R"cpp(
1715 #include "foo.h"
1716 [[Foo]]::[[Foo]]() {}
1717 [[Foo]]::~[[Foo]]() {}
1719 void func() {
1720 [[Foo]] foo;
1722 )cpp",
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;
1739 )cpp",
1742 // class methods.
1743 R"cpp(
1744 class Foo {
1745 void [[f^oo]]();
1747 )cpp",
1748 R"cpp(
1749 #include "foo.h"
1750 void Foo::[[foo]]() {}
1752 void func(Foo* p) {
1753 p->[[foo]]();
1755 )cpp",
1758 // virtual methods.
1759 R"cpp(
1760 class Base {
1761 virtual void [[foo]]();
1763 class Derived1 : public Base {
1764 void [[f^oo]]() override;
1766 class NotDerived {
1767 void foo() {};
1769 )cpp",
1770 R"cpp(
1771 #include "foo.h"
1772 void Base::[[foo]]() {}
1773 void Derived1::[[foo]]() {}
1775 class Derived2 : public Derived1 {
1776 void [[foo]]() override {};
1779 void func(Base* b, Derived1* d1,
1780 Derived2* d2, NotDerived* nd) {
1781 b->[[foo]]();
1782 d1->[[foo]]();
1783 d2->[[foo]]();
1784 nd->foo();
1786 )cpp",
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"
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"},
1803 // rename on constructor and destructor.
1804 R"cpp(
1805 class [[Foo]] {
1806 [[^Foo]]();
1807 ~[[Foo^]]();
1809 )cpp",
1810 R"cpp(
1811 #include "foo.h"
1812 [[Foo]]::[[Foo]]() {}
1813 [[Foo]]::~[[Foo]]() {}
1815 void func() {
1816 [[Foo]] foo;
1818 )cpp",
1821 // functions.
1822 R"cpp(
1823 void [[f^oo]]();
1824 )cpp",
1825 R"cpp(
1826 #include "foo.h"
1827 void [[foo]]() {}
1829 void func() {
1830 [[foo]]();
1832 )cpp",
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",
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",
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",
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;
1876 )cpp",
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]];
1888 )cpp",
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;
1904 )cpp",
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
1917 void func([[Foo]] *f) {}
1918 )cpp",
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");
1930 MockFS FS;
1931 FS.Files[FooHPath] = std::string(FooH.code());
1932 FS.Files[FooCCPath] = std::string(FooCC.code());
1934 auto ServerOpts = ClangdServer::optsForTest();
1935 ServerOpts.BuildDynamicSymbolIndex = true;
1936 ClangdServer Server(CDB, FS, ServerOpts);
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());
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)))));
1958 TEST(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;
1969 Case Cases[] = {// --- Zero arg selector
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];
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];
1997 @end
1998 )cpp",
2000 // --- Single arg selector
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];
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];
2028 @end
2029 )cpp",
2031 // --- Multi arg selector
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];
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];
2059 @end
2060 )cpp",
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");
2071 MockFS FS;
2072 FS.Files[FooHPath] = std::string(FooH.code());
2073 FS.Files[FooMPath] = std::string(FooM.code());
2075 auto ServerOpts = ClangdServer::optsForTest();
2076 ServerOpts.BuildDynamicSymbolIndex = true;
2077 ClangdServer Server(CDB, FS, ServerOpts);
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());
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))));
2096 TEST(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)))));
2111 TEST(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());
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"));
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]));
2142 TEST(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[] = {
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",
2161 // subset.
2162 R"cpp(
2163 int [[x]] = 0;
2164 )cpp",
2165 R"cpp(
2166 int [[x]] = 0;
2167 {int x = 0; }
2168 )cpp",
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",
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",
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));
2204 TEST(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[] = {
2212 // no lexed ranges.
2213 "[[]]",
2217 // both line and column are changed, not a near miss.
2218 R"([[]])",
2220 [[]]
2224 // subset.
2225 "[[]]",
2226 "^[[]] [[]]"
2229 // shift columns.
2230 "[[]] [[]]",
2231 " ^[[]] ^[[]] [[]]"
2235 [[]]
2237 [[]] [[]]
2240 // insert a line
2241 ^[[]]
2243 ^[[]] ^[[]]
2248 [[]]
2250 [[]] [[]]
2253 // insert a line
2254 ^[[]]
2255 ^[[]] ^[[]] // column is shifted.
2260 [[]]
2262 [[]] [[]]
2265 // insert a line
2266 [[]]
2268 [[]] [[]] // not mapped (both line and column are changed).
2273 [[]]
2274 [[]]
2276 [[]]
2277 [[]]
2282 // insert a new line
2283 ^[[]]
2284 ^[[]]
2285 [[]] // additional range
2286 ^[[]]
2287 ^[[]]
2288 [[]] // additional range
2292 // non-distinct result (two best results), not a near miss
2294 [[]]
2295 [[]]
2296 [[]]
2299 [[]]
2300 [[]]
2301 [[]]
2302 [[]]
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;
2315 ASSERT_NE(Match, LexedRanges.end());
2316 ExpectedMatches.push_back(*Match);
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));
2328 TEST(CrossFileRenameTests, adjustmentCost) {
2329 struct {
2330 llvm::StringRef RangeCode;
2331 size_t ExpectedCost;
2332 } Tests[] = {
2335 $idx[[]]$lex[[]] // diff: 0
2341 $idx[[]]
2342 $lex[[]] // line diff: +1
2343 $idx[[]]
2344 $lex[[]] // line diff: +1
2345 $idx[[]]
2346 $lex[[]] // line diff: +1
2348 $idx[[]]
2350 $lex[[]] // line diff: +2
2352 1 + 1
2356 $idx[[]]
2357 $lex[[]] // line diff: +1
2358 $idx[[]]
2360 $lex[[]] // line diff: +2
2361 $idx[[]]
2364 $lex[[]] // line diff: +3
2366 1 + 1 + 1
2370 $idx[[]]
2373 $lex[[]] // line diff: +3
2374 $idx[[]]
2376 $lex[[]] // line diff: +2
2377 $idx[[]]
2378 $lex[[]] // line diff: +1
2380 3 + 1 + 1
2384 $idx[[]]
2385 $lex[[]] // line diff: +1
2386 $lex[[]] // line diff: -2
2388 $idx[[]]
2389 $idx[[]]
2392 $lex[[]] // line diff: +3
2394 1 + 3 + 5
2398 $idx[[]] $lex[[]] // column diff: +1
2399 $idx[[]]$lex[[]] // diff: 0
2405 $idx[[]]
2406 $lex[[]] // diff: +1
2407 $idx[[]] $lex[[]] // column diff: +1
2408 $idx[[]]$lex[[]] // diff: 0
2410 1 + 1 + 1
2414 $idx[[]] $lex[[]] // column diff: +1
2420 // column diffs: +1, +2, +3
2421 $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
2423 1 + 1 + 1,
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);
2438 } // namespace
2439 } // namespace clangd
2440 } // namespace clang