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(SourceRange SelectionRange, 33 Optional<SelectedASTNode>)> 34 Consumer; 35 36 public: 37 SelectionFinderVisitor(FileLocation Location, 38 Optional<FileRange> SelectionRange, 39 llvm::function_ref<void(SourceRange SelectionRange, 40 Optional<SelectedASTNode>)> 41 Consumer) 42 : Location(Location), SelectionRange(SelectionRange), Consumer(Consumer) { 43 } 44 45 bool VisitTranslationUnitDecl(const TranslationUnitDecl *TU) { 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, Optional<FileRange> SelectionRange, 69 llvm::function_ref<void(SourceRange SelectionRange, 70 Optional<SelectedASTNode>)> 71 Consumer, 72 SelectionFinderVisitor::Language Language = 73 SelectionFinderVisitor::Lang_CXX11) { 74 SelectionFinderVisitor Visitor(Location, SelectionRange, Consumer); 75 EXPECT_TRUE(Visitor.runOver(Source, Language)); 76 } 77 78 void findSelectedASTNodes( 79 StringRef Source, FileLocation Location, Optional<FileRange> SelectionRange, 80 llvm::function_ref<void(Optional<SelectedASTNode>)> Consumer, 81 SelectionFinderVisitor::Language Language = 82 SelectionFinderVisitor::Lang_CXX11) { 83 findSelectedASTNodesWithRange( 84 Source, Location, SelectionRange, 85 [&](SourceRange, Optional<SelectedASTNode> Selection) { 86 Consumer(std::move(Selection)); 87 }, 88 Language); 89 } 90 91 void checkNodeImpl(bool IsTypeMatched, const SelectedASTNode &Node, 92 SourceSelectionKind SelectionKind, unsigned NumChildren) { 93 ASSERT_TRUE(IsTypeMatched); 94 EXPECT_EQ(Node.Children.size(), NumChildren); 95 ASSERT_EQ(Node.SelectionKind, SelectionKind); 96 } 97 98 void checkDeclName(const SelectedASTNode &Node, StringRef Name) { 99 const auto *ND = Node.Node.get<NamedDecl>(); 100 EXPECT_TRUE(!!ND); 101 ASSERT_EQ(ND->getName(), Name); 102 } 103 104 template <typename T> 105 const SelectedASTNode & 106 checkNode(const SelectedASTNode &StmtNode, SourceSelectionKind SelectionKind, 107 unsigned NumChildren = 0, 108 typename std::enable_if<std::is_base_of<Stmt, T>::value, T>::type 109 *StmtOverloadChecker = nullptr) { 110 checkNodeImpl(isa<T>(StmtNode.Node.get<Stmt>()), StmtNode, SelectionKind, 111 NumChildren); 112 return StmtNode; 113 } 114 115 template <typename T> 116 const SelectedASTNode & 117 checkNode(const SelectedASTNode &DeclNode, SourceSelectionKind SelectionKind, 118 unsigned NumChildren = 0, StringRef Name = "", 119 typename std::enable_if<std::is_base_of<Decl, T>::value, T>::type 120 *DeclOverloadChecker = nullptr) { 121 checkNodeImpl(isa<T>(DeclNode.Node.get<Decl>()), DeclNode, SelectionKind, 122 NumChildren); 123 if (!Name.empty()) 124 checkDeclName(DeclNode, Name); 125 return DeclNode; 126 } 127 128 struct ForAllChildrenOf { 129 const SelectedASTNode &Node; 130 131 static void childKindVerifier(const SelectedASTNode &Node, 132 SourceSelectionKind SelectionKind) { 133 for (const SelectedASTNode &Child : Node.Children) { 134 ASSERT_EQ(Node.SelectionKind, SelectionKind); 135 childKindVerifier(Child, SelectionKind); 136 } 137 } 138 139 public: 140 ForAllChildrenOf(const SelectedASTNode &Node) : Node(Node) {} 141 142 void shouldHaveSelectionKind(SourceSelectionKind Kind) { 143 childKindVerifier(Node, Kind); 144 } 145 }; 146 147 ForAllChildrenOf allChildrenOf(const SelectedASTNode &Node) { 148 return ForAllChildrenOf(Node); 149 } 150 151 TEST(ASTSelectionFinder, CursorNoSelection) { 152 findSelectedASTNodes( 153 " void f() { }", {1, 1}, None, 154 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); }); 155 } 156 157 TEST(ASTSelectionFinder, CursorAtStartOfFunction) { 158 findSelectedASTNodes( 159 "void f() { }", {1, 1}, None, [](Optional<SelectedASTNode> Node) { 160 EXPECT_TRUE(Node); 161 checkNode<TranslationUnitDecl>(*Node, SourceSelectionKind::None, 162 /*NumChildren=*/1); 163 checkNode<FunctionDecl>(Node->Children[0], 164 SourceSelectionKind::ContainsSelection, 165 /*NumChildren=*/0, /*Name=*/"f"); 166 167 // Check that the dumping works. 168 std::string DumpValue; 169 llvm::raw_string_ostream OS(DumpValue); 170 Node->Children[0].dump(OS); 171 ASSERT_EQ(OS.str(), "FunctionDecl \"f\" contains-selection\n"); 172 }); 173 } 174 175 TEST(ASTSelectionFinder, RangeNoSelection) { 176 findSelectedASTNodes( 177 " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}}, 178 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); }); 179 findSelectedASTNodes( 180 " void f() { }", {1, 1}, FileRange{{1, 1}, {1, 2}}, 181 [](Optional<SelectedASTNode> Node) { EXPECT_FALSE(Node); }); 182 } 183 184 TEST(ASTSelectionFinder, EmptyRangeFallbackToCursor) { 185 findSelectedASTNodes("void f() { }", {1, 1}, FileRange{{1, 1}, {1, 1}}, 186 [](Optional<SelectedASTNode> Node) { 187 EXPECT_TRUE(Node); 188 checkNode<FunctionDecl>( 189 Node->Children[0], 190 SourceSelectionKind::ContainsSelection, 191 /*NumChildren=*/0, /*Name=*/"f"); 192 }); 193 } 194 195 TEST(ASTSelectionFinder, WholeFunctionSelection) { 196 StringRef Source = "int f(int x) { return x;\n}\nvoid f2() { }"; 197 // From 'int' until just after '}': 198 199 findSelectedASTNodes( 200 Source, {1, 1}, FileRange{{1, 1}, {2, 2}}, 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 checkNode<ParmVarDecl>(Fn.Children[0], 208 SourceSelectionKind::InsideSelection); 209 const auto &Body = checkNode<CompoundStmt>( 210 Fn.Children[1], SourceSelectionKind::InsideSelection, 211 /*NumChildren=*/1); 212 const auto &Return = checkNode<ReturnStmt>( 213 Body.Children[0], SourceSelectionKind::InsideSelection, 214 /*NumChildren=*/1); 215 checkNode<ImplicitCastExpr>(Return.Children[0], 216 SourceSelectionKind::InsideSelection, 217 /*NumChildren=*/1); 218 checkNode<DeclRefExpr>(Return.Children[0].Children[0], 219 SourceSelectionKind::InsideSelection); 220 }); 221 222 // From 'int' until just before '}': 223 findSelectedASTNodes( 224 Source, {2, 1}, FileRange{{1, 1}, {2, 1}}, 225 [](Optional<SelectedASTNode> Node) { 226 EXPECT_TRUE(Node); 227 EXPECT_EQ(Node->Children.size(), 1u); 228 const auto &Fn = checkNode<FunctionDecl>( 229 Node->Children[0], SourceSelectionKind::ContainsSelection, 230 /*NumChildren=*/2, /*Name=*/"f"); 231 const auto &Body = checkNode<CompoundStmt>( 232 Fn.Children[1], SourceSelectionKind::ContainsSelectionEnd, 233 /*NumChildren=*/1); 234 checkNode<ReturnStmt>(Body.Children[0], 235 SourceSelectionKind::InsideSelection, 236 /*NumChildren=*/1); 237 }); 238 // From '{' until just after '}': 239 findSelectedASTNodes( 240 Source, {1, 14}, FileRange{{1, 14}, {2, 2}}, 241 [](Optional<SelectedASTNode> Node) { 242 EXPECT_TRUE(Node); 243 EXPECT_EQ(Node->Children.size(), 1u); 244 const auto &Fn = checkNode<FunctionDecl>( 245 Node->Children[0], SourceSelectionKind::ContainsSelection, 246 /*NumChildren=*/1, /*Name=*/"f"); 247 const auto &Body = checkNode<CompoundStmt>( 248 Fn.Children[0], SourceSelectionKind::ContainsSelection, 249 /*NumChildren=*/1); 250 checkNode<ReturnStmt>(Body.Children[0], 251 SourceSelectionKind::InsideSelection, 252 /*NumChildren=*/1); 253 }); 254 // From 'x' until just after '}': 255 findSelectedASTNodes( 256 Source, {2, 2}, FileRange{{1, 11}, {2, 2}}, 257 [](Optional<SelectedASTNode> Node) { 258 EXPECT_TRUE(Node); 259 EXPECT_EQ(Node->Children.size(), 1u); 260 const auto &Fn = checkNode<FunctionDecl>( 261 Node->Children[0], SourceSelectionKind::ContainsSelection, 262 /*NumChildren=*/2, /*Name=*/"f"); 263 checkNode<ParmVarDecl>(Fn.Children[0], 264 SourceSelectionKind::ContainsSelectionStart); 265 const auto &Body = checkNode<CompoundStmt>( 266 Fn.Children[1], SourceSelectionKind::InsideSelection, 267 /*NumChildren=*/1); 268 checkNode<ReturnStmt>(Body.Children[0], 269 SourceSelectionKind::InsideSelection, 270 /*NumChildren=*/1); 271 }); 272 } 273 274 TEST(ASTSelectionFinder, MultipleFunctionSelection) { 275 StringRef Source = R"(void f0() { 276 } 277 void f1() { } 278 void f2() { } 279 void f3() { } 280 )"; 281 auto SelectedF1F2 = [](Optional<SelectedASTNode> Node) { 282 EXPECT_TRUE(Node); 283 EXPECT_EQ(Node->Children.size(), 2u); 284 checkNode<FunctionDecl>(Node->Children[0], 285 SourceSelectionKind::InsideSelection, 286 /*NumChildren=*/1, /*Name=*/"f1"); 287 checkNode<FunctionDecl>(Node->Children[1], 288 SourceSelectionKind::InsideSelection, 289 /*NumChildren=*/1, /*Name=*/"f2"); 290 }; 291 // Just after '}' of f0 and just before 'void' of f3: 292 findSelectedASTNodes(Source, {2, 2}, FileRange{{2, 2}, {5, 1}}, SelectedF1F2); 293 // Just before 'void' of f1 and just after '}' of f2: 294 findSelectedASTNodes(Source, {3, 1}, FileRange{{3, 1}, {4, 14}}, 295 SelectedF1F2); 296 } 297 298 TEST(ASTSelectionFinder, MultipleStatementSelection) { 299 StringRef Source = R"(void f(int x, int y) { 300 int z = x; 301 f(2, 3); 302 if (x == 0) { 303 return; 304 } 305 x = 1; 306 return; 307 })"; 308 // From 'f(2,3)' until just before 'x = 1;': 309 findSelectedASTNodes( 310 Source, {3, 2}, FileRange{{3, 2}, {7, 1}}, 311 [](Optional<SelectedASTNode> Node) { 312 EXPECT_TRUE(Node); 313 EXPECT_EQ(Node->Children.size(), 1u); 314 const auto &Fn = checkNode<FunctionDecl>( 315 Node->Children[0], SourceSelectionKind::ContainsSelection, 316 /*NumChildren=*/1, /*Name=*/"f"); 317 const auto &Body = checkNode<CompoundStmt>( 318 Fn.Children[0], SourceSelectionKind::ContainsSelection, 319 /*NumChildren=*/2); 320 allChildrenOf(checkNode<CallExpr>(Body.Children[0], 321 SourceSelectionKind::InsideSelection, 322 /*NumChildren=*/3)) 323 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection); 324 allChildrenOf(checkNode<IfStmt>(Body.Children[1], 325 SourceSelectionKind::InsideSelection, 326 /*NumChildren=*/2)) 327 .shouldHaveSelectionKind(SourceSelectionKind::InsideSelection); 328 }); 329 // From 'f(2,3)' until just before ';' in 'x = 1;': 330 findSelectedASTNodes( 331 Source, {3, 2}, FileRange{{3, 2}, {7, 8}}, 332 [](Optional<SelectedASTNode> Node) { 333 EXPECT_TRUE(Node); 334 EXPECT_EQ(Node->Children.size(), 1u); 335 const auto &Fn = checkNode<FunctionDecl>( 336 Node->Children[0], SourceSelectionKind::ContainsSelection, 337 /*NumChildren=*/1, /*Name=*/"f"); 338 const auto &Body = checkNode<CompoundStmt>( 339 Fn.Children[0], SourceSelectionKind::ContainsSelection, 340 /*NumChildren=*/3); 341 checkNode<CallExpr>(Body.Children[0], 342 SourceSelectionKind::InsideSelection, 343 /*NumChildren=*/3); 344 checkNode<IfStmt>(Body.Children[1], 345 SourceSelectionKind::InsideSelection, 346 /*NumChildren=*/2); 347 checkNode<BinaryOperator>(Body.Children[2], 348 SourceSelectionKind::InsideSelection, 349 /*NumChildren=*/2); 350 }); 351 // From the middle of 'int z = 3' until the middle of 'x = 1;': 352 findSelectedASTNodes( 353 Source, {2, 10}, FileRange{{2, 10}, {7, 5}}, 354 [](Optional<SelectedASTNode> Node) { 355 EXPECT_TRUE(Node); 356 EXPECT_EQ(Node->Children.size(), 1u); 357 const auto &Fn = checkNode<FunctionDecl>( 358 Node->Children[0], SourceSelectionKind::ContainsSelection, 359 /*NumChildren=*/1, /*Name=*/"f"); 360 const auto &Body = checkNode<CompoundStmt>( 361 Fn.Children[0], SourceSelectionKind::ContainsSelection, 362 /*NumChildren=*/4); 363 checkNode<DeclStmt>(Body.Children[0], 364 SourceSelectionKind::ContainsSelectionStart, 365 /*NumChildren=*/1); 366 checkNode<CallExpr>(Body.Children[1], 367 SourceSelectionKind::InsideSelection, 368 /*NumChildren=*/3); 369 checkNode<IfStmt>(Body.Children[2], 370 SourceSelectionKind::InsideSelection, 371 /*NumChildren=*/2); 372 checkNode<BinaryOperator>(Body.Children[3], 373 SourceSelectionKind::ContainsSelectionEnd, 374 /*NumChildren=*/1); 375 }); 376 } 377 378 TEST(ASTSelectionFinder, SelectionInFunctionInObjCImplementation) { 379 StringRef Source = R"( 380 @interface I 381 @end 382 @implementation I 383 384 int notSelected() { } 385 386 int selected(int x) { 387 return x; 388 } 389 390 @end 391 @implementation I(Cat) 392 393 void catF() { } 394 395 @end 396 397 void outerFunction() { } 398 )"; 399 // Just the 'x' expression in 'selected': 400 findSelectedASTNodes( 401 Source, {9, 10}, FileRange{{9, 10}, {9, 11}}, 402 [](Optional<SelectedASTNode> Node) { 403 EXPECT_TRUE(Node); 404 EXPECT_EQ(Node->Children.size(), 1u); 405 const auto &Impl = checkNode<ObjCImplementationDecl>( 406 Node->Children[0], SourceSelectionKind::ContainsSelection, 407 /*NumChildren=*/1, /*Name=*/"I"); 408 const auto &Fn = checkNode<FunctionDecl>( 409 Impl.Children[0], SourceSelectionKind::ContainsSelection, 410 /*NumChildren=*/1, /*Name=*/"selected"); 411 allChildrenOf(Fn).shouldHaveSelectionKind( 412 SourceSelectionKind::ContainsSelection); 413 }, 414 SelectionFinderVisitor::Lang_OBJC); 415 // The entire 'catF': 416 findSelectedASTNodes( 417 Source, {15, 1}, FileRange{{15, 1}, {15, 16}}, 418 [](Optional<SelectedASTNode> Node) { 419 EXPECT_TRUE(Node); 420 EXPECT_EQ(Node->Children.size(), 1u); 421 const auto &Impl = checkNode<ObjCCategoryImplDecl>( 422 Node->Children[0], SourceSelectionKind::ContainsSelection, 423 /*NumChildren=*/1, /*Name=*/"Cat"); 424 const auto &Fn = checkNode<FunctionDecl>( 425 Impl.Children[0], SourceSelectionKind::ContainsSelection, 426 /*NumChildren=*/1, /*Name=*/"catF"); 427 allChildrenOf(Fn).shouldHaveSelectionKind( 428 SourceSelectionKind::ContainsSelection); 429 }, 430 SelectionFinderVisitor::Lang_OBJC); 431 // From the line before 'selected' to the line after 'catF': 432 findSelectedASTNodes( 433 Source, {16, 1}, FileRange{{7, 1}, {16, 1}}, 434 [](Optional<SelectedASTNode> Node) { 435 EXPECT_TRUE(Node); 436 EXPECT_EQ(Node->Children.size(), 2u); 437 const auto &Impl = checkNode<ObjCImplementationDecl>( 438 Node->Children[0], SourceSelectionKind::ContainsSelectionStart, 439 /*NumChildren=*/1, /*Name=*/"I"); 440 const auto &Selected = checkNode<FunctionDecl>( 441 Impl.Children[0], SourceSelectionKind::InsideSelection, 442 /*NumChildren=*/2, /*Name=*/"selected"); 443 allChildrenOf(Selected).shouldHaveSelectionKind( 444 SourceSelectionKind::InsideSelection); 445 const auto &Cat = checkNode<ObjCCategoryImplDecl>( 446 Node->Children[1], SourceSelectionKind::ContainsSelectionEnd, 447 /*NumChildren=*/1, /*Name=*/"Cat"); 448 const auto &CatF = checkNode<FunctionDecl>( 449 Cat.Children[0], SourceSelectionKind::InsideSelection, 450 /*NumChildren=*/1, /*Name=*/"catF"); 451 allChildrenOf(CatF).shouldHaveSelectionKind( 452 SourceSelectionKind::InsideSelection); 453 }, 454 SelectionFinderVisitor::Lang_OBJC); 455 // Just the 'outer' function: 456 findSelectedASTNodes(Source, {19, 1}, FileRange{{19, 1}, {19, 25}}, 457 [](Optional<SelectedASTNode> Node) { 458 EXPECT_TRUE(Node); 459 EXPECT_EQ(Node->Children.size(), 1u); 460 checkNode<FunctionDecl>( 461 Node->Children[0], 462 SourceSelectionKind::ContainsSelection, 463 /*NumChildren=*/1, /*Name=*/"outerFunction"); 464 }, 465 SelectionFinderVisitor::Lang_OBJC); 466 } 467 468 TEST(ASTSelectionFinder, FunctionInObjCImplementationCarefulWithEarlyExit) { 469 StringRef Source = R"( 470 @interface I 471 @end 472 @implementation I 473 474 void selected() { 475 } 476 477 - (void) method { } 478 479 @end 480 )"; 481 // Just 'selected' 482 findSelectedASTNodes( 483 Source, {6, 1}, FileRange{{6, 1}, {7, 2}}, 484 [](Optional<SelectedASTNode> Node) { 485 EXPECT_TRUE(Node); 486 EXPECT_EQ(Node->Children.size(), 1u); 487 const auto &Impl = checkNode<ObjCImplementationDecl>( 488 Node->Children[0], SourceSelectionKind::ContainsSelection, 489 /*NumChildren=*/1, /*Name=*/"I"); 490 checkNode<FunctionDecl>(Impl.Children[0], 491 SourceSelectionKind::ContainsSelection, 492 /*NumChildren=*/1, /*Name=*/"selected"); 493 }, 494 SelectionFinderVisitor::Lang_OBJC); 495 } 496 497 TEST(ASTSelectionFinder, AvoidImplicitDeclarations) { 498 StringRef Source = R"( 499 struct Copy { 500 int x; 501 }; 502 void foo() { 503 Copy x; 504 Copy y = x; 505 } 506 )"; 507 // The entire struct 'Copy': 508 findSelectedASTNodes( 509 Source, {2, 1}, FileRange{{2, 1}, {4, 3}}, 510 [](Optional<SelectedASTNode> Node) { 511 EXPECT_TRUE(Node); 512 EXPECT_EQ(Node->Children.size(), 1u); 513 const auto &Record = checkNode<CXXRecordDecl>( 514 Node->Children[0], SourceSelectionKind::InsideSelection, 515 /*NumChildren=*/1, /*Name=*/"Copy"); 516 checkNode<FieldDecl>(Record.Children[0], 517 SourceSelectionKind::InsideSelection); 518 }); 519 } 520 521 TEST(ASTSelectionFinder, CorrectEndForObjectiveCImplementation) { 522 StringRef Source = R"( 523 @interface I 524 @end 525 @implementation I 526 @ end 527 )"; 528 // Just after '@ end' 529 findSelectedASTNodes(Source, {5, 6}, None, 530 [](Optional<SelectedASTNode> Node) { 531 EXPECT_TRUE(Node); 532 EXPECT_EQ(Node->Children.size(), 1u); 533 checkNode<ObjCImplementationDecl>( 534 Node->Children[0], 535 SourceSelectionKind::ContainsSelection); 536 }, 537 SelectionFinderVisitor::Lang_OBJC); 538 } 539 540 const SelectedASTNode &checkFnBody(const Optional<SelectedASTNode> &Node, 541 StringRef Name) { 542 EXPECT_TRUE(Node); 543 EXPECT_EQ(Node->Children.size(), 1u); 544 const auto &Fn = checkNode<FunctionDecl>( 545 Node->Children[0], SourceSelectionKind::ContainsSelection, 546 /*NumChildren=*/1, Name); 547 return checkNode<CompoundStmt>(Fn.Children[0], 548 SourceSelectionKind::ContainsSelection, 549 /*NumChildren=*/1); 550 } 551 552 TEST(ASTSelectionFinder, SelectObjectiveCPseudoObjectExprs) { 553 StringRef Source = R"( 554 @interface I 555 @property(readwrite) int prop; 556 @end 557 void selectProp(I *i) { 558 (void)i.prop; 559 i.prop = 21; 560 } 561 562 563 @interface NSMutableArray 564 - (id)objectAtIndexedSubscript:(unsigned int)index; 565 - (void)setObject:(id)object atIndexedSubscript:(unsigned int)index; 566 @end 567 568 void selectSubscript(NSMutableArray *array, I *i) { 569 (void)array[10]; 570 array[i.prop] = i; 571 } 572 )"; 573 // Just 'i.prop'. 574 findSelectedASTNodes( 575 Source, {6, 7}, FileRange{{6, 7}, {6, 13}}, 576 [](Optional<SelectedASTNode> Node) { 577 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp"); 578 const auto &CCast = checkNode<CStyleCastExpr>( 579 CS.Children[0], SourceSelectionKind::ContainsSelection, 580 /*NumChildren=*/1); 581 const auto &POE = checkNode<PseudoObjectExpr>( 582 CCast.Children[0], SourceSelectionKind::ContainsSelection, 583 /*NumChildren=*/1); 584 const auto &PRE = checkNode<ObjCPropertyRefExpr>( 585 POE.Children[0], SourceSelectionKind::ContainsSelection, 586 /*NumChildren=*/1); 587 const auto &Cast = checkNode<ImplicitCastExpr>( 588 PRE.Children[0], SourceSelectionKind::InsideSelection, 589 /*NumChildren=*/1); 590 checkNode<DeclRefExpr>(Cast.Children[0], 591 SourceSelectionKind::InsideSelection); 592 }, 593 SelectionFinderVisitor::Lang_OBJC); 594 // Just 'i.prop = 21' 595 findSelectedASTNodes( 596 Source, {7, 1}, FileRange{{7, 1}, {7, 12}}, 597 [](Optional<SelectedASTNode> Node) { 598 const auto &CS = checkFnBody(Node, /*Name=*/"selectProp"); 599 const auto &POE = checkNode<PseudoObjectExpr>( 600 CS.Children[0], SourceSelectionKind::ContainsSelection, 601 /*NumChildren=*/1); 602 const auto &BinOp = checkNode<BinaryOperator>( 603 POE.Children[0], SourceSelectionKind::ContainsSelection, 604 /*NumChildren=*/2); 605 const auto &PRE = checkNode<ObjCPropertyRefExpr>( 606 BinOp.Children[0], SourceSelectionKind::InsideSelection, 607 /*NumChildren=*/1); 608 const auto &Cast = checkNode<ImplicitCastExpr>( 609 PRE.Children[0], SourceSelectionKind::InsideSelection, 610 /*NumChildren=*/1); 611 checkNode<DeclRefExpr>(Cast.Children[0], 612 SourceSelectionKind::InsideSelection); 613 checkNode<IntegerLiteral>(BinOp.Children[1], 614 SourceSelectionKind::InsideSelection); 615 }, 616 SelectionFinderVisitor::Lang_OBJC); 617 // Just 'array[10]' 618 findSelectedASTNodes( 619 Source, {17, 9}, FileRange{{17, 9}, {17, 18}}, 620 [](Optional<SelectedASTNode> Node) { 621 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript"); 622 const auto &CCast = checkNode<CStyleCastExpr>( 623 CS.Children[0], SourceSelectionKind::ContainsSelection, 624 /*NumChildren=*/1); 625 const auto &POE = checkNode<PseudoObjectExpr>( 626 CCast.Children[0], SourceSelectionKind::ContainsSelection, 627 /*NumChildren=*/1); 628 const auto &SRE = checkNode<ObjCSubscriptRefExpr>( 629 POE.Children[0], SourceSelectionKind::ContainsSelection, 630 /*NumChildren=*/2); 631 const auto &Cast = checkNode<ImplicitCastExpr>( 632 SRE.Children[0], SourceSelectionKind::InsideSelection, 633 /*NumChildren=*/1); 634 checkNode<DeclRefExpr>(Cast.Children[0], 635 SourceSelectionKind::InsideSelection); 636 checkNode<IntegerLiteral>(SRE.Children[1], 637 SourceSelectionKind::InsideSelection); 638 }, 639 SelectionFinderVisitor::Lang_OBJC); 640 // Just 'array[i.prop] = array' 641 findSelectedASTNodes( 642 Source, {18, 3}, FileRange{{18, 3}, {18, 20}}, 643 [](Optional<SelectedASTNode> Node) { 644 const auto &CS = checkFnBody(Node, /*Name=*/"selectSubscript"); 645 const auto &POE = checkNode<PseudoObjectExpr>( 646 CS.Children[0], SourceSelectionKind::ContainsSelection, 647 /*NumChildren=*/1); 648 const auto &BinOp = checkNode<BinaryOperator>( 649 POE.Children[0], SourceSelectionKind::ContainsSelection, 650 /*NumChildren=*/2); 651 const auto &SRE = checkNode<ObjCSubscriptRefExpr>( 652 BinOp.Children[0], SourceSelectionKind::InsideSelection, 653 /*NumChildren=*/2); 654 const auto &Cast = checkNode<ImplicitCastExpr>( 655 SRE.Children[0], SourceSelectionKind::InsideSelection, 656 /*NumChildren=*/1); 657 checkNode<DeclRefExpr>(Cast.Children[0], 658 SourceSelectionKind::InsideSelection); 659 const auto &POE2 = checkNode<PseudoObjectExpr>( 660 SRE.Children[1], SourceSelectionKind::InsideSelection, 661 /*NumChildren=*/1); 662 const auto &PRE = checkNode<ObjCPropertyRefExpr>( 663 POE2.Children[0], SourceSelectionKind::InsideSelection, 664 /*NumChildren=*/1); 665 const auto &Cast2 = checkNode<ImplicitCastExpr>( 666 PRE.Children[0], SourceSelectionKind::InsideSelection, 667 /*NumChildren=*/1); 668 checkNode<DeclRefExpr>(Cast2.Children[0], 669 SourceSelectionKind::InsideSelection); 670 checkNode<DeclRefExpr>(BinOp.Children[1], 671 SourceSelectionKind::InsideSelection); 672 }, 673 SelectionFinderVisitor::Lang_OBJC); 674 } 675 676 TEST(ASTSelectionFinder, SimpleCodeRangeASTSelection) { 677 StringRef Source = R"(void f(int x, int y) { 678 int z = x; 679 f(2, 3); 680 if (x == 0) { 681 return; 682 } 683 x = 1; 684 return; 685 } 686 void f2() { 687 int m = 0; 688 } 689 )"; 690 // No selection range. 691 findSelectedASTNodesWithRange( 692 Source, {2, 2}, None, 693 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 694 EXPECT_TRUE(Node); 695 Optional<CodeRangeASTSelection> SelectedCode = 696 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 697 EXPECT_FALSE(SelectedCode); 698 }); 699 findSelectedASTNodesWithRange( 700 Source, {2, 2}, FileRange{{2, 2}, {2, 2}}, 701 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 702 EXPECT_TRUE(Node); 703 Optional<CodeRangeASTSelection> SelectedCode = 704 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 705 EXPECT_FALSE(SelectedCode); 706 }); 707 // Range that spans multiple functions is an invalid code range. 708 findSelectedASTNodesWithRange( 709 Source, {2, 2}, FileRange{{7, 2}, {12, 1}}, 710 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 711 EXPECT_TRUE(Node); 712 Optional<CodeRangeASTSelection> SelectedCode = 713 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 714 EXPECT_FALSE(SelectedCode); 715 }); 716 // Just 'z = x;': 717 findSelectedASTNodesWithRange( 718 Source, {2, 2}, FileRange{{2, 2}, {2, 13}}, 719 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 720 EXPECT_TRUE(Node); 721 Optional<CodeRangeASTSelection> SelectedCode = 722 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 723 EXPECT_TRUE(SelectedCode); 724 EXPECT_EQ(SelectedCode->size(), 1u); 725 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0])); 726 ArrayRef<SelectedASTNode::ReferenceType> Parents = 727 SelectedCode->getParents(); 728 EXPECT_EQ(Parents.size(), 3u); 729 EXPECT_TRUE( 730 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>())); 731 // Function 'f' definition. 732 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>())); 733 // Function body of function 'F'. 734 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>())); 735 }); 736 // From 'f(2,3)' until just before 'x = 1;': 737 findSelectedASTNodesWithRange( 738 Source, {3, 2}, FileRange{{3, 2}, {7, 1}}, 739 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 740 EXPECT_TRUE(Node); 741 Optional<CodeRangeASTSelection> SelectedCode = 742 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 743 EXPECT_TRUE(SelectedCode); 744 EXPECT_EQ(SelectedCode->size(), 2u); 745 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0])); 746 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1])); 747 ArrayRef<SelectedASTNode::ReferenceType> Parents = 748 SelectedCode->getParents(); 749 EXPECT_EQ(Parents.size(), 3u); 750 EXPECT_TRUE( 751 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>())); 752 // Function 'f' definition. 753 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>())); 754 // Function body of function 'F'. 755 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>())); 756 }); 757 // From 'f(2,3)' until just before ';' in 'x = 1;': 758 findSelectedASTNodesWithRange( 759 Source, {3, 2}, FileRange{{3, 2}, {7, 8}}, 760 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 761 EXPECT_TRUE(Node); 762 Optional<CodeRangeASTSelection> SelectedCode = 763 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 764 EXPECT_TRUE(SelectedCode); 765 EXPECT_EQ(SelectedCode->size(), 3u); 766 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[0])); 767 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[1])); 768 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[2])); 769 }); 770 // From the middle of 'int z = 3' until the middle of 'x = 1;': 771 findSelectedASTNodesWithRange( 772 Source, {2, 10}, FileRange{{2, 10}, {7, 5}}, 773 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 774 EXPECT_TRUE(Node); 775 EXPECT_TRUE(Node); 776 Optional<CodeRangeASTSelection> SelectedCode = 777 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 778 EXPECT_TRUE(SelectedCode); 779 EXPECT_EQ(SelectedCode->size(), 4u); 780 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0])); 781 EXPECT_TRUE(isa<CallExpr>((*SelectedCode)[1])); 782 EXPECT_TRUE(isa<IfStmt>((*SelectedCode)[2])); 783 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[3])); 784 }); 785 } 786 787 TEST(ASTSelectionFinder, OutOfBodyCodeRange) { 788 StringRef Source = R"( 789 int codeRange = 2 + 3; 790 )"; 791 // '2+3' expression. 792 findSelectedASTNodesWithRange( 793 Source, {2, 17}, FileRange{{2, 17}, {2, 22}}, 794 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 795 EXPECT_TRUE(Node); 796 Optional<CodeRangeASTSelection> SelectedCode = 797 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 798 EXPECT_TRUE(SelectedCode); 799 EXPECT_EQ(SelectedCode->size(), 1u); 800 EXPECT_TRUE(isa<BinaryOperator>((*SelectedCode)[0])); 801 ArrayRef<SelectedASTNode::ReferenceType> Parents = 802 SelectedCode->getParents(); 803 EXPECT_EQ(Parents.size(), 2u); 804 EXPECT_TRUE( 805 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>())); 806 // Variable 'codeRange'. 807 EXPECT_TRUE(isa<VarDecl>(Parents[1].get().Node.get<Decl>())); 808 }); 809 } 810 811 TEST(ASTSelectionFinder, SelectVarDeclStmt) { 812 StringRef Source = R"( 813 void f() { 814 { 815 int a; 816 } 817 } 818 )"; 819 // 'int a' 820 findSelectedASTNodesWithRange( 821 Source, {4, 8}, FileRange{{4, 8}, {4, 14}}, 822 [](SourceRange SelectionRange, Optional<SelectedASTNode> Node) { 823 EXPECT_TRUE(Node); 824 Optional<CodeRangeASTSelection> SelectedCode = 825 CodeRangeASTSelection::create(SelectionRange, std::move(*Node)); 826 EXPECT_TRUE(SelectedCode); 827 EXPECT_EQ(SelectedCode->size(), 1u); 828 EXPECT_TRUE(isa<DeclStmt>((*SelectedCode)[0])); 829 ArrayRef<SelectedASTNode::ReferenceType> Parents = 830 SelectedCode->getParents(); 831 EXPECT_EQ(Parents.size(), 4u); 832 EXPECT_TRUE( 833 isa<TranslationUnitDecl>(Parents[0].get().Node.get<Decl>())); 834 // Function 'f' definition. 835 EXPECT_TRUE(isa<FunctionDecl>(Parents[1].get().Node.get<Decl>())); 836 // Function body of function 'F'. 837 EXPECT_TRUE(isa<CompoundStmt>(Parents[2].get().Node.get<Stmt>())); 838 // Compound statement in body of 'F'. 839 EXPECT_TRUE(isa<CompoundStmt>(Parents[3].get().Node.get<Stmt>())); 840 }); 841 } 842 843 } // end anonymous namespace 844