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