brintos

brintos / llvm-project-archived public Read only

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