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