1 //===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "TestVisitor.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Tooling/Refactoring/ASTSelection.h"
13 using namespace clang
;
14 using namespace tooling
;
19 unsigned Line
, Column
;
21 SourceLocation
translate(const SourceManager
&SM
) {
22 return SM
.translateLineCol(SM
.getMainFileID(), Line
, Column
);
26 using FileRange
= std::pair
<FileLocation
, FileLocation
>;
28 class SelectionFinderVisitor
: public TestVisitor
<SelectionFinderVisitor
> {
29 FileLocation Location
;
30 Optional
<FileRange
> SelectionRange
;
31 llvm::function_ref
<void(SourceRange SelectionRange
,
32 Optional
<SelectedASTNode
>)>
36 SelectionFinderVisitor(FileLocation Location
,
37 Optional
<FileRange
> SelectionRange
,
38 llvm::function_ref
<void(SourceRange SelectionRange
,
39 Optional
<SelectedASTNode
>)>
41 : Location(Location
), SelectionRange(SelectionRange
), Consumer(Consumer
) {
44 bool VisitTranslationUnitDecl(const TranslationUnitDecl
*TU
) {
45 const ASTContext
&Context
= TU
->getASTContext();
46 const SourceManager
&SM
= Context
.getSourceManager();
50 SelRange
= SourceRange(SelectionRange
->first
.translate(SM
),
51 SelectionRange
->second
.translate(SM
));
53 SourceLocation Loc
= Location
.translate(SM
);
54 SelRange
= SourceRange(Loc
, Loc
);
56 Consumer(SelRange
, findSelectedASTNodes(Context
, SelRange
));
61 /// This is a test utility function that computes the AST selection at the
62 /// given location with an optional selection range.
64 /// A location roughly corresponds to a cursor location in an editor, while
65 /// the optional range corresponds to the selection range in an editor.
66 void findSelectedASTNodesWithRange(
67 StringRef Source
, FileLocation Location
, Optional
<FileRange
> SelectionRange
,
68 llvm::function_ref
<void(SourceRange SelectionRange
,
69 Optional
<SelectedASTNode
>)>
71 SelectionFinderVisitor::Language Language
=
72 SelectionFinderVisitor::Lang_CXX11
) {
73 SelectionFinderVisitor
Visitor(Location
, SelectionRange
, Consumer
);
74 EXPECT_TRUE(Visitor
.runOver(Source
, Language
));
77 void findSelectedASTNodes(
78 StringRef Source
, FileLocation Location
, Optional
<FileRange
> SelectionRange
,
79 llvm::function_ref
<void(Optional
<SelectedASTNode
>)> Consumer
,
80 SelectionFinderVisitor::Language Language
=
81 SelectionFinderVisitor::Lang_CXX11
) {
82 findSelectedASTNodesWithRange(
83 Source
, Location
, SelectionRange
,
84 [&](SourceRange
, Optional
<SelectedASTNode
> Selection
) {
85 Consumer(std::move(Selection
));
90 void checkNodeImpl(bool IsTypeMatched
, const SelectedASTNode
&Node
,
91 SourceSelectionKind SelectionKind
, unsigned NumChildren
) {
92 ASSERT_TRUE(IsTypeMatched
);
93 EXPECT_EQ(Node
.Children
.size(), NumChildren
);
94 ASSERT_EQ(Node
.SelectionKind
, SelectionKind
);
97 void checkDeclName(const SelectedASTNode
&Node
, StringRef Name
) {
98 const auto *ND
= Node
.Node
.get
<NamedDecl
>();
100 ASSERT_EQ(ND
->getName(), Name
);
103 template <typename T
>
104 const SelectedASTNode
&checkNode(
105 const SelectedASTNode
&StmtNode
, SourceSelectionKind SelectionKind
,
106 unsigned NumChildren
= 0,
107 std::enable_if_t
<std::is_base_of
<Stmt
, T
>::value
, T
> *StmtOverloadChecker
=
109 checkNodeImpl(isa
<T
>(StmtNode
.Node
.get
<Stmt
>()), StmtNode
, SelectionKind
,
114 template <typename T
>
115 const SelectedASTNode
&checkNode(
116 const SelectedASTNode
&DeclNode
, SourceSelectionKind SelectionKind
,
117 unsigned NumChildren
= 0, StringRef Name
= "",
118 std::enable_if_t
<std::is_base_of
<Decl
, T
>::value
, T
> *DeclOverloadChecker
=
120 checkNodeImpl(isa
<T
>(DeclNode
.Node
.get
<Decl
>()), DeclNode
, SelectionKind
,
123 checkDeclName(DeclNode
, Name
);
127 struct ForAllChildrenOf
{
128 const SelectedASTNode
&Node
;
130 static void childKindVerifier(const SelectedASTNode
&Node
,
131 SourceSelectionKind SelectionKind
) {
132 for (const SelectedASTNode
&Child
: Node
.Children
) {
133 ASSERT_EQ(Node
.SelectionKind
, SelectionKind
);
134 childKindVerifier(Child
, SelectionKind
);
139 ForAllChildrenOf(const SelectedASTNode
&Node
) : Node(Node
) {}
141 void shouldHaveSelectionKind(SourceSelectionKind Kind
) {
142 childKindVerifier(Node
, Kind
);
146 ForAllChildrenOf
allChildrenOf(const SelectedASTNode
&Node
) {
147 return ForAllChildrenOf(Node
);
150 TEST(ASTSelectionFinder
, CursorNoSelection
) {
151 findSelectedASTNodes(
152 " void f() { }", {1, 1}, None
,
153 [](Optional
<SelectedASTNode
> Node
) { EXPECT_FALSE(Node
); });
156 TEST(ASTSelectionFinder
, CursorAtStartOfFunction
) {
157 findSelectedASTNodes(
158 "void f() { }", {1, 1}, None
, [](Optional
<SelectedASTNode
> Node
) {
160 checkNode
<TranslationUnitDecl
>(*Node
, SourceSelectionKind::None
,
162 checkNode
<FunctionDecl
>(Node
->Children
[0],
163 SourceSelectionKind::ContainsSelection
,
164 /*NumChildren=*/0, /*Name=*/"f");
166 // Check that the dumping works.
167 std::string DumpValue
;
168 llvm::raw_string_ostream
OS(DumpValue
);
169 Node
->Children
[0].dump(OS
);
170 ASSERT_EQ(OS
.str(), "FunctionDecl \"f\" contains-selection\n");
174 TEST(ASTSelectionFinder
, RangeNoSelection
) {
175 findSelectedASTNodes(
176 " void f() { }", {1, 1}, FileRange
{{1, 1}, {1, 1}},
177 [](Optional
<SelectedASTNode
> Node
) { EXPECT_FALSE(Node
); });
178 findSelectedASTNodes(
179 " void f() { }", {1, 1}, FileRange
{{1, 1}, {1, 2}},
180 [](Optional
<SelectedASTNode
> Node
) { EXPECT_FALSE(Node
); });
183 TEST(ASTSelectionFinder
, EmptyRangeFallbackToCursor
) {
184 findSelectedASTNodes("void f() { }", {1, 1}, FileRange
{{1, 1}, {1, 1}},
185 [](Optional
<SelectedASTNode
> Node
) {
187 checkNode
<FunctionDecl
>(
189 SourceSelectionKind::ContainsSelection
,
190 /*NumChildren=*/0, /*Name=*/"f");
194 TEST(ASTSelectionFinder
, WholeFunctionSelection
) {
195 StringRef Source
= "int f(int x) { return x;\n}\nvoid f2() { }";
196 // From 'int' until just after '}':
198 findSelectedASTNodes(
199 Source
, {1, 1}, FileRange
{{1, 1}, {2, 2}},
200 [](Optional
<SelectedASTNode
> Node
) {
202 EXPECT_EQ(Node
->Children
.size(), 1u);
203 const auto &Fn
= checkNode
<FunctionDecl
>(
204 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
205 /*NumChildren=*/2, /*Name=*/"f");
206 checkNode
<ParmVarDecl
>(Fn
.Children
[0],
207 SourceSelectionKind::InsideSelection
);
208 const auto &Body
= checkNode
<CompoundStmt
>(
209 Fn
.Children
[1], SourceSelectionKind::InsideSelection
,
211 const auto &Return
= checkNode
<ReturnStmt
>(
212 Body
.Children
[0], SourceSelectionKind::InsideSelection
,
214 checkNode
<ImplicitCastExpr
>(Return
.Children
[0],
215 SourceSelectionKind::InsideSelection
,
217 checkNode
<DeclRefExpr
>(Return
.Children
[0].Children
[0],
218 SourceSelectionKind::InsideSelection
);
221 // From 'int' until just before '}':
222 findSelectedASTNodes(
223 Source
, {2, 1}, FileRange
{{1, 1}, {2, 1}},
224 [](Optional
<SelectedASTNode
> Node
) {
226 EXPECT_EQ(Node
->Children
.size(), 1u);
227 const auto &Fn
= checkNode
<FunctionDecl
>(
228 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
229 /*NumChildren=*/2, /*Name=*/"f");
230 const auto &Body
= checkNode
<CompoundStmt
>(
231 Fn
.Children
[1], SourceSelectionKind::ContainsSelectionEnd
,
233 checkNode
<ReturnStmt
>(Body
.Children
[0],
234 SourceSelectionKind::InsideSelection
,
237 // From '{' until just after '}':
238 findSelectedASTNodes(
239 Source
, {1, 14}, FileRange
{{1, 14}, {2, 2}},
240 [](Optional
<SelectedASTNode
> Node
) {
242 EXPECT_EQ(Node
->Children
.size(), 1u);
243 const auto &Fn
= checkNode
<FunctionDecl
>(
244 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
245 /*NumChildren=*/1, /*Name=*/"f");
246 const auto &Body
= checkNode
<CompoundStmt
>(
247 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
249 checkNode
<ReturnStmt
>(Body
.Children
[0],
250 SourceSelectionKind::InsideSelection
,
253 // From 'x' until just after '}':
254 findSelectedASTNodes(
255 Source
, {2, 2}, FileRange
{{1, 11}, {2, 2}},
256 [](Optional
<SelectedASTNode
> Node
) {
258 EXPECT_EQ(Node
->Children
.size(), 1u);
259 const auto &Fn
= checkNode
<FunctionDecl
>(
260 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
261 /*NumChildren=*/2, /*Name=*/"f");
262 checkNode
<ParmVarDecl
>(Fn
.Children
[0],
263 SourceSelectionKind::ContainsSelectionStart
);
264 const auto &Body
= checkNode
<CompoundStmt
>(
265 Fn
.Children
[1], SourceSelectionKind::InsideSelection
,
267 checkNode
<ReturnStmt
>(Body
.Children
[0],
268 SourceSelectionKind::InsideSelection
,
273 TEST(ASTSelectionFinder
, MultipleFunctionSelection
) {
274 StringRef Source
= R
"(void f0() {
280 auto SelectedF1F2
= [](Optional
<SelectedASTNode
> Node
) {
282 EXPECT_EQ(Node
->Children
.size(), 2u);
283 checkNode
<FunctionDecl
>(Node
->Children
[0],
284 SourceSelectionKind::InsideSelection
,
285 /*NumChildren=*/1, /*Name=*/"f1");
286 checkNode
<FunctionDecl
>(Node
->Children
[1],
287 SourceSelectionKind::InsideSelection
,
288 /*NumChildren=*/1, /*Name=*/"f2");
290 // Just after '}' of f0 and just before 'void' of f3:
291 findSelectedASTNodes(Source
, {2, 2}, FileRange
{{2, 2}, {5, 1}}, SelectedF1F2
);
292 // Just before 'void' of f1 and just after '}' of f2:
293 findSelectedASTNodes(Source
, {3, 1}, FileRange
{{3, 1}, {4, 14}},
297 TEST(ASTSelectionFinder
, MultipleStatementSelection
) {
298 StringRef Source
= R
"(void f(int x, int y) {
307 // From 'f(2,3)' until just before 'x = 1;':
308 findSelectedASTNodes(
309 Source
, {3, 2}, FileRange
{{3, 2}, {7, 1}},
310 [](Optional
<SelectedASTNode
> Node
) {
312 EXPECT_EQ(Node
->Children
.size(), 1u);
313 const auto &Fn
= checkNode
<FunctionDecl
>(
314 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
315 /*NumChildren=*/1, /*Name=*/"f");
316 const auto &Body
= checkNode
<CompoundStmt
>(
317 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
319 allChildrenOf(checkNode
<CallExpr
>(Body
.Children
[0],
320 SourceSelectionKind::InsideSelection
,
322 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection
);
323 allChildrenOf(checkNode
<IfStmt
>(Body
.Children
[1],
324 SourceSelectionKind::InsideSelection
,
326 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection
);
328 // From 'f(2,3)' until just before ';' in 'x = 1;':
329 findSelectedASTNodes(
330 Source
, {3, 2}, FileRange
{{3, 2}, {7, 8}},
331 [](Optional
<SelectedASTNode
> Node
) {
333 EXPECT_EQ(Node
->Children
.size(), 1u);
334 const auto &Fn
= checkNode
<FunctionDecl
>(
335 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
336 /*NumChildren=*/1, /*Name=*/"f");
337 const auto &Body
= checkNode
<CompoundStmt
>(
338 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
340 checkNode
<CallExpr
>(Body
.Children
[0],
341 SourceSelectionKind::InsideSelection
,
343 checkNode
<IfStmt
>(Body
.Children
[1],
344 SourceSelectionKind::InsideSelection
,
346 checkNode
<BinaryOperator
>(Body
.Children
[2],
347 SourceSelectionKind::InsideSelection
,
350 // From the middle of 'int z = 3' until the middle of 'x = 1;':
351 findSelectedASTNodes(
352 Source
, {2, 10}, FileRange
{{2, 10}, {7, 5}},
353 [](Optional
<SelectedASTNode
> Node
) {
355 EXPECT_EQ(Node
->Children
.size(), 1u);
356 const auto &Fn
= checkNode
<FunctionDecl
>(
357 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
358 /*NumChildren=*/1, /*Name=*/"f");
359 const auto &Body
= checkNode
<CompoundStmt
>(
360 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
362 checkNode
<DeclStmt
>(Body
.Children
[0],
363 SourceSelectionKind::ContainsSelectionStart
,
365 checkNode
<CallExpr
>(Body
.Children
[1],
366 SourceSelectionKind::InsideSelection
,
368 checkNode
<IfStmt
>(Body
.Children
[2],
369 SourceSelectionKind::InsideSelection
,
371 checkNode
<BinaryOperator
>(Body
.Children
[3],
372 SourceSelectionKind::ContainsSelectionEnd
,
377 TEST(ASTSelectionFinder
, SelectionInFunctionInObjCImplementation
) {
378 StringRef Source
= R
"(
383 int notSelected() { }
385 int selected(int x) {
390 @implementation I(Cat)
396 void outerFunction() { }
398 // Just the 'x' expression in 'selected':
399 findSelectedASTNodes(
400 Source
, {9, 10}, FileRange
{{9, 10}, {9, 11}},
401 [](Optional
<SelectedASTNode
> Node
) {
403 EXPECT_EQ(Node
->Children
.size(), 1u);
404 const auto &Impl
= checkNode
<ObjCImplementationDecl
>(
405 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
406 /*NumChildren=*/1, /*Name=*/"I");
407 const auto &Fn
= checkNode
<FunctionDecl
>(
408 Impl
.Children
[0], SourceSelectionKind::ContainsSelection
,
409 /*NumChildren=*/1, /*Name=*/"selected");
410 allChildrenOf(Fn
).shouldHaveSelectionKind(
411 SourceSelectionKind::ContainsSelection
);
413 SelectionFinderVisitor::Lang_OBJC
);
414 // The entire 'catF':
415 findSelectedASTNodes(
416 Source
, {15, 1}, FileRange
{{15, 1}, {15, 16}},
417 [](Optional
<SelectedASTNode
> Node
) {
419 EXPECT_EQ(Node
->Children
.size(), 1u);
420 const auto &Impl
= checkNode
<ObjCCategoryImplDecl
>(
421 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
422 /*NumChildren=*/1, /*Name=*/"Cat");
423 const auto &Fn
= checkNode
<FunctionDecl
>(
424 Impl
.Children
[0], SourceSelectionKind::ContainsSelection
,
425 /*NumChildren=*/1, /*Name=*/"catF");
426 allChildrenOf(Fn
).shouldHaveSelectionKind(
427 SourceSelectionKind::ContainsSelection
);
429 SelectionFinderVisitor::Lang_OBJC
);
430 // From the line before 'selected' to the line after 'catF':
431 findSelectedASTNodes(
432 Source
, {16, 1}, FileRange
{{7, 1}, {16, 1}},
433 [](Optional
<SelectedASTNode
> Node
) {
435 EXPECT_EQ(Node
->Children
.size(), 2u);
436 const auto &Impl
= checkNode
<ObjCImplementationDecl
>(
437 Node
->Children
[0], SourceSelectionKind::ContainsSelectionStart
,
438 /*NumChildren=*/1, /*Name=*/"I");
439 const auto &Selected
= checkNode
<FunctionDecl
>(
440 Impl
.Children
[0], SourceSelectionKind::InsideSelection
,
441 /*NumChildren=*/2, /*Name=*/"selected");
442 allChildrenOf(Selected
).shouldHaveSelectionKind(
443 SourceSelectionKind::InsideSelection
);
444 const auto &Cat
= checkNode
<ObjCCategoryImplDecl
>(
445 Node
->Children
[1], SourceSelectionKind::ContainsSelectionEnd
,
446 /*NumChildren=*/1, /*Name=*/"Cat");
447 const auto &CatF
= checkNode
<FunctionDecl
>(
448 Cat
.Children
[0], SourceSelectionKind::InsideSelection
,
449 /*NumChildren=*/1, /*Name=*/"catF");
450 allChildrenOf(CatF
).shouldHaveSelectionKind(
451 SourceSelectionKind::InsideSelection
);
453 SelectionFinderVisitor::Lang_OBJC
);
454 // Just the 'outer' function:
455 findSelectedASTNodes(Source
, {19, 1}, FileRange
{{19, 1}, {19, 25}},
456 [](Optional
<SelectedASTNode
> Node
) {
458 EXPECT_EQ(Node
->Children
.size(), 1u);
459 checkNode
<FunctionDecl
>(
461 SourceSelectionKind::ContainsSelection
,
462 /*NumChildren=*/1, /*Name=*/"outerFunction");
464 SelectionFinderVisitor::Lang_OBJC
);
467 TEST(ASTSelectionFinder
, FunctionInObjCImplementationCarefulWithEarlyExit
) {
468 StringRef Source
= R
"(
481 findSelectedASTNodes(
482 Source
, {6, 1}, FileRange
{{6, 1}, {7, 2}},
483 [](Optional
<SelectedASTNode
> Node
) {
485 EXPECT_EQ(Node
->Children
.size(), 1u);
486 const auto &Impl
= checkNode
<ObjCImplementationDecl
>(
487 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
488 /*NumChildren=*/1, /*Name=*/"I");
489 checkNode
<FunctionDecl
>(Impl
.Children
[0],
490 SourceSelectionKind::ContainsSelection
,
491 /*NumChildren=*/1, /*Name=*/"selected");
493 SelectionFinderVisitor::Lang_OBJC
);
496 TEST(ASTSelectionFinder
, AvoidImplicitDeclarations
) {
497 StringRef Source
= R
"(
506 // The entire struct 'Copy':
507 findSelectedASTNodes(
508 Source
, {2, 1}, FileRange
{{2, 1}, {4, 3}},
509 [](Optional
<SelectedASTNode
> Node
) {
511 EXPECT_EQ(Node
->Children
.size(), 1u);
512 const auto &Record
= checkNode
<CXXRecordDecl
>(
513 Node
->Children
[0], SourceSelectionKind::InsideSelection
,
514 /*NumChildren=*/1, /*Name=*/"Copy");
515 checkNode
<FieldDecl
>(Record
.Children
[0],
516 SourceSelectionKind::InsideSelection
);
520 TEST(ASTSelectionFinder
, CorrectEndForObjectiveCImplementation
) {
521 StringRef Source
= R
"(
527 // Just after '@ end'
528 findSelectedASTNodes(Source
, {5, 6}, None
,
529 [](Optional
<SelectedASTNode
> Node
) {
531 EXPECT_EQ(Node
->Children
.size(), 1u);
532 checkNode
<ObjCImplementationDecl
>(
534 SourceSelectionKind::ContainsSelection
);
536 SelectionFinderVisitor::Lang_OBJC
);
539 const SelectedASTNode
&checkFnBody(const Optional
<SelectedASTNode
> &Node
,
542 EXPECT_EQ(Node
->Children
.size(), 1u);
543 const auto &Fn
= checkNode
<FunctionDecl
>(
544 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
545 /*NumChildren=*/1, Name
);
546 return checkNode
<CompoundStmt
>(Fn
.Children
[0],
547 SourceSelectionKind::ContainsSelection
,
551 TEST(ASTSelectionFinder
, SelectObjectiveCPseudoObjectExprs
) {
552 StringRef Source
= R
"(
554 @property(readwrite) int prop;
556 void selectProp(I *i) {
562 @interface NSMutableArray
563 - (id)objectAtIndexedSubscript:(unsigned int)index;
564 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
567 void selectSubscript(NSMutableArray *array, I *i) {
573 findSelectedASTNodes(
574 Source
, {6, 7}, FileRange
{{6, 7}, {6, 13}},
575 [](Optional
<SelectedASTNode
> Node
) {
576 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectProp");
577 const auto &CCast
= checkNode
<CStyleCastExpr
>(
578 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
580 const auto &POE
= checkNode
<PseudoObjectExpr
>(
581 CCast
.Children
[0], SourceSelectionKind::ContainsSelection
,
583 const auto &PRE
= checkNode
<ObjCPropertyRefExpr
>(
584 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
586 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
587 PRE
.Children
[0], SourceSelectionKind::InsideSelection
,
589 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
590 SourceSelectionKind::InsideSelection
);
592 SelectionFinderVisitor::Lang_OBJC
);
593 // Just 'i.prop = 21'
594 findSelectedASTNodes(
595 Source
, {7, 1}, FileRange
{{7, 1}, {7, 12}},
596 [](Optional
<SelectedASTNode
> Node
) {
597 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectProp");
598 const auto &POE
= checkNode
<PseudoObjectExpr
>(
599 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
601 const auto &BinOp
= checkNode
<BinaryOperator
>(
602 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
604 const auto &PRE
= checkNode
<ObjCPropertyRefExpr
>(
605 BinOp
.Children
[0], SourceSelectionKind::InsideSelection
,
607 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
608 PRE
.Children
[0], SourceSelectionKind::InsideSelection
,
610 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
611 SourceSelectionKind::InsideSelection
);
612 checkNode
<IntegerLiteral
>(BinOp
.Children
[1],
613 SourceSelectionKind::InsideSelection
);
615 SelectionFinderVisitor::Lang_OBJC
);
617 findSelectedASTNodes(
618 Source
, {17, 9}, FileRange
{{17, 9}, {17, 18}},
619 [](Optional
<SelectedASTNode
> Node
) {
620 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectSubscript");
621 const auto &CCast
= checkNode
<CStyleCastExpr
>(
622 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
624 const auto &POE
= checkNode
<PseudoObjectExpr
>(
625 CCast
.Children
[0], SourceSelectionKind::ContainsSelection
,
627 const auto &SRE
= checkNode
<ObjCSubscriptRefExpr
>(
628 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
630 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
631 SRE
.Children
[0], SourceSelectionKind::InsideSelection
,
633 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
634 SourceSelectionKind::InsideSelection
);
635 checkNode
<IntegerLiteral
>(SRE
.Children
[1],
636 SourceSelectionKind::InsideSelection
);
638 SelectionFinderVisitor::Lang_OBJC
);
639 // Just 'array[i.prop] = array'
640 findSelectedASTNodes(
641 Source
, {18, 3}, FileRange
{{18, 3}, {18, 20}},
642 [](Optional
<SelectedASTNode
> Node
) {
643 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectSubscript");
644 const auto &POE
= checkNode
<PseudoObjectExpr
>(
645 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
647 const auto &BinOp
= checkNode
<BinaryOperator
>(
648 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
650 const auto &SRE
= checkNode
<ObjCSubscriptRefExpr
>(
651 BinOp
.Children
[0], SourceSelectionKind::InsideSelection
,
653 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
654 SRE
.Children
[0], SourceSelectionKind::InsideSelection
,
656 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
657 SourceSelectionKind::InsideSelection
);
658 const auto &POE2
= checkNode
<PseudoObjectExpr
>(
659 SRE
.Children
[1], SourceSelectionKind::InsideSelection
,
661 const auto &PRE
= checkNode
<ObjCPropertyRefExpr
>(
662 POE2
.Children
[0], SourceSelectionKind::InsideSelection
,
664 const auto &Cast2
= checkNode
<ImplicitCastExpr
>(
665 PRE
.Children
[0], SourceSelectionKind::InsideSelection
,
667 checkNode
<DeclRefExpr
>(Cast2
.Children
[0],
668 SourceSelectionKind::InsideSelection
);
669 checkNode
<DeclRefExpr
>(BinOp
.Children
[1],
670 SourceSelectionKind::InsideSelection
);
672 SelectionFinderVisitor::Lang_OBJC
);
675 TEST(ASTSelectionFinder
, SimpleCodeRangeASTSelection
) {
676 StringRef Source
= R
"(void f(int x, int y) {
689 // No selection range.
690 findSelectedASTNodesWithRange(
691 Source
, {2, 2}, None
,
692 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
694 Optional
<CodeRangeASTSelection
> SelectedCode
=
695 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
696 EXPECT_FALSE(SelectedCode
);
698 findSelectedASTNodesWithRange(
699 Source
, {2, 2}, FileRange
{{2, 2}, {2, 2}},
700 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
702 Optional
<CodeRangeASTSelection
> SelectedCode
=
703 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
704 EXPECT_FALSE(SelectedCode
);
706 // Range that spans multiple functions is an invalid code range.
707 findSelectedASTNodesWithRange(
708 Source
, {2, 2}, FileRange
{{7, 2}, {12, 1}},
709 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
711 Optional
<CodeRangeASTSelection
> SelectedCode
=
712 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
713 EXPECT_FALSE(SelectedCode
);
716 findSelectedASTNodesWithRange(
717 Source
, {2, 2}, FileRange
{{2, 2}, {2, 13}},
718 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
720 Optional
<CodeRangeASTSelection
> SelectedCode
=
721 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
722 EXPECT_TRUE(SelectedCode
);
723 EXPECT_EQ(SelectedCode
->size(), 1u);
724 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
725 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
726 SelectedCode
->getParents();
727 EXPECT_EQ(Parents
.size(), 3u);
729 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
730 // Function 'f' definition.
731 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
732 // Function body of function 'F'.
733 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
735 // From 'f(2,3)' until just before 'x = 1;':
736 findSelectedASTNodesWithRange(
737 Source
, {3, 2}, FileRange
{{3, 2}, {7, 1}},
738 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
740 Optional
<CodeRangeASTSelection
> SelectedCode
=
741 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
742 EXPECT_TRUE(SelectedCode
);
743 EXPECT_EQ(SelectedCode
->size(), 2u);
744 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[0]));
745 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[1]));
746 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
747 SelectedCode
->getParents();
748 EXPECT_EQ(Parents
.size(), 3u);
750 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
751 // Function 'f' definition.
752 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
753 // Function body of function 'F'.
754 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
756 // From 'f(2,3)' until just before ';' in 'x = 1;':
757 findSelectedASTNodesWithRange(
758 Source
, {3, 2}, FileRange
{{3, 2}, {7, 8}},
759 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
761 Optional
<CodeRangeASTSelection
> SelectedCode
=
762 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
763 EXPECT_TRUE(SelectedCode
);
764 EXPECT_EQ(SelectedCode
->size(), 3u);
765 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[0]));
766 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[1]));
767 EXPECT_TRUE(isa
<BinaryOperator
>((*SelectedCode
)[2]));
769 // From the middle of 'int z = 3' until the middle of 'x = 1;':
770 findSelectedASTNodesWithRange(
771 Source
, {2, 10}, FileRange
{{2, 10}, {7, 5}},
772 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
775 Optional
<CodeRangeASTSelection
> SelectedCode
=
776 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
777 EXPECT_TRUE(SelectedCode
);
778 EXPECT_EQ(SelectedCode
->size(), 4u);
779 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
780 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[1]));
781 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[2]));
782 EXPECT_TRUE(isa
<BinaryOperator
>((*SelectedCode
)[3]));
786 TEST(ASTSelectionFinder
, OutOfBodyCodeRange
) {
787 StringRef Source
= R
"(
788 int codeRange = 2 + 3;
791 findSelectedASTNodesWithRange(
792 Source
, {2, 17}, FileRange
{{2, 17}, {2, 22}},
793 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
795 Optional
<CodeRangeASTSelection
> SelectedCode
=
796 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
797 EXPECT_TRUE(SelectedCode
);
798 EXPECT_EQ(SelectedCode
->size(), 1u);
799 EXPECT_TRUE(isa
<BinaryOperator
>((*SelectedCode
)[0]));
800 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
801 SelectedCode
->getParents();
802 EXPECT_EQ(Parents
.size(), 2u);
804 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
805 // Variable 'codeRange'.
806 EXPECT_TRUE(isa
<VarDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
810 TEST(ASTSelectionFinder
, SelectVarDeclStmt
) {
811 StringRef Source
= R
"(
819 findSelectedASTNodesWithRange(
820 Source
, {4, 8}, FileRange
{{4, 8}, {4, 14}},
821 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
823 Optional
<CodeRangeASTSelection
> SelectedCode
=
824 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
825 EXPECT_TRUE(SelectedCode
);
826 EXPECT_EQ(SelectedCode
->size(), 1u);
827 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
828 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
829 SelectedCode
->getParents();
830 EXPECT_EQ(Parents
.size(), 4u);
832 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
833 // Function 'f' definition.
834 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
835 // Function body of function 'F'.
836 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
837 // Compound statement in body of 'F'.
838 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[3].get().Node
.get
<Stmt
>()));
842 TEST(ASTSelectionFinder
, SelectEntireDeclStmtRange
) {
843 StringRef Source
= R
"(
844 void f(int x, int y) {
849 findSelectedASTNodesWithRange(
850 Source
, {3, 4}, FileRange
{{3, 4}, {3, 17}},
851 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
853 Optional
<CodeRangeASTSelection
> SelectedCode
=
854 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
855 EXPECT_TRUE(SelectedCode
);
856 EXPECT_EQ(SelectedCode
->size(), 1u);
857 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
858 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
859 SelectedCode
->getParents();
860 EXPECT_EQ(Parents
.size(), 3u);
862 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
863 // Function 'f' definition.
864 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
865 // Function body of function 'F'.
866 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
870 TEST(ASTSelectionFinder
, SelectEntireDeclStmtRangeWithMultipleDecls
) {
871 StringRef Source
= R
"(
872 void f(int x, int y) {
873 int a = x * y, b = x - y;
877 findSelectedASTNodesWithRange(
878 Source
, {3, 19}, FileRange
{{3, 19}, {3, 28}},
879 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
881 Optional
<CodeRangeASTSelection
> SelectedCode
=
882 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
883 EXPECT_TRUE(SelectedCode
);
884 EXPECT_EQ(SelectedCode
->size(), 1u);
885 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
886 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
887 SelectedCode
->getParents();
888 EXPECT_EQ(Parents
.size(), 3u);
890 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
891 // Function 'f' definition.
892 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
893 // Function body of function 'F'.
894 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
898 TEST(ASTSelectionFinder
, SimpleCodeRangeASTSelectionInObjCMethod
) {
899 StringRef Source
= R
"(@interface I @end
901 - (void) f:(int)x with:(int) y {
915 // Range that spans multiple methods is an invalid code range.
916 findSelectedASTNodesWithRange(
917 Source
, {9, 2}, FileRange
{{9, 2}, {13, 1}},
918 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
920 Optional
<CodeRangeASTSelection
> SelectedCode
=
921 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
922 EXPECT_FALSE(SelectedCode
);
924 SelectionFinderVisitor::Lang_OBJC
);
926 findSelectedASTNodesWithRange(
927 Source
, {4, 2}, FileRange
{{4, 2}, {4, 13}},
928 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
930 Optional
<CodeRangeASTSelection
> SelectedCode
=
931 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
932 EXPECT_TRUE(SelectedCode
);
933 EXPECT_EQ(SelectedCode
->size(), 1u);
934 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
935 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
936 SelectedCode
->getParents();
937 EXPECT_EQ(Parents
.size(), 4u);
939 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
940 // 'I' @implementation.
941 EXPECT_TRUE(isa
<ObjCImplDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
942 // Function 'f' definition.
943 EXPECT_TRUE(isa
<ObjCMethodDecl
>(Parents
[2].get().Node
.get
<Decl
>()));
944 // Function body of function 'F'.
945 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[3].get().Node
.get
<Stmt
>()));
947 SelectionFinderVisitor::Lang_OBJC
);
948 // From '[self f: 2 with: 3]' until just before 'x = 1;':
949 findSelectedASTNodesWithRange(
950 Source
, {5, 2}, FileRange
{{5, 2}, {9, 1}},
951 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
953 Optional
<CodeRangeASTSelection
> SelectedCode
=
954 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
955 EXPECT_TRUE(SelectedCode
);
956 EXPECT_EQ(SelectedCode
->size(), 2u);
957 EXPECT_TRUE(isa
<ObjCMessageExpr
>((*SelectedCode
)[0]));
958 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[1]));
959 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
960 SelectedCode
->getParents();
961 EXPECT_EQ(Parents
.size(), 4u);
963 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
964 // 'I' @implementation.
965 EXPECT_TRUE(isa
<ObjCImplDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
966 // Function 'f' definition.
967 EXPECT_TRUE(isa
<ObjCMethodDecl
>(Parents
[2].get().Node
.get
<Decl
>()));
968 // Function body of function 'F'.
969 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[3].get().Node
.get
<Stmt
>()));
971 SelectionFinderVisitor::Lang_OBJC
);
974 TEST(ASTSelectionFinder
, CanonicalizeObjCStringLiteral
) {
975 StringRef Source
= R
"(
981 findSelectedASTNodesWithRange(
982 Source
, {3, 10}, FileRange
{{3, 10}, {3, 16}},
983 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
985 Optional
<CodeRangeASTSelection
> SelectedCode
=
986 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
987 EXPECT_TRUE(SelectedCode
);
988 EXPECT_EQ(SelectedCode
->size(), 1u);
989 EXPECT_TRUE(isa
<ObjCStringLiteral
>((*SelectedCode
)[0]));
991 SelectionFinderVisitor::Lang_OBJC
);
993 findSelectedASTNodesWithRange(
994 Source
, {3, 11}, FileRange
{{3, 11}, {3, 15}},
995 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
997 Optional
<CodeRangeASTSelection
> SelectedCode
=
998 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
999 EXPECT_TRUE(SelectedCode
);
1000 EXPECT_EQ(SelectedCode
->size(), 1u);
1001 EXPECT_TRUE(isa
<ObjCStringLiteral
>((*SelectedCode
)[0]));
1003 SelectionFinderVisitor::Lang_OBJC
);
1006 TEST(ASTSelectionFinder
, CanonicalizeMemberCalleeToCall
) {
1007 StringRef Source
= R
"(
1008 class AClass { public:
1011 void selectWholeCallWhenJustMethodSelected(int &i) {
1015 void selectWholeCallWhenJustMethodSelected() {
1019 void dontSelectArgument(AClass &a) {
1020 a.selectWholeCallWhenJustMethodSelected(a.afield);
1023 // Just 'method' with implicit 'this':
1024 findSelectedASTNodesWithRange(
1025 Source
, {6, 5}, FileRange
{{6, 5}, {6, 11}},
1026 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
1028 Optional
<CodeRangeASTSelection
> SelectedCode
=
1029 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1030 EXPECT_TRUE(SelectedCode
);
1031 EXPECT_EQ(SelectedCode
->size(), 1u);
1032 EXPECT_TRUE(isa
<CXXMemberCallExpr
>((*SelectedCode
)[0]));
1035 findSelectedASTNodesWithRange(
1036 Source
, {11, 5}, FileRange
{{11, 5}, {11, 11}},
1037 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
1039 Optional
<CodeRangeASTSelection
> SelectedCode
=
1040 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1041 EXPECT_TRUE(SelectedCode
);
1042 EXPECT_EQ(SelectedCode
->size(), 1u);
1043 EXPECT_TRUE(isa
<CXXMemberCallExpr
>((*SelectedCode
)[0]));
1045 // Just 'afield', which should not select the call.
1046 findSelectedASTNodesWithRange(
1047 Source
, {14, 5}, FileRange
{{14, 45}, {14, 51}},
1048 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
1050 Optional
<CodeRangeASTSelection
> SelectedCode
=
1051 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1052 EXPECT_TRUE(SelectedCode
);
1053 EXPECT_EQ(SelectedCode
->size(), 1u);
1054 EXPECT_FALSE(isa
<CXXMemberCallExpr
>((*SelectedCode
)[0]));
1058 TEST(ASTSelectionFinder
, CanonicalizeFuncCalleeToCall
) {
1059 StringRef Source
= R
"(
1067 findSelectedASTNodesWithRange(
1068 Source
, {5, 3}, FileRange
{{5, 3}, {5, 11}},
1069 [](SourceRange SelectionRange
, Optional
<SelectedASTNode
> Node
) {
1072 Optional
<CodeRangeASTSelection
> SelectedCode
=
1073 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1074 EXPECT_TRUE(SelectedCode
);
1075 EXPECT_EQ(SelectedCode
->size(), 1u);
1076 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[0]));
1077 EXPECT_TRUE(isa
<CompoundStmt
>(
1078 SelectedCode
->getParents()[SelectedCode
->getParents().size() - 1]
1080 .Node
.get
<Stmt
>()));
1084 } // end anonymous namespace