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