1 //===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TestVisitor.h"
10 #include "clang/Basic/SourceManager.h"
11 #include "clang/Tooling/Refactoring/ASTSelection.h"
12 
13 using namespace clang;
14 using namespace tooling;
15 
16 namespace {
17 
18 struct FileLocation {
19   unsigned Line, Column;
20 
translate__anon73ce52880111::FileLocation21   SourceLocation translate(const SourceManager &SM) {
22     return SM.translateLineCol(SM.getMainFileID(), Line, Column);
23   }
24 };
25 
26 using FileRange = std::pair<FileLocation, FileLocation>;
27 
28 class SelectionFinderVisitor : public TestVisitor<SelectionFinderVisitor> {
29   FileLocation Location;
30   Optional<FileRange> SelectionRange;
31   llvm::function_ref<void(SourceRange SelectionRange,
32                           Optional<SelectedASTNode>)>
33       Consumer;
34 
35 public:
SelectionFinderVisitor(FileLocation Location,Optional<FileRange> SelectionRange,llvm::function_ref<void (SourceRange SelectionRange,Optional<SelectedASTNode>)> Consumer)36   SelectionFinderVisitor(FileLocation Location,
37                          Optional<FileRange> SelectionRange,
38                          llvm::function_ref<void(SourceRange SelectionRange,
39                                                  Optional<SelectedASTNode>)>
40                              Consumer)
41       : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
42   }
43 
VisitTranslationUnitDecl(const TranslationUnitDecl * TU)44   bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) {
45     const ASTContext &Context = TU->getASTContext();
46     const SourceManager &SM = Context.getSourceManager();
47 
48     SourceRange SelRange;
49     if (SelectionRange) {
50       SelRange = SourceRange(SelectionRange->first.translate(SM),
51                              SelectionRange->second.translate(SM));
52     } else {
53       SourceLocation Loc = Location.translate(SM);
54       SelRange = SourceRange(Loc, Loc);
55     }
56     Consumer(SelRange, findSelectedASTNodes(Context, SelRange));
57     return false;
58   }
59 };
60 
61 /// This is a test utility function that computes the AST selection at the
62 /// given location with an optional selection range.
63 ///
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.
findSelectedASTNodesWithRange(StringRef Source,FileLocation Location,Optional<FileRange> SelectionRange,llvm::function_ref<void (SourceRange SelectionRange,Optional<SelectedASTNode>)> Consumer,SelectionFinderVisitor::Language Language=SelectionFinderVisitor::Lang_CXX11)66 void findSelectedASTNodesWithRange(
67     StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange,
68     llvm::function_ref<void(SourceRange SelectionRange,
69                             Optional<SelectedASTNode>)>
70         Consumer,
71     SelectionFinderVisitor::Language Language =
72         SelectionFinderVisitor::Lang_CXX11) {
73   SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
74   EXPECT_TRUE(Visitor.runOver(Source, Language));
75 }
76 
findSelectedASTNodes(StringRef Source,FileLocation Location,Optional<FileRange> SelectionRange,llvm::function_ref<void (Optional<SelectedASTNode>)> Consumer,SelectionFinderVisitor::Language Language=SelectionFinderVisitor::Lang_CXX11)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));
86       },
87       Language);
88 }
89 
checkNodeImpl(bool IsTypeMatched,const SelectedASTNode & Node,SourceSelectionKind SelectionKind,unsigned NumChildren)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);
95 }
96 
checkDeclName(const SelectedASTNode & Node,StringRef Name)97 void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
98   const auto *ND = Node.Node.get<NamedDecl>();
99   EXPECT_TRUE(!!ND);
100   ASSERT_EQ(ND->getName(), Name);
101 }
102 
103 template <typename T>
checkNode(const SelectedASTNode & StmtNode,SourceSelectionKind SelectionKind,unsigned NumChildren=0,std::enable_if_t<std::is_base_of<Stmt,T>::value,T> * StmtOverloadChecker=nullptr)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 =
108         nullptr) {
109   checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
110                 NumChildren);
111   return StmtNode;
112 }
113 
114 template <typename T>
checkNode(const SelectedASTNode & DeclNode,SourceSelectionKind SelectionKind,unsigned NumChildren=0,StringRef Name="",std::enable_if_t<std::is_base_of<Decl,T>::value,T> * DeclOverloadChecker=nullptr)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 =
119         nullptr) {
120   checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
121                 NumChildren);
122   if (!Name.empty())
123     checkDeclName(DeclNode, Name);
124   return DeclNode;
125 }
126 
127 struct ForAllChildrenOf {
128   const SelectedASTNode &Node;
129 
childKindVerifier__anon73ce52880111::ForAllChildrenOf130   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);
135     }
136   }
137 
138 public:
ForAllChildrenOf__anon73ce52880111::ForAllChildrenOf139   ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
140 
shouldHaveSelectionKind__anon73ce52880111::ForAllChildrenOf141   void shouldHaveSelectionKind(SourceSelectionKind Kind) {
142     childKindVerifier(Node, Kind);
143   }
144 };
145 
allChildrenOf(const SelectedASTNode & Node)146 ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
147   return ForAllChildrenOf(Node);
148 }
149 
TEST(ASTSelectionFinder,CursorNoSelection)150 TEST(ASTSelectionFinder, CursorNoSelection) {
151   findSelectedASTNodes(
152       " void f() { }", {1, 1}, None,
153       [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
154 }
155 
TEST(ASTSelectionFinder,CursorAtStartOfFunction)156 TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
157   findSelectedASTNodes(
158       "void f() { }", {1, 1}, None, [](Optional<SelectedASTNode> Node) {
159         EXPECT_TRUE(Node);
160         checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None,
161                                        /*NumChildren=*/1);
162         checkNode<FunctionDecl>(Node->Children[0],
163                                 SourceSelectionKind::ContainsSelection,
164                                 /*NumChildren=*/0, /*Name=*/"f");
165 
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");
171       });
172 }
173 
TEST(ASTSelectionFinder,RangeNoSelection)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); });
181 }
182 
TEST(ASTSelectionFinder,EmptyRangeFallbackToCursor)183 TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
184   findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
185                        [](Optional<SelectedASTNode> Node) {
186                          EXPECT_TRUE(Node);
187                          checkNode<FunctionDecl>(
188                              Node->Children[0],
189                              SourceSelectionKind::ContainsSelection,
190                              /*NumChildren=*/0, /*Name=*/"f");
191                        });
192 }
193 
TEST(ASTSelectionFinder,WholeFunctionSelection)194 TEST(ASTSelectionFinder, WholeFunctionSelection) {
195   StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
196   // From 'int' until just after '}':
197 
198   findSelectedASTNodes(
199       Source, {1, 1}, FileRange{{1, 1}, {2, 2}},
200       [](Optional<SelectedASTNode> Node) {
201         EXPECT_TRUE(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,
210             /*NumChildren=*/1);
211         const auto &Return = checkNode<ReturnStmt>(
212             Body.Children[0], SourceSelectionKind::InsideSelection,
213             /*NumChildren=*/1);
214         checkNode<ImplicitCastExpr>(Return.Children[0],
215                                     SourceSelectionKind::InsideSelection,
216                                     /*NumChildren=*/1);
217         checkNode<DeclRefExpr>(Return.Children[0].Children[0],
218                                SourceSelectionKind::InsideSelection);
219       });
220 
221   // From 'int' until just before '}':
222   findSelectedASTNodes(
223       Source, {2, 1}, FileRange{{1, 1}, {2, 1}},
224       [](Optional<SelectedASTNode> Node) {
225         EXPECT_TRUE(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,
232             /*NumChildren=*/1);
233         checkNode<ReturnStmt>(Body.Children[0],
234                               SourceSelectionKind::InsideSelection,
235                               /*NumChildren=*/1);
236       });
237   // From '{' until just after '}':
238   findSelectedASTNodes(
239       Source, {1, 14}, FileRange{{1, 14}, {2, 2}},
240       [](Optional<SelectedASTNode> Node) {
241         EXPECT_TRUE(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,
248             /*NumChildren=*/1);
249         checkNode<ReturnStmt>(Body.Children[0],
250                               SourceSelectionKind::InsideSelection,
251                               /*NumChildren=*/1);
252       });
253   // From 'x' until just after '}':
254   findSelectedASTNodes(
255       Source, {2, 2}, FileRange{{1, 11}, {2, 2}},
256       [](Optional<SelectedASTNode> Node) {
257         EXPECT_TRUE(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,
266             /*NumChildren=*/1);
267         checkNode<ReturnStmt>(Body.Children[0],
268                               SourceSelectionKind::InsideSelection,
269                               /*NumChildren=*/1);
270       });
271 }
272 
TEST(ASTSelectionFinder,MultipleFunctionSelection)273 TEST(ASTSelectionFinder, MultipleFunctionSelection) {
274   StringRef Source = R"(void f0() {
275 }
276 void f1() { }
277 void f2() { }
278 void f3() { }
279 )";
280   auto SelectedF1F2 = [](Optional<SelectedASTNode> Node) {
281     EXPECT_TRUE(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");
289   };
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}},
294                        SelectedF1F2);
295 }
296 
TEST(ASTSelectionFinder,MultipleStatementSelection)297 TEST(ASTSelectionFinder, MultipleStatementSelection) {
298   StringRef Source = R"(void f(int x, int y) {
299   int z = x;
300   f(2, 3);
301   if (x == 0) {
302     return;
303   }
304   x = 1;
305   return;
306 })";
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) {
311         EXPECT_TRUE(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,
318             /*NumChildren=*/2);
319         allChildrenOf(checkNode<CallExpr>(Body.Children[0],
320                                           SourceSelectionKind::InsideSelection,
321                                           /*NumChildren=*/3))
322             .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
323         allChildrenOf(checkNode<IfStmt>(Body.Children[1],
324                                         SourceSelectionKind::InsideSelection,
325                                         /*NumChildren=*/2))
326             .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
327       });
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) {
332         EXPECT_TRUE(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,
339             /*NumChildren=*/3);
340         checkNode<CallExpr>(Body.Children[0],
341                             SourceSelectionKind::InsideSelection,
342                             /*NumChildren=*/3);
343         checkNode<IfStmt>(Body.Children[1],
344                           SourceSelectionKind::InsideSelection,
345                           /*NumChildren=*/2);
346         checkNode<BinaryOperator>(Body.Children[2],
347                                   SourceSelectionKind::InsideSelection,
348                                   /*NumChildren=*/2);
349       });
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) {
354         EXPECT_TRUE(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,
361             /*NumChildren=*/4);
362         checkNode<DeclStmt>(Body.Children[0],
363                             SourceSelectionKind::ContainsSelectionStart,
364                             /*NumChildren=*/1);
365         checkNode<CallExpr>(Body.Children[1],
366                             SourceSelectionKind::InsideSelection,
367                             /*NumChildren=*/3);
368         checkNode<IfStmt>(Body.Children[2],
369                           SourceSelectionKind::InsideSelection,
370                           /*NumChildren=*/2);
371         checkNode<BinaryOperator>(Body.Children[3],
372                                   SourceSelectionKind::ContainsSelectionEnd,
373                                   /*NumChildren=*/1);
374       });
375 }
376 
TEST(ASTSelectionFinder,SelectionInFunctionInObjCImplementation)377 TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
378   StringRef Source = R"(
379 @interface I
380 @end
381 @implementation I
382 
383 int notSelected() { }
384 
385 int selected(int x) {
386   return x;
387 }
388 
389 @end
390 @implementation I(Cat)
391 
392 void catF() { }
393 
394 @end
395 
396 void outerFunction() { }
397 )";
398   // Just the 'x' expression in 'selected':
399   findSelectedASTNodes(
400       Source, {9, 10}, FileRange{{9, 10}, {9, 11}},
401       [](Optional<SelectedASTNode> Node) {
402         EXPECT_TRUE(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);
412       },
413       SelectionFinderVisitor::Lang_OBJC);
414   // The entire 'catF':
415   findSelectedASTNodes(
416       Source, {15, 1}, FileRange{{15, 1}, {15, 16}},
417       [](Optional<SelectedASTNode> Node) {
418         EXPECT_TRUE(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);
428       },
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) {
434         EXPECT_TRUE(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);
452       },
453       SelectionFinderVisitor::Lang_OBJC);
454   // Just the 'outer' function:
455   findSelectedASTNodes(Source, {19, 1}, FileRange{{19, 1}, {19, 25}},
456                        [](Optional<SelectedASTNode> Node) {
457                          EXPECT_TRUE(Node);
458                          EXPECT_EQ(Node->Children.size(), 1u);
459                          checkNode<FunctionDecl>(
460                              Node->Children[0],
461                              SourceSelectionKind::ContainsSelection,
462                              /*NumChildren=*/1, /*Name=*/"outerFunction");
463                        },
464                        SelectionFinderVisitor::Lang_OBJC);
465 }
466 
TEST(ASTSelectionFinder,FunctionInObjCImplementationCarefulWithEarlyExit)467 TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
468   StringRef Source = R"(
469 @interface I
470 @end
471 @implementation I
472 
473 void selected() {
474 }
475 
476 - (void) method { }
477 
478 @end
479 )";
480   // Just 'selected'
481   findSelectedASTNodes(
482       Source, {6, 1}, FileRange{{6, 1}, {7, 2}},
483       [](Optional<SelectedASTNode> Node) {
484         EXPECT_TRUE(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");
492       },
493       SelectionFinderVisitor::Lang_OBJC);
494 }
495 
TEST(ASTSelectionFinder,AvoidImplicitDeclarations)496 TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
497   StringRef Source = R"(
498 struct Copy {
499   int x;
500 };
501 void foo() {
502   Copy x;
503   Copy y = x;
504 }
505 )";
506   // The entire struct 'Copy':
507   findSelectedASTNodes(
508       Source, {2, 1}, FileRange{{2, 1}, {4, 3}},
509       [](Optional<SelectedASTNode> Node) {
510         EXPECT_TRUE(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);
517       });
518 }
519 
TEST(ASTSelectionFinder,CorrectEndForObjectiveCImplementation)520 TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
521   StringRef Source = R"(
522 @interface I
523 @end
524 @implementation I
525 @ end
526 )";
527   // Just after '@ end'
528   findSelectedASTNodes(Source, {5, 6}, None,
529                        [](Optional<SelectedASTNode> Node) {
530                          EXPECT_TRUE(Node);
531                          EXPECT_EQ(Node->Children.size(), 1u);
532                          checkNode<ObjCImplementationDecl>(
533                              Node->Children[0],
534                              SourceSelectionKind::ContainsSelection);
535                        },
536                        SelectionFinderVisitor::Lang_OBJC);
537 }
538 
checkFnBody(const Optional<SelectedASTNode> & Node,StringRef Name)539 const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node,
540                                    StringRef Name) {
541   EXPECT_TRUE(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,
548                                  /*NumChildren=*/1);
549 }
550 
TEST(ASTSelectionFinder,SelectObjectiveCPseudoObjectExprs)551 TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
552   StringRef Source = R"(
553 @interface I
554 @property(readwrite) int prop;
555 @end
556 void selectProp(I *i) {
557 (void)i.prop;
558 i.prop = 21;
559 }
560 
561 
562 @interface NSMutableArray
563 - (id)objectAtIndexedSubscript:(unsigned int)index;
564 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
565 @end
566 
567 void selectSubscript(NSMutableArray *array, I *i) {
568   (void)array[10];
569   array[i.prop] = i;
570 }
571 )";
572   // Just 'i.prop'.
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,
579             /*NumChildren=*/1);
580         const auto &POE = checkNode<PseudoObjectExpr>(
581             CCast.Children[0], SourceSelectionKind::ContainsSelection,
582             /*NumChildren=*/1);
583         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
584             POE.Children[0], SourceSelectionKind::ContainsSelection,
585             /*NumChildren=*/1);
586         const auto &Cast = checkNode<ImplicitCastExpr>(
587             PRE.Children[0], SourceSelectionKind::InsideSelection,
588             /*NumChildren=*/1);
589         checkNode<DeclRefExpr>(Cast.Children[0],
590                                SourceSelectionKind::InsideSelection);
591       },
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,
600             /*NumChildren=*/1);
601         const auto &BinOp = checkNode<BinaryOperator>(
602             POE.Children[0], SourceSelectionKind::ContainsSelection,
603             /*NumChildren=*/2);
604         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
605             BinOp.Children[0], SourceSelectionKind::InsideSelection,
606             /*NumChildren=*/1);
607         const auto &Cast = checkNode<ImplicitCastExpr>(
608             PRE.Children[0], SourceSelectionKind::InsideSelection,
609             /*NumChildren=*/1);
610         checkNode<DeclRefExpr>(Cast.Children[0],
611                                SourceSelectionKind::InsideSelection);
612         checkNode<IntegerLiteral>(BinOp.Children[1],
613                                   SourceSelectionKind::InsideSelection);
614       },
615       SelectionFinderVisitor::Lang_OBJC);
616   // Just 'array[10]'
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,
623             /*NumChildren=*/1);
624         const auto &POE = checkNode<PseudoObjectExpr>(
625             CCast.Children[0], SourceSelectionKind::ContainsSelection,
626             /*NumChildren=*/1);
627         const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
628             POE.Children[0], SourceSelectionKind::ContainsSelection,
629             /*NumChildren=*/2);
630         const auto &Cast = checkNode<ImplicitCastExpr>(
631             SRE.Children[0], SourceSelectionKind::InsideSelection,
632             /*NumChildren=*/1);
633         checkNode<DeclRefExpr>(Cast.Children[0],
634                                SourceSelectionKind::InsideSelection);
635         checkNode<IntegerLiteral>(SRE.Children[1],
636                                   SourceSelectionKind::InsideSelection);
637       },
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,
646             /*NumChildren=*/1);
647         const auto &BinOp = checkNode<BinaryOperator>(
648             POE.Children[0], SourceSelectionKind::ContainsSelection,
649             /*NumChildren=*/2);
650         const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
651             BinOp.Children[0], SourceSelectionKind::InsideSelection,
652             /*NumChildren=*/2);
653         const auto &Cast = checkNode<ImplicitCastExpr>(
654             SRE.Children[0], SourceSelectionKind::InsideSelection,
655             /*NumChildren=*/1);
656         checkNode<DeclRefExpr>(Cast.Children[0],
657                                SourceSelectionKind::InsideSelection);
658         const auto &POE2 = checkNode<PseudoObjectExpr>(
659             SRE.Children[1], SourceSelectionKind::InsideSelection,
660             /*NumChildren=*/1);
661         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
662             POE2.Children[0], SourceSelectionKind::InsideSelection,
663             /*NumChildren=*/1);
664         const auto &Cast2 = checkNode<ImplicitCastExpr>(
665             PRE.Children[0], SourceSelectionKind::InsideSelection,
666             /*NumChildren=*/1);
667         checkNode<DeclRefExpr>(Cast2.Children[0],
668                                SourceSelectionKind::InsideSelection);
669         checkNode<DeclRefExpr>(BinOp.Children[1],
670                                SourceSelectionKind::InsideSelection);
671       },
672       SelectionFinderVisitor::Lang_OBJC);
673 }
674 
TEST(ASTSelectionFinder,SimpleCodeRangeASTSelection)675 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelection) {
676   StringRef Source = R"(void f(int x, int y) {
677   int z = x;
678   f(2, 3);
679   if (x == 0) {
680     return;
681   }
682   x = 1;
683   return;
684 }
685 void f2() {
686   int m = 0;
687 }
688 )";
689   // No selection range.
690   findSelectedASTNodesWithRange(
691       Source, {2, 2}, None,
692       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
693         EXPECT_TRUE(Node);
694         Optional<CodeRangeASTSelection> SelectedCode =
695             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
696         EXPECT_FALSE(SelectedCode);
697       });
698   findSelectedASTNodesWithRange(
699       Source, {2, 2}, FileRange{{2, 2}, {2, 2}},
700       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
701         EXPECT_TRUE(Node);
702         Optional<CodeRangeASTSelection> SelectedCode =
703             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
704         EXPECT_FALSE(SelectedCode);
705       });
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) {
710         EXPECT_TRUE(Node);
711         Optional<CodeRangeASTSelection> SelectedCode =
712             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
713         EXPECT_FALSE(SelectedCode);
714       });
715   // Just 'z = x;':
716   findSelectedASTNodesWithRange(
717       Source, {2, 2}, FileRange{{2, 2}, {2, 13}},
718       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
719         EXPECT_TRUE(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);
728         EXPECT_TRUE(
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>()));
734       });
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) {
739         EXPECT_TRUE(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);
749         EXPECT_TRUE(
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>()));
755       });
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) {
760         EXPECT_TRUE(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]));
768       });
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) {
773         EXPECT_TRUE(Node);
774         EXPECT_TRUE(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]));
783       });
784 }
785 
TEST(ASTSelectionFinder,OutOfBodyCodeRange)786 TEST(ASTSelectionFinder, OutOfBodyCodeRange) {
787   StringRef Source = R"(
788 int codeRange = 2 + 3;
789 )";
790   // '2+3' expression.
791   findSelectedASTNodesWithRange(
792       Source, {2, 17}, FileRange{{2, 17}, {2, 22}},
793       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
794         EXPECT_TRUE(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);
803         EXPECT_TRUE(
804             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
805         // Variable 'codeRange'.
806         EXPECT_TRUE(isa<VarDecl>(Parents[1].get().Node.get<Decl>()));
807       });
808 }
809 
TEST(ASTSelectionFinder,SelectVarDeclStmt)810 TEST(ASTSelectionFinder, SelectVarDeclStmt) {
811   StringRef Source = R"(
812 void f() {
813    {
814        int a;
815    }
816 }
817 )";
818   // 'int a'
819   findSelectedASTNodesWithRange(
820       Source, {4, 8}, FileRange{{4, 8}, {4, 14}},
821       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
822         EXPECT_TRUE(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);
831         EXPECT_TRUE(
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>()));
839       });
840 }
841 
TEST(ASTSelectionFinder,SelectEntireDeclStmtRange)842 TEST(ASTSelectionFinder, SelectEntireDeclStmtRange) {
843   StringRef Source = R"(
844 void f(int x, int y) {
845    int a = x * y;
846 }
847 )";
848   // 'int a = x * y'
849   findSelectedASTNodesWithRange(
850       Source, {3, 4}, FileRange{{3, 4}, {3, 17}},
851       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
852         EXPECT_TRUE(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);
861         EXPECT_TRUE(
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>()));
867       });
868 }
869 
TEST(ASTSelectionFinder,SelectEntireDeclStmtRangeWithMultipleDecls)870 TEST(ASTSelectionFinder, SelectEntireDeclStmtRangeWithMultipleDecls) {
871   StringRef Source = R"(
872 void f(int x, int y) {
873    int a = x * y, b = x - y;
874 }
875 )";
876   // 'b = x - y'
877   findSelectedASTNodesWithRange(
878       Source, {3, 19}, FileRange{{3, 19}, {3, 28}},
879       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
880         EXPECT_TRUE(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);
889         EXPECT_TRUE(
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>()));
895       });
896 }
897 
TEST(ASTSelectionFinder,SimpleCodeRangeASTSelectionInObjCMethod)898 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelectionInObjCMethod) {
899   StringRef Source = R"(@interface I @end
900 @implementation I
901 - (void) f:(int)x with:(int) y {
902   int z = x;
903   [self f: 2 with: 3];
904   if (x == 0) {
905     return;
906   }
907   x = 1;
908   return;
909 }
910 - (void)f2 {
911   int m = 0;
912 }
913 @end
914 )";
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) {
919         EXPECT_TRUE(Node);
920         Optional<CodeRangeASTSelection> SelectedCode =
921             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
922         EXPECT_FALSE(SelectedCode);
923       },
924       SelectionFinderVisitor::Lang_OBJC);
925   // Just 'z = x;':
926   findSelectedASTNodesWithRange(
927       Source, {4, 2}, FileRange{{4, 2}, {4, 13}},
928       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
929         EXPECT_TRUE(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);
938         EXPECT_TRUE(
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>()));
946       },
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) {
952         EXPECT_TRUE(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);
962         EXPECT_TRUE(
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>()));
970       },
971       SelectionFinderVisitor::Lang_OBJC);
972 }
973 
TEST(ASTSelectionFinder,CanonicalizeObjCStringLiteral)974 TEST(ASTSelectionFinder, CanonicalizeObjCStringLiteral) {
975   StringRef Source = R"(
976 void foo() {
977   (void)@"test";
978 }
979       )";
980   // Just '"test"':
981   findSelectedASTNodesWithRange(
982       Source, {3, 10}, FileRange{{3, 10}, {3, 16}},
983       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
984         EXPECT_TRUE(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]));
990       },
991       SelectionFinderVisitor::Lang_OBJC);
992   // Just 'test':
993   findSelectedASTNodesWithRange(
994       Source, {3, 11}, FileRange{{3, 11}, {3, 15}},
995       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
996         EXPECT_TRUE(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]));
1002       },
1003       SelectionFinderVisitor::Lang_OBJC);
1004 }
1005 
TEST(ASTSelectionFinder,CanonicalizeMemberCalleeToCall)1006 TEST(ASTSelectionFinder, CanonicalizeMemberCalleeToCall) {
1007   StringRef Source = R"(
1008 class AClass { public:
1009   void method();
1010   int afield;
1011   void selectWholeCallWhenJustMethodSelected(int &i) {
1012     method();
1013   }
1014 };
1015 void selectWholeCallWhenJustMethodSelected() {
1016   AClass a;
1017   a.method();
1018 }
1019 void dontSelectArgument(AClass &a) {
1020   a.selectWholeCallWhenJustMethodSelected(a.afield);
1021 }
1022      )";
1023   // Just 'method' with implicit 'this':
1024   findSelectedASTNodesWithRange(
1025       Source, {6, 5}, FileRange{{6, 5}, {6, 11}},
1026       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1027         EXPECT_TRUE(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]));
1033       });
1034   // Just 'method':
1035   findSelectedASTNodesWithRange(
1036       Source, {11, 5}, FileRange{{11, 5}, {11, 11}},
1037       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1038         EXPECT_TRUE(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]));
1044       });
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) {
1049         EXPECT_TRUE(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]));
1055       });
1056 }
1057 
TEST(ASTSelectionFinder,CanonicalizeFuncCalleeToCall)1058 TEST(ASTSelectionFinder, CanonicalizeFuncCalleeToCall) {
1059   StringRef Source = R"(
1060 void function();
1061 
1062 void test() {
1063   function();
1064 }
1065      )";
1066   // Just 'function':
1067   findSelectedASTNodesWithRange(
1068       Source, {5, 3}, FileRange{{5, 3}, {5, 11}},
1069       [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) {
1070         EXPECT_TRUE(Node);
1071         Node->dump();
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]
1079                 .get()
1080                 .Node.get<Stmt>()));
1081       });
1082 }
1083 
1084 } // end anonymous namespace
1085