xref: /llvm-project/clang/unittests/Tooling/ASTSelectionTest.cpp (revision 12f78e740c5419f7d1fbcf8f2106e7a40cd1d6f7)
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 #include <optional>
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 {
30   FileLocation Location;
31   std::optional<FileRange> SelectionRange;
32   llvm::function_ref<void(SourceRange SelectionRange,
33                           std::optional<SelectedASTNode>)>
34       Consumer;
35 
36 public:
37   SelectionFinderVisitor(
38       FileLocation Location, std::optional<FileRange> SelectionRange,
39       llvm::function_ref<void(SourceRange SelectionRange,
40                               std::optional<SelectedASTNode>)>
41           Consumer)
42       : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) {
43   }
44 
45   bool VisitTranslationUnitDecl(TranslationUnitDecl *TU) override {
46     const ASTContext &Context = TU->getASTContext();
47     const SourceManager &SM = Context.getSourceManager();
48 
49     SourceRange SelRange;
50     if (SelectionRange) {
51       SelRange = SourceRange(SelectionRange->first.translate(SM),
52                              SelectionRange->second.translate(SM));
53     } else {
54       SourceLocation Loc = Location.translate(SM);
55       SelRange = SourceRange(Loc, Loc);
56     }
57     Consumer(SelRange, findSelectedASTNodes(Context, SelRange));
58     return false;
59   }
60 };
61 
62 /// This is a test utility function that computes the AST selection at the
63 /// given location with an optional selection range.
64 ///
65 /// A location roughly corresponds to a cursor location in an editor, while
66 /// the optional range corresponds to the selection range in an editor.
67 void findSelectedASTNodesWithRange(
68     StringRef Source, FileLocation Location,
69     std::optional<FileRange> SelectionRange,
70     llvm::function_ref<void(SourceRange SelectionRange,
71                             std::optional<SelectedASTNode>)>
72         Consumer,
73     SelectionFinderVisitor::Language Language =
74         SelectionFinderVisitor::Lang_CXX11) {
75   SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer);
76   EXPECT_TRUE(Visitor.runOver(Source, Language));
77 }
78 
79 void findSelectedASTNodes(
80     StringRef Source, FileLocation Location,
81     std::optional<FileRange> SelectionRange,
82     llvm::function_ref<void(std::optional<SelectedASTNode>)> Consumer,
83     SelectionFinderVisitor::Language Language =
84         SelectionFinderVisitor::Lang_CXX11) {
85   findSelectedASTNodesWithRange(
86       Source, Location, SelectionRange,
87       [&](SourceRange, std::optional<SelectedASTNode> Selection) {
88         Consumer(std::move(Selection));
89       },
90       Language);
91 }
92 
93 void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node,
94                    SourceSelectionKind SelectionKind, unsigned NumChildren) {
95   ASSERT_TRUE(IsTypeMatched);
96   EXPECT_EQ(Node.Children.size(), NumChildren);
97   ASSERT_EQ(Node.SelectionKind, SelectionKind);
98 }
99 
100 void checkDeclName(const SelectedASTNode &Node, StringRef Name) {
101   const auto *ND = Node.Node.get<NamedDecl>();
102   EXPECT_TRUE(!!ND);
103   ASSERT_EQ(ND->getName(), Name);
104 }
105 
106 template <typename T>
107 const SelectedASTNode &
108 checkNode(const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind,
109           unsigned NumChildren = 0,
110           std::enable_if_t<std::is_base_of_v<Stmt, T>, T> *StmtOverloadChecker =
111               nullptr) {
112   checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind,
113                 NumChildren);
114   return StmtNode;
115 }
116 
117 template <typename T>
118 const SelectedASTNode &
119 checkNode(const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind,
120           unsigned NumChildren = 0, StringRef Name = "",
121           std::enable_if_t<std::is_base_of_v<Decl, T>, T> *DeclOverloadChecker =
122               nullptr) {
123   checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind,
124                 NumChildren);
125   if (!Name.empty())
126     checkDeclName(DeclNode, Name);
127   return DeclNode;
128 }
129 
130 struct ForAllChildrenOf {
131   const SelectedASTNode &Node;
132 
133   static void childKindVerifier(const SelectedASTNode &Node,
134                                 SourceSelectionKind SelectionKind) {
135     for (const SelectedASTNode &Child : Node.Children) {
136       ASSERT_EQ(Node.SelectionKind, SelectionKind);
137       childKindVerifier(Child, SelectionKind);
138     }
139   }
140 
141 public:
142   ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {}
143 
144   void shouldHaveSelectionKind(SourceSelectionKind Kind) {
145     childKindVerifier(Node, Kind);
146   }
147 };
148 
149 ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) {
150   return ForAllChildrenOf(Node);
151 }
152 
153 TEST(ASTSelectionFinder, CursorNoSelection) {
154   findSelectedASTNodes(
155       " void f() { }", {1, 1}, std::nullopt,
156       [](std::optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
157 }
158 
159 TEST(ASTSelectionFinder, CursorAtStartOfFunction) {
160   findSelectedASTNodes(
161       "void f() { }", {1, 1}, std::nullopt,
162       [](std::optional<SelectedASTNode> Node) {
163         EXPECT_TRUE(Node);
164         checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None,
165                                        /*NumChildren=*/1);
166         checkNode<FunctionDecl>(Node->Children[0],
167                                 SourceSelectionKind::ContainsSelection,
168                                 /*NumChildren=*/0, /*Name=*/"f");
169 
170         // Check that the dumping works.
171         std::string DumpValue;
172         llvm::raw_string_ostream OS(DumpValue);
173         Node->Children[0].dump(OS);
174         ASSERT_EQ(DumpValue, "FunctionDecl \"f\" contains-selection\n");
175       });
176 }
177 
178 TEST(ASTSelectionFinder, RangeNoSelection) {
179   findSelectedASTNodes(
180       " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
181       [](std::optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
182   findSelectedASTNodes(
183       "  void f() { }", {1, 1}, FileRange{{1, 1}, {1, 2}},
184       [](std::optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); });
185 }
186 
187 TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) {
188   findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}},
189                        [](std::optional<SelectedASTNode> Node) {
190                          EXPECT_TRUE(Node);
191                          checkNode<FunctionDecl>(
192                              Node->Children[0],
193                              SourceSelectionKind::ContainsSelection,
194                              /*NumChildren=*/0, /*Name=*/"f");
195                        });
196 }
197 
198 TEST(ASTSelectionFinder, WholeFunctionSelection) {
199   StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }";
200   // From 'int' until just after '}':
201 
202   findSelectedASTNodes(
203       Source, {1, 1}, FileRange{{1, 1}, {2, 2}},
204       [](std::optional<SelectedASTNode> Node) {
205         EXPECT_TRUE(Node);
206         EXPECT_EQ(Node->Children.size(), 1u);
207         const auto &Fn = checkNode<FunctionDecl>(
208             Node->Children[0], SourceSelectionKind::ContainsSelection,
209             /*NumChildren=*/2, /*Name=*/"f");
210         checkNode<ParmVarDecl>(Fn.Children[0],
211                                SourceSelectionKind::InsideSelection);
212         const auto &Body = checkNode<CompoundStmt>(
213             Fn.Children[1], SourceSelectionKind::InsideSelection,
214             /*NumChildren=*/1);
215         const auto &Return = checkNode<ReturnStmt>(
216             Body.Children[0], SourceSelectionKind::InsideSelection,
217             /*NumChildren=*/1);
218         checkNode<ImplicitCastExpr>(Return.Children[0],
219                                     SourceSelectionKind::InsideSelection,
220                                     /*NumChildren=*/1);
221         checkNode<DeclRefExpr>(Return.Children[0].Children[0],
222                                SourceSelectionKind::InsideSelection);
223       });
224 
225   // From 'int' until just before '}':
226   findSelectedASTNodes(
227       Source, {2, 1}, FileRange{{1, 1}, {2, 1}},
228       [](std::optional<SelectedASTNode> Node) {
229         EXPECT_TRUE(Node);
230         EXPECT_EQ(Node->Children.size(), 1u);
231         const auto &Fn = checkNode<FunctionDecl>(
232             Node->Children[0], SourceSelectionKind::ContainsSelection,
233             /*NumChildren=*/2, /*Name=*/"f");
234         const auto &Body = checkNode<CompoundStmt>(
235             Fn.Children[1], SourceSelectionKind::ContainsSelectionEnd,
236             /*NumChildren=*/1);
237         checkNode<ReturnStmt>(Body.Children[0],
238                               SourceSelectionKind::InsideSelection,
239                               /*NumChildren=*/1);
240       });
241   // From '{' until just after '}':
242   findSelectedASTNodes(
243       Source, {1, 14}, FileRange{{1, 14}, {2, 2}},
244       [](std::optional<SelectedASTNode> Node) {
245         EXPECT_TRUE(Node);
246         EXPECT_EQ(Node->Children.size(), 1u);
247         const auto &Fn = checkNode<FunctionDecl>(
248             Node->Children[0], SourceSelectionKind::ContainsSelection,
249             /*NumChildren=*/1, /*Name=*/"f");
250         const auto &Body = checkNode<CompoundStmt>(
251             Fn.Children[0], SourceSelectionKind::ContainsSelection,
252             /*NumChildren=*/1);
253         checkNode<ReturnStmt>(Body.Children[0],
254                               SourceSelectionKind::InsideSelection,
255                               /*NumChildren=*/1);
256       });
257   // From 'x' until just after '}':
258   findSelectedASTNodes(
259       Source, {2, 2}, FileRange{{1, 11}, {2, 2}},
260       [](std::optional<SelectedASTNode> Node) {
261         EXPECT_TRUE(Node);
262         EXPECT_EQ(Node->Children.size(), 1u);
263         const auto &Fn = checkNode<FunctionDecl>(
264             Node->Children[0], SourceSelectionKind::ContainsSelection,
265             /*NumChildren=*/2, /*Name=*/"f");
266         checkNode<ParmVarDecl>(Fn.Children[0],
267                                SourceSelectionKind::ContainsSelectionStart);
268         const auto &Body = checkNode<CompoundStmt>(
269             Fn.Children[1], SourceSelectionKind::InsideSelection,
270             /*NumChildren=*/1);
271         checkNode<ReturnStmt>(Body.Children[0],
272                               SourceSelectionKind::InsideSelection,
273                               /*NumChildren=*/1);
274       });
275 }
276 
277 TEST(ASTSelectionFinder, MultipleFunctionSelection) {
278   StringRef Source = R"(void f0() {
279 }
280 void f1() { }
281 void f2() { }
282 void f3() { }
283 )";
284   auto SelectedF1F2 = [](std::optional<SelectedASTNode> Node) {
285     EXPECT_TRUE(Node);
286     EXPECT_EQ(Node->Children.size(), 2u);
287     checkNode<FunctionDecl>(Node->Children[0],
288                             SourceSelectionKind::InsideSelection,
289                             /*NumChildren=*/1, /*Name=*/"f1");
290     checkNode<FunctionDecl>(Node->Children[1],
291                             SourceSelectionKind::InsideSelection,
292                             /*NumChildren=*/1, /*Name=*/"f2");
293   };
294   // Just after '}' of f0 and just before 'void' of f3:
295   findSelectedASTNodes(Source, {2, 2}, FileRange{{2, 2}, {5, 1}}, SelectedF1F2);
296   // Just before 'void' of f1 and just after '}' of f2:
297   findSelectedASTNodes(Source, {3, 1}, FileRange{{3, 1}, {4, 14}},
298                        SelectedF1F2);
299 }
300 
301 TEST(ASTSelectionFinder, MultipleStatementSelection) {
302   StringRef Source = R"(void f(int x, int y) {
303   int z = x;
304   f(2, 3);
305   if (x == 0) {
306     return;
307   }
308   x = 1;
309   return;
310 })";
311   // From 'f(2,3)' until just before 'x = 1;':
312   findSelectedASTNodes(
313       Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
314       [](std::optional<SelectedASTNode> Node) {
315         EXPECT_TRUE(Node);
316         EXPECT_EQ(Node->Children.size(), 1u);
317         const auto &Fn = checkNode<FunctionDecl>(
318             Node->Children[0], SourceSelectionKind::ContainsSelection,
319             /*NumChildren=*/1, /*Name=*/"f");
320         const auto &Body = checkNode<CompoundStmt>(
321             Fn.Children[0], SourceSelectionKind::ContainsSelection,
322             /*NumChildren=*/2);
323         allChildrenOf(checkNode<CallExpr>(Body.Children[0],
324                                           SourceSelectionKind::InsideSelection,
325                                           /*NumChildren=*/3))
326             .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
327         allChildrenOf(checkNode<IfStmt>(Body.Children[1],
328                                         SourceSelectionKind::InsideSelection,
329                                         /*NumChildren=*/2))
330             .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection);
331       });
332   // From 'f(2,3)' until just before ';' in 'x = 1;':
333   findSelectedASTNodes(
334       Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
335       [](std::optional<SelectedASTNode> Node) {
336         EXPECT_TRUE(Node);
337         EXPECT_EQ(Node->Children.size(), 1u);
338         const auto &Fn = checkNode<FunctionDecl>(
339             Node->Children[0], SourceSelectionKind::ContainsSelection,
340             /*NumChildren=*/1, /*Name=*/"f");
341         const auto &Body = checkNode<CompoundStmt>(
342             Fn.Children[0], SourceSelectionKind::ContainsSelection,
343             /*NumChildren=*/3);
344         checkNode<CallExpr>(Body.Children[0],
345                             SourceSelectionKind::InsideSelection,
346                             /*NumChildren=*/3);
347         checkNode<IfStmt>(Body.Children[1],
348                           SourceSelectionKind::InsideSelection,
349                           /*NumChildren=*/2);
350         checkNode<BinaryOperator>(Body.Children[2],
351                                   SourceSelectionKind::InsideSelection,
352                                   /*NumChildren=*/2);
353       });
354   // From the middle of 'int z = 3' until the middle of 'x = 1;':
355   findSelectedASTNodes(
356       Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
357       [](std::optional<SelectedASTNode> Node) {
358         EXPECT_TRUE(Node);
359         EXPECT_EQ(Node->Children.size(), 1u);
360         const auto &Fn = checkNode<FunctionDecl>(
361             Node->Children[0], SourceSelectionKind::ContainsSelection,
362             /*NumChildren=*/1, /*Name=*/"f");
363         const auto &Body = checkNode<CompoundStmt>(
364             Fn.Children[0], SourceSelectionKind::ContainsSelection,
365             /*NumChildren=*/4);
366         checkNode<DeclStmt>(Body.Children[0],
367                             SourceSelectionKind::ContainsSelectionStart,
368                             /*NumChildren=*/1);
369         checkNode<CallExpr>(Body.Children[1],
370                             SourceSelectionKind::InsideSelection,
371                             /*NumChildren=*/3);
372         checkNode<IfStmt>(Body.Children[2],
373                           SourceSelectionKind::InsideSelection,
374                           /*NumChildren=*/2);
375         checkNode<BinaryOperator>(Body.Children[3],
376                                   SourceSelectionKind::ContainsSelectionEnd,
377                                   /*NumChildren=*/1);
378       });
379 }
380 
381 TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) {
382   StringRef Source = R"(
383 @interface I
384 @end
385 @implementation I
386 
387 int notSelected() { return 0; }
388 
389 int selected(int x) {
390   return x;
391 }
392 
393 @end
394 @implementation I(Cat)
395 
396 void catF() { }
397 
398 @end
399 
400 void outerFunction() { }
401 )";
402   // Just the 'x' expression in 'selected':
403   findSelectedASTNodes(
404       Source, {9, 10}, FileRange{{9, 10}, {9, 11}},
405       [](std::optional<SelectedASTNode> Node) {
406         EXPECT_TRUE(Node);
407         EXPECT_EQ(Node->Children.size(), 1u);
408         const auto &Impl = checkNode<ObjCImplementationDecl>(
409             Node->Children[0], SourceSelectionKind::ContainsSelection,
410             /*NumChildren=*/1, /*Name=*/"I");
411         const auto &Fn = checkNode<FunctionDecl>(
412             Impl.Children[0], SourceSelectionKind::ContainsSelection,
413             /*NumChildren=*/1, /*Name=*/"selected");
414         allChildrenOf(Fn).shouldHaveSelectionKind(
415             SourceSelectionKind::ContainsSelection);
416       },
417       SelectionFinderVisitor::Lang_OBJC);
418   // The entire 'catF':
419   findSelectedASTNodes(
420       Source, {15, 1}, FileRange{{15, 1}, {15, 16}},
421       [](std::optional<SelectedASTNode> Node) {
422         EXPECT_TRUE(Node);
423         EXPECT_EQ(Node->Children.size(), 1u);
424         const auto &Impl = checkNode<ObjCCategoryImplDecl>(
425             Node->Children[0], SourceSelectionKind::ContainsSelection,
426             /*NumChildren=*/1, /*Name=*/"Cat");
427         const auto &Fn = checkNode<FunctionDecl>(
428             Impl.Children[0], SourceSelectionKind::ContainsSelection,
429             /*NumChildren=*/1, /*Name=*/"catF");
430         allChildrenOf(Fn).shouldHaveSelectionKind(
431             SourceSelectionKind::ContainsSelection);
432       },
433       SelectionFinderVisitor::Lang_OBJC);
434   // From the line before 'selected' to the line after 'catF':
435   findSelectedASTNodes(
436       Source, {16, 1}, FileRange{{7, 1}, {16, 1}},
437       [](std::optional<SelectedASTNode> Node) {
438         EXPECT_TRUE(Node);
439         EXPECT_EQ(Node->Children.size(), 2u);
440         const auto &Impl = checkNode<ObjCImplementationDecl>(
441             Node->Children[0], SourceSelectionKind::ContainsSelectionStart,
442             /*NumChildren=*/1, /*Name=*/"I");
443         const auto &Selected = checkNode<FunctionDecl>(
444             Impl.Children[0], SourceSelectionKind::InsideSelection,
445             /*NumChildren=*/2, /*Name=*/"selected");
446         allChildrenOf(Selected).shouldHaveSelectionKind(
447             SourceSelectionKind::InsideSelection);
448         const auto &Cat = checkNode<ObjCCategoryImplDecl>(
449             Node->Children[1], SourceSelectionKind::ContainsSelectionEnd,
450             /*NumChildren=*/1, /*Name=*/"Cat");
451         const auto &CatF = checkNode<FunctionDecl>(
452             Cat.Children[0], SourceSelectionKind::InsideSelection,
453             /*NumChildren=*/1, /*Name=*/"catF");
454         allChildrenOf(CatF).shouldHaveSelectionKind(
455             SourceSelectionKind::InsideSelection);
456       },
457       SelectionFinderVisitor::Lang_OBJC);
458   // Just the 'outer' function:
459   findSelectedASTNodes(
460       Source, {19, 1}, FileRange{{19, 1}, {19, 25}},
461       [](std::optional<SelectedASTNode> Node) {
462         EXPECT_TRUE(Node);
463         EXPECT_EQ(Node->Children.size(), 1u);
464         checkNode<FunctionDecl>(Node->Children[0],
465                                 SourceSelectionKind::ContainsSelection,
466                                 /*NumChildren=*/1, /*Name=*/"outerFunction");
467       },
468       SelectionFinderVisitor::Lang_OBJC);
469 }
470 
471 TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) {
472   StringRef Source = R"(
473 @interface I
474 @end
475 @implementation I
476 
477 void selected() {
478 }
479 
480 - (void) method { }
481 
482 @end
483 )";
484   // Just 'selected'
485   findSelectedASTNodes(
486       Source, {6, 1}, FileRange{{6, 1}, {7, 2}},
487       [](std::optional<SelectedASTNode> Node) {
488         EXPECT_TRUE(Node);
489         EXPECT_EQ(Node->Children.size(), 1u);
490         const auto &Impl = checkNode<ObjCImplementationDecl>(
491             Node->Children[0], SourceSelectionKind::ContainsSelection,
492             /*NumChildren=*/1, /*Name=*/"I");
493         checkNode<FunctionDecl>(Impl.Children[0],
494                                 SourceSelectionKind::ContainsSelection,
495                                 /*NumChildren=*/1, /*Name=*/"selected");
496       },
497       SelectionFinderVisitor::Lang_OBJC);
498 }
499 
500 TEST(ASTSelectionFinder, AvoidImplicitDeclarations) {
501   StringRef Source = R"(
502 struct Copy {
503   int x;
504 };
505 void foo() {
506   Copy x;
507   Copy y = x;
508 }
509 )";
510   // The entire struct 'Copy':
511   findSelectedASTNodes(
512       Source, {2, 1}, FileRange{{2, 1}, {4, 3}},
513       [](std::optional<SelectedASTNode> Node) {
514         EXPECT_TRUE(Node);
515         EXPECT_EQ(Node->Children.size(), 1u);
516         const auto &Record = checkNode<CXXRecordDecl>(
517             Node->Children[0], SourceSelectionKind::InsideSelection,
518             /*NumChildren=*/1, /*Name=*/"Copy");
519         checkNode<FieldDecl>(Record.Children[0],
520                              SourceSelectionKind::InsideSelection);
521       });
522 }
523 
524 TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) {
525   StringRef Source = R"(
526 @interface I
527 @end
528 @implementation I
529 @ end
530 )";
531   // Just after '@ end'
532   findSelectedASTNodes(
533       Source, {5, 6}, std::nullopt,
534       [](std::optional<SelectedASTNode> Node) {
535         EXPECT_TRUE(Node);
536         EXPECT_EQ(Node->Children.size(), 1u);
537         checkNode<ObjCImplementationDecl>(
538             Node->Children[0], SourceSelectionKind::ContainsSelection);
539       },
540       SelectionFinderVisitor::Lang_OBJC);
541 }
542 
543 const SelectedASTNode &checkFnBody(const std::optional<SelectedASTNode> &Node,
544                                    StringRef Name) {
545   EXPECT_TRUE(Node);
546   EXPECT_EQ(Node->Children.size(), 1u);
547   const auto &Fn = checkNode<FunctionDecl>(
548       Node->Children[0], SourceSelectionKind::ContainsSelection,
549       /*NumChildren=*/1, Name);
550   return checkNode<CompoundStmt>(Fn.Children[0],
551                                  SourceSelectionKind::ContainsSelection,
552                                  /*NumChildren=*/1);
553 }
554 
555 TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) {
556   StringRef Source = R"(
557 @interface I
558 @property(readwrite) int prop;
559 @end
560 void selectProp(I *i) {
561 (void)i.prop;
562 i.prop = 21;
563 }
564 
565 
566 @interface NSMutableArray
567 - (id)objectAtIndexedSubscript:(unsigned int)index;
568 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index;
569 @end
570 
571 void selectSubscript(NSMutableArray *array, I *i) {
572   (void)array[10];
573   array[i.prop] = i;
574 }
575 )";
576   // Just 'i.prop'.
577   findSelectedASTNodes(
578       Source, {6, 7}, FileRange{{6, 7}, {6, 13}},
579       [](std::optional<SelectedASTNode> Node) {
580         const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
581         const auto &CCast = checkNode<CStyleCastExpr>(
582             CS.Children[0], SourceSelectionKind::ContainsSelection,
583             /*NumChildren=*/1);
584         const auto &POE = checkNode<PseudoObjectExpr>(
585             CCast.Children[0], SourceSelectionKind::ContainsSelection,
586             /*NumChildren=*/1);
587         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
588             POE.Children[0], SourceSelectionKind::ContainsSelection,
589             /*NumChildren=*/1);
590         const auto &Cast = checkNode<ImplicitCastExpr>(
591             PRE.Children[0], SourceSelectionKind::InsideSelection,
592             /*NumChildren=*/1);
593         checkNode<DeclRefExpr>(Cast.Children[0],
594                                SourceSelectionKind::InsideSelection);
595       },
596       SelectionFinderVisitor::Lang_OBJC);
597   // Just 'i.prop = 21'
598   findSelectedASTNodes(
599       Source, {7, 1}, FileRange{{7, 1}, {7, 12}},
600       [](std::optional<SelectedASTNode> Node) {
601         const auto &CS = checkFnBody(Node, /*Name=*/"selectProp");
602         const auto &POE = checkNode<PseudoObjectExpr>(
603             CS.Children[0], SourceSelectionKind::ContainsSelection,
604             /*NumChildren=*/1);
605         const auto &BinOp = checkNode<BinaryOperator>(
606             POE.Children[0], SourceSelectionKind::ContainsSelection,
607             /*NumChildren=*/2);
608         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
609             BinOp.Children[0], SourceSelectionKind::InsideSelection,
610             /*NumChildren=*/1);
611         const auto &Cast = checkNode<ImplicitCastExpr>(
612             PRE.Children[0], SourceSelectionKind::InsideSelection,
613             /*NumChildren=*/1);
614         checkNode<DeclRefExpr>(Cast.Children[0],
615                                SourceSelectionKind::InsideSelection);
616         checkNode<IntegerLiteral>(BinOp.Children[1],
617                                   SourceSelectionKind::InsideSelection);
618       },
619       SelectionFinderVisitor::Lang_OBJC);
620   // Just 'array[10]'
621   findSelectedASTNodes(
622       Source, {17, 9}, FileRange{{17, 9}, {17, 18}},
623       [](std::optional<SelectedASTNode> Node) {
624         const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
625         const auto &CCast = checkNode<CStyleCastExpr>(
626             CS.Children[0], SourceSelectionKind::ContainsSelection,
627             /*NumChildren=*/1);
628         const auto &POE = checkNode<PseudoObjectExpr>(
629             CCast.Children[0], SourceSelectionKind::ContainsSelection,
630             /*NumChildren=*/1);
631         const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
632             POE.Children[0], SourceSelectionKind::ContainsSelection,
633             /*NumChildren=*/2);
634         const auto &Cast = checkNode<ImplicitCastExpr>(
635             SRE.Children[0], SourceSelectionKind::InsideSelection,
636             /*NumChildren=*/1);
637         checkNode<DeclRefExpr>(Cast.Children[0],
638                                SourceSelectionKind::InsideSelection);
639         checkNode<IntegerLiteral>(SRE.Children[1],
640                                   SourceSelectionKind::InsideSelection);
641       },
642       SelectionFinderVisitor::Lang_OBJC);
643   // Just 'array[i.prop] = array'
644   findSelectedASTNodes(
645       Source, {18, 3}, FileRange{{18, 3}, {18, 20}},
646       [](std::optional<SelectedASTNode> Node) {
647         const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript");
648         const auto &POE = checkNode<PseudoObjectExpr>(
649             CS.Children[0], SourceSelectionKind::ContainsSelection,
650             /*NumChildren=*/1);
651         const auto &BinOp = checkNode<BinaryOperator>(
652             POE.Children[0], SourceSelectionKind::ContainsSelection,
653             /*NumChildren=*/2);
654         const auto &SRE = checkNode<ObjCSubscriptRefExpr>(
655             BinOp.Children[0], SourceSelectionKind::InsideSelection,
656             /*NumChildren=*/2);
657         const auto &Cast = checkNode<ImplicitCastExpr>(
658             SRE.Children[0], SourceSelectionKind::InsideSelection,
659             /*NumChildren=*/1);
660         checkNode<DeclRefExpr>(Cast.Children[0],
661                                SourceSelectionKind::InsideSelection);
662         const auto &POE2 = checkNode<PseudoObjectExpr>(
663             SRE.Children[1], SourceSelectionKind::InsideSelection,
664             /*NumChildren=*/1);
665         const auto &PRE = checkNode<ObjCPropertyRefExpr>(
666             POE2.Children[0], SourceSelectionKind::InsideSelection,
667             /*NumChildren=*/1);
668         const auto &Cast2 = checkNode<ImplicitCastExpr>(
669             PRE.Children[0], SourceSelectionKind::InsideSelection,
670             /*NumChildren=*/1);
671         checkNode<DeclRefExpr>(Cast2.Children[0],
672                                SourceSelectionKind::InsideSelection);
673         checkNode<DeclRefExpr>(BinOp.Children[1],
674                                SourceSelectionKind::InsideSelection);
675       },
676       SelectionFinderVisitor::Lang_OBJC);
677 }
678 
679 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelection) {
680   StringRef Source = R"(void f(int x, int y) {
681   int z = x;
682   f(2, 3);
683   if (x == 0) {
684     return;
685   }
686   x = 1;
687   return;
688 }
689 void f2() {
690   int m = 0;
691 }
692 )";
693   // No selection range.
694   findSelectedASTNodesWithRange(
695       Source, {2, 2}, std::nullopt,
696       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
697         EXPECT_TRUE(Node);
698         std::optional<CodeRangeASTSelection> SelectedCode =
699             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
700         EXPECT_FALSE(SelectedCode);
701       });
702   findSelectedASTNodesWithRange(
703       Source, {2, 2}, FileRange{{2, 2}, {2, 2}},
704       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
705         EXPECT_TRUE(Node);
706         std::optional<CodeRangeASTSelection> SelectedCode =
707             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
708         EXPECT_FALSE(SelectedCode);
709       });
710   // Range that spans multiple functions is an invalid code range.
711   findSelectedASTNodesWithRange(
712       Source, {2, 2}, FileRange{{7, 2}, {12, 1}},
713       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
714         EXPECT_TRUE(Node);
715         std::optional<CodeRangeASTSelection> SelectedCode =
716             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
717         EXPECT_FALSE(SelectedCode);
718       });
719   // Just 'z = x;':
720   findSelectedASTNodesWithRange(
721       Source, {2, 2}, FileRange{{2, 2}, {2, 13}},
722       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
723         EXPECT_TRUE(Node);
724         std::optional<CodeRangeASTSelection> SelectedCode =
725             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
726         EXPECT_TRUE(SelectedCode);
727         EXPECT_EQ(SelectedCode->size(), 1u);
728         EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
729         ArrayRef<SelectedASTNode::ReferenceType> Parents =
730             SelectedCode->getParents();
731         EXPECT_EQ(Parents.size(), 3u);
732         EXPECT_TRUE(
733             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
734         // Function 'f' definition.
735         EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
736         // Function body of function 'F'.
737         EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
738       });
739   // From 'f(2,3)' until just before 'x = 1;':
740   findSelectedASTNodesWithRange(
741       Source, {3, 2}, FileRange{{3, 2}, {7, 1}},
742       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
743         EXPECT_TRUE(Node);
744         std::optional<CodeRangeASTSelection> SelectedCode =
745             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
746         EXPECT_TRUE(SelectedCode);
747         EXPECT_EQ(SelectedCode->size(), 2u);
748         EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
749         EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
750         ArrayRef<SelectedASTNode::ReferenceType> Parents =
751             SelectedCode->getParents();
752         EXPECT_EQ(Parents.size(), 3u);
753         EXPECT_TRUE(
754             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
755         // Function 'f' definition.
756         EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
757         // Function body of function 'F'.
758         EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
759       });
760   // From 'f(2,3)' until just before ';' in 'x = 1;':
761   findSelectedASTNodesWithRange(
762       Source, {3, 2}, FileRange{{3, 2}, {7, 8}},
763       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
764         EXPECT_TRUE(Node);
765         std::optional<CodeRangeASTSelection> SelectedCode =
766             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
767         EXPECT_TRUE(SelectedCode);
768         EXPECT_EQ(SelectedCode->size(), 3u);
769         EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
770         EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
771         EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[2]));
772       });
773   // From the middle of 'int z = 3' until the middle of 'x = 1;':
774   findSelectedASTNodesWithRange(
775       Source, {2, 10}, FileRange{{2, 10}, {7, 5}},
776       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
777         EXPECT_TRUE(Node);
778         EXPECT_TRUE(Node);
779         std::optional<CodeRangeASTSelection> SelectedCode =
780             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
781         EXPECT_TRUE(SelectedCode);
782         EXPECT_EQ(SelectedCode->size(), 4u);
783         EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
784         EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[1]));
785         EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[2]));
786         EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[3]));
787       });
788 }
789 
790 TEST(ASTSelectionFinder, OutOfBodyCodeRange) {
791   StringRef Source = R"(
792 int codeRange = 2 + 3;
793 )";
794   // '2+3' expression.
795   findSelectedASTNodesWithRange(
796       Source, {2, 17}, FileRange{{2, 17}, {2, 22}},
797       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
798         EXPECT_TRUE(Node);
799         std::optional<CodeRangeASTSelection> SelectedCode =
800             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
801         EXPECT_TRUE(SelectedCode);
802         EXPECT_EQ(SelectedCode->size(), 1u);
803         EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[0]));
804         ArrayRef<SelectedASTNode::ReferenceType> Parents =
805             SelectedCode->getParents();
806         EXPECT_EQ(Parents.size(), 2u);
807         EXPECT_TRUE(
808             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
809         // Variable 'codeRange'.
810         EXPECT_TRUE(isa<VarDecl>(Parents[1].get().Node.get<Decl>()));
811       });
812 }
813 
814 TEST(ASTSelectionFinder, SelectVarDeclStmt) {
815   StringRef Source = R"(
816 void f() {
817    {
818        int a;
819    }
820 }
821 )";
822   // 'int a'
823   findSelectedASTNodesWithRange(
824       Source, {4, 8}, FileRange{{4, 8}, {4, 14}},
825       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
826         EXPECT_TRUE(Node);
827         std::optional<CodeRangeASTSelection> SelectedCode =
828             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
829         EXPECT_TRUE(SelectedCode);
830         EXPECT_EQ(SelectedCode->size(), 1u);
831         EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
832         ArrayRef<SelectedASTNode::ReferenceType> Parents =
833             SelectedCode->getParents();
834         EXPECT_EQ(Parents.size(), 4u);
835         EXPECT_TRUE(
836             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
837         // Function 'f' definition.
838         EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
839         // Function body of function 'F'.
840         EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
841         // Compound statement in body of 'F'.
842         EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
843       });
844 }
845 
846 TEST(ASTSelectionFinder, SelectEntireDeclStmtRange) {
847   StringRef Source = R"(
848 void f(int x, int y) {
849    int a = x * y;
850 }
851 )";
852   // 'int a = x * y'
853   findSelectedASTNodesWithRange(
854       Source, {3, 4}, FileRange{{3, 4}, {3, 17}},
855       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
856         EXPECT_TRUE(Node);
857         std::optional<CodeRangeASTSelection> SelectedCode =
858             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
859         EXPECT_TRUE(SelectedCode);
860         EXPECT_EQ(SelectedCode->size(), 1u);
861         EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
862         ArrayRef<SelectedASTNode::ReferenceType> Parents =
863             SelectedCode->getParents();
864         EXPECT_EQ(Parents.size(), 3u);
865         EXPECT_TRUE(
866             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
867         // Function 'f' definition.
868         EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
869         // Function body of function 'F'.
870         EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
871       });
872 }
873 
874 TEST(ASTSelectionFinder, SelectEntireDeclStmtRangeWithMultipleDecls) {
875   StringRef Source = R"(
876 void f(int x, int y) {
877    int a = x * y, b = x - y;
878 }
879 )";
880   // 'b = x - y'
881   findSelectedASTNodesWithRange(
882       Source, {3, 19}, FileRange{{3, 19}, {3, 28}},
883       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
884         EXPECT_TRUE(Node);
885         std::optional<CodeRangeASTSelection> SelectedCode =
886             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
887         EXPECT_TRUE(SelectedCode);
888         EXPECT_EQ(SelectedCode->size(), 1u);
889         EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
890         ArrayRef<SelectedASTNode::ReferenceType> Parents =
891             SelectedCode->getParents();
892         EXPECT_EQ(Parents.size(), 3u);
893         EXPECT_TRUE(
894             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
895         // Function 'f' definition.
896         EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>()));
897         // Function body of function 'F'.
898         EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>()));
899       });
900 }
901 
902 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelectionInObjCMethod) {
903   StringRef Source = R"(@interface I @end
904 @implementation I
905 - (void) f:(int)x with:(int) y {
906   int z = x;
907   [self f: 2 with: 3];
908   if (x == 0) {
909     return;
910   }
911   x = 1;
912   return;
913 }
914 - (void)f2 {
915   int m = 0;
916 }
917 @end
918 )";
919   // Range that spans multiple methods is an invalid code range.
920   findSelectedASTNodesWithRange(
921       Source, {9, 2}, FileRange{{9, 2}, {13, 1}},
922       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
923         EXPECT_TRUE(Node);
924         std::optional<CodeRangeASTSelection> SelectedCode =
925             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
926         EXPECT_FALSE(SelectedCode);
927       },
928       SelectionFinderVisitor::Lang_OBJC);
929   // Just 'z = x;':
930   findSelectedASTNodesWithRange(
931       Source, {4, 2}, FileRange{{4, 2}, {4, 13}},
932       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
933         EXPECT_TRUE(Node);
934         std::optional<CodeRangeASTSelection> SelectedCode =
935             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
936         EXPECT_TRUE(SelectedCode);
937         EXPECT_EQ(SelectedCode->size(), 1u);
938         EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0]));
939         ArrayRef<SelectedASTNode::ReferenceType> Parents =
940             SelectedCode->getParents();
941         EXPECT_EQ(Parents.size(), 4u);
942         EXPECT_TRUE(
943             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
944         // 'I' @implementation.
945         EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
946         // Function 'f' definition.
947         EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
948         // Function body of function 'F'.
949         EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
950       },
951       SelectionFinderVisitor::Lang_OBJC);
952   // From '[self f: 2 with: 3]' until just before 'x = 1;':
953   findSelectedASTNodesWithRange(
954       Source, {5, 2}, FileRange{{5, 2}, {9, 1}},
955       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
956         EXPECT_TRUE(Node);
957         std::optional<CodeRangeASTSelection> SelectedCode =
958             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
959         EXPECT_TRUE(SelectedCode);
960         EXPECT_EQ(SelectedCode->size(), 2u);
961         EXPECT_TRUE(isa<ObjCMessageExpr>((*SelectedCode)[0]));
962         EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1]));
963         ArrayRef<SelectedASTNode::ReferenceType> Parents =
964             SelectedCode->getParents();
965         EXPECT_EQ(Parents.size(), 4u);
966         EXPECT_TRUE(
967             isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>()));
968         // 'I' @implementation.
969         EXPECT_TRUE(isa<ObjCImplDecl>(Parents[1].get().Node.get<Decl>()));
970         // Function 'f' definition.
971         EXPECT_TRUE(isa<ObjCMethodDecl>(Parents[2].get().Node.get<Decl>()));
972         // Function body of function 'F'.
973         EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>()));
974       },
975       SelectionFinderVisitor::Lang_OBJC);
976 }
977 
978 TEST(ASTSelectionFinder, CanonicalizeObjCStringLiteral) {
979   StringRef Source = R"(
980 void foo() {
981   (void)@"test";
982 }
983       )";
984   // Just '"test"':
985   findSelectedASTNodesWithRange(
986       Source, {3, 10}, FileRange{{3, 10}, {3, 16}},
987       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
988         EXPECT_TRUE(Node);
989         std::optional<CodeRangeASTSelection> SelectedCode =
990             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
991         EXPECT_TRUE(SelectedCode);
992         EXPECT_EQ(SelectedCode->size(), 1u);
993         EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
994       },
995       SelectionFinderVisitor::Lang_OBJC);
996   // Just 'test':
997   findSelectedASTNodesWithRange(
998       Source, {3, 11}, FileRange{{3, 11}, {3, 15}},
999       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1000         EXPECT_TRUE(Node);
1001         std::optional<CodeRangeASTSelection> SelectedCode =
1002             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1003         EXPECT_TRUE(SelectedCode);
1004         EXPECT_EQ(SelectedCode->size(), 1u);
1005         EXPECT_TRUE(isa<ObjCStringLiteral>((*SelectedCode)[0]));
1006       },
1007       SelectionFinderVisitor::Lang_OBJC);
1008 }
1009 
1010 TEST(ASTSelectionFinder, CanonicalizeMemberCalleeToCall) {
1011   StringRef Source = R"(
1012 class AClass { public:
1013   void method();
1014   int afield;
1015   void selectWholeCallWhenJustMethodSelected(int &i) {
1016     method();
1017   }
1018 };
1019 void selectWholeCallWhenJustMethodSelected() {
1020   AClass a;
1021   a.method();
1022 }
1023 void dontSelectArgument(AClass &a) {
1024   a.selectWholeCallWhenJustMethodSelected(a.afield);
1025 }
1026      )";
1027   // Just 'method' with implicit 'this':
1028   findSelectedASTNodesWithRange(
1029       Source, {6, 5}, FileRange{{6, 5}, {6, 11}},
1030       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1031         EXPECT_TRUE(Node);
1032         std::optional<CodeRangeASTSelection> SelectedCode =
1033             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1034         EXPECT_TRUE(SelectedCode);
1035         EXPECT_EQ(SelectedCode->size(), 1u);
1036         EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1037       });
1038   // Just 'method':
1039   findSelectedASTNodesWithRange(
1040       Source, {11, 5}, FileRange{{11, 5}, {11, 11}},
1041       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1042         EXPECT_TRUE(Node);
1043         std::optional<CodeRangeASTSelection> SelectedCode =
1044             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1045         EXPECT_TRUE(SelectedCode);
1046         EXPECT_EQ(SelectedCode->size(), 1u);
1047         EXPECT_TRUE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1048       });
1049   // Just 'afield', which should not select the call.
1050   findSelectedASTNodesWithRange(
1051       Source, {14, 5}, FileRange{{14, 45}, {14, 51}},
1052       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1053         EXPECT_TRUE(Node);
1054         std::optional<CodeRangeASTSelection> SelectedCode =
1055             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1056         EXPECT_TRUE(SelectedCode);
1057         EXPECT_EQ(SelectedCode->size(), 1u);
1058         EXPECT_FALSE(isa<CXXMemberCallExpr>((*SelectedCode)[0]));
1059       });
1060 }
1061 
1062 TEST(ASTSelectionFinder, CanonicalizeFuncCalleeToCall) {
1063   StringRef Source = R"(
1064 void function();
1065 
1066 void test() {
1067   function();
1068 }
1069      )";
1070   // Just 'function':
1071   findSelectedASTNodesWithRange(
1072       Source, {5, 3}, FileRange{{5, 3}, {5, 11}},
1073       [](SourceRange SelectionRange, std::optional<SelectedASTNode> Node) {
1074         EXPECT_TRUE(Node);
1075         Node->dump();
1076         std::optional<CodeRangeASTSelection> SelectedCode =
1077             CodeRangeASTSelection::create(SelectionRange, std::move(*Node));
1078         EXPECT_TRUE(SelectedCode);
1079         EXPECT_EQ(SelectedCode->size(), 1u);
1080         EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0]));
1081         EXPECT_TRUE(isa<CompoundStmt>(
1082             SelectedCode->getParents()[SelectedCode->getParents().size() - 1]
1083                 .get()
1084                 .Node.get<Stmt>()));
1085       });
1086 }
1087 
1088 } // end anonymous namespace
1089