1 //===- unittest/Tooling/ASTSelectionTest.cpp ------------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "TestVisitor.h"
11 #include "clang/Basic/SourceManager.h"
12 #include "clang/Tooling/Refactoring/ASTSelection.h"
13 
14 using namespace clang;
15 using namespace tooling;
16 
17 namespace {
18 
19 struct FileLocation {
20   unsigned Line, Column;
21 
22   SourceLocation translate(const SourceManager &SM) {
23     return SM.translateLineCol(SM.getMainFileID(), Line, Column);
24   }
25 };
26 
27 using FileRange = std::pair<FileLocation, FileLocation>;
28 
29 class SelectionFinderVisitor : public TestVisitor<SelectionFinderVisitor> {
30   FileLocation Location;
31   Optional<FileRange> SelectionRange;
32   llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer;
33 
34 public:
35   SelectionFinderVisitor(
36       FileLocation Location, Optional<FileRange> SelectionRange,
37       llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer)
38       : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
39   }
40 
41   bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) {
42     const ASTContext &Context = TU->getASTContext();
43     const SourceManager &SM = Context.getSourceManager();
44 
45     SourceRange SelRange;
46     if (SelectionRange) {
47       SelRange = SourceRange(SelectionRange->first.translate(SM),
48                              SelectionRange->second.translate(SM));
49     } else {
50       SourceLocation Loc = Location.translate(SM);
51       SelRange = SourceRange(Loc, Loc);
52     }
53     Consumer(findSelectedASTNodes(Context, SelRange));
54     return false;
55   }
56 };
57 
58 void findSelectedASTNodes(
59     StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange,
60     llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer,
61     SelectionFinderVisitor::Language Language =
62         SelectionFinderVisitor::Lang_CXX11) {
63   SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
64   EXPECT_TRUE(Visitor.runOver(Source, Language));
65 }
66 
67 void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node,
68                    SourceSelectionKind SelectionKind, unsigned NumChildren) {
69   ASSERT_TRUE(IsTypeMatched);
70   EXPECT_EQ(Node.Children.size(), NumChildren);
71   ASSERT_EQ(Node.SelectionKind, SelectionKind);
72 }
73 
74 void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
75   const auto *ND = Node.Node.get<NamedDecl>();
76   EXPECT_TRUE(!!ND);
77   ASSERT_EQ(ND->getName(), Name);
78 }
79 
80 template <typename T>
81 const SelectedASTNode &
82 checkNode(const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind,
83           unsigned NumChildren = 0,
84           typename std::enable_if<std::is_base_of<Stmt, T>::value, T>::type
85               *StmtOverloadChecker = nullptr) {
86   checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
87                 NumChildren);
88   return StmtNode;
89 }
90 
91 template <typename T>
92 const SelectedASTNode &
93 checkNode(const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind,
94           unsigned NumChildren = 0, StringRef Name = "",
95           typename std::enable_if<std::is_base_of<Decl, T>::value, T>::type
96               *DeclOverloadChecker = nullptr) {
97   checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
98                 NumChildren);
99   if (!Name.empty())
100     checkDeclName(DeclNode, Name);
101   return DeclNode;
102 }
103 
104 struct ForAllChildrenOf {
105   const SelectedASTNode &Node;
106 
107   static void childKindVerifier(const SelectedASTNode &Node,
108                                 SourceSelectionKind SelectionKind) {
109     for (const SelectedASTNode &Child : Node.Children) {
110       ASSERT_EQ(Node.SelectionKind, SelectionKind);
111       childKindVerifier(Child, SelectionKind);
112     }
113   }
114 
115 public:
116   ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
117 
118   void shouldHaveSelectionKind(SourceSelectionKind Kind) {
119     childKindVerifier(Node, Kind);
120   }
121 };
122 
123 ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
124   return ForAllChildrenOf(Node);
125 }
126 
127 TEST(ASTSelectionFinder, CursorNoSelection) {
128   findSelectedASTNodes(
129       " void f() { }", {1, 1}, None,
130       [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
131 }
132 
133 TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
134   findSelectedASTNodes(
135       "void f() { }", {1, 1}, None, [](Optional<SelectedASTNode> Node) {
136         EXPECT_TRUE(Node);
137         checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None,
138                                        /*NumChildren=*/1);
139         checkNode<FunctionDecl>(Node->Children[0],
140                                 SourceSelectionKind::ContainsSelection,
141                                 /*NumChildren=*/0, /*Name=*/"f");
142 
143         // Check that the dumping works.
144         std::string DumpValue;
145         llvm::raw_string_ostream OS(DumpValue);
146         Node->Children[0].dump(OS);
147         ASSERT_EQ(OS.str(), "FunctionDecl \"f\" contains-selection\n");
148       });
149 }
150 
151 TEST(ASTSelectionFinder, RangeNoSelection) {
152   findSelectedASTNodes(
153       " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
154       [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
155   findSelectedASTNodes(
156       "  void f() { }", {1, 1}, FileRange{{1, 1}, {1, 2}},
157       [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
158 }
159 
160 TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
161   findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
162                        [](Optional<SelectedASTNode> Node) {
163                          EXPECT_TRUE(Node);
164                          checkNode<FunctionDecl>(
165                              Node->Children[0],
166                              SourceSelectionKind::ContainsSelection,
167                              /*NumChildren=*/0, /*Name=*/"f");
168                        });
169 }
170 
171 TEST(ASTSelectionFinder, WholeFunctionSelection) {
172   StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
173   // From 'int' until just after '}':
174 
175   findSelectedASTNodes(
176       Source, {1, 1}, FileRange{{1, 1}, {2, 2}},
177       [](Optional<SelectedASTNode> Node) {
178         EXPECT_TRUE(Node);
179         EXPECT_EQ(Node->Children.size(), 1u);
180         const auto &Fn = checkNode<FunctionDecl>(
181             Node->Children[0], SourceSelectionKind::ContainsSelection,
182             /*NumChildren=*/2, /*Name=*/"f");
183         checkNode<ParmVarDecl>(Fn.Children[0],
184                                SourceSelectionKind::InsideSelection);
185         const auto &Body = checkNode<CompoundStmt>(
186             Fn.Children[1], SourceSelectionKind::InsideSelection,
187             /*NumChildren=*/1);
188         const auto &Return = checkNode<ReturnStmt>(
189             Body.Children[0], SourceSelectionKind::InsideSelection,
190             /*NumChildren=*/1);
191         checkNode<ImplicitCastExpr>(Return.Children[0],
192                                     SourceSelectionKind::InsideSelection,
193                                     /*NumChildren=*/1);
194         checkNode<DeclRefExpr>(Return.Children[0].Children[0],
195                                SourceSelectionKind::InsideSelection);
196       });
197 
198   // From 'int' until just before '}':
199   findSelectedASTNodes(
200       Source, {2, 1}, FileRange{{1, 1}, {2, 1}},
201       [](Optional<SelectedASTNode> Node) {
202         EXPECT_TRUE(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         const auto &Body = checkNode<CompoundStmt>(
208             Fn.Children[1], SourceSelectionKind::ContainsSelectionEnd,
209             /*NumChildren=*/1);
210         checkNode<ReturnStmt>(Body.Children[0],
211                               SourceSelectionKind::InsideSelection,
212                               /*NumChildren=*/1);
213       });
214   // From '{' until just after '}':
215   findSelectedASTNodes(
216       Source, {1, 14}, FileRange{{1, 14}, {2, 2}},
217       [](Optional<SelectedASTNode> Node) {
218         EXPECT_TRUE(Node);
219         EXPECT_EQ(Node->Children.size(), 1u);
220         const auto &Fn = checkNode<FunctionDecl>(
221             Node->Children[0], SourceSelectionKind::ContainsSelection,
222             /*NumChildren=*/1, /*Name=*/"f");
223         const auto &Body = checkNode<CompoundStmt>(
224             Fn.Children[0], SourceSelectionKind::ContainsSelection,
225             /*NumChildren=*/1);
226         checkNode<ReturnStmt>(Body.Children[0],
227                               SourceSelectionKind::InsideSelection,
228                               /*NumChildren=*/1);
229       });
230   // From 'x' until just after '}':
231   findSelectedASTNodes(
232       Source, {2, 2}, FileRange{{1, 11}, {2, 2}},
233       [](Optional<SelectedASTNode> Node) {
234         EXPECT_TRUE(Node);
235         EXPECT_EQ(Node->Children.size(), 1u);
236         const auto &Fn = checkNode<FunctionDecl>(
237             Node->Children[0], SourceSelectionKind::ContainsSelection,
238             /*NumChildren=*/2, /*Name=*/"f");
239         checkNode<ParmVarDecl>(Fn.Children[0],
240                                SourceSelectionKind::ContainsSelectionStart);
241         const auto &Body = checkNode<CompoundStmt>(
242             Fn.Children[1], SourceSelectionKind::InsideSelection,
243             /*NumChildren=*/1);
244         checkNode<ReturnStmt>(Body.Children[0],
245                               SourceSelectionKind::InsideSelection,
246                               /*NumChildren=*/1);
247       });
248 }
249 
250 TEST(ASTSelectionFinder, MultipleFunctionSelection) {
251   StringRef Source = R"(void f0() {
252 }
253 void f1() { }
254 void f2() { }
255 void f3() { }
256 )";
257   auto SelectedF1F2 = [](Optional<SelectedASTNode> Node) {
258     EXPECT_TRUE(Node);
259     EXPECT_EQ(Node->Children.size(), 2u);
260     checkNode<FunctionDecl>(Node->Children[0],
261                             SourceSelectionKind::InsideSelection,
262                             /*NumChildren=*/1, /*Name=*/"f1");
263     checkNode<FunctionDecl>(Node->Children[1],
264                             SourceSelectionKind::InsideSelection,
265                             /*NumChildren=*/1, /*Name=*/"f2");
266   };
267   // Just after '}' of f0 and just before 'void' of f3:
268   findSelectedASTNodes(Source, {2, 2}, FileRange{{2, 2}, {5, 1}}, SelectedF1F2);
269   // Just before 'void' of f1 and just after '}' of f2:
270   findSelectedASTNodes(Source, {3, 1}, FileRange{{3, 1}, {4, 14}},
271                        SelectedF1F2);
272 }
273 
274 TEST(ASTSelectionFinder, MultipleStatementSelection) {
275   StringRef Source = R"(void f(int x, int y) {
276   int z = x;
277   f(2, 3);
278   if (x == 0) {
279     return;
280   }
281   x = 1;
282   return;
283 })";
284   // From 'f(2,3)' until just before 'x = 1;':
285   findSelectedASTNodes(
286       Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
287       [](Optional<SelectedASTNode> Node) {
288         EXPECT_TRUE(Node);
289         EXPECT_EQ(Node->Children.size(), 1u);
290         const auto &Fn = checkNode<FunctionDecl>(
291             Node->Children[0], SourceSelectionKind::ContainsSelection,
292             /*NumChildren=*/1, /*Name=*/"f");
293         const auto &Body = checkNode<CompoundStmt>(
294             Fn.Children[0], SourceSelectionKind::ContainsSelection,
295             /*NumChildren=*/2);
296         allChildrenOf(checkNode<CallExpr>(Body.Children[0],
297                                           SourceSelectionKind::InsideSelection,
298                                           /*NumChildren=*/3))
299             .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
300         allChildrenOf(checkNode<IfStmt>(Body.Children[1],
301                                         SourceSelectionKind::InsideSelection,
302                                         /*NumChildren=*/2))
303             .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
304       });
305   // From 'f(2,3)' until just before ';' in 'x = 1;':
306   findSelectedASTNodes(
307       Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
308       [](Optional<SelectedASTNode> Node) {
309         EXPECT_TRUE(Node);
310         EXPECT_EQ(Node->Children.size(), 1u);
311         const auto &Fn = checkNode<FunctionDecl>(
312             Node->Children[0], SourceSelectionKind::ContainsSelection,
313             /*NumChildren=*/1, /*Name=*/"f");
314         const auto &Body = checkNode<CompoundStmt>(
315             Fn.Children[0], SourceSelectionKind::ContainsSelection,
316             /*NumChildren=*/3);
317         checkNode<CallExpr>(Body.Children[0],
318                             SourceSelectionKind::InsideSelection,
319                             /*NumChildren=*/3);
320         checkNode<IfStmt>(Body.Children[1],
321                           SourceSelectionKind::InsideSelection,
322                           /*NumChildren=*/2);
323         checkNode<BinaryOperator>(Body.Children[2],
324                                   SourceSelectionKind::InsideSelection,
325                                   /*NumChildren=*/2);
326       });
327   // From the middle of 'int z = 3' until the middle of 'x = 1;':
328   findSelectedASTNodes(
329       Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
330       [](Optional<SelectedASTNode> Node) {
331         EXPECT_TRUE(Node);
332         EXPECT_EQ(Node->Children.size(), 1u);
333         const auto &Fn = checkNode<FunctionDecl>(
334             Node->Children[0], SourceSelectionKind::ContainsSelection,
335             /*NumChildren=*/1, /*Name=*/"f");
336         const auto &Body = checkNode<CompoundStmt>(
337             Fn.Children[0], SourceSelectionKind::ContainsSelection,
338             /*NumChildren=*/4);
339         checkNode<DeclStmt>(Body.Children[0],
340                             SourceSelectionKind::ContainsSelectionStart,
341                             /*NumChildren=*/1);
342         checkNode<CallExpr>(Body.Children[1],
343                             SourceSelectionKind::InsideSelection,
344                             /*NumChildren=*/3);
345         checkNode<IfStmt>(Body.Children[2],
346                           SourceSelectionKind::InsideSelection,
347                           /*NumChildren=*/2);
348         checkNode<BinaryOperator>(Body.Children[3],
349                                   SourceSelectionKind::ContainsSelectionEnd,
350                                   /*NumChildren=*/1);
351       });
352 }
353 
354 TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
355   StringRef Source = R"(
356 @interface I
357 @end
358 @implementation I
359 
360 int notSelected() { }
361 
362 int selected(int x) {
363   return x;
364 }
365 
366 @end
367 @implementation I(Cat)
368 
369 void catF() { }
370 
371 @end
372 
373 void outerFunction() { }
374 )";
375   // Just the 'x' expression in 'selected':
376   findSelectedASTNodes(
377       Source, {9, 10}, FileRange{{9, 10}, {9, 11}},
378       [](Optional<SelectedASTNode> Node) {
379         EXPECT_TRUE(Node);
380         EXPECT_EQ(Node->Children.size(), 1u);
381         const auto &Impl = checkNode<ObjCImplementationDecl>(
382             Node->Children[0], SourceSelectionKind::ContainsSelection,
383             /*NumChildren=*/1, /*Name=*/"I");
384         const auto &Fn = checkNode<FunctionDecl>(
385             Impl.Children[0], SourceSelectionKind::ContainsSelection,
386             /*NumChildren=*/1, /*Name=*/"selected");
387         allChildrenOf(Fn).shouldHaveSelectionKind(
388             SourceSelectionKind::ContainsSelection);
389       },
390       SelectionFinderVisitor::Lang_OBJC);
391   // The entire 'catF':
392   findSelectedASTNodes(
393       Source, {15, 1}, FileRange{{15, 1}, {15, 16}},
394       [](Optional<SelectedASTNode> Node) {
395         EXPECT_TRUE(Node);
396         EXPECT_EQ(Node->Children.size(), 1u);
397         const auto &Impl = checkNode<ObjCCategoryImplDecl>(
398             Node->Children[0], SourceSelectionKind::ContainsSelection,
399             /*NumChildren=*/1, /*Name=*/"Cat");
400         const auto &Fn = checkNode<FunctionDecl>(
401             Impl.Children[0], SourceSelectionKind::ContainsSelection,
402             /*NumChildren=*/1, /*Name=*/"catF");
403         allChildrenOf(Fn).shouldHaveSelectionKind(
404             SourceSelectionKind::ContainsSelection);
405       },
406       SelectionFinderVisitor::Lang_OBJC);
407   // From the line before 'selected' to the line after 'catF':
408   findSelectedASTNodes(
409       Source, {16, 1}, FileRange{{7, 1}, {16, 1}},
410       [](Optional<SelectedASTNode> Node) {
411         EXPECT_TRUE(Node);
412         EXPECT_EQ(Node->Children.size(), 2u);
413         const auto &Impl = checkNode<ObjCImplementationDecl>(
414             Node->Children[0], SourceSelectionKind::ContainsSelectionStart,
415             /*NumChildren=*/1, /*Name=*/"I");
416         const auto &Selected = checkNode<FunctionDecl>(
417             Impl.Children[0], SourceSelectionKind::InsideSelection,
418             /*NumChildren=*/2, /*Name=*/"selected");
419         allChildrenOf(Selected).shouldHaveSelectionKind(
420             SourceSelectionKind::InsideSelection);
421         const auto &Cat = checkNode<ObjCCategoryImplDecl>(
422             Node->Children[1], SourceSelectionKind::ContainsSelectionEnd,
423             /*NumChildren=*/1, /*Name=*/"Cat");
424         const auto &CatF = checkNode<FunctionDecl>(
425             Cat.Children[0], SourceSelectionKind::InsideSelection,
426             /*NumChildren=*/1, /*Name=*/"catF");
427         allChildrenOf(CatF).shouldHaveSelectionKind(
428             SourceSelectionKind::InsideSelection);
429       },
430       SelectionFinderVisitor::Lang_OBJC);
431   // Just the 'outer' function:
432   findSelectedASTNodes(Source, {19, 1}, FileRange{{19, 1}, {19, 25}},
433                        [](Optional<SelectedASTNode> Node) {
434                          EXPECT_TRUE(Node);
435                          EXPECT_EQ(Node->Children.size(), 1u);
436                          checkNode<FunctionDecl>(
437                              Node->Children[0],
438                              SourceSelectionKind::ContainsSelection,
439                              /*NumChildren=*/1, /*Name=*/"outerFunction");
440                        },
441                        SelectionFinderVisitor::Lang_OBJC);
442 }
443 
444 TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
445   StringRef Source = R"(
446 @interface I
447 @end
448 @implementation I
449 
450 void selected() {
451 }
452 
453 - (void) method { }
454 
455 @end
456 )";
457   // Just 'selected'
458   findSelectedASTNodes(
459       Source, {6, 1}, FileRange{{6, 1}, {7, 2}},
460       [](Optional<SelectedASTNode> Node) {
461         EXPECT_TRUE(Node);
462         EXPECT_EQ(Node->Children.size(), 1u);
463         const auto &Impl = checkNode<ObjCImplementationDecl>(
464             Node->Children[0], SourceSelectionKind::ContainsSelection,
465             /*NumChildren=*/1, /*Name=*/"I");
466         checkNode<FunctionDecl>(Impl.Children[0],
467                                 SourceSelectionKind::ContainsSelection,
468                                 /*NumChildren=*/1, /*Name=*/"selected");
469       },
470       SelectionFinderVisitor::Lang_OBJC);
471 }
472 
473 TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
474   StringRef Source = R"(
475 struct Copy {
476   int x;
477 };
478 void foo() {
479   Copy x;
480   Copy y = x;
481 }
482 )";
483   // The entire struct 'Copy':
484   findSelectedASTNodes(
485       Source, {2, 1}, FileRange{{2, 1}, {4, 3}},
486       [](Optional<SelectedASTNode> Node) {
487         EXPECT_TRUE(Node);
488         EXPECT_EQ(Node->Children.size(), 1u);
489         const auto &Record = checkNode<CXXRecordDecl>(
490             Node->Children[0], SourceSelectionKind::InsideSelection,
491             /*NumChildren=*/1, /*Name=*/"Copy");
492         checkNode<FieldDecl>(Record.Children[0],
493                              SourceSelectionKind::InsideSelection);
494       });
495 }
496 
497 TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
498   StringRef Source = R"(
499 @interface I
500 @end
501 @implementation I
502 @ end
503 )";
504   // Just after '@ end'
505   findSelectedASTNodes(Source, {5, 6}, None,
506                        [](Optional<SelectedASTNode> Node) {
507                          EXPECT_TRUE(Node);
508                          EXPECT_EQ(Node->Children.size(), 1u);
509                          checkNode<ObjCImplementationDecl>(
510                              Node->Children[0],
511                              SourceSelectionKind::ContainsSelection);
512                        },
513                        SelectionFinderVisitor::Lang_OBJC);
514 }
515 
516 const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node,
517                                    StringRef Name) {
518   EXPECT_TRUE(Node);
519   EXPECT_EQ(Node->Children.size(), 1u);
520   const auto &Fn = checkNode<FunctionDecl>(
521       Node->Children[0], SourceSelectionKind::ContainsSelection,
522       /*NumChildren=*/1, Name);
523   return checkNode<CompoundStmt>(Fn.Children[0],
524                                  SourceSelectionKind::ContainsSelection,
525                                  /*NumChildren=*/1);
526 }
527 
528 TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
529   StringRef Source = R"(
530 @interface I
531 @property(readwrite) int prop;
532 @end
533 void selectProp(I *i) {
534 (void)i.prop;
535 i.prop = 21;
536 }
537 
538 
539 @interface NSMutableArray
540 - (id)objectAtIndexedSubscript:(unsigned int)index;
541 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
542 @end
543 
544 void selectSubscript(NSMutableArray *array, I *i) {
545   (void)array[10];
546   array[i.prop] = i;
547 }
548 )";
549   // Just 'i.prop'.
550   findSelectedASTNodes(
551       Source, {6, 7}, FileRange{{6, 7}, {6, 13}},
552       [](Optional<SelectedASTNode> Node) {
553         const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
554         const auto &CCast = checkNode<CStyleCastExpr>(
555             CS.Children[0], SourceSelectionKind::ContainsSelection,
556             /*NumChildren=*/1);
557         const auto &POE = checkNode<PseudoObjectExpr>(
558             CCast.Children[0], SourceSelectionKind::ContainsSelection,
559             /*NumChildren=*/1);
560         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
561             POE.Children[0], SourceSelectionKind::ContainsSelection,
562             /*NumChildren=*/1);
563         const auto &Cast = checkNode<ImplicitCastExpr>(
564             PRE.Children[0], SourceSelectionKind::InsideSelection,
565             /*NumChildren=*/1);
566         checkNode<DeclRefExpr>(Cast.Children[0],
567                                SourceSelectionKind::InsideSelection);
568       },
569       SelectionFinderVisitor::Lang_OBJC);
570   // Just 'i.prop = 21'
571   findSelectedASTNodes(
572       Source, {7, 1}, FileRange{{7, 1}, {7, 12}},
573       [](Optional<SelectedASTNode> Node) {
574         const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
575         const auto &POE = checkNode<PseudoObjectExpr>(
576             CS.Children[0], SourceSelectionKind::ContainsSelection,
577             /*NumChildren=*/1);
578         const auto &BinOp = checkNode<BinaryOperator>(
579             POE.Children[0], SourceSelectionKind::ContainsSelection,
580             /*NumChildren=*/2);
581         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
582             BinOp.Children[0], SourceSelectionKind::InsideSelection,
583             /*NumChildren=*/1);
584         const auto &Cast = checkNode<ImplicitCastExpr>(
585             PRE.Children[0], SourceSelectionKind::InsideSelection,
586             /*NumChildren=*/1);
587         checkNode<DeclRefExpr>(Cast.Children[0],
588                                SourceSelectionKind::InsideSelection);
589         checkNode<IntegerLiteral>(BinOp.Children[1],
590                                   SourceSelectionKind::InsideSelection);
591       },
592       SelectionFinderVisitor::Lang_OBJC);
593   // Just 'array[10]'
594   findSelectedASTNodes(
595       Source, {17, 9}, FileRange{{17, 9}, {17, 18}},
596       [](Optional<SelectedASTNode> Node) {
597         const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
598         const auto &CCast = checkNode<CStyleCastExpr>(
599             CS.Children[0], SourceSelectionKind::ContainsSelection,
600             /*NumChildren=*/1);
601         const auto &POE = checkNode<PseudoObjectExpr>(
602             CCast.Children[0], SourceSelectionKind::ContainsSelection,
603             /*NumChildren=*/1);
604         const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
605             POE.Children[0], SourceSelectionKind::ContainsSelection,
606             /*NumChildren=*/2);
607         const auto &Cast = checkNode<ImplicitCastExpr>(
608             SRE.Children[0], SourceSelectionKind::InsideSelection,
609             /*NumChildren=*/1);
610         checkNode<DeclRefExpr>(Cast.Children[0],
611                                SourceSelectionKind::InsideSelection);
612         checkNode<IntegerLiteral>(SRE.Children[1],
613                                   SourceSelectionKind::InsideSelection);
614       },
615       SelectionFinderVisitor::Lang_OBJC);
616   // Just 'array[i.prop] = array'
617   findSelectedASTNodes(
618       Source, {18, 3}, FileRange{{18, 3}, {18, 20}},
619       [](Optional<SelectedASTNode> Node) {
620         const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
621         const auto &POE = checkNode<PseudoObjectExpr>(
622             CS.Children[0], SourceSelectionKind::ContainsSelection,
623             /*NumChildren=*/1);
624         const auto &BinOp = checkNode<BinaryOperator>(
625             POE.Children[0], SourceSelectionKind::ContainsSelection,
626             /*NumChildren=*/2);
627         const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
628             BinOp.Children[0], SourceSelectionKind::InsideSelection,
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         const auto &POE2 = checkNode<PseudoObjectExpr>(
636             SRE.Children[1], SourceSelectionKind::InsideSelection,
637             /*NumChildren=*/1);
638         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
639             POE2.Children[0], SourceSelectionKind::InsideSelection,
640             /*NumChildren=*/1);
641         const auto &Cast2 = checkNode<ImplicitCastExpr>(
642             PRE.Children[0], SourceSelectionKind::InsideSelection,
643             /*NumChildren=*/1);
644         checkNode<DeclRefExpr>(Cast2.Children[0],
645                                SourceSelectionKind::InsideSelection);
646         checkNode<DeclRefExpr>(BinOp.Children[1],
647                                SourceSelectionKind::InsideSelection);
648       },
649       SelectionFinderVisitor::Lang_OBJC);
650 }
651 
652 } // end anonymous namespace
653