brintos

brintos / llvm-project-archived public Read only

0
0
Text · 41.7 KiB · 0f60749 Raw
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