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"
14 using namespace clang
;
15 using namespace tooling
;
20 unsigned Line
, Column
;
22 SourceLocation
translate(const SourceManager
&SM
) {
23 return SM
.translateLineCol(SM
.getMainFileID(), Line
, Column
);
27 using FileRange
= std::pair
<FileLocation
, FileLocation
>;
29 class SelectionFinderVisitor
: public TestVisitor
{
30 FileLocation Location
;
31 std::optional
<FileRange
> SelectionRange
;
32 llvm::function_ref
<void(SourceRange SelectionRange
,
33 std::optional
<SelectedASTNode
>)>
37 SelectionFinderVisitor(
38 FileLocation Location
, std::optional
<FileRange
> SelectionRange
,
39 llvm::function_ref
<void(SourceRange SelectionRange
,
40 std::optional
<SelectedASTNode
>)>
42 : Location(Location
), SelectionRange(SelectionRange
), Consumer(Consumer
) {
45 bool VisitTranslationUnitDecl(TranslationUnitDecl
*TU
) override
{
46 const ASTContext
&Context
= TU
->getASTContext();
47 const SourceManager
&SM
= Context
.getSourceManager();
51 SelRange
= SourceRange(SelectionRange
->first
.translate(SM
),
52 SelectionRange
->second
.translate(SM
));
54 SourceLocation Loc
= Location
.translate(SM
);
55 SelRange
= SourceRange(Loc
, Loc
);
57 Consumer(SelRange
, findSelectedASTNodes(Context
, SelRange
));
62 /// This is a test utility function that computes the AST selection at the
63 /// given location with an optional selection range.
65 /// A location roughly corresponds to a cursor location in an editor, while
66 /// the optional range corresponds to the selection range in an editor.
67 void findSelectedASTNodesWithRange(
68 StringRef Source
, FileLocation Location
,
69 std::optional
<FileRange
> SelectionRange
,
70 llvm::function_ref
<void(SourceRange SelectionRange
,
71 std::optional
<SelectedASTNode
>)>
73 SelectionFinderVisitor::Language Language
=
74 SelectionFinderVisitor::Lang_CXX11
) {
75 SelectionFinderVisitor
Visitor(Location
, SelectionRange
, Consumer
);
76 EXPECT_TRUE(Visitor
.runOver(Source
, Language
));
79 void findSelectedASTNodes(
80 StringRef Source
, FileLocation Location
,
81 std::optional
<FileRange
> SelectionRange
,
82 llvm::function_ref
<void(std::optional
<SelectedASTNode
>)> Consumer
,
83 SelectionFinderVisitor::Language Language
=
84 SelectionFinderVisitor::Lang_CXX11
) {
85 findSelectedASTNodesWithRange(
86 Source
, Location
, SelectionRange
,
87 [&](SourceRange
, std::optional
<SelectedASTNode
> Selection
) {
88 Consumer(std::move(Selection
));
93 void checkNodeImpl(bool IsTypeMatched
, const SelectedASTNode
&Node
,
94 SourceSelectionKind SelectionKind
, unsigned NumChildren
) {
95 ASSERT_TRUE(IsTypeMatched
);
96 EXPECT_EQ(Node
.Children
.size(), NumChildren
);
97 ASSERT_EQ(Node
.SelectionKind
, SelectionKind
);
100 void checkDeclName(const SelectedASTNode
&Node
, StringRef Name
) {
101 const auto *ND
= Node
.Node
.get
<NamedDecl
>();
103 ASSERT_EQ(ND
->getName(), Name
);
106 template <typename T
>
107 const SelectedASTNode
&
108 checkNode(const SelectedASTNode
&StmtNode
, SourceSelectionKind SelectionKind
,
109 unsigned NumChildren
= 0,
110 std::enable_if_t
<std::is_base_of_v
<Stmt
, T
>, T
> *StmtOverloadChecker
=
112 checkNodeImpl(isa
<T
>(StmtNode
.Node
.get
<Stmt
>()), StmtNode
, SelectionKind
,
117 template <typename T
>
118 const SelectedASTNode
&
119 checkNode(const SelectedASTNode
&DeclNode
, SourceSelectionKind SelectionKind
,
120 unsigned NumChildren
= 0, StringRef Name
= "",
121 std::enable_if_t
<std::is_base_of_v
<Decl
, T
>, T
> *DeclOverloadChecker
=
123 checkNodeImpl(isa
<T
>(DeclNode
.Node
.get
<Decl
>()), DeclNode
, SelectionKind
,
126 checkDeclName(DeclNode
, Name
);
130 struct ForAllChildrenOf
{
131 const SelectedASTNode
&Node
;
133 static void childKindVerifier(const SelectedASTNode
&Node
,
134 SourceSelectionKind SelectionKind
) {
135 for (const SelectedASTNode
&Child
: Node
.Children
) {
136 ASSERT_EQ(Node
.SelectionKind
, SelectionKind
);
137 childKindVerifier(Child
, SelectionKind
);
142 ForAllChildrenOf(const SelectedASTNode
&Node
) : Node(Node
) {}
144 void shouldHaveSelectionKind(SourceSelectionKind Kind
) {
145 childKindVerifier(Node
, Kind
);
149 ForAllChildrenOf
allChildrenOf(const SelectedASTNode
&Node
) {
150 return ForAllChildrenOf(Node
);
153 TEST(ASTSelectionFinder
, CursorNoSelection
) {
154 findSelectedASTNodes(
155 " void f() { }", {1, 1}, std::nullopt
,
156 [](std::optional
<SelectedASTNode
> Node
) { EXPECT_FALSE(Node
); });
159 TEST(ASTSelectionFinder
, CursorAtStartOfFunction
) {
160 findSelectedASTNodes(
161 "void f() { }", {1, 1}, std::nullopt
,
162 [](std::optional
<SelectedASTNode
> Node
) {
164 checkNode
<TranslationUnitDecl
>(*Node
, SourceSelectionKind::None
,
166 checkNode
<FunctionDecl
>(Node
->Children
[0],
167 SourceSelectionKind::ContainsSelection
,
168 /*NumChildren=*/0, /*Name=*/"f");
170 // Check that the dumping works.
171 std::string DumpValue
;
172 llvm::raw_string_ostream
OS(DumpValue
);
173 Node
->Children
[0].dump(OS
);
174 ASSERT_EQ(DumpValue
, "FunctionDecl \"f\" contains-selection\n");
178 TEST(ASTSelectionFinder
, RangeNoSelection
) {
179 findSelectedASTNodes(
180 " void f() { }", {1, 1}, FileRange
{{1, 1}, {1, 1}},
181 [](std::optional
<SelectedASTNode
> Node
) { EXPECT_FALSE(Node
); });
182 findSelectedASTNodes(
183 " void f() { }", {1, 1}, FileRange
{{1, 1}, {1, 2}},
184 [](std::optional
<SelectedASTNode
> Node
) { EXPECT_FALSE(Node
); });
187 TEST(ASTSelectionFinder
, EmptyRangeFallbackToCursor
) {
188 findSelectedASTNodes("void f() { }", {1, 1}, FileRange
{{1, 1}, {1, 1}},
189 [](std::optional
<SelectedASTNode
> Node
) {
191 checkNode
<FunctionDecl
>(
193 SourceSelectionKind::ContainsSelection
,
194 /*NumChildren=*/0, /*Name=*/"f");
198 TEST(ASTSelectionFinder
, WholeFunctionSelection
) {
199 StringRef Source
= "int f(int x) { return x;\n}\nvoid f2() { }";
200 // From 'int' until just after '}':
202 findSelectedASTNodes(
203 Source
, {1, 1}, FileRange
{{1, 1}, {2, 2}},
204 [](std::optional
<SelectedASTNode
> Node
) {
206 EXPECT_EQ(Node
->Children
.size(), 1u);
207 const auto &Fn
= checkNode
<FunctionDecl
>(
208 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
209 /*NumChildren=*/2, /*Name=*/"f");
210 checkNode
<ParmVarDecl
>(Fn
.Children
[0],
211 SourceSelectionKind::InsideSelection
);
212 const auto &Body
= checkNode
<CompoundStmt
>(
213 Fn
.Children
[1], SourceSelectionKind::InsideSelection
,
215 const auto &Return
= checkNode
<ReturnStmt
>(
216 Body
.Children
[0], SourceSelectionKind::InsideSelection
,
218 checkNode
<ImplicitCastExpr
>(Return
.Children
[0],
219 SourceSelectionKind::InsideSelection
,
221 checkNode
<DeclRefExpr
>(Return
.Children
[0].Children
[0],
222 SourceSelectionKind::InsideSelection
);
225 // From 'int' until just before '}':
226 findSelectedASTNodes(
227 Source
, {2, 1}, FileRange
{{1, 1}, {2, 1}},
228 [](std::optional
<SelectedASTNode
> Node
) {
230 EXPECT_EQ(Node
->Children
.size(), 1u);
231 const auto &Fn
= checkNode
<FunctionDecl
>(
232 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
233 /*NumChildren=*/2, /*Name=*/"f");
234 const auto &Body
= checkNode
<CompoundStmt
>(
235 Fn
.Children
[1], SourceSelectionKind::ContainsSelectionEnd
,
237 checkNode
<ReturnStmt
>(Body
.Children
[0],
238 SourceSelectionKind::InsideSelection
,
241 // From '{' until just after '}':
242 findSelectedASTNodes(
243 Source
, {1, 14}, FileRange
{{1, 14}, {2, 2}},
244 [](std::optional
<SelectedASTNode
> Node
) {
246 EXPECT_EQ(Node
->Children
.size(), 1u);
247 const auto &Fn
= checkNode
<FunctionDecl
>(
248 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
249 /*NumChildren=*/1, /*Name=*/"f");
250 const auto &Body
= checkNode
<CompoundStmt
>(
251 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
253 checkNode
<ReturnStmt
>(Body
.Children
[0],
254 SourceSelectionKind::InsideSelection
,
257 // From 'x' until just after '}':
258 findSelectedASTNodes(
259 Source
, {2, 2}, FileRange
{{1, 11}, {2, 2}},
260 [](std::optional
<SelectedASTNode
> Node
) {
262 EXPECT_EQ(Node
->Children
.size(), 1u);
263 const auto &Fn
= checkNode
<FunctionDecl
>(
264 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
265 /*NumChildren=*/2, /*Name=*/"f");
266 checkNode
<ParmVarDecl
>(Fn
.Children
[0],
267 SourceSelectionKind::ContainsSelectionStart
);
268 const auto &Body
= checkNode
<CompoundStmt
>(
269 Fn
.Children
[1], SourceSelectionKind::InsideSelection
,
271 checkNode
<ReturnStmt
>(Body
.Children
[0],
272 SourceSelectionKind::InsideSelection
,
277 TEST(ASTSelectionFinder
, MultipleFunctionSelection
) {
278 StringRef Source
= R
"(void f0() {
284 auto SelectedF1F2
= [](std::optional
<SelectedASTNode
> Node
) {
286 EXPECT_EQ(Node
->Children
.size(), 2u);
287 checkNode
<FunctionDecl
>(Node
->Children
[0],
288 SourceSelectionKind::InsideSelection
,
289 /*NumChildren=*/1, /*Name=*/"f1");
290 checkNode
<FunctionDecl
>(Node
->Children
[1],
291 SourceSelectionKind::InsideSelection
,
292 /*NumChildren=*/1, /*Name=*/"f2");
294 // Just after '}' of f0 and just before 'void' of f3:
295 findSelectedASTNodes(Source
, {2, 2}, FileRange
{{2, 2}, {5, 1}}, SelectedF1F2
);
296 // Just before 'void' of f1 and just after '}' of f2:
297 findSelectedASTNodes(Source
, {3, 1}, FileRange
{{3, 1}, {4, 14}},
301 TEST(ASTSelectionFinder
, MultipleStatementSelection
) {
302 StringRef Source
= R
"(void f(int x, int y) {
311 // From 'f(2,3)' until just before 'x = 1;':
312 findSelectedASTNodes(
313 Source
, {3, 2}, FileRange
{{3, 2}, {7, 1}},
314 [](std::optional
<SelectedASTNode
> Node
) {
316 EXPECT_EQ(Node
->Children
.size(), 1u);
317 const auto &Fn
= checkNode
<FunctionDecl
>(
318 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
319 /*NumChildren=*/1, /*Name=*/"f");
320 const auto &Body
= checkNode
<CompoundStmt
>(
321 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
323 allChildrenOf(checkNode
<CallExpr
>(Body
.Children
[0],
324 SourceSelectionKind::InsideSelection
,
326 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection
);
327 allChildrenOf(checkNode
<IfStmt
>(Body
.Children
[1],
328 SourceSelectionKind::InsideSelection
,
330 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection
);
332 // From 'f(2,3)' until just before ';' in 'x = 1;':
333 findSelectedASTNodes(
334 Source
, {3, 2}, FileRange
{{3, 2}, {7, 8}},
335 [](std::optional
<SelectedASTNode
> Node
) {
337 EXPECT_EQ(Node
->Children
.size(), 1u);
338 const auto &Fn
= checkNode
<FunctionDecl
>(
339 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
340 /*NumChildren=*/1, /*Name=*/"f");
341 const auto &Body
= checkNode
<CompoundStmt
>(
342 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
344 checkNode
<CallExpr
>(Body
.Children
[0],
345 SourceSelectionKind::InsideSelection
,
347 checkNode
<IfStmt
>(Body
.Children
[1],
348 SourceSelectionKind::InsideSelection
,
350 checkNode
<BinaryOperator
>(Body
.Children
[2],
351 SourceSelectionKind::InsideSelection
,
354 // From the middle of 'int z = 3' until the middle of 'x = 1;':
355 findSelectedASTNodes(
356 Source
, {2, 10}, FileRange
{{2, 10}, {7, 5}},
357 [](std::optional
<SelectedASTNode
> Node
) {
359 EXPECT_EQ(Node
->Children
.size(), 1u);
360 const auto &Fn
= checkNode
<FunctionDecl
>(
361 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
362 /*NumChildren=*/1, /*Name=*/"f");
363 const auto &Body
= checkNode
<CompoundStmt
>(
364 Fn
.Children
[0], SourceSelectionKind::ContainsSelection
,
366 checkNode
<DeclStmt
>(Body
.Children
[0],
367 SourceSelectionKind::ContainsSelectionStart
,
369 checkNode
<CallExpr
>(Body
.Children
[1],
370 SourceSelectionKind::InsideSelection
,
372 checkNode
<IfStmt
>(Body
.Children
[2],
373 SourceSelectionKind::InsideSelection
,
375 checkNode
<BinaryOperator
>(Body
.Children
[3],
376 SourceSelectionKind::ContainsSelectionEnd
,
381 TEST(ASTSelectionFinder
, SelectionInFunctionInObjCImplementation
) {
382 StringRef Source
= R
"(
387 int notSelected() { }
389 int selected(int x) {
394 @implementation I(Cat)
400 void outerFunction() { }
402 // Just the 'x' expression in 'selected':
403 findSelectedASTNodes(
404 Source
, {9, 10}, FileRange
{{9, 10}, {9, 11}},
405 [](std::optional
<SelectedASTNode
> Node
) {
407 EXPECT_EQ(Node
->Children
.size(), 1u);
408 const auto &Impl
= checkNode
<ObjCImplementationDecl
>(
409 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
410 /*NumChildren=*/1, /*Name=*/"I");
411 const auto &Fn
= checkNode
<FunctionDecl
>(
412 Impl
.Children
[0], SourceSelectionKind::ContainsSelection
,
413 /*NumChildren=*/1, /*Name=*/"selected");
414 allChildrenOf(Fn
).shouldHaveSelectionKind(
415 SourceSelectionKind::ContainsSelection
);
417 SelectionFinderVisitor::Lang_OBJC
);
418 // The entire 'catF':
419 findSelectedASTNodes(
420 Source
, {15, 1}, FileRange
{{15, 1}, {15, 16}},
421 [](std::optional
<SelectedASTNode
> Node
) {
423 EXPECT_EQ(Node
->Children
.size(), 1u);
424 const auto &Impl
= checkNode
<ObjCCategoryImplDecl
>(
425 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
426 /*NumChildren=*/1, /*Name=*/"Cat");
427 const auto &Fn
= checkNode
<FunctionDecl
>(
428 Impl
.Children
[0], SourceSelectionKind::ContainsSelection
,
429 /*NumChildren=*/1, /*Name=*/"catF");
430 allChildrenOf(Fn
).shouldHaveSelectionKind(
431 SourceSelectionKind::ContainsSelection
);
433 SelectionFinderVisitor::Lang_OBJC
);
434 // From the line before 'selected' to the line after 'catF':
435 findSelectedASTNodes(
436 Source
, {16, 1}, FileRange
{{7, 1}, {16, 1}},
437 [](std::optional
<SelectedASTNode
> Node
) {
439 EXPECT_EQ(Node
->Children
.size(), 2u);
440 const auto &Impl
= checkNode
<ObjCImplementationDecl
>(
441 Node
->Children
[0], SourceSelectionKind::ContainsSelectionStart
,
442 /*NumChildren=*/1, /*Name=*/"I");
443 const auto &Selected
= checkNode
<FunctionDecl
>(
444 Impl
.Children
[0], SourceSelectionKind::InsideSelection
,
445 /*NumChildren=*/2, /*Name=*/"selected");
446 allChildrenOf(Selected
).shouldHaveSelectionKind(
447 SourceSelectionKind::InsideSelection
);
448 const auto &Cat
= checkNode
<ObjCCategoryImplDecl
>(
449 Node
->Children
[1], SourceSelectionKind::ContainsSelectionEnd
,
450 /*NumChildren=*/1, /*Name=*/"Cat");
451 const auto &CatF
= checkNode
<FunctionDecl
>(
452 Cat
.Children
[0], SourceSelectionKind::InsideSelection
,
453 /*NumChildren=*/1, /*Name=*/"catF");
454 allChildrenOf(CatF
).shouldHaveSelectionKind(
455 SourceSelectionKind::InsideSelection
);
457 SelectionFinderVisitor::Lang_OBJC
);
458 // Just the 'outer' function:
459 findSelectedASTNodes(
460 Source
, {19, 1}, FileRange
{{19, 1}, {19, 25}},
461 [](std::optional
<SelectedASTNode
> Node
) {
463 EXPECT_EQ(Node
->Children
.size(), 1u);
464 checkNode
<FunctionDecl
>(Node
->Children
[0],
465 SourceSelectionKind::ContainsSelection
,
466 /*NumChildren=*/1, /*Name=*/"outerFunction");
468 SelectionFinderVisitor::Lang_OBJC
);
471 TEST(ASTSelectionFinder
, FunctionInObjCImplementationCarefulWithEarlyExit
) {
472 StringRef Source
= R
"(
485 findSelectedASTNodes(
486 Source
, {6, 1}, FileRange
{{6, 1}, {7, 2}},
487 [](std::optional
<SelectedASTNode
> Node
) {
489 EXPECT_EQ(Node
->Children
.size(), 1u);
490 const auto &Impl
= checkNode
<ObjCImplementationDecl
>(
491 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
492 /*NumChildren=*/1, /*Name=*/"I");
493 checkNode
<FunctionDecl
>(Impl
.Children
[0],
494 SourceSelectionKind::ContainsSelection
,
495 /*NumChildren=*/1, /*Name=*/"selected");
497 SelectionFinderVisitor::Lang_OBJC
);
500 TEST(ASTSelectionFinder
, AvoidImplicitDeclarations
) {
501 StringRef Source
= R
"(
510 // The entire struct 'Copy':
511 findSelectedASTNodes(
512 Source
, {2, 1}, FileRange
{{2, 1}, {4, 3}},
513 [](std::optional
<SelectedASTNode
> Node
) {
515 EXPECT_EQ(Node
->Children
.size(), 1u);
516 const auto &Record
= checkNode
<CXXRecordDecl
>(
517 Node
->Children
[0], SourceSelectionKind::InsideSelection
,
518 /*NumChildren=*/1, /*Name=*/"Copy");
519 checkNode
<FieldDecl
>(Record
.Children
[0],
520 SourceSelectionKind::InsideSelection
);
524 TEST(ASTSelectionFinder
, CorrectEndForObjectiveCImplementation
) {
525 StringRef Source
= R
"(
531 // Just after '@ end'
532 findSelectedASTNodes(
533 Source
, {5, 6}, std::nullopt
,
534 [](std::optional
<SelectedASTNode
> Node
) {
536 EXPECT_EQ(Node
->Children
.size(), 1u);
537 checkNode
<ObjCImplementationDecl
>(
538 Node
->Children
[0], SourceSelectionKind::ContainsSelection
);
540 SelectionFinderVisitor::Lang_OBJC
);
543 const SelectedASTNode
&checkFnBody(const std::optional
<SelectedASTNode
> &Node
,
546 EXPECT_EQ(Node
->Children
.size(), 1u);
547 const auto &Fn
= checkNode
<FunctionDecl
>(
548 Node
->Children
[0], SourceSelectionKind::ContainsSelection
,
549 /*NumChildren=*/1, Name
);
550 return checkNode
<CompoundStmt
>(Fn
.Children
[0],
551 SourceSelectionKind::ContainsSelection
,
555 TEST(ASTSelectionFinder
, SelectObjectiveCPseudoObjectExprs
) {
556 StringRef Source
= R
"(
558 @property(readwrite) int prop;
560 void selectProp(I *i) {
566 @interface NSMutableArray
567 - (id)objectAtIndexedSubscript:(unsigned int)index;
568 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
571 void selectSubscript(NSMutableArray *array, I *i) {
577 findSelectedASTNodes(
578 Source
, {6, 7}, FileRange
{{6, 7}, {6, 13}},
579 [](std::optional
<SelectedASTNode
> Node
) {
580 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectProp");
581 const auto &CCast
= checkNode
<CStyleCastExpr
>(
582 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
584 const auto &POE
= checkNode
<PseudoObjectExpr
>(
585 CCast
.Children
[0], SourceSelectionKind::ContainsSelection
,
587 const auto &PRE
= checkNode
<ObjCPropertyRefExpr
>(
588 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
590 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
591 PRE
.Children
[0], SourceSelectionKind::InsideSelection
,
593 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
594 SourceSelectionKind::InsideSelection
);
596 SelectionFinderVisitor::Lang_OBJC
);
597 // Just 'i.prop = 21'
598 findSelectedASTNodes(
599 Source
, {7, 1}, FileRange
{{7, 1}, {7, 12}},
600 [](std::optional
<SelectedASTNode
> Node
) {
601 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectProp");
602 const auto &POE
= checkNode
<PseudoObjectExpr
>(
603 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
605 const auto &BinOp
= checkNode
<BinaryOperator
>(
606 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
608 const auto &PRE
= checkNode
<ObjCPropertyRefExpr
>(
609 BinOp
.Children
[0], SourceSelectionKind::InsideSelection
,
611 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
612 PRE
.Children
[0], SourceSelectionKind::InsideSelection
,
614 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
615 SourceSelectionKind::InsideSelection
);
616 checkNode
<IntegerLiteral
>(BinOp
.Children
[1],
617 SourceSelectionKind::InsideSelection
);
619 SelectionFinderVisitor::Lang_OBJC
);
621 findSelectedASTNodes(
622 Source
, {17, 9}, FileRange
{{17, 9}, {17, 18}},
623 [](std::optional
<SelectedASTNode
> Node
) {
624 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectSubscript");
625 const auto &CCast
= checkNode
<CStyleCastExpr
>(
626 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
628 const auto &POE
= checkNode
<PseudoObjectExpr
>(
629 CCast
.Children
[0], SourceSelectionKind::ContainsSelection
,
631 const auto &SRE
= checkNode
<ObjCSubscriptRefExpr
>(
632 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
634 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
635 SRE
.Children
[0], SourceSelectionKind::InsideSelection
,
637 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
638 SourceSelectionKind::InsideSelection
);
639 checkNode
<IntegerLiteral
>(SRE
.Children
[1],
640 SourceSelectionKind::InsideSelection
);
642 SelectionFinderVisitor::Lang_OBJC
);
643 // Just 'array[i.prop] = array'
644 findSelectedASTNodes(
645 Source
, {18, 3}, FileRange
{{18, 3}, {18, 20}},
646 [](std::optional
<SelectedASTNode
> Node
) {
647 const auto &CS
= checkFnBody(Node
, /*Name=*/"selectSubscript");
648 const auto &POE
= checkNode
<PseudoObjectExpr
>(
649 CS
.Children
[0], SourceSelectionKind::ContainsSelection
,
651 const auto &BinOp
= checkNode
<BinaryOperator
>(
652 POE
.Children
[0], SourceSelectionKind::ContainsSelection
,
654 const auto &SRE
= checkNode
<ObjCSubscriptRefExpr
>(
655 BinOp
.Children
[0], SourceSelectionKind::InsideSelection
,
657 const auto &Cast
= checkNode
<ImplicitCastExpr
>(
658 SRE
.Children
[0], SourceSelectionKind::InsideSelection
,
660 checkNode
<DeclRefExpr
>(Cast
.Children
[0],
661 SourceSelectionKind::InsideSelection
);
662 const auto &POE2
= checkNode
<PseudoObjectExpr
>(
663 SRE
.Children
[1], SourceSelectionKind::InsideSelection
,
665 const auto &PRE
= checkNode
<ObjCPropertyRefExpr
>(
666 POE2
.Children
[0], SourceSelectionKind::InsideSelection
,
668 const auto &Cast2
= checkNode
<ImplicitCastExpr
>(
669 PRE
.Children
[0], SourceSelectionKind::InsideSelection
,
671 checkNode
<DeclRefExpr
>(Cast2
.Children
[0],
672 SourceSelectionKind::InsideSelection
);
673 checkNode
<DeclRefExpr
>(BinOp
.Children
[1],
674 SourceSelectionKind::InsideSelection
);
676 SelectionFinderVisitor::Lang_OBJC
);
679 TEST(ASTSelectionFinder
, SimpleCodeRangeASTSelection
) {
680 StringRef Source
= R
"(void f(int x, int y) {
693 // No selection range.
694 findSelectedASTNodesWithRange(
695 Source
, {2, 2}, std::nullopt
,
696 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
698 std::optional
<CodeRangeASTSelection
> SelectedCode
=
699 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
700 EXPECT_FALSE(SelectedCode
);
702 findSelectedASTNodesWithRange(
703 Source
, {2, 2}, FileRange
{{2, 2}, {2, 2}},
704 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
706 std::optional
<CodeRangeASTSelection
> SelectedCode
=
707 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
708 EXPECT_FALSE(SelectedCode
);
710 // Range that spans multiple functions is an invalid code range.
711 findSelectedASTNodesWithRange(
712 Source
, {2, 2}, FileRange
{{7, 2}, {12, 1}},
713 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
715 std::optional
<CodeRangeASTSelection
> SelectedCode
=
716 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
717 EXPECT_FALSE(SelectedCode
);
720 findSelectedASTNodesWithRange(
721 Source
, {2, 2}, FileRange
{{2, 2}, {2, 13}},
722 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
724 std::optional
<CodeRangeASTSelection
> SelectedCode
=
725 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
726 EXPECT_TRUE(SelectedCode
);
727 EXPECT_EQ(SelectedCode
->size(), 1u);
728 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
729 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
730 SelectedCode
->getParents();
731 EXPECT_EQ(Parents
.size(), 3u);
733 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
734 // Function 'f' definition.
735 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
736 // Function body of function 'F'.
737 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
739 // From 'f(2,3)' until just before 'x = 1;':
740 findSelectedASTNodesWithRange(
741 Source
, {3, 2}, FileRange
{{3, 2}, {7, 1}},
742 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
744 std::optional
<CodeRangeASTSelection
> SelectedCode
=
745 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
746 EXPECT_TRUE(SelectedCode
);
747 EXPECT_EQ(SelectedCode
->size(), 2u);
748 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[0]));
749 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[1]));
750 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
751 SelectedCode
->getParents();
752 EXPECT_EQ(Parents
.size(), 3u);
754 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
755 // Function 'f' definition.
756 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
757 // Function body of function 'F'.
758 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
760 // From 'f(2,3)' until just before ';' in 'x = 1;':
761 findSelectedASTNodesWithRange(
762 Source
, {3, 2}, FileRange
{{3, 2}, {7, 8}},
763 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
765 std::optional
<CodeRangeASTSelection
> SelectedCode
=
766 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
767 EXPECT_TRUE(SelectedCode
);
768 EXPECT_EQ(SelectedCode
->size(), 3u);
769 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[0]));
770 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[1]));
771 EXPECT_TRUE(isa
<BinaryOperator
>((*SelectedCode
)[2]));
773 // From the middle of 'int z = 3' until the middle of 'x = 1;':
774 findSelectedASTNodesWithRange(
775 Source
, {2, 10}, FileRange
{{2, 10}, {7, 5}},
776 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
779 std::optional
<CodeRangeASTSelection
> SelectedCode
=
780 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
781 EXPECT_TRUE(SelectedCode
);
782 EXPECT_EQ(SelectedCode
->size(), 4u);
783 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
784 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[1]));
785 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[2]));
786 EXPECT_TRUE(isa
<BinaryOperator
>((*SelectedCode
)[3]));
790 TEST(ASTSelectionFinder
, OutOfBodyCodeRange
) {
791 StringRef Source
= R
"(
792 int codeRange = 2 + 3;
795 findSelectedASTNodesWithRange(
796 Source
, {2, 17}, FileRange
{{2, 17}, {2, 22}},
797 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
799 std::optional
<CodeRangeASTSelection
> SelectedCode
=
800 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
801 EXPECT_TRUE(SelectedCode
);
802 EXPECT_EQ(SelectedCode
->size(), 1u);
803 EXPECT_TRUE(isa
<BinaryOperator
>((*SelectedCode
)[0]));
804 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
805 SelectedCode
->getParents();
806 EXPECT_EQ(Parents
.size(), 2u);
808 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
809 // Variable 'codeRange'.
810 EXPECT_TRUE(isa
<VarDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
814 TEST(ASTSelectionFinder
, SelectVarDeclStmt
) {
815 StringRef Source
= R
"(
823 findSelectedASTNodesWithRange(
824 Source
, {4, 8}, FileRange
{{4, 8}, {4, 14}},
825 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
827 std::optional
<CodeRangeASTSelection
> SelectedCode
=
828 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
829 EXPECT_TRUE(SelectedCode
);
830 EXPECT_EQ(SelectedCode
->size(), 1u);
831 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
832 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
833 SelectedCode
->getParents();
834 EXPECT_EQ(Parents
.size(), 4u);
836 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
837 // Function 'f' definition.
838 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
839 // Function body of function 'F'.
840 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
841 // Compound statement in body of 'F'.
842 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[3].get().Node
.get
<Stmt
>()));
846 TEST(ASTSelectionFinder
, SelectEntireDeclStmtRange
) {
847 StringRef Source
= R
"(
848 void f(int x, int y) {
853 findSelectedASTNodesWithRange(
854 Source
, {3, 4}, FileRange
{{3, 4}, {3, 17}},
855 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
857 std::optional
<CodeRangeASTSelection
> SelectedCode
=
858 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
859 EXPECT_TRUE(SelectedCode
);
860 EXPECT_EQ(SelectedCode
->size(), 1u);
861 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
862 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
863 SelectedCode
->getParents();
864 EXPECT_EQ(Parents
.size(), 3u);
866 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
867 // Function 'f' definition.
868 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
869 // Function body of function 'F'.
870 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
874 TEST(ASTSelectionFinder
, SelectEntireDeclStmtRangeWithMultipleDecls
) {
875 StringRef Source
= R
"(
876 void f(int x, int y) {
877 int a = x * y, b = x - y;
881 findSelectedASTNodesWithRange(
882 Source
, {3, 19}, FileRange
{{3, 19}, {3, 28}},
883 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
885 std::optional
<CodeRangeASTSelection
> SelectedCode
=
886 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
887 EXPECT_TRUE(SelectedCode
);
888 EXPECT_EQ(SelectedCode
->size(), 1u);
889 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
890 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
891 SelectedCode
->getParents();
892 EXPECT_EQ(Parents
.size(), 3u);
894 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
895 // Function 'f' definition.
896 EXPECT_TRUE(isa
<FunctionDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
897 // Function body of function 'F'.
898 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[2].get().Node
.get
<Stmt
>()));
902 TEST(ASTSelectionFinder
, SimpleCodeRangeASTSelectionInObjCMethod
) {
903 StringRef Source
= R
"(@interface I @end
905 - (void) f:(int)x with:(int) y {
919 // Range that spans multiple methods is an invalid code range.
920 findSelectedASTNodesWithRange(
921 Source
, {9, 2}, FileRange
{{9, 2}, {13, 1}},
922 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
924 std::optional
<CodeRangeASTSelection
> SelectedCode
=
925 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
926 EXPECT_FALSE(SelectedCode
);
928 SelectionFinderVisitor::Lang_OBJC
);
930 findSelectedASTNodesWithRange(
931 Source
, {4, 2}, FileRange
{{4, 2}, {4, 13}},
932 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
934 std::optional
<CodeRangeASTSelection
> SelectedCode
=
935 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
936 EXPECT_TRUE(SelectedCode
);
937 EXPECT_EQ(SelectedCode
->size(), 1u);
938 EXPECT_TRUE(isa
<DeclStmt
>((*SelectedCode
)[0]));
939 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
940 SelectedCode
->getParents();
941 EXPECT_EQ(Parents
.size(), 4u);
943 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
944 // 'I' @implementation.
945 EXPECT_TRUE(isa
<ObjCImplDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
946 // Function 'f' definition.
947 EXPECT_TRUE(isa
<ObjCMethodDecl
>(Parents
[2].get().Node
.get
<Decl
>()));
948 // Function body of function 'F'.
949 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[3].get().Node
.get
<Stmt
>()));
951 SelectionFinderVisitor::Lang_OBJC
);
952 // From '[self f: 2 with: 3]' until just before 'x = 1;':
953 findSelectedASTNodesWithRange(
954 Source
, {5, 2}, FileRange
{{5, 2}, {9, 1}},
955 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
957 std::optional
<CodeRangeASTSelection
> SelectedCode
=
958 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
959 EXPECT_TRUE(SelectedCode
);
960 EXPECT_EQ(SelectedCode
->size(), 2u);
961 EXPECT_TRUE(isa
<ObjCMessageExpr
>((*SelectedCode
)[0]));
962 EXPECT_TRUE(isa
<IfStmt
>((*SelectedCode
)[1]));
963 ArrayRef
<SelectedASTNode::ReferenceType
> Parents
=
964 SelectedCode
->getParents();
965 EXPECT_EQ(Parents
.size(), 4u);
967 isa
<TranslationUnitDecl
>(Parents
[0].get().Node
.get
<Decl
>()));
968 // 'I' @implementation.
969 EXPECT_TRUE(isa
<ObjCImplDecl
>(Parents
[1].get().Node
.get
<Decl
>()));
970 // Function 'f' definition.
971 EXPECT_TRUE(isa
<ObjCMethodDecl
>(Parents
[2].get().Node
.get
<Decl
>()));
972 // Function body of function 'F'.
973 EXPECT_TRUE(isa
<CompoundStmt
>(Parents
[3].get().Node
.get
<Stmt
>()));
975 SelectionFinderVisitor::Lang_OBJC
);
978 TEST(ASTSelectionFinder
, CanonicalizeObjCStringLiteral
) {
979 StringRef Source
= R
"(
985 findSelectedASTNodesWithRange(
986 Source
, {3, 10}, FileRange
{{3, 10}, {3, 16}},
987 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
989 std::optional
<CodeRangeASTSelection
> SelectedCode
=
990 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
991 EXPECT_TRUE(SelectedCode
);
992 EXPECT_EQ(SelectedCode
->size(), 1u);
993 EXPECT_TRUE(isa
<ObjCStringLiteral
>((*SelectedCode
)[0]));
995 SelectionFinderVisitor::Lang_OBJC
);
997 findSelectedASTNodesWithRange(
998 Source
, {3, 11}, FileRange
{{3, 11}, {3, 15}},
999 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
1001 std::optional
<CodeRangeASTSelection
> SelectedCode
=
1002 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1003 EXPECT_TRUE(SelectedCode
);
1004 EXPECT_EQ(SelectedCode
->size(), 1u);
1005 EXPECT_TRUE(isa
<ObjCStringLiteral
>((*SelectedCode
)[0]));
1007 SelectionFinderVisitor::Lang_OBJC
);
1010 TEST(ASTSelectionFinder
, CanonicalizeMemberCalleeToCall
) {
1011 StringRef Source
= R
"(
1012 class AClass { public:
1015 void selectWholeCallWhenJustMethodSelected(int &i) {
1019 void selectWholeCallWhenJustMethodSelected() {
1023 void dontSelectArgument(AClass &a) {
1024 a.selectWholeCallWhenJustMethodSelected(a.afield);
1027 // Just 'method' with implicit 'this':
1028 findSelectedASTNodesWithRange(
1029 Source
, {6, 5}, FileRange
{{6, 5}, {6, 11}},
1030 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
1032 std::optional
<CodeRangeASTSelection
> SelectedCode
=
1033 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1034 EXPECT_TRUE(SelectedCode
);
1035 EXPECT_EQ(SelectedCode
->size(), 1u);
1036 EXPECT_TRUE(isa
<CXXMemberCallExpr
>((*SelectedCode
)[0]));
1039 findSelectedASTNodesWithRange(
1040 Source
, {11, 5}, FileRange
{{11, 5}, {11, 11}},
1041 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
1043 std::optional
<CodeRangeASTSelection
> SelectedCode
=
1044 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1045 EXPECT_TRUE(SelectedCode
);
1046 EXPECT_EQ(SelectedCode
->size(), 1u);
1047 EXPECT_TRUE(isa
<CXXMemberCallExpr
>((*SelectedCode
)[0]));
1049 // Just 'afield', which should not select the call.
1050 findSelectedASTNodesWithRange(
1051 Source
, {14, 5}, FileRange
{{14, 45}, {14, 51}},
1052 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
1054 std::optional
<CodeRangeASTSelection
> SelectedCode
=
1055 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1056 EXPECT_TRUE(SelectedCode
);
1057 EXPECT_EQ(SelectedCode
->size(), 1u);
1058 EXPECT_FALSE(isa
<CXXMemberCallExpr
>((*SelectedCode
)[0]));
1062 TEST(ASTSelectionFinder
, CanonicalizeFuncCalleeToCall
) {
1063 StringRef Source
= R
"(
1071 findSelectedASTNodesWithRange(
1072 Source
, {5, 3}, FileRange
{{5, 3}, {5, 11}},
1073 [](SourceRange SelectionRange
, std::optional
<SelectedASTNode
> Node
) {
1076 std::optional
<CodeRangeASTSelection
> SelectedCode
=
1077 CodeRangeASTSelection::create(SelectionRange
, std::move(*Node
));
1078 EXPECT_TRUE(SelectedCode
);
1079 EXPECT_EQ(SelectedCode
->size(), 1u);
1080 EXPECT_TRUE(isa
<CallExpr
>((*SelectedCode
)[0]));
1081 EXPECT_TRUE(isa
<CompoundStmt
>(
1082 SelectedCode
->getParents()[SelectedCode
->getParents().size() - 1]
1084 .Node
.get
<Stmt
>()));
1088 } // end anonymous namespace