1 //===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "TestVisitor.h"
11 #include "clang/Basic/SourceManager.h"
12 #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<SelectionFinderVisitor> {
30 FileLocation Location;
31 Optional<FileRange> SelectionRange;
32 llvm::function_ref<void(SourceRange SelectionRange,
33 Optional<SelectedASTNode>)>
37 SelectionFinderVisitor(FileLocation Location,
38 Optional<FileRange> SelectionRange,
39 llvm::function_ref<void(SourceRange SelectionRange,
40 Optional<SelectedASTNode>)>
42 : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
45 bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) {
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, Optional<FileRange> SelectionRange,
69 llvm::function_ref<void(SourceRange SelectionRange,
70 Optional<SelectedASTNode>)>
72 SelectionFinderVisitor::Language Language =
73 SelectionFinderVisitor::Lang_CXX11) {
74 SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
75 EXPECT_TRUE(Visitor.runOver(Source, Language));
78 void findSelectedASTNodes(
79 StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange,
80 llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer,
81 SelectionFinderVisitor::Language Language =
82 SelectionFinderVisitor::Lang_CXX11) {
83 findSelectedASTNodesWithRange(
84 Source, Location, SelectionRange,
85 [&](SourceRange, Optional<SelectedASTNode> Selection) {
86 Consumer(std::move(Selection));
91 void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node,
92 SourceSelectionKind SelectionKind, unsigned NumChildren) {
93 ASSERT_TRUE(IsTypeMatched);
94 EXPECT_EQ(Node.Children.size(), NumChildren);
95 ASSERT_EQ(Node.SelectionKind, SelectionKind);
98 void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
99 const auto *ND = Node.Node.get<NamedDecl>();
101 ASSERT_EQ(ND->getName(), Name);
104 template <typename T>
105 const SelectedASTNode &
106 checkNode(const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind,
107 unsigned NumChildren = 0,
108 typename std::enable_if<std::is_base_of<Stmt, T>::value, T>::type
109 *StmtOverloadChecker = nullptr) {
110 checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
115 template <typename T>
116 const SelectedASTNode &
117 checkNode(const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind,
118 unsigned NumChildren = 0, StringRef Name = "",
119 typename std::enable_if<std::is_base_of<Decl, T>::value, T>::type
120 *DeclOverloadChecker = nullptr) {
121 checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
124 checkDeclName(DeclNode, Name);
128 struct ForAllChildrenOf {
129 const SelectedASTNode &Node;
131 static void childKindVerifier(const SelectedASTNode &Node,
132 SourceSelectionKind SelectionKind) {
133 for (const SelectedASTNode &Child : Node.Children) {
134 ASSERT_EQ(Node.SelectionKind, SelectionKind);
135 childKindVerifier(Child, SelectionKind);
140 ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
142 void shouldHaveSelectionKind(SourceSelectionKind Kind) {
143 childKindVerifier(Node, Kind);
147 ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
148 return ForAllChildrenOf(Node);
151 TEST(ASTSelectionFinder, CursorNoSelection) {
152 findSelectedASTNodes(
153 " void f() { }", {1, 1}, None,
154 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
157 TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
158 findSelectedASTNodes(
159 "void f() { }", {1, 1}, None, [](Optional<SelectedASTNode> Node) {
161 checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None,
163 checkNode<FunctionDecl>(Node->Children[0],
164 SourceSelectionKind::ContainsSelection,
165 /*NumChildren=*/0, /*Name=*/"f");
167 // Check that the dumping works.
168 std::string DumpValue;
169 llvm::raw_string_ostream OS(DumpValue);
170 Node->Children[0].dump(OS);
171 ASSERT_EQ(OS.str(), "FunctionDecl \"f\" contains-selection\n");
175 TEST(ASTSelectionFinder, RangeNoSelection) {
176 findSelectedASTNodes(
177 " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
178 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
179 findSelectedASTNodes(
180 " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 2}},
181 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
184 TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
185 findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
186 [](Optional<SelectedASTNode> Node) {
188 checkNode<FunctionDecl>(
190 SourceSelectionKind::ContainsSelection,
191 /*NumChildren=*/0, /*Name=*/"f");
195 TEST(ASTSelectionFinder, WholeFunctionSelection) {
196 StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
197 // From 'int' until just after '}':
199 findSelectedASTNodes(
200 Source, {1, 1}, FileRange{{1, 1}, {2, 2}},
201 [](Optional<SelectedASTNode> Node) {
203 EXPECT_EQ(Node->Children.size(), 1u);
204 const auto &Fn = checkNode<FunctionDecl>(
205 Node->Children[0], SourceSelectionKind::ContainsSelection,
206 /*NumChildren=*/2, /*Name=*/"f");
207 checkNode<ParmVarDecl>(Fn.Children[0],
208 SourceSelectionKind::InsideSelection);
209 const auto &Body = checkNode<CompoundStmt>(
210 Fn.Children[1], SourceSelectionKind::InsideSelection,
212 const auto &Return = checkNode<ReturnStmt>(
213 Body.Children[0], SourceSelectionKind::InsideSelection,
215 checkNode<ImplicitCastExpr>(Return.Children[0],
216 SourceSelectionKind::InsideSelection,
218 checkNode<DeclRefExpr>(Return.Children[0].Children[0],
219 SourceSelectionKind::InsideSelection);
222 // From 'int' until just before '}':
223 findSelectedASTNodes(
224 Source, {2, 1}, FileRange{{1, 1}, {2, 1}},
225 [](Optional<SelectedASTNode> Node) {
227 EXPECT_EQ(Node->Children.size(), 1u);
228 const auto &Fn = checkNode<FunctionDecl>(
229 Node->Children[0], SourceSelectionKind::ContainsSelection,
230 /*NumChildren=*/2, /*Name=*/"f");
231 const auto &Body = checkNode<CompoundStmt>(
232 Fn.Children[1], SourceSelectionKind::ContainsSelectionEnd,
234 checkNode<ReturnStmt>(Body.Children[0],
235 SourceSelectionKind::InsideSelection,
238 // From '{' until just after '}':
239 findSelectedASTNodes(
240 Source, {1, 14}, FileRange{{1, 14}, {2, 2}},
241 [](Optional<SelectedASTNode> Node) {
243 EXPECT_EQ(Node->Children.size(), 1u);
244 const auto &Fn = checkNode<FunctionDecl>(
245 Node->Children[0], SourceSelectionKind::ContainsSelection,
246 /*NumChildren=*/1, /*Name=*/"f");
247 const auto &Body = checkNode<CompoundStmt>(
248 Fn.Children[0], SourceSelectionKind::ContainsSelection,
250 checkNode<ReturnStmt>(Body.Children[0],
251 SourceSelectionKind::InsideSelection,
254 // From 'x' until just after '}':
255 findSelectedASTNodes(
256 Source, {2, 2}, FileRange{{1, 11}, {2, 2}},
257 [](Optional<SelectedASTNode> Node) {
259 EXPECT_EQ(Node->Children.size(), 1u);
260 const auto &Fn = checkNode<FunctionDecl>(
261 Node->Children[0], SourceSelectionKind::ContainsSelection,
262 /*NumChildren=*/2, /*Name=*/"f");
263 checkNode<ParmVarDecl>(Fn.Children[0],
264 SourceSelectionKind::ContainsSelectionStart);
265 const auto &Body = checkNode<CompoundStmt>(
266 Fn.Children[1], SourceSelectionKind::InsideSelection,
268 checkNode<ReturnStmt>(Body.Children[0],
269 SourceSelectionKind::InsideSelection,
274 TEST(ASTSelectionFinder, MultipleFunctionSelection) {
275 StringRef Source = R"(void f0() {
281 auto SelectedF1F2 = [](Optional<SelectedASTNode> Node) {
283 EXPECT_EQ(Node->Children.size(), 2u);
284 checkNode<FunctionDecl>(Node->Children[0],
285 SourceSelectionKind::InsideSelection,
286 /*NumChildren=*/1, /*Name=*/"f1");
287 checkNode<FunctionDecl>(Node->Children[1],
288 SourceSelectionKind::InsideSelection,
289 /*NumChildren=*/1, /*Name=*/"f2");
291 // Just after '}' of f0 and just before 'void' of f3:
292 findSelectedASTNodes(Source, {2, 2}, FileRange{{2, 2}, {5, 1}}, SelectedF1F2);
293 // Just before 'void' of f1 and just after '}' of f2:
294 findSelectedASTNodes(Source, {3, 1}, FileRange{{3, 1}, {4, 14}},
298 TEST(ASTSelectionFinder, MultipleStatementSelection) {
299 StringRef Source = R"(void f(int x, int y) {
308 // From 'f(2,3)' until just before 'x = 1;':
309 findSelectedASTNodes(
310 Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
311 [](Optional<SelectedASTNode> Node) {
313 EXPECT_EQ(Node->Children.size(), 1u);
314 const auto &Fn = checkNode<FunctionDecl>(
315 Node->Children[0], SourceSelectionKind::ContainsSelection,
316 /*NumChildren=*/1, /*Name=*/"f");
317 const auto &Body = checkNode<CompoundStmt>(
318 Fn.Children[0], SourceSelectionKind::ContainsSelection,
320 allChildrenOf(checkNode<CallExpr>(Body.Children[0],
321 SourceSelectionKind::InsideSelection,
323 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
324 allChildrenOf(checkNode<IfStmt>(Body.Children[1],
325 SourceSelectionKind::InsideSelection,
327 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
329 // From 'f(2,3)' until just before ';' in 'x = 1;':
330 findSelectedASTNodes(
331 Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
332 [](Optional<SelectedASTNode> Node) {
334 EXPECT_EQ(Node->Children.size(), 1u);
335 const auto &Fn = checkNode<FunctionDecl>(
336 Node->Children[0], SourceSelectionKind::ContainsSelection,
337 /*NumChildren=*/1, /*Name=*/"f");
338 const auto &Body = checkNode<CompoundStmt>(
339 Fn.Children[0], SourceSelectionKind::ContainsSelection,
341 checkNode<CallExpr>(Body.Children[0],
342 SourceSelectionKind::InsideSelection,
344 checkNode<IfStmt>(Body.Children[1],
345 SourceSelectionKind::InsideSelection,
347 checkNode<BinaryOperator>(Body.Children[2],
348 SourceSelectionKind::InsideSelection,
351 // From the middle of 'int z = 3' until the middle of 'x = 1;':
352 findSelectedASTNodes(
353 Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
354 [](Optional<SelectedASTNode> Node) {
356 EXPECT_EQ(Node->Children.size(), 1u);
357 const auto &Fn = checkNode<FunctionDecl>(
358 Node->Children[0], SourceSelectionKind::ContainsSelection,
359 /*NumChildren=*/1, /*Name=*/"f");
360 const auto &Body = checkNode<CompoundStmt>(
361 Fn.Children[0], SourceSelectionKind::ContainsSelection,
363 checkNode<DeclStmt>(Body.Children[0],
364 SourceSelectionKind::ContainsSelectionStart,
366 checkNode<CallExpr>(Body.Children[1],
367 SourceSelectionKind::InsideSelection,
369 checkNode<IfStmt>(Body.Children[2],
370 SourceSelectionKind::InsideSelection,
372 checkNode<BinaryOperator>(Body.Children[3],
373 SourceSelectionKind::ContainsSelectionEnd,
378 TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
379 StringRef Source = R"(
384 int notSelected() { }
386 int selected(int x) {
391 @implementation I(Cat)
397 void outerFunction() { }
399 // Just the 'x' expression in 'selected':
400 findSelectedASTNodes(
401 Source, {9, 10}, FileRange{{9, 10}, {9, 11}},
402 [](Optional<SelectedASTNode> Node) {
404 EXPECT_EQ(Node->Children.size(), 1u);
405 const auto &Impl = checkNode<ObjCImplementationDecl>(
406 Node->Children[0], SourceSelectionKind::ContainsSelection,
407 /*NumChildren=*/1, /*Name=*/"I");
408 const auto &Fn = checkNode<FunctionDecl>(
409 Impl.Children[0], SourceSelectionKind::ContainsSelection,
410 /*NumChildren=*/1, /*Name=*/"selected");
411 allChildrenOf(Fn).shouldHaveSelectionKind(
412 SourceSelectionKind::ContainsSelection);
414 SelectionFinderVisitor::Lang_OBJC);
415 // The entire 'catF':
416 findSelectedASTNodes(
417 Source, {15, 1}, FileRange{{15, 1}, {15, 16}},
418 [](Optional<SelectedASTNode> Node) {
420 EXPECT_EQ(Node->Children.size(), 1u);
421 const auto &Impl = checkNode<ObjCCategoryImplDecl>(
422 Node->Children[0], SourceSelectionKind::ContainsSelection,
423 /*NumChildren=*/1, /*Name=*/"Cat");
424 const auto &Fn = checkNode<FunctionDecl>(
425 Impl.Children[0], SourceSelectionKind::ContainsSelection,
426 /*NumChildren=*/1, /*Name=*/"catF");
427 allChildrenOf(Fn).shouldHaveSelectionKind(
428 SourceSelectionKind::ContainsSelection);
430 SelectionFinderVisitor::Lang_OBJC);
431 // From the line before 'selected' to the line after 'catF':
432 findSelectedASTNodes(
433 Source, {16, 1}, FileRange{{7, 1}, {16, 1}},
434 [](Optional<SelectedASTNode> Node) {
436 EXPECT_EQ(Node->Children.size(), 2u);
437 const auto &Impl = checkNode<ObjCImplementationDecl>(
438 Node->Children[0], SourceSelectionKind::ContainsSelectionStart,
439 /*NumChildren=*/1, /*Name=*/"I");
440 const auto &Selected = checkNode<FunctionDecl>(
441 Impl.Children[0], SourceSelectionKind::InsideSelection,
442 /*NumChildren=*/2, /*Name=*/"selected");
443 allChildrenOf(Selected).shouldHaveSelectionKind(
444 SourceSelectionKind::InsideSelection);
445 const auto &Cat = checkNode<ObjCCategoryImplDecl>(
446 Node->Children[1], SourceSelectionKind::ContainsSelectionEnd,
447 /*NumChildren=*/1, /*Name=*/"Cat");
448 const auto &CatF = checkNode<FunctionDecl>(
449 Cat.Children[0], SourceSelectionKind::InsideSelection,
450 /*NumChildren=*/1, /*Name=*/"catF");
451 allChildrenOf(CatF).shouldHaveSelectionKind(
452 SourceSelectionKind::InsideSelection);
454 SelectionFinderVisitor::Lang_OBJC);
455 // Just the 'outer' function:
456 findSelectedASTNodes(Source, {19, 1}, FileRange{{19, 1}, {19, 25}},
457 [](Optional<SelectedASTNode> Node) {
459 EXPECT_EQ(Node->Children.size(), 1u);
460 checkNode<FunctionDecl>(
462 SourceSelectionKind::ContainsSelection,
463 /*NumChildren=*/1, /*Name=*/"outerFunction");
465 SelectionFinderVisitor::Lang_OBJC);
468 TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
469 StringRef Source = R"(
482 findSelectedASTNodes(
483 Source, {6, 1}, FileRange{{6, 1}, {7, 2}},
484 [](Optional<SelectedASTNode> Node) {
486 EXPECT_EQ(Node->Children.size(), 1u);
487 const auto &Impl = checkNode<ObjCImplementationDecl>(
488 Node->Children[0], SourceSelectionKind::ContainsSelection,
489 /*NumChildren=*/1, /*Name=*/"I");
490 checkNode<FunctionDecl>(Impl.Children[0],
491 SourceSelectionKind::ContainsSelection,
492 /*NumChildren=*/1, /*Name=*/"selected");
494 SelectionFinderVisitor::Lang_OBJC);
497 TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
498 StringRef Source = R"(
507 // The entire struct 'Copy':
508 findSelectedASTNodes(
509 Source, {2, 1}, FileRange{{2, 1}, {4, 3}},
510 [](Optional<SelectedASTNode> Node) {
512 EXPECT_EQ(Node->Children.size(), 1u);
513 const auto &Record = checkNode<CXXRecordDecl>(
514 Node->Children[0], SourceSelectionKind::InsideSelection,
515 /*NumChildren=*/1, /*Name=*/"Copy");
516 checkNode<FieldDecl>(Record.Children[0],
517 SourceSelectionKind::InsideSelection);
521 TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
522 StringRef Source = R"(
528 // Just after '@ end'
529 findSelectedASTNodes(Source, {5, 6}, None,
530 [](Optional<SelectedASTNode> Node) {
532 EXPECT_EQ(Node->Children.size(), 1u);
533 checkNode<ObjCImplementationDecl>(
535 SourceSelectionKind::ContainsSelection);
537 SelectionFinderVisitor::Lang_OBJC);
540 const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node,
543 EXPECT_EQ(Node->Children.size(), 1u);
544 const auto &Fn = checkNode<FunctionDecl>(
545 Node->Children[0], SourceSelectionKind::ContainsSelection,
546 /*NumChildren=*/1, Name);
547 return checkNode<CompoundStmt>(Fn.Children[0],
548 SourceSelectionKind::ContainsSelection,
552 TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
553 StringRef Source = R"(
555 @property(readwrite) int prop;
557 void selectProp(I *i) {
563 @interface NSMutableArray
564 - (id)objectAtIndexedSubscript:(unsigned int)index;
565 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
568 void selectSubscript(NSMutableArray *array, I *i) {
574 findSelectedASTNodes(
575 Source, {6, 7}, FileRange{{6, 7}, {6, 13}},
576 [](Optional<SelectedASTNode> Node) {
577 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
578 const auto &CCast = checkNode<CStyleCastExpr>(
579 CS.Children[0], SourceSelectionKind::ContainsSelection,
581 const auto &POE = checkNode<PseudoObjectExpr>(
582 CCast.Children[0], SourceSelectionKind::ContainsSelection,
584 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
585 POE.Children[0], SourceSelectionKind::ContainsSelection,
587 const auto &Cast = checkNode<ImplicitCastExpr>(
588 PRE.Children[0], SourceSelectionKind::InsideSelection,
590 checkNode<DeclRefExpr>(Cast.Children[0],
591 SourceSelectionKind::InsideSelection);
593 SelectionFinderVisitor::Lang_OBJC);
594 // Just 'i.prop = 21'
595 findSelectedASTNodes(
596 Source, {7, 1}, FileRange{{7, 1}, {7, 12}},
597 [](Optional<SelectedASTNode> Node) {
598 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
599 const auto &POE = checkNode<PseudoObjectExpr>(
600 CS.Children[0], SourceSelectionKind::ContainsSelection,
602 const auto &BinOp = checkNode<BinaryOperator>(
603 POE.Children[0], SourceSelectionKind::ContainsSelection,
605 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
606 BinOp.Children[0], SourceSelectionKind::InsideSelection,
608 const auto &Cast = checkNode<ImplicitCastExpr>(
609 PRE.Children[0], SourceSelectionKind::InsideSelection,
611 checkNode<DeclRefExpr>(Cast.Children[0],
612 SourceSelectionKind::InsideSelection);
613 checkNode<IntegerLiteral>(BinOp.Children[1],
614 SourceSelectionKind::InsideSelection);
616 SelectionFinderVisitor::Lang_OBJC);
618 findSelectedASTNodes(
619 Source, {17, 9}, FileRange{{17, 9}, {17, 18}},
620 [](Optional<SelectedASTNode> Node) {
621 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
622 const auto &CCast = checkNode<CStyleCastExpr>(
623 CS.Children[0], SourceSelectionKind::ContainsSelection,
625 const auto &POE = checkNode<PseudoObjectExpr>(
626 CCast.Children[0], SourceSelectionKind::ContainsSelection,
628 const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
629 POE.Children[0], SourceSelectionKind::ContainsSelection,
631 const auto &Cast = checkNode<ImplicitCastExpr>(
632 SRE.Children[0], SourceSelectionKind::InsideSelection,
634 checkNode<DeclRefExpr>(Cast.Children[0],
635 SourceSelectionKind::InsideSelection);
636 checkNode<IntegerLiteral>(SRE.Children[1],
637 SourceSelectionKind::InsideSelection);
639 SelectionFinderVisitor::Lang_OBJC);
640 // Just 'array[i.prop] = array'
641 findSelectedASTNodes(
642 Source, {18, 3}, FileRange{{18, 3}, {18, 20}},
643 [](Optional<SelectedASTNode> Node) {
644 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
645 const auto &POE = checkNode<PseudoObjectExpr>(
646 CS.Children[0], SourceSelectionKind::ContainsSelection,
648 const auto &BinOp = checkNode<BinaryOperator>(
649 POE.Children[0], SourceSelectionKind::ContainsSelection,
651 const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
652 BinOp.Children[0], SourceSelectionKind::InsideSelection,
654 const auto &Cast = checkNode<ImplicitCastExpr>(
655 SRE.Children[0], SourceSelectionKind::InsideSelection,
657 checkNode<DeclRefExpr>(Cast.Children[0],
658 SourceSelectionKind::InsideSelection);
659 const auto &POE2 = checkNode<PseudoObjectExpr>(
660 SRE.Children[1], SourceSelectionKind::InsideSelection,
662 const auto &PRE = checkNode<ObjCPropertyRefExpr>(
663 POE2.Children[0], SourceSelectionKind::InsideSelection,
665 const auto &Cast2 = checkNode<ImplicitCastExpr>(
666 PRE.Children[0], SourceSelectionKind::InsideSelection,
668 checkNode<DeclRefExpr>(Cast2.Children[0],
669 SourceSelectionKind::InsideSelection);
670 checkNode<DeclRefExpr>(BinOp.Children[1],
671 SourceSelectionKind::InsideSelection);
673 SelectionFinderVisitor::Lang_OBJC);
676 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelection) {
677 StringRef Source = R"(void f(int x, int y) {
690 // No selection range.
691 findSelectedASTNodesWithRange(
692 Source, {2, 2}, None,
693 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
695 Optional<CodeRangeASTSelection> SelectedCode =
696 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
697 EXPECT_FALSE(SelectedCode);
699 findSelectedASTNodesWithRange(
700 Source, {2, 2}, FileRange{{2, 2}, {2, 2}},
701 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
703 Optional<CodeRangeASTSelection> SelectedCode =
704 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
705 EXPECT_FALSE(SelectedCode);
707 // Range that spans multiple functions is an invalid code range.
708 findSelectedASTNodesWithRange(
709 Source, {2, 2}, FileRange{{7, 2}, {12, 1}},
710 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
712 Optional<CodeRangeASTSelection> SelectedCode =
713 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
714 EXPECT_FALSE(SelectedCode);
717 findSelectedASTNodesWithRange(
718 Source, {2, 2}, FileRange{{2, 2}, {2, 13}},
719 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
721 Optional<CodeRangeASTSelection> SelectedCode =
722 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
723 EXPECT_TRUE(SelectedCode);
724 EXPECT_EQ(SelectedCode->size(), 1u);
725 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
726 ArrayRef<SelectedASTNode::ReferenceType> Parents =
727 SelectedCode->getParents();
728 EXPECT_EQ(Parents.size(), 3u);
730 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
731 // Function 'f' definition.
732 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
733 // Function body of function 'F'.
734 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
736 // From 'f(2,3)' until just before 'x = 1;':
737 findSelectedASTNodesWithRange(
738 Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
739 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
741 Optional<CodeRangeASTSelection> SelectedCode =
742 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
743 EXPECT_TRUE(SelectedCode);
744 EXPECT_EQ(SelectedCode->size(), 2u);
745 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
746 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
747 ArrayRef<SelectedASTNode::ReferenceType> Parents =
748 SelectedCode->getParents();
749 EXPECT_EQ(Parents.size(), 3u);
751 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
752 // Function 'f' definition.
753 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
754 // Function body of function 'F'.
755 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
757 // From 'f(2,3)' until just before ';' in 'x = 1;':
758 findSelectedASTNodesWithRange(
759 Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
760 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
762 Optional<CodeRangeASTSelection> SelectedCode =
763 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
764 EXPECT_TRUE(SelectedCode);
765 EXPECT_EQ(SelectedCode->size(), 3u);
766 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
767 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
768 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[2]));
770 // From the middle of 'int z = 3' until the middle of 'x = 1;':
771 findSelectedASTNodesWithRange(
772 Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
773 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
776 Optional<CodeRangeASTSelection> SelectedCode =
777 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
778 EXPECT_TRUE(SelectedCode);
779 EXPECT_EQ(SelectedCode->size(), 4u);
780 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
781 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[1]));
782 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[2]));
783 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[3]));
787 TEST(ASTSelectionFinder, OutOfBodyCodeRange) {
788 StringRef Source = R"(
789 int codeRange = 2 + 3;
792 findSelectedASTNodesWithRange(
793 Source, {2, 17}, FileRange{{2, 17}, {2, 22}},
794 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
796 Optional<CodeRangeASTSelection> SelectedCode =
797 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
798 EXPECT_TRUE(SelectedCode);
799 EXPECT_EQ(SelectedCode->size(), 1u);
800 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[0]));
801 ArrayRef<SelectedASTNode::ReferenceType> Parents =
802 SelectedCode->getParents();
803 EXPECT_EQ(Parents.size(), 2u);
805 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
806 // Variable 'codeRange'.
807 EXPECT_TRUE(isa<VarDecl>(Parents[1].get().Node.get<Decl>()));
811 TEST(ASTSelectionFinder, SelectVarDeclStmt) {
812 StringRef Source = R"(
820 findSelectedASTNodesWithRange(
821 Source, {4, 8}, FileRange{{4, 8}, {4, 14}},
822 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
824 Optional<CodeRangeASTSelection> SelectedCode =
825 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
826 EXPECT_TRUE(SelectedCode);
827 EXPECT_EQ(SelectedCode->size(), 1u);
828 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
829 ArrayRef<SelectedASTNode::ReferenceType> Parents =
830 SelectedCode->getParents();
831 EXPECT_EQ(Parents.size(), 4u);
833 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
834 // Function 'f' definition.
835 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
836 // Function body of function 'F'.
837 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
838 // Compound statement in body of 'F'.
839 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
843 TEST(ASTSelectionFinder, SelectEntireDeclStmtRange) {
844 StringRef Source = R"(
845 void f(int x, int y) {
850 findSelectedASTNodesWithRange(
851 Source, {3, 4}, FileRange{{3, 4}, {3, 17}},
852 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
854 Optional<CodeRangeASTSelection> SelectedCode =
855 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
856 EXPECT_TRUE(SelectedCode);
857 EXPECT_EQ(SelectedCode->size(), 1u);
858 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
859 ArrayRef<SelectedASTNode::ReferenceType> Parents =
860 SelectedCode->getParents();
861 EXPECT_EQ(Parents.size(), 3u);
863 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
864 // Function 'f' definition.
865 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
866 // Function body of function 'F'.
867 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
871 TEST(ASTSelectionFinder, SelectEntireDeclStmtRangeWithMultipleDecls) {
872 StringRef Source = R"(
873 void f(int x, int y) {
874 int a = x * y, b = x - y;
878 findSelectedASTNodesWithRange(
879 Source, {3, 19}, FileRange{{3, 19}, {3, 28}},
880 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
882 Optional<CodeRangeASTSelection> SelectedCode =
883 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
884 EXPECT_TRUE(SelectedCode);
885 EXPECT_EQ(SelectedCode->size(), 1u);
886 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
887 ArrayRef<SelectedASTNode::ReferenceType> Parents =
888 SelectedCode->getParents();
889 EXPECT_EQ(Parents.size(), 3u);
891 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
892 // Function 'f' definition.
893 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
894 // Function body of function 'F'.
895 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
899 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelectionInObjCMethod) {
900 StringRef Source = R"(@interface I @end
902 - (void) f:(int)x with:(int) y {
916 // Range that spans multiple methods is an invalid code range.
917 findSelectedASTNodesWithRange(
918 Source, {9, 2}, FileRange{{9, 2}, {13, 1}},
919 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
921 Optional<CodeRangeASTSelection> SelectedCode =
922 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
923 EXPECT_FALSE(SelectedCode);
925 SelectionFinderVisitor::Lang_OBJC);
927 findSelectedASTNodesWithRange(
928 Source, {4, 2}, FileRange{{4, 2}, {4, 13}},
929 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
931 Optional<CodeRangeASTSelection> SelectedCode =
932 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
933 EXPECT_TRUE(SelectedCode);
934 EXPECT_EQ(SelectedCode->size(), 1u);
935 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
936 ArrayRef<SelectedASTNode::ReferenceType> Parents =
937 SelectedCode->getParents();
938 EXPECT_EQ(Parents.size(), 4u);
940 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
941 // 'I' @implementation.
942 EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
943 // Function 'f' definition.
944 EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
945 // Function body of function 'F'.
946 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
948 SelectionFinderVisitor::Lang_OBJC);
949 // From '[self f: 2 with: 3]' until just before 'x = 1;':
950 findSelectedASTNodesWithRange(
951 Source, {5, 2}, FileRange{{5, 2}, {9, 1}},
952 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
954 Optional<CodeRangeASTSelection> SelectedCode =
955 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
956 EXPECT_TRUE(SelectedCode);
957 EXPECT_EQ(SelectedCode->size(), 2u);
958 EXPECT_TRUE(isa<ObjCMessageExpr>((*SelectedCode)[0]));
959 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
960 ArrayRef<SelectedASTNode::ReferenceType> Parents =
961 SelectedCode->getParents();
962 EXPECT_EQ(Parents.size(), 4u);
964 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
965 // 'I' @implementation.
966 EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
967 // Function 'f' definition.
968 EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
969 // Function body of function 'F'.
970 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
972 SelectionFinderVisitor::Lang_OBJC);
975 TEST(ASTSelectionFinder, CanonicalizeObjCStringLiteral) {
976 StringRef Source = R"(
982 findSelectedASTNodesWithRange(
983 Source, {3, 10}, FileRange{{3, 10}, {3, 16}},
984 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
986 Optional<CodeRangeASTSelection> SelectedCode =
987 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
988 EXPECT_TRUE(SelectedCode);
989 EXPECT_EQ(SelectedCode->size(), 1u);
990 EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
992 SelectionFinderVisitor::Lang_OBJC);
994 findSelectedASTNodesWithRange(
995 Source, {3, 11}, FileRange{{3, 11}, {3, 15}},
996 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
998 Optional<CodeRangeASTSelection> SelectedCode =
999 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1000 EXPECT_TRUE(SelectedCode);
1001 EXPECT_EQ(SelectedCode->size(), 1u);
1002 EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
1004 SelectionFinderVisitor::Lang_OBJC);
1007 TEST(ASTSelectionFinder, CanonicalizeMemberCalleeToCall) {
1008 StringRef Source = R"(
1009 class AClass { public:
1012 void selectWholeCallWhenJustMethodSelected(int &i) {
1016 void selectWholeCallWhenJustMethodSelected() {
1020 void dontSelectArgument(AClass &a) {
1021 a.selectWholeCallWhenJustMethodSelected(a.afield);
1024 // Just 'method' with implicit 'this':
1025 findSelectedASTNodesWithRange(
1026 Source, {6, 5}, FileRange{{6, 5}, {6, 11}},
1027 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1029 Optional<CodeRangeASTSelection> SelectedCode =
1030 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1031 EXPECT_TRUE(SelectedCode);
1032 EXPECT_EQ(SelectedCode->size(), 1u);
1033 EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1036 findSelectedASTNodesWithRange(
1037 Source, {11, 5}, FileRange{{11, 5}, {11, 11}},
1038 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1040 Optional<CodeRangeASTSelection> SelectedCode =
1041 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1042 EXPECT_TRUE(SelectedCode);
1043 EXPECT_EQ(SelectedCode->size(), 1u);
1044 EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1046 // Just 'afield', which should not select the call.
1047 findSelectedASTNodesWithRange(
1048 Source, {14, 5}, FileRange{{14, 45}, {14, 51}},
1049 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1051 Optional<CodeRangeASTSelection> SelectedCode =
1052 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1053 EXPECT_TRUE(SelectedCode);
1054 EXPECT_EQ(SelectedCode->size(), 1u);
1055 EXPECT_FALSE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1059 TEST(ASTSelectionFinder, CanonicalizeFuncCalleeToCall) {
1060 StringRef Source = R"(
1068 findSelectedASTNodesWithRange(
1069 Source, {5, 3}, FileRange{{5, 3}, {5, 11}},
1070 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1073 Optional<CodeRangeASTSelection> SelectedCode =
1074 CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1075 EXPECT_TRUE(SelectedCode);
1076 EXPECT_EQ(SelectedCode->size(), 1u);
1077 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
1078 EXPECT_TRUE(isa<CompoundStmt>(
1079 SelectedCode->getParents()[SelectedCode->getParents().size() - 1]
1081 .Node.get<Stmt>()));
1085 } // end anonymous namespace