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