clang-tools 10.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 "clang/AST/Decl.h"
13 #include "llvm/Support/Casting.h"
14 #include "gmock/gmock.h"
15 #include "gtest/gtest.h"
16
17 namespace clang {
18 namespace clangd {
19 namespace {
20 using ::testing::UnorderedElementsAreArray;
21
22 SelectionTree makeSelectionTree(const StringRef MarkedCode, ParsedAST &AST) {
23 Annotations Test(MarkedCode);
24 switch (Test.points().size()) {
25 case 1: // Point selection.
26 return SelectionTree(AST.getASTContext(), AST.getTokens(),
27 cantFail(positionToOffset(Test.code(), Test.point())));
28 case 2: // Range selection.
29 return SelectionTree(
30 AST.getASTContext(), AST.getTokens(),
31 cantFail(positionToOffset(Test.code(), Test.points()[0])),
32 cantFail(positionToOffset(Test.code(), Test.points()[1])));
33 default:
34 ADD_FAILURE() << "Expected 1-2 points for selection.\n" << MarkedCode;
35 return SelectionTree(AST.getASTContext(), AST.getTokens(), 0u, 0u);
36 }
37 }
38
39 Range nodeRange(const SelectionTree::Node *N, ParsedAST &AST) {
40 if (!N)
41 return Range{};
42 const SourceManager &SM = AST.getSourceManager();
43 const LangOptions &LangOpts = AST.getLangOpts();
44 StringRef Buffer = SM.getBufferData(SM.getMainFileID());
45 if (llvm::isa_and_nonnull<TranslationUnitDecl>(N->ASTNode.get<Decl>()))
46 return Range{Position{}, offsetToPosition(Buffer, Buffer.size())};
47 auto FileRange =
48 toHalfOpenFileRange(SM, LangOpts, N->ASTNode.getSourceRange());
49 assert(FileRange && "We should be able to get the File Range");
50 return Range{
51 offsetToPosition(Buffer, SM.getFileOffset(FileRange->getBegin())),
52 offsetToPosition(Buffer, SM.getFileOffset(FileRange->getEnd()))};
53 }
54
55 std::string nodeKind(const SelectionTree::Node *N) {
56 return N ? N->kind() : "<null>";
57 }
58
59 std::vector<const SelectionTree::Node *> allNodes(const SelectionTree &T) {
60 std::vector<const SelectionTree::Node *> Result = {&T.root()};
61 for (unsigned I = 0; I < Result.size(); ++I) {
62 const SelectionTree::Node *N = Result[I];
63 Result.insert(Result.end(), N->Children.begin(), N->Children.end());
64 }
65 return Result;
66 }
67
68 // Returns true if Common is a descendent of Root.
69 // Verifies nothing is selected above Common.
70 bool verifyCommonAncestor(const SelectionTree::Node &Root,
71 const SelectionTree::Node *Common,
72 StringRef MarkedCode) {
73 if (&Root == Common)
74 return true;
75 if (Root.Selected)
76 ADD_FAILURE() << "Selected nodes outside common ancestor\n" << MarkedCode;
77 bool Seen = false;
78 for (const SelectionTree::Node *Child : Root.Children)
79 if (verifyCommonAncestor(*Child, Common, MarkedCode)) {
80 if (Seen)
81 ADD_FAILURE() << "Saw common ancestor twice\n" << MarkedCode;
82 Seen = true;
83 }
84 return Seen;
85 }
86
87 TEST(SelectionTest, CommonAncestor) {
88 struct Case {
89 // Selection is between ^marks^.
90 // common ancestor marked with a [[range]].
91 const char *Code;
92 const char *CommonAncestorKind;
93 };
94 Case Cases[] = {
95 {
96 R"cpp(
97 template <typename T>
98 int x = [[T::^U::]]ccc();
99 )cpp",
100 "NestedNameSpecifierLoc",
101 },
102 {
103 R"cpp(
104 struct AAA { struct BBB { static int ccc(); };};
105 int x = AAA::[[B^B^B]]::ccc();
106 )cpp",
107 "RecordTypeLoc",
108 },
109 {
110 R"cpp(
111 struct AAA { struct BBB { static int ccc(); };};
112 int x = AAA::[[B^BB^]]::ccc();
113 )cpp",
114 "RecordTypeLoc",
115 },
116 {
117 R"cpp(
118 struct AAA { struct BBB { static int ccc(); };};
119 int x = [[AAA::BBB::c^c^c]]();
120 )cpp",
121 "DeclRefExpr",
122 },
123 {
124 R"cpp(
125 struct AAA { struct BBB { static int ccc(); };};
126 int x = [[AAA::BBB::cc^c(^)]];
127 )cpp",
128 "CallExpr",
129 },
130
131 {
132 R"cpp(
133 void foo() { [[if (1^11) { return; } else {^ }]] }
134 )cpp",
135 "IfStmt",
136 },
137 {
138 R"cpp(
139 int x(int);
140 #define M(foo) x(foo)
141 int a = 42;
142 int b = M([[^a]]);
143 )cpp",
144 "DeclRefExpr",
145 },
146 {
147 R"cpp(
148 void foo();
149 #define CALL_FUNCTION(X) X()
150 void bar() { CALL_FUNCTION([[f^o^o]]); }
151 )cpp",
152 "DeclRefExpr",
153 },
154 {
155 R"cpp(
156 void foo();
157 #define CALL_FUNCTION(X) X()
158 void bar() { [[CALL_FUNC^TION(fo^o)]]; }
159 )cpp",
160 "CallExpr",
161 },
162 {
163 R"cpp(
164 void foo();
165 #define CALL_FUNCTION(X) X()
166 void bar() { [[C^ALL_FUNC^TION(foo)]]; }
167 )cpp",
168 "CallExpr",
169 },
170 {
171 R"cpp(
172 void foo();
173 #define CALL_FUNCTION(X) X^()^
174 void bar() { CALL_FUNCTION(foo); }
175 )cpp",
176 nullptr,
177 },
178 {
179 R"cpp(
180 struct S { S(const char*); };
181 S [[s ^= "foo"]];
182 )cpp",
183 "CXXConstructExpr",
184 },
185 {
186 R"cpp(
187 struct S { S(const char*); };
188 [[S ^s = "foo"]];
189 )cpp",
190 "VarDecl",
191 },
192 {
193 R"cpp(
194 [[^void]] (*S)(int) = nullptr;
195 )cpp",
196 "BuiltinTypeLoc",
197 },
198 {
199 R"cpp(
200 [[void (*S)^(int)]] = nullptr;
201 )cpp",
202 "FunctionProtoTypeLoc",
203 },
204 {
205 R"cpp(
206 [[void (^*S)(int)]] = nullptr;
207 )cpp",
208 "FunctionProtoTypeLoc",
209 },
210 {
211 R"cpp(
212 [[void (*^S)(int) = nullptr]];
213 )cpp",
214 "VarDecl",
215 },
216 {
217 R"cpp(
218 [[void ^(*S)(int)]] = nullptr;
219 )cpp",
220 "FunctionProtoTypeLoc",
221 },
222 {
223 R"cpp(
224 struct S {
225 int foo() const;
226 int bar() { return [[f^oo]](); }
227 };
228 )cpp",
229 "MemberExpr", // Not implicit CXXThisExpr, or its implicit cast!
230 },
231 {
232 R"cpp(
233 auto lambda = [](const char*){ return 0; };
234 int x = lambda([["y^"]]);
235 )cpp",
236 "StringLiteral", // Not DeclRefExpr to operator()!
237 },
238
239 // Point selections.
240 {"void foo() { [[^foo]](); }", "DeclRefExpr"},
241 {"void foo() { [[f^oo]](); }", "DeclRefExpr"},
242 {"void foo() { [[fo^o]](); }", "DeclRefExpr"},
243 {"void foo() { [[foo^()]]; }", "CallExpr"},
244 {"void foo() { [[foo^]] (); }", "DeclRefExpr"},
245 {"int bar; void foo() [[{ foo (); }]]^", "CompoundStmt"},
246 {"int x = [[42]]^;", "IntegerLiteral"},
247
248 // Ignores whitespace, comments, and semicolons in the selection.
249 {"void foo() { [[foo^()]]; /*comment*/^}", "CallExpr"},
250
251 // Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it.
252 {"[[^void]] foo();", "BuiltinTypeLoc"},
253 {"[[void foo^()]];", "FunctionProtoTypeLoc"},
254 {"[[^void foo^()]];", "FunctionDecl"},
255 {"[[void ^foo()]];", "FunctionDecl"},
256 // Tricky case: two VarDecls share a specifier.
257 {"[[int ^a]], b;", "VarDecl"},
258 {"[[int a, ^b]];", "VarDecl"},
259 // Tricky case: CXXConstructExpr wants to claim the whole init range.
260 {
261 R"cpp(
262 class X { X(int); };
263 class Y {
264 X x;
265 Y() : [[^x(4)]] {}
266 };
267 )cpp",
268 "CXXCtorInitializer", // Not the CXXConstructExpr!
269 },
270 // Tricky case: anonymous struct is a sibling of the VarDecl.
271 {"[[st^ruct {int x;}]] y;", "CXXRecordDecl"},
272 {"[[struct {int x;} ^y]];", "VarDecl"},
273 {"struct {[[int ^x]];} y;", "FieldDecl"},
274 // FIXME: the AST has no location info for qualifiers.
275 {"const [[a^uto]] x = 42;", "AutoTypeLoc"},
276 {"[[co^nst auto x = 42]];", "VarDecl"},
277
278 {"^", nullptr},
279 {"void foo() { [[foo^^]] (); }", "DeclRefExpr"},
280
281 // FIXME: Ideally we'd get a declstmt or the VarDecl itself here.
282 // This doesn't happen now; the RAV doesn't traverse a node containing ;.
283 {"int x = 42;^", nullptr},
284
285 // Common ancestor is logically TUDecl, but we never return that.
286 {"^int x; int y;^", nullptr},
287
288 // Node types that have caused problems in the past.
289 {"template <typename T> void foo() { [[^T]] t; }",
290 "TemplateTypeParmTypeLoc"},
291
292 // No crash
293 {
294 R"cpp(
295 template <class T> struct Foo {};
296 template <[[template<class> class /*cursor here*/^U]]>
297 struct Foo<U<int>*> {};
298 )cpp",
299 "TemplateTemplateParmDecl"},
300
301 // Foreach has a weird AST, ensure we can select parts of the range init.
302 // This used to fail, because the DeclStmt for C claimed the whole range.
303 {
304 R"cpp(
305 struct Str {
306 const char *begin();
307 const char *end();
308 };
309 Str makeStr(const char*);
310 void loop() {
311 for (const char* C : [[mak^eStr("foo"^)]])
312 ;
313 }
314 )cpp",
315 "CallExpr"},
316
317 // User-defined literals are tricky: is 12_i one token or two?
318 // For now we treat it as one, and the UserDefinedLiteral as a leaf.
319 {
320 R"cpp(
321 struct Foo{};
322 Foo operator""_ud(unsigned long long);
323 Foo x = [[^12_ud]];
324 )cpp",
325 "UserDefinedLiteral"},
326 {
327 R"cpp(
328 int a;
329 decltype([[^a]] + a) b;
330 )cpp",
331 "DeclRefExpr"},
332 };
333 for (const Case &C : Cases) {
334 Annotations Test(C.Code);
335
336 TestTU TU;
337 TU.Code = Test.code();
338
339 // FIXME: Auto-completion in a template requires disabling delayed template
340 // parsing.
341 TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
342
343 auto AST = TU.build();
344 auto T = makeSelectionTree(C.Code, AST);
345 EXPECT_EQ("TranslationUnitDecl", nodeKind(&T.root())) << C.Code;
346
347 if (Test.ranges().empty()) {
348 // If no [[range]] is marked in the example, there should be no selection.
349 EXPECT_FALSE(T.commonAncestor()) << C.Code << "\n" << T;
350 } else {
351 // If there is an expected selection, common ancestor should exist
352 // with the appropriate node type.
353 EXPECT_EQ(C.CommonAncestorKind, nodeKind(T.commonAncestor()))
354 << C.Code << "\n"
355 << T;
356 // Convert the reported common ancestor to a range and verify it.
357 EXPECT_EQ(nodeRange(T.commonAncestor(), AST), Test.range())
358 << C.Code << "\n"
359 << T;
360
361 // Check that common ancestor is reachable on exactly one path from root,
362 // and no nodes outside it are selected.
363 EXPECT_TRUE(verifyCommonAncestor(T.root(), T.commonAncestor(), C.Code))
364 << C.Code;
365 }
366 }
367 }
368
369 // Regression test: this used to match the injected X, not the outer X.
370 TEST(SelectionTest, InjectedClassName) {
371 const char* Code = "struct ^X { int x; };";
372 auto AST = TestTU::withCode(Annotations(Code).code()).build();
373 auto T = makeSelectionTree(Code, AST);
374 ASSERT_EQ("CXXRecordDecl", nodeKind(T.commonAncestor())) << T;
375 auto *D = dyn_cast<CXXRecordDecl>(T.commonAncestor()->ASTNode.get<Decl>());
376 EXPECT_FALSE(D->isInjectedClassName());
377 }
378
379 // FIXME: Doesn't select the binary operator node in
380 // #define FOO(X) X + 1
381 // int a, b = [[FOO(a)]];
382 TEST(SelectionTest, Selected) {
383 // Selection with ^marks^.
384 // Partially selected nodes marked with a [[range]].
385 // Completely selected nodes marked with a $C[[range]].
386 const char *Cases[] = {
387 R"cpp( int abc, xyz = [[^ab^c]]; )cpp",
388 R"cpp( int abc, xyz = [[a^bc^]]; )cpp",
389 R"cpp( int abc, xyz = $C[[^abc^]]; )cpp",
390 R"cpp(
391 void foo() {
392 [[if ([[1^11]]) $C[[{
393 $C[[return]];
394 }]] else [[{^
395 }]]]]
396 char z;
397 }
398 )cpp",
399 R"cpp(
400 template <class T>
401 struct unique_ptr {};
402 void foo(^$C[[unique_ptr<$C[[unique_ptr<$C[[int]]>]]>]]^ a) {}
403 )cpp",
404 R"cpp(int a = [[5 >^> 1]];)cpp",
405 R"cpp(
406 #define ECHO(X) X
407 ECHO(EC^HO($C[[int]]) EC^HO(a));
408 )cpp",
409 R"cpp( $C[[^$C[[int]] a^]]; )cpp",
410 R"cpp( $C[[^$C[[int]] a = $C[[5]]^]]; )cpp",
411 };
412 for (const char *C : Cases) {
413 Annotations Test(C);
414 auto AST = TestTU::withCode(Test.code()).build();
415 auto T = makeSelectionTree(C, AST);
416
417 std::vector<Range> Complete, Partial;
418 for (const SelectionTree::Node *N : allNodes(T))
419 if (N->Selected == SelectionTree::Complete)
420 Complete.push_back(nodeRange(N, AST));
421 else if (N->Selected == SelectionTree::Partial)
422 Partial.push_back(nodeRange(N, AST));
423 EXPECT_THAT(Complete, UnorderedElementsAreArray(Test.ranges("C"))) << C;
424 EXPECT_THAT(Partial, UnorderedElementsAreArray(Test.ranges())) << C;
425 }
426 }
427
428 TEST(SelectionTest, PathologicalPreprocessor) {
429 const char *Case = R"cpp(
430 #define MACRO while(1)
431 void test() {
432 #include "Expand.inc"
433 br^eak;
434 }
435 )cpp";
436 Annotations Test(Case);
437 auto TU = TestTU::withCode(Test.code());
438 TU.AdditionalFiles["Expand.inc"] = "MACRO\n";
439 auto AST = TU.build();
440 EXPECT_THAT(AST.getDiagnostics(), ::testing::IsEmpty());
441 auto T = makeSelectionTree(Case, AST);
442
443 EXPECT_EQ("BreakStmt", T.commonAncestor()->kind());
444 EXPECT_EQ("WhileStmt", T.commonAncestor()->Parent->kind());
445 }
446
447 TEST(SelectionTest, IncludedFile) {
448 const char *Case = R"cpp(
449 void test() {
450 #include "Exp^and.inc"
451 break;
452 }
453 )cpp";
454 Annotations Test(Case);
455 auto TU = TestTU::withCode(Test.code());
456 TU.AdditionalFiles["Expand.inc"] = "while(1)\n";
457 auto AST = TU.build();
458 auto T = makeSelectionTree(Case, AST);
459
460 EXPECT_EQ("WhileStmt", T.commonAncestor()->kind());
461 }
462
463 TEST(SelectionTest, MacroArgExpansion) {
464 // If a macro arg is expanded several times, we consider them all selected.
465 const char *Case = R"cpp(
466 int mul(int, int);
467 #define SQUARE(X) mul(X, X);
468 int nine = SQUARE(^3);
469 )cpp";
470 Annotations Test(Case);
471 auto AST = TestTU::withCode(Test.code()).build();
472 auto T = makeSelectionTree(Case, AST);
473 // Unfortunately, this makes the common ancestor the CallExpr...
474 // FIXME: hack around this by picking one?
475 EXPECT_EQ("CallExpr", T.commonAncestor()->kind());
476 EXPECT_FALSE(T.commonAncestor()->Selected);
477 EXPECT_EQ(2u, T.commonAncestor()->Children.size());
478 for (const auto* N : T.commonAncestor()->Children) {
479 EXPECT_EQ("IntegerLiteral", N->kind());
480 EXPECT_TRUE(N->Selected);
481 }
482
483 // Verify that the common assert() macro doesn't suffer from this.
484 // (This is because we don't associate the stringified token with the arg).
485 Case = R"cpp(
486 void die(const char*);
487 #define assert(x) (x ? (void)0 : die(#x)
488 void foo() { assert(^42); }
489 )cpp";
490 Test = Annotations(Case);
491 AST = TestTU::withCode(Test.code()).build();
492 T = makeSelectionTree(Case, AST);
493
494 EXPECT_EQ("IntegerLiteral", T.commonAncestor()->kind());
495 }
496
497 TEST(SelectionTest, Implicit) {
498 const char* Test = R"cpp(
499 struct S { S(const char*); };
500 int f(S);
501 int x = f("^");
502 )cpp";
503 auto AST = TestTU::withCode(Annotations(Test).code()).build();
504 auto T = makeSelectionTree(Test, AST);
505
506 const SelectionTree::Node *Str = T.commonAncestor();
507 EXPECT_EQ("StringLiteral", nodeKind(Str)) << "Implicit selected?";
508 EXPECT_EQ("ImplicitCastExpr", nodeKind(Str->Parent));
509 EXPECT_EQ("CXXConstructExpr", nodeKind(Str->Parent->Parent));
510 EXPECT_EQ(Str, &Str->Parent->Parent->ignoreImplicit())
511 << "Didn't unwrap " << nodeKind(&Str->Parent->Parent->ignoreImplicit());
512
513 EXPECT_EQ("CXXConstructExpr", nodeKind(&Str->outerImplicit()));
514 }
515
516 } // namespace
517 } // namespace clangd
518 } // namespace clang
clang::clangd::TestTU::build
ParsedAST build() const
Definition: TestTU.cpp:22
Code
std::string Code
Definition: FindTargetTests.cpp:67
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:93
SourceCode.h
TestTU.h
clang::clangd::SymbolOrigin::AST
Annotations.h
clang::clangd::offsetToPosition
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
Definition: SourceCode.cpp:188
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:647
clang::clangd::positionToOffset
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:155
clang::clangd::SelectionTree::Partial
Definition: Selection.h:89
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
clang::clangd::toHalfOpenFileRange
llvm::Optional< SourceRange > toHalfOpenFileRange(const SourceManager &SM, const LangOptions &LangOpts, SourceRange R)
Turns a token range into a half-open range and checks its correctness.
Definition: SourceCode.cpp:538
clang::clangd::SelectionTree::Complete
Definition: Selection.h:91
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
Selected
SelectionTree::Selection Selected
Definition: Selection.cpp:315
Selection.h
clang::clangd::TestTU::AdditionalFiles
llvm::StringMap< std::string > AdditionalFiles
Definition: TestTU.h:54