clang-tools  9.0.0
SelectionTests.cpp
Go to the documentation of this file.
1 //===-- SelectionTests.cpp - ----------------------------------------------===//
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 #include "Annotations.h"
9 #include "Selection.h"
10 #include "SourceCode.h"
11 #include "TestTU.h"
12 #include "gmock/gmock.h"
13 #include "gtest/gtest.h"
14 
15 namespace clang {
16 namespace clangd {
17 namespace {
18 using ::testing::UnorderedElementsAreArray;
19 
20 SelectionTree makeSelectionTree(const StringRef MarkedCode, ParsedAST &AST) {
21  Annotations Test(MarkedCode);
22  switch (Test.points().size()) {
23  case 1: // Point selection.
24  return SelectionTree(AST.getASTContext(),
25  cantFail(positionToOffset(Test.code(), Test.point())));
26  case 2: // Range selection.
27  return SelectionTree(
28  AST.getASTContext(),
29  cantFail(positionToOffset(Test.code(), Test.points()[0])),
30  cantFail(positionToOffset(Test.code(), Test.points()[1])));
31  default:
32  ADD_FAILURE() << "Expected 1-2 points for selection.\n" << MarkedCode;
33  return SelectionTree(AST.getASTContext(), 0u, 0u);
34  }
35 }
36 
37 Range nodeRange(const SelectionTree::Node *N, ParsedAST &AST) {
38  if (!N)
39  return Range{};
40  SourceManager &SM = AST.getSourceManager();
41  StringRef Buffer = SM.getBufferData(SM.getMainFileID());
42  SourceRange SR = N->ASTNode.getSourceRange();
43  SR.setBegin(SM.getFileLoc(SR.getBegin()));
44  SR.setEnd(SM.getFileLoc(SR.getEnd()));
45  CharSourceRange R =
46  Lexer::getAsCharRange(SR, SM, AST.getASTContext().getLangOpts());
47  return Range{offsetToPosition(Buffer, SM.getFileOffset(R.getBegin())),
48  offsetToPosition(Buffer, SM.getFileOffset(R.getEnd()))};
49 }
50 
51 std::string nodeKind(const SelectionTree::Node *N) {
52  if (!N)
53  return "<null>";
54  return N->ASTNode.getNodeKind().asStringRef().str();
55 }
56 
57 std::vector<const SelectionTree::Node *> allNodes(const SelectionTree &T) {
58  std::vector<const SelectionTree::Node *> Result = {T.root()};
59  for (unsigned I = 0; I < Result.size(); ++I) {
60  const SelectionTree::Node *N = Result[I];
61  Result.insert(Result.end(), N->Children.begin(), N->Children.end());
62  }
63  return Result;
64 }
65 
66 // Returns true if Common is a descendent of Root.
67 // Verifies nothing is selected above Common.
68 bool verifyCommonAncestor(const SelectionTree::Node *Root,
69  const SelectionTree::Node *Common,
70  StringRef MarkedCode) {
71  if (Root == Common)
72  return true;
73  if (Root->Selected)
74  ADD_FAILURE() << "Selected nodes outside common ancestor\n" << MarkedCode;
75  bool Seen = false;
76  for (const SelectionTree::Node *Child : Root->Children)
77  if (verifyCommonAncestor(Child, Common, MarkedCode)) {
78  if (Seen)
79  ADD_FAILURE() << "Saw common ancestor twice\n" << MarkedCode;
80  Seen = true;
81  }
82  return Seen;
83 }
84 
85 TEST(SelectionTest, CommonAncestor) {
86  struct Case {
87  // Selection is between ^marks^.
88  // common ancestor marked with a [[range]].
89  const char *Code;
90  const char *CommonAncestorKind;
91  };
92  Case Cases[] = {
93  {
94  R"cpp(
95  template <typename T>
96  int x = [[T::^U::]]ccc();
97  )cpp",
98  "NestedNameSpecifierLoc",
99  },
100  {
101  R"cpp(
102  struct AAA { struct BBB { static int ccc(); };};
103  int x = AAA::[[B^B^B]]::ccc();
104  )cpp",
105  "TypeLoc",
106  },
107  {
108  R"cpp(
109  struct AAA { struct BBB { static int ccc(); };};
110  int x = AAA::[[B^BB^]]::ccc();
111  )cpp",
112  "TypeLoc",
113  },
114  {
115  R"cpp(
116  struct AAA { struct BBB { static int ccc(); };};
117  int x = [[AAA::BBB::c^c^c]]();
118  )cpp",
119  "DeclRefExpr",
120  },
121  {
122  R"cpp(
123  struct AAA { struct BBB { static int ccc(); };};
124  int x = [[AAA::BBB::cc^c(^)]];
125  )cpp",
126  "CallExpr",
127  },
128 
129  {
130  R"cpp(
131  void foo() { [[if (1^11) { return; } else {^ }]] }
132  )cpp",
133  "IfStmt",
134  },
135  {
136  R"cpp(
137  void foo();
138  #define CALL_FUNCTION(X) X()
139  void bar() { CALL_FUNCTION([[f^o^o]]); }
140  )cpp",
141  "DeclRefExpr",
142  },
143  {
144  R"cpp(
145  void foo();
146  #define CALL_FUNCTION(X) X()
147  void bar() [[{ CALL_FUNC^TION(fo^o); }]]
148  )cpp",
149  "CompoundStmt",
150  },
151  {
152  R"cpp(
153  void foo();
154  #define CALL_FUNCTION(X) X()
155  void bar() [[{ C^ALL_FUNC^TION(foo); }]]
156  )cpp",
157  "CompoundStmt",
158  },
159  {
160  R"cpp(
161  void foo();
162  #define CALL_FUNCTION(X) X^()^
163  void bar() { CALL_FUNCTION(foo); }
164  )cpp",
165  nullptr,
166  },
167  {
168  R"cpp(
169  struct S { S(const char*); };
170  S [[s ^= "foo"]];
171  )cpp",
172  "CXXConstructExpr",
173  },
174  {
175  R"cpp(
176  struct S { S(const char*); };
177  [[S ^s = "foo"]];
178  )cpp",
179  "VarDecl",
180  },
181  {
182  R"cpp(
183  [[^void]] (*S)(int) = nullptr;
184  )cpp",
185  "TypeLoc",
186  },
187  {
188  R"cpp(
189  [[void (*S)^(int)]] = nullptr;
190  )cpp",
191  "TypeLoc",
192  },
193  {
194  R"cpp(
195  [[void (^*S)(int)]] = nullptr;
196  )cpp",
197  "TypeLoc",
198  },
199  {
200  R"cpp(
201  [[void (*^S)(int) = nullptr]];
202  )cpp",
203  "VarDecl",
204  },
205  {
206  R"cpp(
207  [[void ^(*S)(int)]] = nullptr;
208  )cpp",
209  "TypeLoc",
210  },
211 
212  // Point selections.
213  {"void foo() { [[^foo]](); }", "DeclRefExpr"},
214  {"void foo() { [[f^oo]](); }", "DeclRefExpr"},
215  {"void foo() { [[fo^o]](); }", "DeclRefExpr"},
216  {"void foo() { [[foo^()]]; }", "CallExpr"},
217  {"void foo() { [[foo^]] (); }", "DeclRefExpr"},
218  {"int bar; void foo() [[{ foo (); }]]^", "CompoundStmt"},
219 
220  // Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it.
221  {"[[^void]] foo();", "TypeLoc"},
222  {"[[void foo^()]];", "TypeLoc"},
223  {"[[^void foo^()]];", "FunctionDecl"},
224  {"[[void ^foo()]];", "FunctionDecl"},
225  // Tricky case: two VarDecls share a specifier.
226  {"[[int ^a]], b;", "VarDecl"},
227  {"[[int a, ^b]];", "VarDecl"},
228  // Tricky case: anonymous struct is a sibling of the VarDecl.
229  {"[[st^ruct {int x;}]] y;", "CXXRecordDecl"},
230  {"[[struct {int x;} ^y]];", "VarDecl"},
231  {"struct {[[int ^x]];} y;", "FieldDecl"},
232 
233  {"^", nullptr},
234  {"void foo() { [[foo^^]] (); }", "DeclRefExpr"},
235 
236  // FIXME: Ideally we'd get a declstmt or the VarDecl itself here.
237  // This doesn't happen now; the RAV doesn't traverse a node containing ;.
238  {"int x = 42;^", nullptr},
239  {"int x = 42^;", nullptr},
240 
241  // Node types that have caused problems in the past.
242  {"template <typename T> void foo() { [[^T]] t; }", "TypeLoc"},
243 
244  // No crash
245  {
246  R"cpp(
247  template <class T> struct Foo {};
248  template <[[template<class> class /*cursor here*/^U]]>
249  struct Foo<U<int>*> {};
250  )cpp",
251  "TemplateTemplateParmDecl"},
252  };
253  for (const Case &C : Cases) {
254  Annotations Test(C.Code);
255  auto AST = TestTU::withCode(Test.code()).build();
256  auto T = makeSelectionTree(C.Code, AST);
257 
258  if (Test.ranges().empty()) {
259  // If no [[range]] is marked in the example, there should be no selection.
260  EXPECT_FALSE(T.commonAncestor()) << C.Code << "\n" << T;
261  EXPECT_FALSE(T.root()) << C.Code << "\n" << T;
262  } else {
263  // If there is an expected selection, both common ancestor and root
264  // should exist with the appropriate node types in them.
265  EXPECT_EQ(C.CommonAncestorKind, nodeKind(T.commonAncestor()))
266  << C.Code << "\n"
267  << T;
268  EXPECT_EQ("TranslationUnitDecl", nodeKind(T.root())) << C.Code;
269  // Convert the reported common ancestor to a range and verify it.
270  EXPECT_EQ(nodeRange(T.commonAncestor(), AST), Test.range())
271  << C.Code << "\n"
272  << T;
273 
274  // Check that common ancestor is reachable on exactly one path from root,
275  // and no nodes outside it are selected.
276  EXPECT_TRUE(verifyCommonAncestor(T.root(), T.commonAncestor(), C.Code))
277  << C.Code;
278  }
279  }
280 }
281 
282 // Regression test: this used to match the injected X, not the outer X.
283 TEST(SelectionTest, InjectedClassName) {
284  const char* Code = "struct ^X { int x; };";
285  auto AST = TestTU::withCode(Annotations(Code).code()).build();
286  auto T = makeSelectionTree(Code, AST);
287  ASSERT_EQ("CXXRecordDecl", nodeKind(T.commonAncestor())) << T;
288  auto *D = dyn_cast<CXXRecordDecl>(T.commonAncestor()->ASTNode.get<Decl>());
289  EXPECT_FALSE(D->isInjectedClassName());
290 }
291 
292 TEST(SelectionTest, Selected) {
293  // Selection with ^marks^.
294  // Partially selected nodes marked with a [[range]].
295  // Completely selected nodes marked with a $C[[range]].
296  const char *Cases[] = {
297  R"cpp( int abc, xyz = [[^ab^c]]; )cpp",
298  R"cpp( int abc, xyz = [[a^bc^]]; )cpp",
299  R"cpp( int abc, xyz = $C[[^abc^]]; )cpp",
300  R"cpp(
301  void foo() {
302  [[if ([[1^11]]) $C[[{
303  $C[[return]];
304  }]] else [[{^
305  }]]]]
306  }
307  )cpp",
308  R"cpp(
309  template <class T>
310  struct unique_ptr {};
311  void foo(^$C[[unique_ptr<unique_ptr<$C[[int]]>>]]^ a) {}
312  )cpp",
313  };
314  for (const char *C : Cases) {
315  Annotations Test(C);
316  auto AST = TestTU::withCode(Test.code()).build();
317  auto T = makeSelectionTree(C, AST);
318 
319  std::vector<Range> Complete, Partial;
320  for (const SelectionTree::Node *N : allNodes(T))
321  if (N->Selected == SelectionTree::Complete)
322  Complete.push_back(nodeRange(N, AST));
323  else if (N->Selected == SelectionTree::Partial)
324  Partial.push_back(nodeRange(N, AST));
325  EXPECT_THAT(Complete, UnorderedElementsAreArray(Test.ranges("C"))) << C;
326  EXPECT_THAT(Partial, UnorderedElementsAreArray(Test.ranges())) << C;
327  }
328 }
329 
330 } // namespace
331 } // namespace clangd
332 } // namespace clang
ParsedAST build() const
Definition: TestTU.cpp:20
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
Definition: SourceCode.cpp:174
TEST(BackgroundQueueTest, Priority)
llvm::Expected< size_t > positionToOffset(llvm::StringRef Code, Position P, bool AllowColumnsBeyondLineLength)
Turn a [line, column] pair into an offset in Code.
Definition: SourceCode.cpp:141
const Decl * D
Definition: XRefs.cpp:868
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
llvm::Optional< llvm::Expected< tooling::AtomicChanges > > Result
Definition: Rename.cpp:36