xref: /llvm-project/clang/lib/Tooling/Refactoring/ASTSelection.cpp (revision 6ad0788c332bb2043142954d300c49ac3e537f34)
1a844f396SAlex Lorenz //===--- ASTSelection.cpp - Clang refactoring library ---------------------===//
2a844f396SAlex Lorenz //
32946cd70SChandler Carruth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
42946cd70SChandler Carruth // See https://llvm.org/LICENSE.txt for license information.
52946cd70SChandler Carruth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a844f396SAlex Lorenz //
7a844f396SAlex Lorenz //===----------------------------------------------------------------------===//
8a844f396SAlex Lorenz 
9a844f396SAlex Lorenz #include "clang/Tooling/Refactoring/ASTSelection.h"
10a844f396SAlex Lorenz #include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h"
11a844f396SAlex Lorenz #include "clang/Lex/Lexer.h"
12410ef383SAlex Lorenz #include "llvm/Support/SaveAndRestore.h"
13a1580d7bSKazu Hirata #include <optional>
14a844f396SAlex Lorenz 
15a844f396SAlex Lorenz using namespace clang;
16a844f396SAlex Lorenz using namespace tooling;
17a844f396SAlex Lorenz 
18a844f396SAlex Lorenz namespace {
19a844f396SAlex Lorenz 
getLexicalDeclRange(Decl * D,const SourceManager & SM,const LangOptions & LangOpts)2023654b50SAlex Lorenz CharSourceRange getLexicalDeclRange(Decl *D, const SourceManager &SM,
2123654b50SAlex Lorenz                                     const LangOptions &LangOpts) {
2223654b50SAlex Lorenz   if (!isa<ObjCImplDecl>(D))
2323654b50SAlex Lorenz     return CharSourceRange::getTokenRange(D->getSourceRange());
2423654b50SAlex Lorenz   // Objective-C implementation declarations end at the '@' instead of the 'end'
2523654b50SAlex Lorenz   // keyword. Use the lexer to find the location right after 'end'.
2623654b50SAlex Lorenz   SourceRange R = D->getSourceRange();
2723654b50SAlex Lorenz   SourceLocation LocAfterEnd = Lexer::findLocationAfterToken(
2823654b50SAlex Lorenz       R.getEnd(), tok::raw_identifier, SM, LangOpts,
2923654b50SAlex Lorenz       /*SkipTrailingWhitespaceAndNewLine=*/false);
3023654b50SAlex Lorenz   return LocAfterEnd.isValid()
3123654b50SAlex Lorenz              ? CharSourceRange::getCharRange(R.getBegin(), LocAfterEnd)
3223654b50SAlex Lorenz              : CharSourceRange::getTokenRange(R);
3323654b50SAlex Lorenz }
3423654b50SAlex Lorenz 
35a844f396SAlex Lorenz /// Constructs the tree of selected AST nodes that either contain the location
36a844f396SAlex Lorenz /// of the cursor or overlap with the selection range.
37a844f396SAlex Lorenz class ASTSelectionFinder
38a844f396SAlex Lorenz     : public LexicallyOrderedRecursiveASTVisitor<ASTSelectionFinder> {
39a844f396SAlex Lorenz public:
ASTSelectionFinder(SourceRange Selection,FileID TargetFile,const ASTContext & Context)40a844f396SAlex Lorenz   ASTSelectionFinder(SourceRange Selection, FileID TargetFile,
41a844f396SAlex Lorenz                      const ASTContext &Context)
42a844f396SAlex Lorenz       : LexicallyOrderedRecursiveASTVisitor(Context.getSourceManager()),
43a844f396SAlex Lorenz         SelectionBegin(Selection.getBegin()),
44a844f396SAlex Lorenz         SelectionEnd(Selection.getBegin() == Selection.getEnd()
45a844f396SAlex Lorenz                          ? SourceLocation()
46a844f396SAlex Lorenz                          : Selection.getEnd()),
47a844f396SAlex Lorenz         TargetFile(TargetFile), Context(Context) {
48a844f396SAlex Lorenz     // The TU decl is the root of the selected node tree.
49a844f396SAlex Lorenz     SelectionStack.push_back(
50a844f396SAlex Lorenz         SelectedASTNode(DynTypedNode::create(*Context.getTranslationUnitDecl()),
51a844f396SAlex Lorenz                         SourceSelectionKind::None));
52a844f396SAlex Lorenz   }
53a844f396SAlex Lorenz 
getSelectedASTNode()54*6ad0788cSKazu Hirata   std::optional<SelectedASTNode> getSelectedASTNode() {
55a844f396SAlex Lorenz     assert(SelectionStack.size() == 1 && "stack was not popped");
56a844f396SAlex Lorenz     SelectedASTNode Result = std::move(SelectionStack.back());
57a844f396SAlex Lorenz     SelectionStack.pop_back();
58a844f396SAlex Lorenz     if (Result.Children.empty())
595891420eSKazu Hirata       return std::nullopt;
600eaa4832SAlex Lorenz     return std::move(Result);
61a844f396SAlex Lorenz   }
62a844f396SAlex Lorenz 
TraversePseudoObjectExpr(PseudoObjectExpr * E)63410ef383SAlex Lorenz   bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
64410ef383SAlex Lorenz     // Avoid traversing the semantic expressions. They should be handled by
65410ef383SAlex Lorenz     // looking through the appropriate opaque expressions in order to build
66410ef383SAlex Lorenz     // a meaningful selection tree.
67abf0c6c0SJan Svoboda     llvm::SaveAndRestore LookThrough(LookThroughOpaqueValueExprs, true);
68410ef383SAlex Lorenz     return TraverseStmt(E->getSyntacticForm());
69410ef383SAlex Lorenz   }
70410ef383SAlex Lorenz 
TraverseOpaqueValueExpr(OpaqueValueExpr * E)71410ef383SAlex Lorenz   bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) {
72410ef383SAlex Lorenz     if (!LookThroughOpaqueValueExprs)
73410ef383SAlex Lorenz       return true;
74abf0c6c0SJan Svoboda     llvm::SaveAndRestore LookThrough(LookThroughOpaqueValueExprs, false);
75410ef383SAlex Lorenz     return TraverseStmt(E->getSourceExpr());
76410ef383SAlex Lorenz   }
77410ef383SAlex Lorenz 
TraverseDecl(Decl * D)78a844f396SAlex Lorenz   bool TraverseDecl(Decl *D) {
79a844f396SAlex Lorenz     if (isa<TranslationUnitDecl>(D))
80a844f396SAlex Lorenz       return LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
81a844f396SAlex Lorenz     if (D->isImplicit())
82a844f396SAlex Lorenz       return true;
83a844f396SAlex Lorenz 
84a844f396SAlex Lorenz     // Check if this declaration is written in the file of interest.
85a844f396SAlex Lorenz     const SourceRange DeclRange = D->getSourceRange();
86a844f396SAlex Lorenz     const SourceManager &SM = Context.getSourceManager();
87a844f396SAlex Lorenz     SourceLocation FileLoc;
88a844f396SAlex Lorenz     if (DeclRange.getBegin().isMacroID() && !DeclRange.getEnd().isMacroID())
89a844f396SAlex Lorenz       FileLoc = DeclRange.getEnd();
90a844f396SAlex Lorenz     else
91a844f396SAlex Lorenz       FileLoc = SM.getSpellingLoc(DeclRange.getBegin());
92a844f396SAlex Lorenz     if (SM.getFileID(FileLoc) != TargetFile)
93a844f396SAlex Lorenz       return true;
94a844f396SAlex Lorenz 
95a844f396SAlex Lorenz     SourceSelectionKind SelectionKind =
9623654b50SAlex Lorenz         selectionKindFor(getLexicalDeclRange(D, SM, Context.getLangOpts()));
97a844f396SAlex Lorenz     SelectionStack.push_back(
98a844f396SAlex Lorenz         SelectedASTNode(DynTypedNode::create(*D), SelectionKind));
99a844f396SAlex Lorenz     LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
100a844f396SAlex Lorenz     popAndAddToSelectionIfSelected(SelectionKind);
101a844f396SAlex Lorenz 
102a844f396SAlex Lorenz     if (DeclRange.getEnd().isValid() &&
103a844f396SAlex Lorenz         SM.isBeforeInTranslationUnit(SelectionEnd.isValid() ? SelectionEnd
104a844f396SAlex Lorenz                                                             : SelectionBegin,
105a844f396SAlex Lorenz                                      DeclRange.getEnd())) {
106a844f396SAlex Lorenz       // Stop early when we've reached a declaration after the selection.
107a844f396SAlex Lorenz       return false;
108a844f396SAlex Lorenz     }
109a844f396SAlex Lorenz     return true;
110a844f396SAlex Lorenz   }
111a844f396SAlex Lorenz 
TraverseStmt(Stmt * S)112a844f396SAlex Lorenz   bool TraverseStmt(Stmt *S) {
113a844f396SAlex Lorenz     if (!S)
114a844f396SAlex Lorenz       return true;
115410ef383SAlex Lorenz     if (auto *Opaque = dyn_cast<OpaqueValueExpr>(S))
116410ef383SAlex Lorenz       return TraverseOpaqueValueExpr(Opaque);
117f64d0a4dSAlex Lorenz     // Avoid selecting implicit 'this' expressions.
118f64d0a4dSAlex Lorenz     if (auto *TE = dyn_cast<CXXThisExpr>(S)) {
119f64d0a4dSAlex Lorenz       if (TE->isImplicit())
120f64d0a4dSAlex Lorenz         return true;
121f64d0a4dSAlex Lorenz     }
122a844f396SAlex Lorenz     // FIXME (Alex Lorenz): Improve handling for macro locations.
123a844f396SAlex Lorenz     SourceSelectionKind SelectionKind =
124a844f396SAlex Lorenz         selectionKindFor(CharSourceRange::getTokenRange(S->getSourceRange()));
125a844f396SAlex Lorenz     SelectionStack.push_back(
126a844f396SAlex Lorenz         SelectedASTNode(DynTypedNode::create(*S), SelectionKind));
127a844f396SAlex Lorenz     LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S);
128a844f396SAlex Lorenz     popAndAddToSelectionIfSelected(SelectionKind);
129a844f396SAlex Lorenz     return true;
130a844f396SAlex Lorenz   }
131a844f396SAlex Lorenz 
132a844f396SAlex Lorenz private:
popAndAddToSelectionIfSelected(SourceSelectionKind SelectionKind)133a844f396SAlex Lorenz   void popAndAddToSelectionIfSelected(SourceSelectionKind SelectionKind) {
134a844f396SAlex Lorenz     SelectedASTNode Node = std::move(SelectionStack.back());
135a844f396SAlex Lorenz     SelectionStack.pop_back();
136a844f396SAlex Lorenz     if (SelectionKind != SourceSelectionKind::None || !Node.Children.empty())
137a844f396SAlex Lorenz       SelectionStack.back().Children.push_back(std::move(Node));
138a844f396SAlex Lorenz   }
139a844f396SAlex Lorenz 
selectionKindFor(CharSourceRange Range)140a844f396SAlex Lorenz   SourceSelectionKind selectionKindFor(CharSourceRange Range) {
141a844f396SAlex Lorenz     SourceLocation End = Range.getEnd();
142a844f396SAlex Lorenz     const SourceManager &SM = Context.getSourceManager();
143a844f396SAlex Lorenz     if (Range.isTokenRange())
144a844f396SAlex Lorenz       End = Lexer::getLocForEndOfToken(End, 0, SM, Context.getLangOpts());
145a844f396SAlex Lorenz     if (!SourceLocation::isPairOfFileLocations(Range.getBegin(), End))
146a844f396SAlex Lorenz       return SourceSelectionKind::None;
147a844f396SAlex Lorenz     if (!SelectionEnd.isValid()) {
148a844f396SAlex Lorenz       // Do a quick check when the selection is of length 0.
149a844f396SAlex Lorenz       if (SM.isPointWithin(SelectionBegin, Range.getBegin(), End))
150a844f396SAlex Lorenz         return SourceSelectionKind::ContainsSelection;
151a844f396SAlex Lorenz       return SourceSelectionKind::None;
152a844f396SAlex Lorenz     }
153a844f396SAlex Lorenz     bool HasStart = SM.isPointWithin(SelectionBegin, Range.getBegin(), End);
154a844f396SAlex Lorenz     bool HasEnd = SM.isPointWithin(SelectionEnd, Range.getBegin(), End);
155a844f396SAlex Lorenz     if (HasStart && HasEnd)
156a844f396SAlex Lorenz       return SourceSelectionKind::ContainsSelection;
157a844f396SAlex Lorenz     if (SM.isPointWithin(Range.getBegin(), SelectionBegin, SelectionEnd) &&
158a844f396SAlex Lorenz         SM.isPointWithin(End, SelectionBegin, SelectionEnd))
159a844f396SAlex Lorenz       return SourceSelectionKind::InsideSelection;
160a844f396SAlex Lorenz     // Ensure there's at least some overlap with the 'start'/'end' selection
161a844f396SAlex Lorenz     // types.
162a844f396SAlex Lorenz     if (HasStart && SelectionBegin != End)
163a844f396SAlex Lorenz       return SourceSelectionKind::ContainsSelectionStart;
164a844f396SAlex Lorenz     if (HasEnd && SelectionEnd != Range.getBegin())
165a844f396SAlex Lorenz       return SourceSelectionKind::ContainsSelectionEnd;
166a844f396SAlex Lorenz 
167a844f396SAlex Lorenz     return SourceSelectionKind::None;
168a844f396SAlex Lorenz   }
169a844f396SAlex Lorenz 
170a844f396SAlex Lorenz   const SourceLocation SelectionBegin, SelectionEnd;
171a844f396SAlex Lorenz   FileID TargetFile;
172a844f396SAlex Lorenz   const ASTContext &Context;
173a844f396SAlex Lorenz   std::vector<SelectedASTNode> SelectionStack;
174410ef383SAlex Lorenz   /// Controls whether we can traverse through the OpaqueValueExpr. This is
175410ef383SAlex Lorenz   /// typically enabled during the traversal of syntactic form for
176410ef383SAlex Lorenz   /// PseudoObjectExprs.
177410ef383SAlex Lorenz   bool LookThroughOpaqueValueExprs = false;
178a844f396SAlex Lorenz };
179a844f396SAlex Lorenz 
180a844f396SAlex Lorenz } // end anonymous namespace
181a844f396SAlex Lorenz 
182*6ad0788cSKazu Hirata std::optional<SelectedASTNode>
findSelectedASTNodes(const ASTContext & Context,SourceRange SelectionRange)183a844f396SAlex Lorenz clang::tooling::findSelectedASTNodes(const ASTContext &Context,
184a844f396SAlex Lorenz                                      SourceRange SelectionRange) {
185a844f396SAlex Lorenz   assert(SelectionRange.isValid() &&
186a844f396SAlex Lorenz          SourceLocation::isPairOfFileLocations(SelectionRange.getBegin(),
187a844f396SAlex Lorenz                                                SelectionRange.getEnd()) &&
188a844f396SAlex Lorenz          "Expected a file range");
189a844f396SAlex Lorenz   FileID TargetFile =
190a844f396SAlex Lorenz       Context.getSourceManager().getFileID(SelectionRange.getBegin());
191a844f396SAlex Lorenz   assert(Context.getSourceManager().getFileID(SelectionRange.getEnd()) ==
192a844f396SAlex Lorenz              TargetFile &&
193a844f396SAlex Lorenz          "selection range must span one file");
194a844f396SAlex Lorenz 
195a844f396SAlex Lorenz   ASTSelectionFinder Visitor(SelectionRange, TargetFile, Context);
196a844f396SAlex Lorenz   Visitor.TraverseDecl(Context.getTranslationUnitDecl());
197a844f396SAlex Lorenz   return Visitor.getSelectedASTNode();
198a844f396SAlex Lorenz }
199a844f396SAlex Lorenz 
selectionKindToString(SourceSelectionKind Kind)200a844f396SAlex Lorenz static const char *selectionKindToString(SourceSelectionKind Kind) {
201a844f396SAlex Lorenz   switch (Kind) {
202a844f396SAlex Lorenz   case SourceSelectionKind::None:
203a844f396SAlex Lorenz     return "none";
204a844f396SAlex Lorenz   case SourceSelectionKind::ContainsSelection:
205a844f396SAlex Lorenz     return "contains-selection";
206a844f396SAlex Lorenz   case SourceSelectionKind::ContainsSelectionStart:
207a844f396SAlex Lorenz     return "contains-selection-start";
208a844f396SAlex Lorenz   case SourceSelectionKind::ContainsSelectionEnd:
209a844f396SAlex Lorenz     return "contains-selection-end";
210a844f396SAlex Lorenz   case SourceSelectionKind::InsideSelection:
211a844f396SAlex Lorenz     return "inside";
212a844f396SAlex Lorenz   }
213a844f396SAlex Lorenz   llvm_unreachable("invalid selection kind");
214a844f396SAlex Lorenz }
215a844f396SAlex Lorenz 
dump(const SelectedASTNode & Node,llvm::raw_ostream & OS,unsigned Indent=0)216a844f396SAlex Lorenz static void dump(const SelectedASTNode &Node, llvm::raw_ostream &OS,
217a844f396SAlex Lorenz                  unsigned Indent = 0) {
218a844f396SAlex Lorenz   OS.indent(Indent * 2);
219a844f396SAlex Lorenz   if (const Decl *D = Node.Node.get<Decl>()) {
220a844f396SAlex Lorenz     OS << D->getDeclKindName() << "Decl";
221a844f396SAlex Lorenz     if (const auto *ND = dyn_cast<NamedDecl>(D))
22219701458SBruno Ricci       OS << " \"" << ND->getDeclName() << '"';
223a844f396SAlex Lorenz   } else if (const Stmt *S = Node.Node.get<Stmt>()) {
224a844f396SAlex Lorenz     OS << S->getStmtClassName();
225a844f396SAlex Lorenz   }
226a844f396SAlex Lorenz   OS << ' ' << selectionKindToString(Node.SelectionKind) << "\n";
227a844f396SAlex Lorenz   for (const auto &Child : Node.Children)
228a844f396SAlex Lorenz     dump(Child, OS, Indent + 1);
229a844f396SAlex Lorenz }
230a844f396SAlex Lorenz 
dump(llvm::raw_ostream & OS) const231a844f396SAlex Lorenz void SelectedASTNode::dump(llvm::raw_ostream &OS) const { ::dump(*this, OS); }
232cd6c7838SAlex Lorenz 
233cd6c7838SAlex Lorenz /// Returns true if the given node has any direct children with the following
234cd6c7838SAlex Lorenz /// selection kind.
235cd6c7838SAlex Lorenz ///
236cd6c7838SAlex Lorenz /// Note: The direct children also include children of direct children with the
237cd6c7838SAlex Lorenz /// "None" selection kind.
hasAnyDirectChildrenWithKind(const SelectedASTNode & Node,SourceSelectionKind Kind)238cd6c7838SAlex Lorenz static bool hasAnyDirectChildrenWithKind(const SelectedASTNode &Node,
239cd6c7838SAlex Lorenz                                          SourceSelectionKind Kind) {
240cd6c7838SAlex Lorenz   assert(Kind != SourceSelectionKind::None && "invalid predicate!");
241cd6c7838SAlex Lorenz   for (const auto &Child : Node.Children) {
242cd6c7838SAlex Lorenz     if (Child.SelectionKind == Kind)
243cd6c7838SAlex Lorenz       return true;
244cd6c7838SAlex Lorenz     if (Child.SelectionKind == SourceSelectionKind::None)
245cd6c7838SAlex Lorenz       return hasAnyDirectChildrenWithKind(Child, Kind);
246cd6c7838SAlex Lorenz   }
247cd6c7838SAlex Lorenz   return false;
248cd6c7838SAlex Lorenz }
249cd6c7838SAlex Lorenz 
250cd6c7838SAlex Lorenz namespace {
251cd6c7838SAlex Lorenz struct SelectedNodeWithParents {
252cd6c7838SAlex Lorenz   SelectedASTNode::ReferenceType Node;
253cd6c7838SAlex Lorenz   llvm::SmallVector<SelectedASTNode::ReferenceType, 8> Parents;
254547e5ad0SAlex Lorenz 
255547e5ad0SAlex Lorenz   /// Canonicalizes the given selection by selecting different related AST nodes
256547e5ad0SAlex Lorenz   /// when it makes sense to do so.
257547e5ad0SAlex Lorenz   void canonicalize();
258cd6c7838SAlex Lorenz };
2598337f81fSAlex Lorenz 
2608337f81fSAlex Lorenz enum SelectionCanonicalizationAction { KeepSelection, SelectParent };
2618337f81fSAlex Lorenz 
2628337f81fSAlex Lorenz /// Returns the canonicalization action which should be applied to the
2638337f81fSAlex Lorenz /// selected statement.
2648337f81fSAlex Lorenz SelectionCanonicalizationAction
getSelectionCanonizalizationAction(const Stmt * S,const Stmt * Parent)2658337f81fSAlex Lorenz getSelectionCanonizalizationAction(const Stmt *S, const Stmt *Parent) {
2668337f81fSAlex Lorenz   // Select the parent expression when:
2678337f81fSAlex Lorenz   // - The string literal in ObjC string literal is selected, e.g.:
2688337f81fSAlex Lorenz   //     @"test"   becomes   @"test"
2698337f81fSAlex Lorenz   //      ~~~~~~             ~~~~~~~
2708337f81fSAlex Lorenz   if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent))
2718337f81fSAlex Lorenz     return SelectParent;
2728337f81fSAlex Lorenz   // The entire call should be selected when just the member expression
2738337f81fSAlex Lorenz   // that refers to the method or the decl ref that refers to the function
2748337f81fSAlex Lorenz   // is selected.
2758337f81fSAlex Lorenz   //    f.call(args)  becomes  f.call(args)
2768337f81fSAlex Lorenz   //      ~~~~                 ~~~~~~~~~~~~
2778337f81fSAlex Lorenz   //    func(args)  becomes  func(args)
2788337f81fSAlex Lorenz   //    ~~~~                 ~~~~~~~~~~
2798337f81fSAlex Lorenz   else if (const auto *CE = dyn_cast<CallExpr>(Parent)) {
2808337f81fSAlex Lorenz     if ((isa<MemberExpr>(S) || isa<DeclRefExpr>(S)) &&
2818337f81fSAlex Lorenz         CE->getCallee()->IgnoreImpCasts() == S)
2828337f81fSAlex Lorenz       return SelectParent;
2838337f81fSAlex Lorenz   }
2848337f81fSAlex Lorenz   // FIXME: Syntactic form -> Entire pseudo-object expr.
2858337f81fSAlex Lorenz   return KeepSelection;
2868337f81fSAlex Lorenz }
2878337f81fSAlex Lorenz 
288cd6c7838SAlex Lorenz } // end anonymous namespace
289cd6c7838SAlex Lorenz 
canonicalize()290547e5ad0SAlex Lorenz void SelectedNodeWithParents::canonicalize() {
291547e5ad0SAlex Lorenz   const Stmt *S = Node.get().Node.get<Stmt>();
292547e5ad0SAlex Lorenz   assert(S && "non statement selection!");
293547e5ad0SAlex Lorenz   const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>();
294547e5ad0SAlex Lorenz   if (!Parent)
295547e5ad0SAlex Lorenz     return;
2968337f81fSAlex Lorenz 
2978337f81fSAlex Lorenz   // Look through the implicit casts in the parents.
2988337f81fSAlex Lorenz   unsigned ParentIndex = 1;
2998337f81fSAlex Lorenz   for (; (ParentIndex + 1) <= Parents.size() && isa<ImplicitCastExpr>(Parent);
3008337f81fSAlex Lorenz        ++ParentIndex) {
3018337f81fSAlex Lorenz     const Stmt *NewParent =
3028337f81fSAlex Lorenz         Parents[Parents.size() - ParentIndex - 1].get().Node.get<Stmt>();
3038337f81fSAlex Lorenz     if (!NewParent)
3048337f81fSAlex Lorenz       break;
3058337f81fSAlex Lorenz     Parent = NewParent;
3068337f81fSAlex Lorenz   }
3078337f81fSAlex Lorenz 
3088337f81fSAlex Lorenz   switch (getSelectionCanonizalizationAction(S, Parent)) {
3098337f81fSAlex Lorenz   case SelectParent:
3108337f81fSAlex Lorenz     Node = Parents[Parents.size() - ParentIndex];
3118337f81fSAlex Lorenz     for (; ParentIndex != 0; --ParentIndex)
3128337f81fSAlex Lorenz       Parents.pop_back();
3138337f81fSAlex Lorenz     break;
3148337f81fSAlex Lorenz   case KeepSelection:
3158337f81fSAlex Lorenz     break;
3168337f81fSAlex Lorenz   }
317547e5ad0SAlex Lorenz }
318547e5ad0SAlex Lorenz 
319cd6c7838SAlex Lorenz /// Finds the set of bottom-most selected AST nodes that are in the selection
320cd6c7838SAlex Lorenz /// tree with the specified selection kind.
321cd6c7838SAlex Lorenz ///
322cd6c7838SAlex Lorenz /// For example, given the following selection tree:
323cd6c7838SAlex Lorenz ///
324cd6c7838SAlex Lorenz /// FunctionDecl "f" contains-selection
325cd6c7838SAlex Lorenz ///   CompoundStmt contains-selection [#1]
326cd6c7838SAlex Lorenz ///     CallExpr inside
327cd6c7838SAlex Lorenz ///     ImplicitCastExpr inside
328cd6c7838SAlex Lorenz ///       DeclRefExpr inside
329cd6c7838SAlex Lorenz ///     IntegerLiteral inside
330cd6c7838SAlex Lorenz ///     IntegerLiteral inside
331cd6c7838SAlex Lorenz /// FunctionDecl "f2" contains-selection
332cd6c7838SAlex Lorenz ///   CompoundStmt contains-selection [#2]
333cd6c7838SAlex Lorenz ///     CallExpr inside
334cd6c7838SAlex Lorenz ///     ImplicitCastExpr inside
335cd6c7838SAlex Lorenz ///       DeclRefExpr inside
336cd6c7838SAlex Lorenz ///     IntegerLiteral inside
337cd6c7838SAlex Lorenz ///     IntegerLiteral inside
338cd6c7838SAlex Lorenz ///
339cd6c7838SAlex Lorenz /// This function will find references to nodes #1 and #2 when searching for the
340cd6c7838SAlex Lorenz /// \c ContainsSelection kind.
findDeepestWithKind(const SelectedASTNode & ASTSelection,llvm::SmallVectorImpl<SelectedNodeWithParents> & MatchingNodes,SourceSelectionKind Kind,llvm::SmallVectorImpl<SelectedASTNode::ReferenceType> & ParentStack)341cd6c7838SAlex Lorenz static void findDeepestWithKind(
342cd6c7838SAlex Lorenz     const SelectedASTNode &ASTSelection,
343cd6c7838SAlex Lorenz     llvm::SmallVectorImpl<SelectedNodeWithParents> &MatchingNodes,
344cd6c7838SAlex Lorenz     SourceSelectionKind Kind,
345cd6c7838SAlex Lorenz     llvm::SmallVectorImpl<SelectedASTNode::ReferenceType> &ParentStack) {
346b87b6bf7SAlex Lorenz   if (ASTSelection.Node.get<DeclStmt>()) {
347b87b6bf7SAlex Lorenz     // Select the entire decl stmt when any of its child declarations is the
348b87b6bf7SAlex Lorenz     // bottom-most.
349b87b6bf7SAlex Lorenz     for (const auto &Child : ASTSelection.Children) {
350b87b6bf7SAlex Lorenz       if (!hasAnyDirectChildrenWithKind(Child, Kind)) {
351b87b6bf7SAlex Lorenz         MatchingNodes.push_back(SelectedNodeWithParents{
352b87b6bf7SAlex Lorenz             std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}});
353b87b6bf7SAlex Lorenz         return;
354b87b6bf7SAlex Lorenz       }
355b87b6bf7SAlex Lorenz     }
356b87b6bf7SAlex Lorenz   } else {
357cd6c7838SAlex Lorenz     if (!hasAnyDirectChildrenWithKind(ASTSelection, Kind)) {
358cd6c7838SAlex Lorenz       // This node is the bottom-most.
359cd6c7838SAlex Lorenz       MatchingNodes.push_back(SelectedNodeWithParents{
360cd6c7838SAlex Lorenz           std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}});
361cd6c7838SAlex Lorenz       return;
362cd6c7838SAlex Lorenz     }
363b87b6bf7SAlex Lorenz   }
364cd6c7838SAlex Lorenz   // Search in the children.
365cd6c7838SAlex Lorenz   ParentStack.push_back(std::cref(ASTSelection));
366cd6c7838SAlex Lorenz   for (const auto &Child : ASTSelection.Children)
367cd6c7838SAlex Lorenz     findDeepestWithKind(Child, MatchingNodes, Kind, ParentStack);
368cd6c7838SAlex Lorenz   ParentStack.pop_back();
369cd6c7838SAlex Lorenz }
370cd6c7838SAlex Lorenz 
findDeepestWithKind(const SelectedASTNode & ASTSelection,llvm::SmallVectorImpl<SelectedNodeWithParents> & MatchingNodes,SourceSelectionKind Kind)371cd6c7838SAlex Lorenz static void findDeepestWithKind(
372cd6c7838SAlex Lorenz     const SelectedASTNode &ASTSelection,
373cd6c7838SAlex Lorenz     llvm::SmallVectorImpl<SelectedNodeWithParents> &MatchingNodes,
374cd6c7838SAlex Lorenz     SourceSelectionKind Kind) {
375cd6c7838SAlex Lorenz   llvm::SmallVector<SelectedASTNode::ReferenceType, 16> ParentStack;
376cd6c7838SAlex Lorenz   findDeepestWithKind(ASTSelection, MatchingNodes, Kind, ParentStack);
377cd6c7838SAlex Lorenz }
378cd6c7838SAlex Lorenz 
379*6ad0788cSKazu Hirata std::optional<CodeRangeASTSelection>
create(SourceRange SelectionRange,const SelectedASTNode & ASTSelection)380cd6c7838SAlex Lorenz CodeRangeASTSelection::create(SourceRange SelectionRange,
381cd6c7838SAlex Lorenz                               const SelectedASTNode &ASTSelection) {
382cd6c7838SAlex Lorenz   // Code range is selected when the selection range is not empty.
383cd6c7838SAlex Lorenz   if (SelectionRange.getBegin() == SelectionRange.getEnd())
3845891420eSKazu Hirata     return std::nullopt;
385cd6c7838SAlex Lorenz   llvm::SmallVector<SelectedNodeWithParents, 4> ContainSelection;
386cd6c7838SAlex Lorenz   findDeepestWithKind(ASTSelection, ContainSelection,
387cd6c7838SAlex Lorenz                       SourceSelectionKind::ContainsSelection);
388cd6c7838SAlex Lorenz   // We are looking for a selection in one body of code, so let's focus on
389cd6c7838SAlex Lorenz   // one matching result.
390cd6c7838SAlex Lorenz   if (ContainSelection.size() != 1)
3915891420eSKazu Hirata     return std::nullopt;
392cd6c7838SAlex Lorenz   SelectedNodeWithParents &Selected = ContainSelection[0];
393cd6c7838SAlex Lorenz   if (!Selected.Node.get().Node.get<Stmt>())
3945891420eSKazu Hirata     return std::nullopt;
395cd6c7838SAlex Lorenz   const Stmt *CodeRangeStmt = Selected.Node.get().Node.get<Stmt>();
396cd6c7838SAlex Lorenz   if (!isa<CompoundStmt>(CodeRangeStmt)) {
397547e5ad0SAlex Lorenz     Selected.canonicalize();
398cd6c7838SAlex Lorenz     return CodeRangeASTSelection(Selected.Node, Selected.Parents,
399cd6c7838SAlex Lorenz                                  /*AreChildrenSelected=*/false);
400cd6c7838SAlex Lorenz   }
4017fe441b2SAlex Lorenz   // FIXME (Alex L): First selected SwitchCase means that first case statement.
4027fe441b2SAlex Lorenz   // is selected actually
4037fe441b2SAlex Lorenz   // (See https://github.com/apple/swift-clang & CompoundStmtRange).
4047fe441b2SAlex Lorenz 
405cd6c7838SAlex Lorenz   // FIXME (Alex L): Tweak selection rules for compound statements, see:
406cd6c7838SAlex Lorenz   // https://github.com/apple/swift-clang/blob/swift-4.1-branch/lib/Tooling/
407cd6c7838SAlex Lorenz   // Refactor/ASTSlice.cpp#L513
408cd6c7838SAlex Lorenz   // The user selected multiple statements in a compound statement.
409cd6c7838SAlex Lorenz   Selected.Parents.push_back(Selected.Node);
410cd6c7838SAlex Lorenz   return CodeRangeASTSelection(Selected.Node, Selected.Parents,
411cd6c7838SAlex Lorenz                                /*AreChildrenSelected=*/true);
412cd6c7838SAlex Lorenz }
4137fe441b2SAlex Lorenz 
isFunctionLikeDeclaration(const Decl * D)4147cd48cd8SAlex Lorenz static bool isFunctionLikeDeclaration(const Decl *D) {
4157cd48cd8SAlex Lorenz   // FIXME (Alex L): Test for BlockDecl.
4167cd48cd8SAlex Lorenz   return isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D);
4177cd48cd8SAlex Lorenz }
4187cd48cd8SAlex Lorenz 
isInFunctionLikeBodyOfCode() const4197fe441b2SAlex Lorenz bool CodeRangeASTSelection::isInFunctionLikeBodyOfCode() const {
4207fe441b2SAlex Lorenz   bool IsPrevCompound = false;
4217fe441b2SAlex Lorenz   // Scan through the parents (bottom-to-top) and check if the selection is
4227fe441b2SAlex Lorenz   // contained in a compound statement that's a body of a function/method
4237fe441b2SAlex Lorenz   // declaration.
4247fe441b2SAlex Lorenz   for (const auto &Parent : llvm::reverse(Parents)) {
4257fe441b2SAlex Lorenz     const DynTypedNode &Node = Parent.get().Node;
4267fe441b2SAlex Lorenz     if (const auto *D = Node.get<Decl>()) {
4277cd48cd8SAlex Lorenz       if (isFunctionLikeDeclaration(D))
4287fe441b2SAlex Lorenz         return IsPrevCompound;
429cc55754aSAlex Lorenz       // Stop the search at any type declaration to avoid returning true for
430cc55754aSAlex Lorenz       // expressions in type declarations in functions, like:
4317fe441b2SAlex Lorenz       // function foo() { struct X {
4327fe441b2SAlex Lorenz       //   int m = /*selection:*/ 1 + 2 /*selection end*/; }; };
433cc55754aSAlex Lorenz       if (isa<TypeDecl>(D))
434cc55754aSAlex Lorenz         return false;
4357fe441b2SAlex Lorenz     }
4367fe441b2SAlex Lorenz     IsPrevCompound = Node.get<CompoundStmt>() != nullptr;
4377fe441b2SAlex Lorenz   }
4387fe441b2SAlex Lorenz   return false;
4397fe441b2SAlex Lorenz }
4407fe441b2SAlex Lorenz 
getFunctionLikeNearestParent() const4417fe441b2SAlex Lorenz const Decl *CodeRangeASTSelection::getFunctionLikeNearestParent() const {
4427fe441b2SAlex Lorenz   for (const auto &Parent : llvm::reverse(Parents)) {
4437fe441b2SAlex Lorenz     const DynTypedNode &Node = Parent.get().Node;
4447fe441b2SAlex Lorenz     if (const auto *D = Node.get<Decl>()) {
4457cd48cd8SAlex Lorenz       if (isFunctionLikeDeclaration(D))
4467fe441b2SAlex Lorenz         return D;
4477fe441b2SAlex Lorenz     }
4487fe441b2SAlex Lorenz   }
4497fe441b2SAlex Lorenz   return nullptr;
4507fe441b2SAlex Lorenz }
451