clang-tools  10.0.0git
RenameTests.cpp
Go to the documentation of this file.
1 //===-- RenameTests.cpp -----------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "Annotations.h"
10 #include "ClangdServer.h"
11 #include "SyncAPI.h"
12 #include "TestFS.h"
13 #include "TestTU.h"
14 #include "index/Ref.h"
15 #include "refactor/Rename.h"
16 #include "clang/Tooling/Core/Replacement.h"
17 #include "llvm/ADT/STLExtras.h"
18 #include "llvm/Support/MemoryBuffer.h"
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21 #include <algorithm>
22 
23 namespace clang {
24 namespace clangd {
25 namespace {
26 
27 using testing::Eq;
28 using testing::Pair;
29 using testing::IsEmpty;
30 using testing::UnorderedElementsAre;
31 using testing::UnorderedElementsAreArray;
32 
33 // Convert a Range to a Ref.
34 Ref refWithRange(const clangd::Range &Range, const std::string &URI) {
35  Ref Result;
36  Result.Kind = RefKind::Reference;
37  Result.Location.Start.setLine(Range.start.line);
38  Result.Location.Start.setColumn(Range.start.character);
39  Result.Location.End.setLine(Range.end.line);
40  Result.Location.End.setColumn(Range.end.character);
41  Result.Location.FileURI = URI.c_str();
42  return Result;
43 }
44 
45 // Build a RefSlab from all marked ranges in the annotation. The ranges are
46 // assumed to associate with the given SymbolName.
47 std::unique_ptr<RefSlab> buildRefSlab(const Annotations &Code,
48  llvm::StringRef SymbolName,
49  llvm::StringRef Path) {
51  TestTU TU;
52  TU.HeaderCode = Code.code();
53  auto Symbols = TU.headerSymbols();
54  const auto &SymbolID = findSymbol(Symbols, SymbolName).ID;
55  std::string PathURI = URI::create(Path).toString();
56  for (const auto &Range : Code.ranges())
57  Builder.insert(SymbolID, refWithRange(Range, PathURI));
58 
59  return std::make_unique<RefSlab>(std::move(Builder).build());
60 }
61 
62 std::vector<
63  std::pair</*FilePath*/ std::string, /*CodeAfterRename*/ std::string>>
64 applyEdits(FileEdits FE) {
65  std::vector<std::pair<std::string, std::string>> Results;
66  for (auto &It : FE)
67  Results.emplace_back(
68  It.first().str(),
69  llvm::cantFail(tooling::applyAllReplacements(
70  It.getValue().InitialCode, It.getValue().Replacements)));
71  return Results;
72 }
73 
74 // Generates an expected rename result by replacing all ranges in the given
75 // annotation with the NewName.
76 std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
77  std::string Result;
78  unsigned NextChar = 0;
79  llvm::StringRef Code = Test.code();
80  for (const auto &R : Test.llvm::Annotations::ranges()) {
81  assert(R.Begin <= R.End && NextChar <= R.Begin);
82  Result += Code.substr(NextChar, R.Begin - NextChar);
83  Result += NewName;
84  NextChar = R.End;
85  }
86  Result += Code.substr(NextChar);
87  return Result;
88 }
89 
90 TEST(RenameTest, WithinFileRename) {
91  // rename is runnning on all "^" points, and "[[]]" ranges point to the
92  // identifier that is being renamed.
93  llvm::StringRef Tests[] = {
94  // Function.
95  R"cpp(
96  void [[foo^]]() {
97  [[fo^o]]();
98  }
99  )cpp",
100 
101  // Type.
102  R"cpp(
103  struct [[foo^]] {};
104  [[foo]] test() {
105  [[f^oo]] x;
106  return x;
107  }
108  )cpp",
109 
110  // Local variable.
111  R"cpp(
112  void bar() {
113  if (auto [[^foo]] = 5) {
114  [[foo]] = 3;
115  }
116  }
117  )cpp",
118 
119  // Rename class, including constructor/destructor.
120  R"cpp(
121  class [[F^oo]] {
122  [[F^oo]]();
123  ~[[Foo]]();
124  void foo(int x);
125  };
126  [[Foo]]::[[Fo^o]]() {}
127  void [[Foo]]::foo(int x) {}
128  )cpp",
129 
130  // Class in template argument.
131  R"cpp(
132  class [[F^oo]] {};
133  template <typename T> void func();
134  template <typename T> class Baz {};
135  int main() {
136  func<[[F^oo]]>();
137  Baz<[[F^oo]]> obj;
138  return 0;
139  }
140  )cpp",
141 
142  // Forward class declaration without definition.
143  R"cpp(
144  class [[F^oo]];
145  [[Foo]] *f();
146  )cpp",
147 
148  // Class methods overrides.
149  R"cpp(
150  struct A {
151  virtual void [[f^oo]]() {}
152  };
153  struct B : A {
154  void [[f^oo]]() override {}
155  };
156  struct C : B {
157  void [[f^oo]]() override {}
158  };
159 
160  void func() {
161  A().[[f^oo]]();
162  B().[[f^oo]]();
163  C().[[f^oo]]();
164  }
165  )cpp",
166 
167  // Template class (partial) specializations.
168  R"cpp(
169  template <typename T>
170  class [[F^oo]] {};
171 
172  template<>
173  class [[F^oo]]<bool> {};
174  template <typename T>
175  class [[F^oo]]<T*> {};
176 
177  void test() {
178  [[Foo]]<int> x;
179  [[Foo]]<bool> y;
180  [[Foo]]<int*> z;
181  }
182  )cpp",
183 
184  // Template class instantiations.
185  R"cpp(
186  template <typename T>
187  class [[F^oo]] {
188  public:
189  T foo(T arg, T& ref, T* ptr) {
190  T value;
191  int number = 42;
192  value = (T)number;
193  value = static_cast<T>(number);
194  return value;
195  }
196  static void foo(T value) {}
197  T member;
198  };
199 
200  template <typename T>
201  void func() {
202  [[F^oo]]<T> obj;
203  obj.member = T();
204  [[Foo]]<T>::foo();
205  }
206 
207  void test() {
208  [[F^oo]]<int> i;
209  i.member = 0;
210  [[F^oo]]<int>::foo(0);
211 
212  [[F^oo]]<bool> b;
213  b.member = false;
214  [[Foo]]<bool>::foo(false);
215  }
216  )cpp",
217 
218  // Template class methods.
219  R"cpp(
220  template <typename T>
221  class A {
222  public:
223  void [[f^oo]]() {}
224  };
225 
226  void func() {
227  A<int>().[[f^oo]]();
228  A<double>().[[f^oo]]();
229  A<float>().[[f^oo]]();
230  }
231  )cpp",
232 
233  // Complicated class type.
234  R"cpp(
235  // Forward declaration.
236  class [[Fo^o]];
237  class Baz {
238  virtual int getValue() const = 0;
239  };
240 
241  class [[F^oo]] : public Baz {
242  public:
243  [[Foo]](int value = 0) : x(value) {}
244 
245  [[Foo]] &operator++(int);
246 
247  bool operator<([[Foo]] const &rhs);
248  int getValue() const;
249  private:
250  int x;
251  };
252 
253  void func() {
254  [[Foo]] *Pointer = 0;
255  [[Foo]] Variable = [[Foo]](10);
256  for ([[Foo]] it; it < Variable; it++);
257  const [[Foo]] *C = new [[Foo]]();
258  const_cast<[[Foo]] *>(C)->getValue();
259  [[Foo]] foo;
260  const Baz &BazReference = foo;
261  const Baz *BazPointer = &foo;
262  reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
263  static_cast<const [[^Foo]] &>(BazReference).getValue();
264  static_cast<const [[^Foo]] *>(BazPointer)->getValue();
265  }
266  )cpp",
267 
268  // CXXConstructor initializer list.
269  R"cpp(
270  class Baz {};
271  class Qux {
272  Baz [[F^oo]];
273  public:
274  Qux();
275  };
276  Qux::Qux() : [[F^oo]]() {}
277  )cpp",
278 
279  // DeclRefExpr.
280  R"cpp(
281  class C {
282  public:
283  static int [[F^oo]];
284  };
285 
286  int foo(int x);
287  #define MACRO(a) foo(a)
288 
289  void func() {
290  C::[[F^oo]] = 1;
291  MACRO(C::[[Foo]]);
292  int y = C::[[F^oo]];
293  }
294  )cpp",
295 
296  // Macros.
297  R"cpp(
298  // no rename inside macro body.
299  #define M1 foo
300  #define M2(x) x
301  int [[fo^o]]();
302  void boo(int);
303 
304  void qoo() {
305  [[foo]]();
306  boo([[foo]]());
307  M1();
308  boo(M1());
309  M2([[foo]]());
310  M2(M1()); // foo is inside the nested macro body.
311  }
312  )cpp",
313 
314  // MemberExpr in macros
315  R"cpp(
316  class Baz {
317  public:
318  int [[F^oo]];
319  };
320  int qux(int x);
321  #define MACRO(a) qux(a)
322 
323  int main() {
324  Baz baz;
325  baz.[[Foo]] = 1;
326  MACRO(baz.[[Foo]]);
327  int y = baz.[[Foo]];
328  }
329  )cpp",
330 
331  // Template parameters.
332  R"cpp(
333  template <typename [[^T]]>
334  class Foo {
335  [[T]] foo([[T]] arg, [[T]]& ref, [[^T]]* ptr) {
336  [[T]] value;
337  int number = 42;
338  value = ([[T]])number;
339  value = static_cast<[[^T]]>(number);
340  return value;
341  }
342  static void foo([[T]] value) {}
343  [[T]] member;
344  };
345  )cpp",
346 
347  // Typedef.
348  R"cpp(
349  namespace std {
350  class basic_string {};
351  typedef basic_string [[s^tring]];
352  } // namespace std
353 
354  std::[[s^tring]] foo();
355  )cpp",
356 
357  // Variable.
358  R"cpp(
359  namespace A {
360  int [[F^oo]];
361  }
362  int Foo;
363  int Qux = Foo;
364  int Baz = A::[[^Foo]];
365  void fun() {
366  struct {
367  int Foo;
368  } b = {100};
369  int Foo = 100;
370  Baz = Foo;
371  {
372  extern int Foo;
373  Baz = Foo;
374  Foo = A::[[F^oo]] + Baz;
375  A::[[Fo^o]] = b.Foo;
376  }
377  Foo = b.Foo;
378  }
379  )cpp",
380 
381  // Namespace alias.
382  R"cpp(
383  namespace a { namespace b { void foo(); } }
384  namespace [[^x]] = a::b;
385  void bar() {
386  [[x]]::foo();
387  }
388  )cpp",
389 
390  // Scope enums.
391  R"cpp(
392  enum class [[K^ind]] { ABC };
393  void ff() {
394  [[K^ind]] s;
395  s = [[Kind]]::ABC;
396  }
397  )cpp",
398 
399  // template class in template argument list.
400  R"cpp(
401  template<typename T>
402  class [[Fo^o]] {};
403  template <template<typename> class Z> struct Bar { };
404  template <> struct Bar<[[Foo]]> {};
405  )cpp",
406  };
407  for (llvm::StringRef T : Tests) {
408  Annotations Code(T);
409  auto TU = TestTU::withCode(Code.code());
410  TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
411  auto AST = TU.build();
412  llvm::StringRef NewName = "abcde";
413  for (const auto &RenamePos : Code.points()) {
414  auto RenameResult =
415  rename({RenamePos, NewName, AST, testPath(TU.Filename)});
416  ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
417  ASSERT_EQ(1u, RenameResult->size());
418  EXPECT_EQ(applyEdits(std::move(*RenameResult)).front().second,
419  expectedResult(Code, NewName));
420  }
421  }
422 }
423 
424 TEST(RenameTest, Renameable) {
425  struct Case {
426  const char *Code;
427  const char* ErrorMessage; // null if no error
428  bool IsHeaderFile;
429  const SymbolIndex *Index;
430  };
431  TestTU OtherFile = TestTU::withCode("Outside s; auto ss = &foo;");
432  const char *CommonHeader = R"cpp(
433  class Outside {};
434  void foo();
435  )cpp";
436  OtherFile.HeaderCode = CommonHeader;
437  OtherFile.Filename = "other.cc";
438  // The index has a "Outside" reference and a "foo" reference.
439  auto OtherFileIndex = OtherFile.index();
440  const SymbolIndex *Index = OtherFileIndex.get();
441 
442  const bool HeaderFile = true;
443  Case Cases[] = {
444  {R"cpp(// allow -- function-local
445  void f(int [[Lo^cal]]) {
446  [[Local]] = 2;
447  }
448  )cpp",
449  nullptr, HeaderFile, Index},
450 
451  {R"cpp(// allow -- symbol is indexable and has no refs in index.
452  void [[On^lyInThisFile]]();
453  )cpp",
454  nullptr, HeaderFile, Index},
455 
456  {R"cpp(// disallow -- symbol is indexable and has other refs in index.
457  void f() {
458  Out^side s;
459  }
460  )cpp",
461  "used outside main file", HeaderFile, Index},
462 
463  {R"cpp(// disallow -- symbol in anonymous namespace in header is not indexable.
464  namespace {
465  class Unin^dexable {};
466  }
467  )cpp",
468  "not eligible for indexing", HeaderFile, Index},
469 
470  {R"cpp(// allow -- symbol in anonymous namespace in non-header file is indexable.
471  namespace {
472  class [[F^oo]] {};
473  }
474  )cpp",
475  nullptr, !HeaderFile, Index},
476 
477  {R"cpp(// disallow -- namespace symbol isn't supported
478  namespace n^s {}
479  )cpp",
480  "not a supported kind", HeaderFile, Index},
481 
482  {
483  R"cpp(
484  #define MACRO 1
485  int s = MAC^RO;
486  )cpp",
487  "not a supported kind", HeaderFile, Index},
488 
489  {
490  R"cpp(
491  struct X { X operator++(int); };
492  void f(X x) {x+^+;})cpp",
493  "no symbol", HeaderFile, Index},
494 
495  {R"cpp(// foo is declared outside the file.
496  void fo^o() {}
497  )cpp",
498  "used outside main file", !HeaderFile /*cc file*/, Index},
499 
500  {R"cpp(
501  // We should detect the symbol is used outside the file from the AST.
502  void fo^o() {})cpp",
503  "used outside main file", !HeaderFile, nullptr /*no index*/},
504 
505  {R"cpp(
506  void foo(int);
507  void foo(char);
508  template <typename T> void f(T t) {
509  fo^o(t);
510  })cpp",
511  "multiple symbols", !HeaderFile, nullptr /*no index*/},
512 
513  {R"cpp(// disallow rename on unrelated token.
514  cl^ass Foo {};
515  )cpp",
516  "no symbol", !HeaderFile, nullptr},
517 
518  {R"cpp(// disallow rename on unrelated token.
519  temp^late<typename T>
520  class Foo {};
521  )cpp",
522  "no symbol", !HeaderFile, nullptr},
523  };
524 
525  for (const auto& Case : Cases) {
526  Annotations T(Case.Code);
527  TestTU TU = TestTU::withCode(T.code());
528  TU.HeaderCode = CommonHeader;
529  TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
530  if (Case.IsHeaderFile) {
531  // We open the .h file as the main file.
532  TU.Filename = "test.h";
533  // Parsing the .h file as C++ include.
534  TU.ExtraArgs.push_back("-xobjective-c++-header");
535  }
536  auto AST = TU.build();
537  llvm::StringRef NewName = "dummyNewName";
538  auto Results =
539  rename({T.point(), NewName, AST, testPath(TU.Filename), Case.Index});
540  bool WantRename = true;
541  if (T.ranges().empty())
542  WantRename = false;
543  if (!WantRename) {
544  assert(Case.ErrorMessage && "Error message must be set!");
545  EXPECT_FALSE(Results)
546  << "expected rename returned an error: " << T.code();
547  auto ActualMessage = llvm::toString(Results.takeError());
548  EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
549  } else {
550  EXPECT_TRUE(bool(Results)) << "rename returned an error: "
551  << llvm::toString(Results.takeError());
552  ASSERT_EQ(1u, Results->size());
553  EXPECT_EQ(applyEdits(std::move(*Results)).front().second,
554  expectedResult(T, NewName));
555  }
556  }
557 }
558 
559 TEST(RenameTest, MainFileReferencesOnly) {
560  // filter out references not from main file.
561  llvm::StringRef Test =
562  R"cpp(
563  void test() {
564  int [[fo^o]] = 1;
565  // rename references not from main file are not included.
566  #include "foo.inc"
567  })cpp";
568 
569  Annotations Code(Test);
570  auto TU = TestTU::withCode(Code.code());
571  TU.AdditionalFiles["foo.inc"] = R"cpp(
572  #define Macro(X) X
573  &Macro(foo);
574  &foo;
575  )cpp";
576  auto AST = TU.build();
577  llvm::StringRef NewName = "abcde";
578 
579  auto RenameResult =
580  rename({Code.point(), NewName, AST, testPath(TU.Filename)});
581  ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << Code.point();
582  ASSERT_EQ(1u, RenameResult->size());
583  EXPECT_EQ(applyEdits(std::move(*RenameResult)).front().second,
584  expectedResult(Code, NewName));
585 }
586 
587 TEST(CrossFileRenameTests, DirtyBuffer) {
588  Annotations FooCode("class [[Foo]] {};");
589  std::string FooPath = testPath("foo.cc");
590  Annotations FooDirtyBuffer("class [[Foo]] {};\n// this is dirty buffer");
591  Annotations BarCode("void [[Bar]]() {}");
592  std::string BarPath = testPath("bar.cc");
593  // Build the index, the index has "Foo" references from foo.cc and "Bar"
594  // references from bar.cc.
595  FileSymbols FSymbols;
596  FSymbols.update(FooPath, nullptr, buildRefSlab(FooCode, "Foo", FooPath),
597  nullptr, false);
598  FSymbols.update(BarPath, nullptr, buildRefSlab(BarCode, "Bar", BarPath),
599  nullptr, false);
600  auto Index = FSymbols.buildIndex(IndexType::Light);
601 
602  Annotations MainCode("class [[Fo^o]] {};");
603  auto MainFilePath = testPath("main.cc");
604  // Dirty buffer for foo.cc.
605  auto GetDirtyBuffer = [&](PathRef Path) -> llvm::Optional<std::string> {
606  if (Path == FooPath)
607  return FooDirtyBuffer.code().str();
608  return llvm::None;
609  };
610 
611  // Run rename on Foo, there is a dirty buffer for foo.cc, rename should
612  // respect the dirty buffer.
613  TestTU TU = TestTU::withCode(MainCode.code());
614  auto AST = TU.build();
615  llvm::StringRef NewName = "newName";
616  auto Results = rename({MainCode.point(), NewName, AST, MainFilePath,
617  Index.get(), /*CrossFile=*/true, GetDirtyBuffer});
618  ASSERT_TRUE(bool(Results)) << Results.takeError();
619  EXPECT_THAT(
620  applyEdits(std::move(*Results)),
621  UnorderedElementsAre(
622  Pair(Eq(FooPath), Eq(expectedResult(FooDirtyBuffer, NewName))),
623  Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
624 
625  // Run rename on Bar, there is no dirty buffer for the affected file bar.cc,
626  // so we should read file content from VFS.
627  MainCode = Annotations("void [[Bar]]() { [[B^ar]](); }");
628  TU = TestTU::withCode(MainCode.code());
629  // Set a file "bar.cc" on disk.
630  TU.AdditionalFiles["bar.cc"] = BarCode.code();
631  AST = TU.build();
632  Results = rename({MainCode.point(), NewName, AST, MainFilePath, Index.get(),
633  /*CrossFile=*/true, GetDirtyBuffer});
634  ASSERT_TRUE(bool(Results)) << Results.takeError();
635  EXPECT_THAT(
636  applyEdits(std::move(*Results)),
637  UnorderedElementsAre(
638  Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
639  Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
640 
641  // Run rename on a pagination index which couldn't return all refs in one
642  // request, we reject rename on this case.
643  class PaginationIndex : public SymbolIndex {
644  bool refs(const RefsRequest &Req,
645  llvm::function_ref<void(const Ref &)> Callback) const override {
646  return true; // has more references
647  }
648 
649  bool fuzzyFind(
650  const FuzzyFindRequest &Req,
651  llvm::function_ref<void(const Symbol &)> Callback) const override {
652  return false;
653  }
654  void
655  lookup(const LookupRequest &Req,
656  llvm::function_ref<void(const Symbol &)> Callback) const override {}
657 
658  void relations(const RelationsRequest &Req,
659  llvm::function_ref<void(const SymbolID &, const Symbol &)>
660  Callback) const override {}
661  size_t estimateMemoryUsage() const override { return 0; }
662  } PIndex;
663  Results = rename({MainCode.point(), NewName, AST, MainFilePath, &PIndex,
664  /*CrossFile=*/true, GetDirtyBuffer});
665  EXPECT_FALSE(Results);
666  EXPECT_THAT(llvm::toString(Results.takeError()),
667  testing::HasSubstr("too many occurrences"));
668 }
669 
670 TEST(CrossFileRenameTests, DeduplicateRefsFromIndex) {
671  auto MainCode = Annotations("int [[^x]] = 2;");
672  auto MainFilePath = testPath("main.cc");
673  auto BarCode = Annotations("int [[x]];");
674  auto BarPath = testPath("bar.cc");
675  auto TU = TestTU::withCode(MainCode.code());
676  // Set a file "bar.cc" on disk.
677  TU.AdditionalFiles["bar.cc"] = BarCode.code();
678  auto AST = TU.build();
679  std::string BarPathURI = URI::create(BarPath).toString();
680  Ref XRefInBarCC = refWithRange(BarCode.range(), BarPathURI);
681  // The index will return duplicated refs, our code should be robost to handle
682  // it.
683  class DuplicatedXRefIndex : public SymbolIndex {
684  public:
685  DuplicatedXRefIndex(const Ref &ReturnedRef) : ReturnedRef(ReturnedRef) {}
686  bool refs(const RefsRequest &Req,
687  llvm::function_ref<void(const Ref &)> Callback) const override {
688  // Return two duplicated refs.
689  Callback(ReturnedRef);
690  Callback(ReturnedRef);
691  return false;
692  }
693 
694  bool fuzzyFind(const FuzzyFindRequest &,
695  llvm::function_ref<void(const Symbol &)>) const override {
696  return false;
697  }
698  void lookup(const LookupRequest &,
699  llvm::function_ref<void(const Symbol &)>) const override {}
700 
701  void relations(const RelationsRequest &,
702  llvm::function_ref<void(const SymbolID &, const Symbol &)>)
703  const override {}
704  size_t estimateMemoryUsage() const override { return 0; }
705  Ref ReturnedRef;
706  } DIndex(XRefInBarCC);
707  llvm::StringRef NewName = "newName";
708  auto Results = rename({MainCode.point(), NewName, AST, MainFilePath, &DIndex,
709  /*CrossFile=*/true});
710  ASSERT_TRUE(bool(Results)) << Results.takeError();
711  EXPECT_THAT(
712  applyEdits(std::move(*Results)),
713  UnorderedElementsAre(
714  Pair(Eq(BarPath), Eq(expectedResult(BarCode, NewName))),
715  Pair(Eq(MainFilePath), Eq(expectedResult(MainCode, NewName)))));
716 }
717 
718 TEST(CrossFileRenameTests, WithUpToDateIndex) {
719  MockCompilationDatabase CDB;
720  CDB.ExtraClangFlags = {"-xc++"};
721  class IgnoreDiagnostics : public DiagnosticsConsumer {
722  void onDiagnosticsReady(PathRef File,
723  std::vector<Diag> Diagnostics) override {}
724  } DiagConsumer;
725  // rename is runnning on the "^" point in FooH, and "[[]]" ranges are the
726  // expected rename occurrences.
727  struct Case {
728  llvm::StringRef FooH;
729  llvm::StringRef FooCC;
730  } Cases[] = {
731  {
732  // classes.
733  R"cpp(
734  class [[Fo^o]] {
735  [[Foo]]();
736  ~[[Foo]]();
737  };
738  )cpp",
739  R"cpp(
740  #include "foo.h"
741  [[Foo]]::[[Foo]]() {}
742  [[Foo]]::~[[Foo]]() {}
743 
744  void func() {
745  [[Foo]] foo;
746  }
747  )cpp",
748  },
749  {
750  // class methods.
751  R"cpp(
752  class Foo {
753  void [[f^oo]]();
754  };
755  )cpp",
756  R"cpp(
757  #include "foo.h"
758  void Foo::[[foo]]() {}
759 
760  void func(Foo* p) {
761  p->[[foo]]();
762  }
763  )cpp",
764  },
765  {
766  // Constructor.
767  R"cpp(
768  class [[Foo]] {
769  [[^Foo]]();
770  ~[[Foo]]();
771  };
772  )cpp",
773  R"cpp(
774  #include "foo.h"
775  [[Foo]]::[[Foo]]() {}
776  [[Foo]]::~[[Foo]]() {}
777 
778  void func() {
779  [[Foo]] foo;
780  }
781  )cpp",
782  },
783  {
784  // Destructor (selecting before the identifier).
785  R"cpp(
786  class [[Foo]] {
787  [[Foo]]();
788  ~[[Foo^]]();
789  };
790  )cpp",
791  R"cpp(
792  #include "foo.h"
793  [[Foo]]::[[Foo]]() {}
794  [[Foo]]::~[[Foo]]() {}
795 
796  void func() {
797  [[Foo]] foo;
798  }
799  )cpp",
800  },
801  {
802  // functions.
803  R"cpp(
804  void [[f^oo]]();
805  )cpp",
806  R"cpp(
807  #include "foo.h"
808  void [[foo]]() {}
809 
810  void func() {
811  [[foo]]();
812  }
813  )cpp",
814  },
815  {
816  // typedefs.
817  R"cpp(
818  typedef int [[IN^T]];
819  [[INT]] foo();
820  )cpp",
821  R"cpp(
822  #include "foo.h"
823  [[INT]] foo() {}
824  )cpp",
825  },
826  {
827  // usings.
828  R"cpp(
829  using [[I^NT]] = int;
830  [[INT]] foo();
831  )cpp",
832  R"cpp(
833  #include "foo.h"
834  [[INT]] foo() {}
835  )cpp",
836  },
837  {
838  // variables.
839  R"cpp(
840  static const int [[VA^R]] = 123;
841  )cpp",
842  R"cpp(
843  #include "foo.h"
844  int s = [[VAR]];
845  )cpp",
846  },
847  {
848  // scope enums.
849  R"cpp(
850  enum class [[K^ind]] { ABC };
851  )cpp",
852  R"cpp(
853  #include "foo.h"
854  [[Kind]] ff() {
855  return [[Kind]]::ABC;
856  }
857  )cpp",
858  },
859  {
860  // enum constants.
861  R"cpp(
862  enum class Kind { [[A^BC]] };
863  )cpp",
864  R"cpp(
865  #include "foo.h"
866  Kind ff() {
867  return Kind::[[ABC]];
868  }
869  )cpp",
870  },
871  };
872 
873  for (const auto& T : Cases) {
874  Annotations FooH(T.FooH);
875  Annotations FooCC(T.FooCC);
876  std::string FooHPath = testPath("foo.h");
877  std::string FooCCPath = testPath("foo.cc");
878 
879  MockFSProvider FS;
880  FS.Files[FooHPath] = FooH.code();
881  FS.Files[FooCCPath] = FooCC.code();
882 
883  auto ServerOpts = ClangdServer::optsForTest();
884  ServerOpts.CrossFileRename = true;
885  ServerOpts.BuildDynamicSymbolIndex = true;
886  ClangdServer Server(CDB, FS, DiagConsumer, ServerOpts);
887 
888  // Add all files to clangd server to make sure the dynamic index has been
889  // built.
890  runAddDocument(Server, FooHPath, FooH.code());
891  runAddDocument(Server, FooCCPath, FooCC.code());
892 
893  llvm::StringRef NewName = "NewName";
894  auto FileEditsList =
895  llvm::cantFail(runRename(Server, FooHPath, FooH.point(), NewName));
896  EXPECT_THAT(applyEdits(std::move(FileEditsList)),
897  UnorderedElementsAre(
898  Pair(Eq(FooHPath), Eq(expectedResult(T.FooH, NewName))),
899  Pair(Eq(FooCCPath), Eq(expectedResult(T.FooCC, NewName)))));
900  }
901 }
902 
903 TEST(CrossFileRenameTests, CrossFileOnLocalSymbol) {
904  // cross-file rename should work for function-local symbols, even there is no
905  // index provided.
906  Annotations Code("void f(int [[abc]]) { [[a^bc]] = 3; }");
907  auto TU = TestTU::withCode(Code.code());
908  auto Path = testPath(TU.Filename);
909  auto AST = TU.build();
910  llvm::StringRef NewName = "newName";
911  auto Results = rename({Code.point(), NewName, AST, Path});
912  ASSERT_TRUE(bool(Results)) << Results.takeError();
913  EXPECT_THAT(
914  applyEdits(std::move(*Results)),
915  UnorderedElementsAre(Pair(Eq(Path), Eq(expectedResult(Code, NewName)))));
916 }
917 
918 TEST(CrossFileRenameTests, BuildRenameEdits) {
919  Annotations Code("[[😂]]");
920  auto LSPRange = Code.range();
921  llvm::StringRef FilePath = "/test/TestTU.cpp";
922  llvm::StringRef NewName = "abc";
923  auto Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
924  ASSERT_TRUE(bool(Edit)) << Edit.takeError();
925  ASSERT_EQ(1UL, Edit->Replacements.size());
926  EXPECT_EQ(FilePath, Edit->Replacements.begin()->getFilePath());
927  EXPECT_EQ(4UL, Edit->Replacements.begin()->getLength());
928 
929  // Test invalid range.
930  LSPRange.end = {10, 0}; // out of range
931  Edit = buildRenameEdit(FilePath, Code.code(), {LSPRange}, NewName);
932  EXPECT_FALSE(Edit);
933  EXPECT_THAT(llvm::toString(Edit.takeError()),
934  testing::HasSubstr("fail to convert"));
935 
936  // Normal ascii characters.
937  Annotations T(R"cpp(
938  [[range]]
939  [[range]]
940  [[range]]
941  )cpp");
942  Edit = buildRenameEdit(FilePath, T.code(), T.ranges(), NewName);
943  ASSERT_TRUE(bool(Edit)) << Edit.takeError();
944  EXPECT_EQ(applyEdits(FileEdits{{T.code(), std::move(*Edit)}}).front().second,
945  expectedResult(T, NewName));
946 }
947 
948 TEST(CrossFileRenameTests, adjustRenameRanges) {
949  // Ranges in IndexedCode indicate the indexed occurrences;
950  // ranges in DraftCode indicate the expected mapped result, empty indicates
951  // we expect no matched result found.
952  struct {
953  llvm::StringRef IndexedCode;
954  llvm::StringRef DraftCode;
955  } Tests[] = {
956  {
957  // both line and column are changed, not a near miss.
958  R"cpp(
959  int [[x]] = 0;
960  )cpp",
961  R"cpp(
962  // insert a line.
963  double x = 0;
964  )cpp",
965  },
966  {
967  // subset.
968  R"cpp(
969  int [[x]] = 0;
970  )cpp",
971  R"cpp(
972  int [[x]] = 0;
973  {int x = 0; }
974  )cpp",
975  },
976  {
977  // shift columns.
978  R"cpp(int [[x]] = 0; void foo(int x);)cpp",
979  R"cpp(double [[x]] = 0; void foo(double x);)cpp",
980  },
981  {
982  // shift lines.
983  R"cpp(
984  int [[x]] = 0;
985  void foo(int x);
986  )cpp",
987  R"cpp(
988  // insert a line.
989  int [[x]] = 0;
990  void foo(int x);
991  )cpp",
992  },
993  };
994  LangOptions LangOpts;
995  LangOpts.CPlusPlus = true;
996  for (const auto &T : Tests) {
997  Annotations Draft(T.DraftCode);
998  auto ActualRanges = adjustRenameRanges(
999  Draft.code(), "x", Annotations(T.IndexedCode).ranges(), LangOpts);
1000  if (!ActualRanges)
1001  EXPECT_THAT(Draft.ranges(), testing::IsEmpty());
1002  else
1003  EXPECT_THAT(Draft.ranges(),
1004  testing::UnorderedElementsAreArray(*ActualRanges))
1005  << T.DraftCode;
1006  }
1007 }
1008 
1009 TEST(RangePatchingHeuristic, GetMappedRanges) {
1010  // ^ in LexedCode marks the ranges we expect to be mapped; no ^ indicates
1011  // there are no mapped ranges.
1012  struct {
1013  llvm::StringRef IndexedCode;
1014  llvm::StringRef LexedCode;
1015  } Tests[] = {
1016  {
1017  // no lexed ranges.
1018  "[[]]",
1019  "",
1020  },
1021  {
1022  // both line and column are changed, not a near miss.
1023  R"([[]])",
1024  R"(
1025  [[]]
1026  )",
1027  },
1028  {
1029  // subset.
1030  "[[]]",
1031  "^[[]] [[]]"
1032  },
1033  {
1034  // shift columns.
1035  "[[]] [[]]",
1036  " ^[[]] ^[[]] [[]]"
1037  },
1038  {
1039  R"(
1040  [[]]
1041 
1042  [[]] [[]]
1043  )",
1044  R"(
1045  // insert a line
1046  ^[[]]
1047 
1048  ^[[]] ^[[]]
1049  )",
1050  },
1051  {
1052  R"(
1053  [[]]
1054 
1055  [[]] [[]]
1056  )",
1057  R"(
1058  // insert a line
1059  ^[[]]
1060  ^[[]] ^[[]] // column is shifted.
1061  )",
1062  },
1063  {
1064  R"(
1065  [[]]
1066 
1067  [[]] [[]]
1068  )",
1069  R"(
1070  // insert a line
1071  [[]]
1072 
1073  [[]] [[]] // not mapped (both line and column are changed).
1074  )",
1075  },
1076  {
1077  R"(
1078  [[]]
1079  [[]]
1080 
1081  [[]]
1082  [[]]
1083 
1084  }
1085  )",
1086  R"(
1087  // insert a new line
1088  ^[[]]
1089  ^[[]]
1090  [[]] // additional range
1091  ^[[]]
1092  ^[[]]
1093  [[]] // additional range
1094  )",
1095  },
1096  {
1097  // non-distinct result (two best results), not a near miss
1098  R"(
1099  [[]]
1100  [[]]
1101  [[]]
1102  )",
1103  R"(
1104  [[]]
1105  [[]]
1106  [[]]
1107  [[]]
1108  )",
1109  }
1110  };
1111  for (const auto &T : Tests) {
1112  auto Lexed = Annotations(T.LexedCode);
1113  auto LexedRanges = Lexed.ranges();
1114  std::vector<Range> ExpectedMatches;
1115  for (auto P : Lexed.points()) {
1116  auto Match = llvm::find_if(LexedRanges, [&P](const Range& R) {
1117  return R.start == P;
1118  });
1119  ASSERT_NE(Match, LexedRanges.end());
1120  ExpectedMatches.push_back(*Match);
1121  }
1122 
1123  auto Mapped =
1124  getMappedRanges(Annotations(T.IndexedCode).ranges(), LexedRanges);
1125  if (!Mapped)
1126  EXPECT_THAT(ExpectedMatches, IsEmpty());
1127  else
1128  EXPECT_THAT(ExpectedMatches, UnorderedElementsAreArray(*Mapped))
1129  << T.IndexedCode;
1130  }
1131 }
1132 
1133 TEST(CrossFileRenameTests, adjustmentCost) {
1134  struct {
1135  llvm::StringRef RangeCode;
1136  size_t ExpectedCost;
1137  } Tests[] = {
1138  {
1139  R"(
1140  $idx[[]]$lex[[]] // diff: 0
1141  )",
1142  0,
1143  },
1144  {
1145  R"(
1146  $idx[[]]
1147  $lex[[]] // line diff: +1
1148  $idx[[]]
1149  $lex[[]] // line diff: +1
1150  $idx[[]]
1151  $lex[[]] // line diff: +1
1152 
1153  $idx[[]]
1154 
1155  $lex[[]] // line diff: +2
1156  )",
1157  1 + 1
1158  },
1159  {
1160  R"(
1161  $idx[[]]
1162  $lex[[]] // line diff: +1
1163  $idx[[]]
1164 
1165  $lex[[]] // line diff: +2
1166  $idx[[]]
1167 
1168 
1169  $lex[[]] // line diff: +3
1170  )",
1171  1 + 1 + 1
1172  },
1173  {
1174  R"(
1175  $idx[[]]
1176 
1177 
1178  $lex[[]] // line diff: +3
1179  $idx[[]]
1180 
1181  $lex[[]] // line diff: +2
1182  $idx[[]]
1183  $lex[[]] // line diff: +1
1184  )",
1185  3 + 1 + 1
1186  },
1187  {
1188  R"(
1189  $idx[[]]
1190  $lex[[]] // line diff: +1
1191  $lex[[]] // line diff: -2
1192 
1193  $idx[[]]
1194  $idx[[]]
1195 
1196 
1197  $lex[[]] // line diff: +3
1198  )",
1199  1 + 3 + 5
1200  },
1201  {
1202  R"(
1203  $idx[[]] $lex[[]] // column diff: +1
1204  $idx[[]]$lex[[]] // diff: 0
1205  )",
1206  1
1207  },
1208  {
1209  R"(
1210  $idx[[]]
1211  $lex[[]] // diff: +1
1212  $idx[[]] $lex[[]] // column diff: +1
1213  $idx[[]]$lex[[]] // diff: 0
1214  )",
1215  1 + 1 + 1
1216  },
1217  {
1218  R"(
1219  $idx[[]] $lex[[]] // column diff: +1
1220  )",
1221  1
1222  },
1223  {
1224  R"(
1225  // column diffs: +1, +2, +3
1226  $idx[[]] $lex[[]] $idx[[]] $lex[[]] $idx[[]] $lex[[]]
1227  )",
1228  1 + 1 + 1,
1229  },
1230  };
1231  for (const auto &T : Tests) {
1232  Annotations C(T.RangeCode);
1233  std::vector<size_t> MappedIndex;
1234  for (size_t I = 0; I < C.ranges("lex").size(); ++I)
1235  MappedIndex.push_back(I);
1236  EXPECT_EQ(renameRangeAdjustmentCost(C.ranges("idx"), C.ranges("lex"),
1237  MappedIndex),
1238  T.ExpectedCost) << T.RangeCode;
1239  }
1240 }
1241 
1242 } // namespace
1243 } // namespace clangd
1244 } // namespace clang
std::string Code
std::string HeaderCode
Definition: TestTU.h:50
llvm::Optional< std::vector< Range > > getMappedRanges(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed)
Calculates the lexed occurrences that the given indexed occurrences map to.
Definition: Rename.cpp:594
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
std::vector< CodeCompletionResult > Results
llvm::unique_function< void(llvm::Expected< T >)> Callback
A Callback<T> is a void function that accepts Expected<T>.
Definition: Function.h:28
MockFSProvider FS
static Options optsForTest()
Documents should not be synced at all.
SymbolID ID
The ID of the symbol.
Definition: Symbol.h:38
std::vector< const char * > ExtraArgs
Definition: TestTU.h:57
std::vector< std::string > lookup(const SymbolIndex &I, llvm::ArrayRef< SymbolID > IDs)
Definition: TestIndex.cpp:106
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
std::string Path
A typedef to represent a file path.
Definition: Path.h:20
SymbolSlab Symbols
llvm::StringMap< Edit > FileEdits
A mapping from absolute file path (the one used for accessing the underlying VFS) to edits...
Definition: SourceCode.h:222
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
CodeCompletionBuilder Builder
static llvm::Expected< URI > create(llvm::StringRef AbsolutePath, llvm::StringRef Scheme)
Creates a URI for a file in the given scheme.
Definition: URI.cpp:197
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangdServer Server
const Symbol & findSymbol(const SymbolSlab &Slab, llvm::StringRef QName)
Definition: TestTU.cpp:97
size_t renameRangeAdjustmentCost(ArrayRef< Range > Indexed, ArrayRef< Range > Lexed, ArrayRef< size_t > MappedIndex)
Evaluates how good the mapped result is.
Definition: Rename.cpp:655
llvm::Expected< FileEdits > runRename(ClangdServer &Server, PathRef File, Position Pos, llvm::StringRef NewName)
Definition: SyncAPI.cpp:99
CharSourceRange Range
SourceRange for the file name.
IgnoreDiagnostics DiagConsumer
llvm::Optional< std::vector< Range > > adjustRenameRanges(llvm::StringRef DraftCode, llvm::StringRef Identifier, std::vector< Range > Indexed, const LangOptions &LangOpts)
Adjusts indexed occurrences to match the current state of the file.
Definition: Rename.cpp:584
llvm::Expected< FileEdits > rename(const RenameInputs &RInputs)
Renames all occurrences of the symbol.
Definition: Rename.cpp:427
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
Definition: ParsedAST.cpp:218
std::array< uint8_t, 20 > SymbolID
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:54
llvm::Expected< Edit > buildRenameEdit(llvm::StringRef AbsFilePath, llvm::StringRef InitialCode, std::vector< Range > Occurrences, llvm::StringRef NewName)
Generates rename edits that replaces all given occurrences with the NewName.
Definition: Rename.cpp:519
void runAddDocument(ClangdServer &Server, PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags)
Definition: SyncAPI.cpp:15
const SymbolIndex * Index
Definition: Dexp.cpp:84