1*e5dd7070Spatrick //===--- ASTSelection.cpp - Clang refactoring library ---------------------===// 2*e5dd7070Spatrick // 3*e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5*e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*e5dd7070Spatrick // 7*e5dd7070Spatrick //===----------------------------------------------------------------------===// 8*e5dd7070Spatrick 9*e5dd7070Spatrick #include "clang/Tooling/Refactoring/ASTSelection.h" 10*e5dd7070Spatrick #include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h" 11*e5dd7070Spatrick #include "clang/Lex/Lexer.h" 12*e5dd7070Spatrick #include "llvm/Support/SaveAndRestore.h" 13*e5dd7070Spatrick 14*e5dd7070Spatrick using namespace clang; 15*e5dd7070Spatrick using namespace tooling; 16*e5dd7070Spatrick using ast_type_traits::DynTypedNode; 17*e5dd7070Spatrick 18*e5dd7070Spatrick namespace { 19*e5dd7070Spatrick 20*e5dd7070Spatrick CharSourceRange getLexicalDeclRange(Decl *D, const SourceManager &SM, 21*e5dd7070Spatrick const LangOptions &LangOpts) { 22*e5dd7070Spatrick if (!isa<ObjCImplDecl>(D)) 23*e5dd7070Spatrick return CharSourceRange::getTokenRange(D->getSourceRange()); 24*e5dd7070Spatrick // Objective-C implementation declarations end at the '@' instead of the 'end' 25*e5dd7070Spatrick // keyword. Use the lexer to find the location right after 'end'. 26*e5dd7070Spatrick SourceRange R = D->getSourceRange(); 27*e5dd7070Spatrick SourceLocation LocAfterEnd = Lexer::findLocationAfterToken( 28*e5dd7070Spatrick R.getEnd(), tok::raw_identifier, SM, LangOpts, 29*e5dd7070Spatrick /*SkipTrailingWhitespaceAndNewLine=*/false); 30*e5dd7070Spatrick return LocAfterEnd.isValid() 31*e5dd7070Spatrick ? CharSourceRange::getCharRange(R.getBegin(), LocAfterEnd) 32*e5dd7070Spatrick : CharSourceRange::getTokenRange(R); 33*e5dd7070Spatrick } 34*e5dd7070Spatrick 35*e5dd7070Spatrick /// Constructs the tree of selected AST nodes that either contain the location 36*e5dd7070Spatrick /// of the cursor or overlap with the selection range. 37*e5dd7070Spatrick class ASTSelectionFinder 38*e5dd7070Spatrick : public LexicallyOrderedRecursiveASTVisitor<ASTSelectionFinder> { 39*e5dd7070Spatrick public: 40*e5dd7070Spatrick ASTSelectionFinder(SourceRange Selection, FileID TargetFile, 41*e5dd7070Spatrick const ASTContext &Context) 42*e5dd7070Spatrick : LexicallyOrderedRecursiveASTVisitor(Context.getSourceManager()), 43*e5dd7070Spatrick SelectionBegin(Selection.getBegin()), 44*e5dd7070Spatrick SelectionEnd(Selection.getBegin() == Selection.getEnd() 45*e5dd7070Spatrick ? SourceLocation() 46*e5dd7070Spatrick : Selection.getEnd()), 47*e5dd7070Spatrick TargetFile(TargetFile), Context(Context) { 48*e5dd7070Spatrick // The TU decl is the root of the selected node tree. 49*e5dd7070Spatrick SelectionStack.push_back( 50*e5dd7070Spatrick SelectedASTNode(DynTypedNode::create(*Context.getTranslationUnitDecl()), 51*e5dd7070Spatrick SourceSelectionKind::None)); 52*e5dd7070Spatrick } 53*e5dd7070Spatrick 54*e5dd7070Spatrick Optional<SelectedASTNode> getSelectedASTNode() { 55*e5dd7070Spatrick assert(SelectionStack.size() == 1 && "stack was not popped"); 56*e5dd7070Spatrick SelectedASTNode Result = std::move(SelectionStack.back()); 57*e5dd7070Spatrick SelectionStack.pop_back(); 58*e5dd7070Spatrick if (Result.Children.empty()) 59*e5dd7070Spatrick return None; 60*e5dd7070Spatrick return std::move(Result); 61*e5dd7070Spatrick } 62*e5dd7070Spatrick 63*e5dd7070Spatrick bool TraversePseudoObjectExpr(PseudoObjectExpr *E) { 64*e5dd7070Spatrick // Avoid traversing the semantic expressions. They should be handled by 65*e5dd7070Spatrick // looking through the appropriate opaque expressions in order to build 66*e5dd7070Spatrick // a meaningful selection tree. 67*e5dd7070Spatrick llvm::SaveAndRestore<bool> LookThrough(LookThroughOpaqueValueExprs, true); 68*e5dd7070Spatrick return TraverseStmt(E->getSyntacticForm()); 69*e5dd7070Spatrick } 70*e5dd7070Spatrick 71*e5dd7070Spatrick bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) { 72*e5dd7070Spatrick if (!LookThroughOpaqueValueExprs) 73*e5dd7070Spatrick return true; 74*e5dd7070Spatrick llvm::SaveAndRestore<bool> LookThrough(LookThroughOpaqueValueExprs, false); 75*e5dd7070Spatrick return TraverseStmt(E->getSourceExpr()); 76*e5dd7070Spatrick } 77*e5dd7070Spatrick 78*e5dd7070Spatrick bool TraverseDecl(Decl *D) { 79*e5dd7070Spatrick if (isa<TranslationUnitDecl>(D)) 80*e5dd7070Spatrick return LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D); 81*e5dd7070Spatrick if (D->isImplicit()) 82*e5dd7070Spatrick return true; 83*e5dd7070Spatrick 84*e5dd7070Spatrick // Check if this declaration is written in the file of interest. 85*e5dd7070Spatrick const SourceRange DeclRange = D->getSourceRange(); 86*e5dd7070Spatrick const SourceManager &SM = Context.getSourceManager(); 87*e5dd7070Spatrick SourceLocation FileLoc; 88*e5dd7070Spatrick if (DeclRange.getBegin().isMacroID() && !DeclRange.getEnd().isMacroID()) 89*e5dd7070Spatrick FileLoc = DeclRange.getEnd(); 90*e5dd7070Spatrick else 91*e5dd7070Spatrick FileLoc = SM.getSpellingLoc(DeclRange.getBegin()); 92*e5dd7070Spatrick if (SM.getFileID(FileLoc) != TargetFile) 93*e5dd7070Spatrick return true; 94*e5dd7070Spatrick 95*e5dd7070Spatrick SourceSelectionKind SelectionKind = 96*e5dd7070Spatrick selectionKindFor(getLexicalDeclRange(D, SM, Context.getLangOpts())); 97*e5dd7070Spatrick SelectionStack.push_back( 98*e5dd7070Spatrick SelectedASTNode(DynTypedNode::create(*D), SelectionKind)); 99*e5dd7070Spatrick LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D); 100*e5dd7070Spatrick popAndAddToSelectionIfSelected(SelectionKind); 101*e5dd7070Spatrick 102*e5dd7070Spatrick if (DeclRange.getEnd().isValid() && 103*e5dd7070Spatrick SM.isBeforeInTranslationUnit(SelectionEnd.isValid() ? SelectionEnd 104*e5dd7070Spatrick : SelectionBegin, 105*e5dd7070Spatrick DeclRange.getEnd())) { 106*e5dd7070Spatrick // Stop early when we've reached a declaration after the selection. 107*e5dd7070Spatrick return false; 108*e5dd7070Spatrick } 109*e5dd7070Spatrick return true; 110*e5dd7070Spatrick } 111*e5dd7070Spatrick 112*e5dd7070Spatrick bool TraverseStmt(Stmt *S) { 113*e5dd7070Spatrick if (!S) 114*e5dd7070Spatrick return true; 115*e5dd7070Spatrick if (auto *Opaque = dyn_cast<OpaqueValueExpr>(S)) 116*e5dd7070Spatrick return TraverseOpaqueValueExpr(Opaque); 117*e5dd7070Spatrick // Avoid selecting implicit 'this' expressions. 118*e5dd7070Spatrick if (auto *TE = dyn_cast<CXXThisExpr>(S)) { 119*e5dd7070Spatrick if (TE->isImplicit()) 120*e5dd7070Spatrick return true; 121*e5dd7070Spatrick } 122*e5dd7070Spatrick // FIXME (Alex Lorenz): Improve handling for macro locations. 123*e5dd7070Spatrick SourceSelectionKind SelectionKind = 124*e5dd7070Spatrick selectionKindFor(CharSourceRange::getTokenRange(S->getSourceRange())); 125*e5dd7070Spatrick SelectionStack.push_back( 126*e5dd7070Spatrick SelectedASTNode(DynTypedNode::create(*S), SelectionKind)); 127*e5dd7070Spatrick LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S); 128*e5dd7070Spatrick popAndAddToSelectionIfSelected(SelectionKind); 129*e5dd7070Spatrick return true; 130*e5dd7070Spatrick } 131*e5dd7070Spatrick 132*e5dd7070Spatrick private: 133*e5dd7070Spatrick void popAndAddToSelectionIfSelected(SourceSelectionKind SelectionKind) { 134*e5dd7070Spatrick SelectedASTNode Node = std::move(SelectionStack.back()); 135*e5dd7070Spatrick SelectionStack.pop_back(); 136*e5dd7070Spatrick if (SelectionKind != SourceSelectionKind::None || !Node.Children.empty()) 137*e5dd7070Spatrick SelectionStack.back().Children.push_back(std::move(Node)); 138*e5dd7070Spatrick } 139*e5dd7070Spatrick 140*e5dd7070Spatrick SourceSelectionKind selectionKindFor(CharSourceRange Range) { 141*e5dd7070Spatrick SourceLocation End = Range.getEnd(); 142*e5dd7070Spatrick const SourceManager &SM = Context.getSourceManager(); 143*e5dd7070Spatrick if (Range.isTokenRange()) 144*e5dd7070Spatrick End = Lexer::getLocForEndOfToken(End, 0, SM, Context.getLangOpts()); 145*e5dd7070Spatrick if (!SourceLocation::isPairOfFileLocations(Range.getBegin(), End)) 146*e5dd7070Spatrick return SourceSelectionKind::None; 147*e5dd7070Spatrick if (!SelectionEnd.isValid()) { 148*e5dd7070Spatrick // Do a quick check when the selection is of length 0. 149*e5dd7070Spatrick if (SM.isPointWithin(SelectionBegin, Range.getBegin(), End)) 150*e5dd7070Spatrick return SourceSelectionKind::ContainsSelection; 151*e5dd7070Spatrick return SourceSelectionKind::None; 152*e5dd7070Spatrick } 153*e5dd7070Spatrick bool HasStart = SM.isPointWithin(SelectionBegin, Range.getBegin(), End); 154*e5dd7070Spatrick bool HasEnd = SM.isPointWithin(SelectionEnd, Range.getBegin(), End); 155*e5dd7070Spatrick if (HasStart && HasEnd) 156*e5dd7070Spatrick return SourceSelectionKind::ContainsSelection; 157*e5dd7070Spatrick if (SM.isPointWithin(Range.getBegin(), SelectionBegin, SelectionEnd) && 158*e5dd7070Spatrick SM.isPointWithin(End, SelectionBegin, SelectionEnd)) 159*e5dd7070Spatrick return SourceSelectionKind::InsideSelection; 160*e5dd7070Spatrick // Ensure there's at least some overlap with the 'start'/'end' selection 161*e5dd7070Spatrick // types. 162*e5dd7070Spatrick if (HasStart && SelectionBegin != End) 163*e5dd7070Spatrick return SourceSelectionKind::ContainsSelectionStart; 164*e5dd7070Spatrick if (HasEnd && SelectionEnd != Range.getBegin()) 165*e5dd7070Spatrick return SourceSelectionKind::ContainsSelectionEnd; 166*e5dd7070Spatrick 167*e5dd7070Spatrick return SourceSelectionKind::None; 168*e5dd7070Spatrick } 169*e5dd7070Spatrick 170*e5dd7070Spatrick const SourceLocation SelectionBegin, SelectionEnd; 171*e5dd7070Spatrick FileID TargetFile; 172*e5dd7070Spatrick const ASTContext &Context; 173*e5dd7070Spatrick std::vector<SelectedASTNode> SelectionStack; 174*e5dd7070Spatrick /// Controls whether we can traverse through the OpaqueValueExpr. This is 175*e5dd7070Spatrick /// typically enabled during the traversal of syntactic form for 176*e5dd7070Spatrick /// PseudoObjectExprs. 177*e5dd7070Spatrick bool LookThroughOpaqueValueExprs = false; 178*e5dd7070Spatrick }; 179*e5dd7070Spatrick 180*e5dd7070Spatrick } // end anonymous namespace 181*e5dd7070Spatrick 182*e5dd7070Spatrick Optional<SelectedASTNode> 183*e5dd7070Spatrick clang::tooling::findSelectedASTNodes(const ASTContext &Context, 184*e5dd7070Spatrick SourceRange SelectionRange) { 185*e5dd7070Spatrick assert(SelectionRange.isValid() && 186*e5dd7070Spatrick SourceLocation::isPairOfFileLocations(SelectionRange.getBegin(), 187*e5dd7070Spatrick SelectionRange.getEnd()) && 188*e5dd7070Spatrick "Expected a file range"); 189*e5dd7070Spatrick FileID TargetFile = 190*e5dd7070Spatrick Context.getSourceManager().getFileID(SelectionRange.getBegin()); 191*e5dd7070Spatrick assert(Context.getSourceManager().getFileID(SelectionRange.getEnd()) == 192*e5dd7070Spatrick TargetFile && 193*e5dd7070Spatrick "selection range must span one file"); 194*e5dd7070Spatrick 195*e5dd7070Spatrick ASTSelectionFinder Visitor(SelectionRange, TargetFile, Context); 196*e5dd7070Spatrick Visitor.TraverseDecl(Context.getTranslationUnitDecl()); 197*e5dd7070Spatrick return Visitor.getSelectedASTNode(); 198*e5dd7070Spatrick } 199*e5dd7070Spatrick 200*e5dd7070Spatrick static const char *selectionKindToString(SourceSelectionKind Kind) { 201*e5dd7070Spatrick switch (Kind) { 202*e5dd7070Spatrick case SourceSelectionKind::None: 203*e5dd7070Spatrick return "none"; 204*e5dd7070Spatrick case SourceSelectionKind::ContainsSelection: 205*e5dd7070Spatrick return "contains-selection"; 206*e5dd7070Spatrick case SourceSelectionKind::ContainsSelectionStart: 207*e5dd7070Spatrick return "contains-selection-start"; 208*e5dd7070Spatrick case SourceSelectionKind::ContainsSelectionEnd: 209*e5dd7070Spatrick return "contains-selection-end"; 210*e5dd7070Spatrick case SourceSelectionKind::InsideSelection: 211*e5dd7070Spatrick return "inside"; 212*e5dd7070Spatrick } 213*e5dd7070Spatrick llvm_unreachable("invalid selection kind"); 214*e5dd7070Spatrick } 215*e5dd7070Spatrick 216*e5dd7070Spatrick static void dump(const SelectedASTNode &Node, llvm::raw_ostream &OS, 217*e5dd7070Spatrick unsigned Indent = 0) { 218*e5dd7070Spatrick OS.indent(Indent * 2); 219*e5dd7070Spatrick if (const Decl *D = Node.Node.get<Decl>()) { 220*e5dd7070Spatrick OS << D->getDeclKindName() << "Decl"; 221*e5dd7070Spatrick if (const auto *ND = dyn_cast<NamedDecl>(D)) 222*e5dd7070Spatrick OS << " \"" << ND->getNameAsString() << '"'; 223*e5dd7070Spatrick } else if (const Stmt *S = Node.Node.get<Stmt>()) { 224*e5dd7070Spatrick OS << S->getStmtClassName(); 225*e5dd7070Spatrick } 226*e5dd7070Spatrick OS << ' ' << selectionKindToString(Node.SelectionKind) << "\n"; 227*e5dd7070Spatrick for (const auto &Child : Node.Children) 228*e5dd7070Spatrick dump(Child, OS, Indent + 1); 229*e5dd7070Spatrick } 230*e5dd7070Spatrick 231*e5dd7070Spatrick void SelectedASTNode::dump(llvm::raw_ostream &OS) const { ::dump(*this, OS); } 232*e5dd7070Spatrick 233*e5dd7070Spatrick /// Returns true if the given node has any direct children with the following 234*e5dd7070Spatrick /// selection kind. 235*e5dd7070Spatrick /// 236*e5dd7070Spatrick /// Note: The direct children also include children of direct children with the 237*e5dd7070Spatrick /// "None" selection kind. 238*e5dd7070Spatrick static bool hasAnyDirectChildrenWithKind(const SelectedASTNode &Node, 239*e5dd7070Spatrick SourceSelectionKind Kind) { 240*e5dd7070Spatrick assert(Kind != SourceSelectionKind::None && "invalid predicate!"); 241*e5dd7070Spatrick for (const auto &Child : Node.Children) { 242*e5dd7070Spatrick if (Child.SelectionKind == Kind) 243*e5dd7070Spatrick return true; 244*e5dd7070Spatrick if (Child.SelectionKind == SourceSelectionKind::None) 245*e5dd7070Spatrick return hasAnyDirectChildrenWithKind(Child, Kind); 246*e5dd7070Spatrick } 247*e5dd7070Spatrick return false; 248*e5dd7070Spatrick } 249*e5dd7070Spatrick 250*e5dd7070Spatrick namespace { 251*e5dd7070Spatrick struct SelectedNodeWithParents { 252*e5dd7070Spatrick SelectedASTNode::ReferenceType Node; 253*e5dd7070Spatrick llvm::SmallVector<SelectedASTNode::ReferenceType, 8> Parents; 254*e5dd7070Spatrick 255*e5dd7070Spatrick /// Canonicalizes the given selection by selecting different related AST nodes 256*e5dd7070Spatrick /// when it makes sense to do so. 257*e5dd7070Spatrick void canonicalize(); 258*e5dd7070Spatrick }; 259*e5dd7070Spatrick 260*e5dd7070Spatrick enum SelectionCanonicalizationAction { KeepSelection, SelectParent }; 261*e5dd7070Spatrick 262*e5dd7070Spatrick /// Returns the canonicalization action which should be applied to the 263*e5dd7070Spatrick /// selected statement. 264*e5dd7070Spatrick SelectionCanonicalizationAction 265*e5dd7070Spatrick getSelectionCanonizalizationAction(const Stmt *S, const Stmt *Parent) { 266*e5dd7070Spatrick // Select the parent expression when: 267*e5dd7070Spatrick // - The string literal in ObjC string literal is selected, e.g.: 268*e5dd7070Spatrick // @"test" becomes @"test" 269*e5dd7070Spatrick // ~~~~~~ ~~~~~~~ 270*e5dd7070Spatrick if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent)) 271*e5dd7070Spatrick return SelectParent; 272*e5dd7070Spatrick // The entire call should be selected when just the member expression 273*e5dd7070Spatrick // that refers to the method or the decl ref that refers to the function 274*e5dd7070Spatrick // is selected. 275*e5dd7070Spatrick // f.call(args) becomes f.call(args) 276*e5dd7070Spatrick // ~~~~ ~~~~~~~~~~~~ 277*e5dd7070Spatrick // func(args) becomes func(args) 278*e5dd7070Spatrick // ~~~~ ~~~~~~~~~~ 279*e5dd7070Spatrick else if (const auto *CE = dyn_cast<CallExpr>(Parent)) { 280*e5dd7070Spatrick if ((isa<MemberExpr>(S) || isa<DeclRefExpr>(S)) && 281*e5dd7070Spatrick CE->getCallee()->IgnoreImpCasts() == S) 282*e5dd7070Spatrick return SelectParent; 283*e5dd7070Spatrick } 284*e5dd7070Spatrick // FIXME: Syntactic form -> Entire pseudo-object expr. 285*e5dd7070Spatrick return KeepSelection; 286*e5dd7070Spatrick } 287*e5dd7070Spatrick 288*e5dd7070Spatrick } // end anonymous namespace 289*e5dd7070Spatrick 290*e5dd7070Spatrick void SelectedNodeWithParents::canonicalize() { 291*e5dd7070Spatrick const Stmt *S = Node.get().Node.get<Stmt>(); 292*e5dd7070Spatrick assert(S && "non statement selection!"); 293*e5dd7070Spatrick const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>(); 294*e5dd7070Spatrick if (!Parent) 295*e5dd7070Spatrick return; 296*e5dd7070Spatrick 297*e5dd7070Spatrick // Look through the implicit casts in the parents. 298*e5dd7070Spatrick unsigned ParentIndex = 1; 299*e5dd7070Spatrick for (; (ParentIndex + 1) <= Parents.size() && isa<ImplicitCastExpr>(Parent); 300*e5dd7070Spatrick ++ParentIndex) { 301*e5dd7070Spatrick const Stmt *NewParent = 302*e5dd7070Spatrick Parents[Parents.size() - ParentIndex - 1].get().Node.get<Stmt>(); 303*e5dd7070Spatrick if (!NewParent) 304*e5dd7070Spatrick break; 305*e5dd7070Spatrick Parent = NewParent; 306*e5dd7070Spatrick } 307*e5dd7070Spatrick 308*e5dd7070Spatrick switch (getSelectionCanonizalizationAction(S, Parent)) { 309*e5dd7070Spatrick case SelectParent: 310*e5dd7070Spatrick Node = Parents[Parents.size() - ParentIndex]; 311*e5dd7070Spatrick for (; ParentIndex != 0; --ParentIndex) 312*e5dd7070Spatrick Parents.pop_back(); 313*e5dd7070Spatrick break; 314*e5dd7070Spatrick case KeepSelection: 315*e5dd7070Spatrick break; 316*e5dd7070Spatrick } 317*e5dd7070Spatrick } 318*e5dd7070Spatrick 319*e5dd7070Spatrick /// Finds the set of bottom-most selected AST nodes that are in the selection 320*e5dd7070Spatrick /// tree with the specified selection kind. 321*e5dd7070Spatrick /// 322*e5dd7070Spatrick /// For example, given the following selection tree: 323*e5dd7070Spatrick /// 324*e5dd7070Spatrick /// FunctionDecl "f" contains-selection 325*e5dd7070Spatrick /// CompoundStmt contains-selection [#1] 326*e5dd7070Spatrick /// CallExpr inside 327*e5dd7070Spatrick /// ImplicitCastExpr inside 328*e5dd7070Spatrick /// DeclRefExpr inside 329*e5dd7070Spatrick /// IntegerLiteral inside 330*e5dd7070Spatrick /// IntegerLiteral inside 331*e5dd7070Spatrick /// FunctionDecl "f2" contains-selection 332*e5dd7070Spatrick /// CompoundStmt contains-selection [#2] 333*e5dd7070Spatrick /// CallExpr inside 334*e5dd7070Spatrick /// ImplicitCastExpr inside 335*e5dd7070Spatrick /// DeclRefExpr inside 336*e5dd7070Spatrick /// IntegerLiteral inside 337*e5dd7070Spatrick /// IntegerLiteral inside 338*e5dd7070Spatrick /// 339*e5dd7070Spatrick /// This function will find references to nodes #1 and #2 when searching for the 340*e5dd7070Spatrick /// \c ContainsSelection kind. 341*e5dd7070Spatrick static void findDeepestWithKind( 342*e5dd7070Spatrick const SelectedASTNode &ASTSelection, 343*e5dd7070Spatrick llvm::SmallVectorImpl<SelectedNodeWithParents> &MatchingNodes, 344*e5dd7070Spatrick SourceSelectionKind Kind, 345*e5dd7070Spatrick llvm::SmallVectorImpl<SelectedASTNode::ReferenceType> &ParentStack) { 346*e5dd7070Spatrick if (ASTSelection.Node.get<DeclStmt>()) { 347*e5dd7070Spatrick // Select the entire decl stmt when any of its child declarations is the 348*e5dd7070Spatrick // bottom-most. 349*e5dd7070Spatrick for (const auto &Child : ASTSelection.Children) { 350*e5dd7070Spatrick if (!hasAnyDirectChildrenWithKind(Child, Kind)) { 351*e5dd7070Spatrick MatchingNodes.push_back(SelectedNodeWithParents{ 352*e5dd7070Spatrick std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}}); 353*e5dd7070Spatrick return; 354*e5dd7070Spatrick } 355*e5dd7070Spatrick } 356*e5dd7070Spatrick } else { 357*e5dd7070Spatrick if (!hasAnyDirectChildrenWithKind(ASTSelection, Kind)) { 358*e5dd7070Spatrick // This node is the bottom-most. 359*e5dd7070Spatrick MatchingNodes.push_back(SelectedNodeWithParents{ 360*e5dd7070Spatrick std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}}); 361*e5dd7070Spatrick return; 362*e5dd7070Spatrick } 363*e5dd7070Spatrick } 364*e5dd7070Spatrick // Search in the children. 365*e5dd7070Spatrick ParentStack.push_back(std::cref(ASTSelection)); 366*e5dd7070Spatrick for (const auto &Child : ASTSelection.Children) 367*e5dd7070Spatrick findDeepestWithKind(Child, MatchingNodes, Kind, ParentStack); 368*e5dd7070Spatrick ParentStack.pop_back(); 369*e5dd7070Spatrick } 370*e5dd7070Spatrick 371*e5dd7070Spatrick static void findDeepestWithKind( 372*e5dd7070Spatrick const SelectedASTNode &ASTSelection, 373*e5dd7070Spatrick llvm::SmallVectorImpl<SelectedNodeWithParents> &MatchingNodes, 374*e5dd7070Spatrick SourceSelectionKind Kind) { 375*e5dd7070Spatrick llvm::SmallVector<SelectedASTNode::ReferenceType, 16> ParentStack; 376*e5dd7070Spatrick findDeepestWithKind(ASTSelection, MatchingNodes, Kind, ParentStack); 377*e5dd7070Spatrick } 378*e5dd7070Spatrick 379*e5dd7070Spatrick Optional<CodeRangeASTSelection> 380*e5dd7070Spatrick CodeRangeASTSelection::create(SourceRange SelectionRange, 381*e5dd7070Spatrick const SelectedASTNode &ASTSelection) { 382*e5dd7070Spatrick // Code range is selected when the selection range is not empty. 383*e5dd7070Spatrick if (SelectionRange.getBegin() == SelectionRange.getEnd()) 384*e5dd7070Spatrick return None; 385*e5dd7070Spatrick llvm::SmallVector<SelectedNodeWithParents, 4> ContainSelection; 386*e5dd7070Spatrick findDeepestWithKind(ASTSelection, ContainSelection, 387*e5dd7070Spatrick SourceSelectionKind::ContainsSelection); 388*e5dd7070Spatrick // We are looking for a selection in one body of code, so let's focus on 389*e5dd7070Spatrick // one matching result. 390*e5dd7070Spatrick if (ContainSelection.size() != 1) 391*e5dd7070Spatrick return None; 392*e5dd7070Spatrick SelectedNodeWithParents &Selected = ContainSelection[0]; 393*e5dd7070Spatrick if (!Selected.Node.get().Node.get<Stmt>()) 394*e5dd7070Spatrick return None; 395*e5dd7070Spatrick const Stmt *CodeRangeStmt = Selected.Node.get().Node.get<Stmt>(); 396*e5dd7070Spatrick if (!isa<CompoundStmt>(CodeRangeStmt)) { 397*e5dd7070Spatrick Selected.canonicalize(); 398*e5dd7070Spatrick return CodeRangeASTSelection(Selected.Node, Selected.Parents, 399*e5dd7070Spatrick /*AreChildrenSelected=*/false); 400*e5dd7070Spatrick } 401*e5dd7070Spatrick // FIXME (Alex L): First selected SwitchCase means that first case statement. 402*e5dd7070Spatrick // is selected actually 403*e5dd7070Spatrick // (See https://github.com/apple/swift-clang & CompoundStmtRange). 404*e5dd7070Spatrick 405*e5dd7070Spatrick // FIXME (Alex L): Tweak selection rules for compound statements, see: 406*e5dd7070Spatrick // https://github.com/apple/swift-clang/blob/swift-4.1-branch/lib/Tooling/ 407*e5dd7070Spatrick // Refactor/ASTSlice.cpp#L513 408*e5dd7070Spatrick // The user selected multiple statements in a compound statement. 409*e5dd7070Spatrick Selected.Parents.push_back(Selected.Node); 410*e5dd7070Spatrick return CodeRangeASTSelection(Selected.Node, Selected.Parents, 411*e5dd7070Spatrick /*AreChildrenSelected=*/true); 412*e5dd7070Spatrick } 413*e5dd7070Spatrick 414*e5dd7070Spatrick static bool isFunctionLikeDeclaration(const Decl *D) { 415*e5dd7070Spatrick // FIXME (Alex L): Test for BlockDecl. 416*e5dd7070Spatrick return isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D); 417*e5dd7070Spatrick } 418*e5dd7070Spatrick 419*e5dd7070Spatrick bool CodeRangeASTSelection::isInFunctionLikeBodyOfCode() const { 420*e5dd7070Spatrick bool IsPrevCompound = false; 421*e5dd7070Spatrick // Scan through the parents (bottom-to-top) and check if the selection is 422*e5dd7070Spatrick // contained in a compound statement that's a body of a function/method 423*e5dd7070Spatrick // declaration. 424*e5dd7070Spatrick for (const auto &Parent : llvm::reverse(Parents)) { 425*e5dd7070Spatrick const DynTypedNode &Node = Parent.get().Node; 426*e5dd7070Spatrick if (const auto *D = Node.get<Decl>()) { 427*e5dd7070Spatrick if (isFunctionLikeDeclaration(D)) 428*e5dd7070Spatrick return IsPrevCompound; 429*e5dd7070Spatrick // Stop the search at any type declaration to avoid returning true for 430*e5dd7070Spatrick // expressions in type declarations in functions, like: 431*e5dd7070Spatrick // function foo() { struct X { 432*e5dd7070Spatrick // int m = /*selection:*/ 1 + 2 /*selection end*/; }; }; 433*e5dd7070Spatrick if (isa<TypeDecl>(D)) 434*e5dd7070Spatrick return false; 435*e5dd7070Spatrick } 436*e5dd7070Spatrick IsPrevCompound = Node.get<CompoundStmt>() != nullptr; 437*e5dd7070Spatrick } 438*e5dd7070Spatrick return false; 439*e5dd7070Spatrick } 440*e5dd7070Spatrick 441*e5dd7070Spatrick const Decl *CodeRangeASTSelection::getFunctionLikeNearestParent() const { 442*e5dd7070Spatrick for (const auto &Parent : llvm::reverse(Parents)) { 443*e5dd7070Spatrick const DynTypedNode &Node = Parent.get().Node; 444*e5dd7070Spatrick if (const auto *D = Node.get<Decl>()) { 445*e5dd7070Spatrick if (isFunctionLikeDeclaration(D)) 446*e5dd7070Spatrick return D; 447*e5dd7070Spatrick } 448*e5dd7070Spatrick } 449*e5dd7070Spatrick return nullptr; 450*e5dd7070Spatrick } 451