clang-tools  9.0.0
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 "TestFS.h"
11 #include "TestTU.h"
12 #include "refactor/Rename.h"
13 #include "clang/Tooling/Core/Replacement.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16 
17 namespace clang {
18 namespace clangd {
19 namespace {
20 
21 MATCHER_P2(RenameRange, Code, Range, "") {
22  return replacementToEdit(Code, arg).range == Range;
23 }
24 
25 TEST(RenameTest, SingleFile) {
26  struct Test {
27  const char* Before;
28  const char* After;
29  } Tests[] = {
30  // Rename function.
31  {
32  R"cpp(
33  void foo() {
34  fo^o();
35  }
36  )cpp",
37  R"cpp(
38  void abcde() {
39  abcde();
40  }
41  )cpp",
42  },
43  // Rename type.
44  {
45  R"cpp(
46  struct foo{};
47  foo test() {
48  f^oo x;
49  return x;
50  }
51  )cpp",
52  R"cpp(
53  struct abcde{};
54  abcde test() {
55  abcde x;
56  return x;
57  }
58  )cpp",
59  },
60  // Rename variable.
61  {
62  R"cpp(
63  void bar() {
64  if (auto ^foo = 5) {
65  foo = 3;
66  }
67  }
68  )cpp",
69  R"cpp(
70  void bar() {
71  if (auto abcde = 5) {
72  abcde = 3;
73  }
74  }
75  )cpp",
76  },
77  };
78  for (const Test &T : Tests) {
79  Annotations Code(T.Before);
80  auto TU = TestTU::withCode(Code.code());
81  auto AST = TU.build();
82  auto RenameResult =
83  renameWithinFile(AST, testPath(TU.Filename), Code.point(), "abcde");
84  ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
85  auto ApplyResult =
86  tooling::applyAllReplacements(Code.code(), *RenameResult);
87  ASSERT_TRUE(bool(ApplyResult)) << ApplyResult.takeError();
88 
89  EXPECT_EQ(T.After, *ApplyResult) << T.Before;
90  }
91 }
92 
93 TEST(RenameTest, Renameable) {
94  struct Case {
95  const char *Code;
96  const char* ErrorMessage; // null if no error
97  bool IsHeaderFile;
98  };
99  const bool HeaderFile = true;
100  Case Cases[] = {
101  {R"cpp(// allow -- function-local
102  void f(int [[Lo^cal]]) {
103  [[Local]] = 2;
104  }
105  )cpp",
106  nullptr, HeaderFile},
107 
108  {R"cpp(// allow -- symbol is indexable and has no refs in index.
109  void [[On^lyInThisFile]]();
110  )cpp",
111  nullptr, HeaderFile},
112 
113  {R"cpp(// disallow -- symbol is indexable and has other refs in index.
114  void f() {
115  Out^side s;
116  }
117  )cpp",
118  "used outside main file", HeaderFile},
119 
120  {R"cpp(// disallow -- symbol is not indexable.
121  namespace {
122  class Unin^dexable {};
123  }
124  )cpp",
125  "not eligible for indexing", HeaderFile},
126 
127  {R"cpp(// disallow -- namespace symbol isn't supported
128  namespace fo^o {}
129  )cpp",
130  "not a supported kind", HeaderFile},
131 
132  {
133  R"cpp(
134  #define MACRO 1
135  int s = MAC^RO;
136  )cpp",
137  "not a supported kind", HeaderFile},
138 
139  {R"cpp(// foo is declared outside the file.
140  void fo^o() {}
141  )cpp", "used outside main file", !HeaderFile/*cc file*/},
142  };
143  const char *CommonHeader = R"cpp(
144  class Outside {};
145  void foo();
146  )cpp";
147  TestTU OtherFile = TestTU::withCode("Outside s; auto ss = &foo;");
148  OtherFile.HeaderCode = CommonHeader;
149  OtherFile.Filename = "other.cc";
150  // The index has a "Outside" reference and a "foo" reference.
151  auto OtherFileIndex = OtherFile.index();
152 
153  for (const auto& Case : Cases) {
154  Annotations T(Case.Code);
155  TestTU TU = TestTU::withCode(T.code());
156  TU.HeaderCode = CommonHeader;
157  if (Case.IsHeaderFile) {
158  // We open the .h file as the main file.
159  TU.Filename = "test.h";
160  // Parsing the .h file as C++ include.
161  TU.ExtraArgs.push_back("-xobjective-c++-header");
162  }
163  auto AST = TU.build();
164 
165  auto Results = renameWithinFile(AST, testPath(TU.Filename), T.point(),
166  "dummyNewName", OtherFileIndex.get());
167  bool WantRename = true;
168  if (T.ranges().empty())
169  WantRename = false;
170  if (!WantRename) {
171  assert(Case.ErrorMessage && "Error message must be set!");
172  EXPECT_FALSE(Results) << "expected renameWithinFile returned an error: "
173  << T.code();
174  auto ActualMessage = llvm::toString(Results.takeError());
175  EXPECT_THAT(ActualMessage, testing::HasSubstr(Case.ErrorMessage));
176  } else {
177  EXPECT_TRUE(bool(Results)) << "renameWithinFile returned an error: "
178  << llvm::toString(Results.takeError());
179  std::vector<testing::Matcher<tooling::Replacement>> Expected;
180  for (const auto &R : T.ranges())
181  Expected.push_back(RenameRange(TU.Code, R));
182  EXPECT_THAT(*Results, UnorderedElementsAreArray(Expected));
183  }
184  }
185 }
186 
187 } // namespace
188 } // namespace clangd
189 } // namespace clang
std::string HeaderCode
Definition: TestTU.h:50
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
std::vector< CodeCompletionResult > Results
llvm::Expected< tooling::Replacements > renameWithinFile(ParsedAST &AST, llvm::StringRef File, Position Pos, llvm::StringRef NewName, const SymbolIndex *Index)
Renames all occurrences of the symbol at Pos to NewName.
Definition: Rename.cpp:160
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
Range range
The range of the text document to be manipulated.
Definition: Protocol.h:204
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
TextEdit replacementToEdit(llvm::StringRef Code, const tooling::Replacement &R)
Definition: SourceCode.cpp:443
CharSourceRange Range
SourceRange for the file name.
std::vector< const char * > Expected