10b57cec5SDimitry Andric //===--- ASTSelection.cpp - Clang refactoring library ---------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric
90b57cec5SDimitry Andric #include "clang/Tooling/Refactoring/ASTSelection.h"
100b57cec5SDimitry Andric #include "clang/AST/LexicallyOrderedRecursiveASTVisitor.h"
110b57cec5SDimitry Andric #include "clang/Lex/Lexer.h"
120b57cec5SDimitry Andric #include "llvm/Support/SaveAndRestore.h"
13*bdd1243dSDimitry Andric #include <optional>
140b57cec5SDimitry Andric
150b57cec5SDimitry Andric using namespace clang;
160b57cec5SDimitry Andric using namespace tooling;
170b57cec5SDimitry Andric
180b57cec5SDimitry Andric namespace {
190b57cec5SDimitry Andric
getLexicalDeclRange(Decl * D,const SourceManager & SM,const LangOptions & LangOpts)200b57cec5SDimitry Andric CharSourceRange getLexicalDeclRange(Decl *D, const SourceManager &SM,
210b57cec5SDimitry Andric const LangOptions &LangOpts) {
220b57cec5SDimitry Andric if (!isa<ObjCImplDecl>(D))
230b57cec5SDimitry Andric return CharSourceRange::getTokenRange(D->getSourceRange());
240b57cec5SDimitry Andric // Objective-C implementation declarations end at the '@' instead of the 'end'
250b57cec5SDimitry Andric // keyword. Use the lexer to find the location right after 'end'.
260b57cec5SDimitry Andric SourceRange R = D->getSourceRange();
270b57cec5SDimitry Andric SourceLocation LocAfterEnd = Lexer::findLocationAfterToken(
280b57cec5SDimitry Andric R.getEnd(), tok::raw_identifier, SM, LangOpts,
290b57cec5SDimitry Andric /*SkipTrailingWhitespaceAndNewLine=*/false);
300b57cec5SDimitry Andric return LocAfterEnd.isValid()
310b57cec5SDimitry Andric ? CharSourceRange::getCharRange(R.getBegin(), LocAfterEnd)
320b57cec5SDimitry Andric : CharSourceRange::getTokenRange(R);
330b57cec5SDimitry Andric }
340b57cec5SDimitry Andric
350b57cec5SDimitry Andric /// Constructs the tree of selected AST nodes that either contain the location
360b57cec5SDimitry Andric /// of the cursor or overlap with the selection range.
370b57cec5SDimitry Andric class ASTSelectionFinder
380b57cec5SDimitry Andric : public LexicallyOrderedRecursiveASTVisitor<ASTSelectionFinder> {
390b57cec5SDimitry Andric public:
ASTSelectionFinder(SourceRange Selection,FileID TargetFile,const ASTContext & Context)400b57cec5SDimitry Andric ASTSelectionFinder(SourceRange Selection, FileID TargetFile,
410b57cec5SDimitry Andric const ASTContext &Context)
420b57cec5SDimitry Andric : LexicallyOrderedRecursiveASTVisitor(Context.getSourceManager()),
430b57cec5SDimitry Andric SelectionBegin(Selection.getBegin()),
440b57cec5SDimitry Andric SelectionEnd(Selection.getBegin() == Selection.getEnd()
450b57cec5SDimitry Andric ? SourceLocation()
460b57cec5SDimitry Andric : Selection.getEnd()),
470b57cec5SDimitry Andric TargetFile(TargetFile), Context(Context) {
480b57cec5SDimitry Andric // The TU decl is the root of the selected node tree.
490b57cec5SDimitry Andric SelectionStack.push_back(
500b57cec5SDimitry Andric SelectedASTNode(DynTypedNode::create(*Context.getTranslationUnitDecl()),
510b57cec5SDimitry Andric SourceSelectionKind::None));
520b57cec5SDimitry Andric }
530b57cec5SDimitry Andric
getSelectedASTNode()54*bdd1243dSDimitry Andric std::optional<SelectedASTNode> getSelectedASTNode() {
550b57cec5SDimitry Andric assert(SelectionStack.size() == 1 && "stack was not popped");
560b57cec5SDimitry Andric SelectedASTNode Result = std::move(SelectionStack.back());
570b57cec5SDimitry Andric SelectionStack.pop_back();
580b57cec5SDimitry Andric if (Result.Children.empty())
59*bdd1243dSDimitry Andric return std::nullopt;
600b57cec5SDimitry Andric return std::move(Result);
610b57cec5SDimitry Andric }
620b57cec5SDimitry Andric
TraversePseudoObjectExpr(PseudoObjectExpr * E)630b57cec5SDimitry Andric bool TraversePseudoObjectExpr(PseudoObjectExpr *E) {
640b57cec5SDimitry Andric // Avoid traversing the semantic expressions. They should be handled by
650b57cec5SDimitry Andric // looking through the appropriate opaque expressions in order to build
660b57cec5SDimitry Andric // a meaningful selection tree.
67*bdd1243dSDimitry Andric llvm::SaveAndRestore LookThrough(LookThroughOpaqueValueExprs, true);
680b57cec5SDimitry Andric return TraverseStmt(E->getSyntacticForm());
690b57cec5SDimitry Andric }
700b57cec5SDimitry Andric
TraverseOpaqueValueExpr(OpaqueValueExpr * E)710b57cec5SDimitry Andric bool TraverseOpaqueValueExpr(OpaqueValueExpr *E) {
720b57cec5SDimitry Andric if (!LookThroughOpaqueValueExprs)
730b57cec5SDimitry Andric return true;
74*bdd1243dSDimitry Andric llvm::SaveAndRestore LookThrough(LookThroughOpaqueValueExprs, false);
750b57cec5SDimitry Andric return TraverseStmt(E->getSourceExpr());
760b57cec5SDimitry Andric }
770b57cec5SDimitry Andric
TraverseDecl(Decl * D)780b57cec5SDimitry Andric bool TraverseDecl(Decl *D) {
790b57cec5SDimitry Andric if (isa<TranslationUnitDecl>(D))
800b57cec5SDimitry Andric return LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
810b57cec5SDimitry Andric if (D->isImplicit())
820b57cec5SDimitry Andric return true;
830b57cec5SDimitry Andric
840b57cec5SDimitry Andric // Check if this declaration is written in the file of interest.
850b57cec5SDimitry Andric const SourceRange DeclRange = D->getSourceRange();
860b57cec5SDimitry Andric const SourceManager &SM = Context.getSourceManager();
870b57cec5SDimitry Andric SourceLocation FileLoc;
880b57cec5SDimitry Andric if (DeclRange.getBegin().isMacroID() && !DeclRange.getEnd().isMacroID())
890b57cec5SDimitry Andric FileLoc = DeclRange.getEnd();
900b57cec5SDimitry Andric else
910b57cec5SDimitry Andric FileLoc = SM.getSpellingLoc(DeclRange.getBegin());
920b57cec5SDimitry Andric if (SM.getFileID(FileLoc) != TargetFile)
930b57cec5SDimitry Andric return true;
940b57cec5SDimitry Andric
950b57cec5SDimitry Andric SourceSelectionKind SelectionKind =
960b57cec5SDimitry Andric selectionKindFor(getLexicalDeclRange(D, SM, Context.getLangOpts()));
970b57cec5SDimitry Andric SelectionStack.push_back(
980b57cec5SDimitry Andric SelectedASTNode(DynTypedNode::create(*D), SelectionKind));
990b57cec5SDimitry Andric LexicallyOrderedRecursiveASTVisitor::TraverseDecl(D);
1000b57cec5SDimitry Andric popAndAddToSelectionIfSelected(SelectionKind);
1010b57cec5SDimitry Andric
1020b57cec5SDimitry Andric if (DeclRange.getEnd().isValid() &&
1030b57cec5SDimitry Andric SM.isBeforeInTranslationUnit(SelectionEnd.isValid() ? SelectionEnd
1040b57cec5SDimitry Andric : SelectionBegin,
1050b57cec5SDimitry Andric DeclRange.getEnd())) {
1060b57cec5SDimitry Andric // Stop early when we've reached a declaration after the selection.
1070b57cec5SDimitry Andric return false;
1080b57cec5SDimitry Andric }
1090b57cec5SDimitry Andric return true;
1100b57cec5SDimitry Andric }
1110b57cec5SDimitry Andric
TraverseStmt(Stmt * S)1120b57cec5SDimitry Andric bool TraverseStmt(Stmt *S) {
1130b57cec5SDimitry Andric if (!S)
1140b57cec5SDimitry Andric return true;
1150b57cec5SDimitry Andric if (auto *Opaque = dyn_cast<OpaqueValueExpr>(S))
1160b57cec5SDimitry Andric return TraverseOpaqueValueExpr(Opaque);
1170b57cec5SDimitry Andric // Avoid selecting implicit 'this' expressions.
1180b57cec5SDimitry Andric if (auto *TE = dyn_cast<CXXThisExpr>(S)) {
1190b57cec5SDimitry Andric if (TE->isImplicit())
1200b57cec5SDimitry Andric return true;
1210b57cec5SDimitry Andric }
1220b57cec5SDimitry Andric // FIXME (Alex Lorenz): Improve handling for macro locations.
1230b57cec5SDimitry Andric SourceSelectionKind SelectionKind =
1240b57cec5SDimitry Andric selectionKindFor(CharSourceRange::getTokenRange(S->getSourceRange()));
1250b57cec5SDimitry Andric SelectionStack.push_back(
1260b57cec5SDimitry Andric SelectedASTNode(DynTypedNode::create(*S), SelectionKind));
1270b57cec5SDimitry Andric LexicallyOrderedRecursiveASTVisitor::TraverseStmt(S);
1280b57cec5SDimitry Andric popAndAddToSelectionIfSelected(SelectionKind);
1290b57cec5SDimitry Andric return true;
1300b57cec5SDimitry Andric }
1310b57cec5SDimitry Andric
1320b57cec5SDimitry Andric private:
popAndAddToSelectionIfSelected(SourceSelectionKind SelectionKind)1330b57cec5SDimitry Andric void popAndAddToSelectionIfSelected(SourceSelectionKind SelectionKind) {
1340b57cec5SDimitry Andric SelectedASTNode Node = std::move(SelectionStack.back());
1350b57cec5SDimitry Andric SelectionStack.pop_back();
1360b57cec5SDimitry Andric if (SelectionKind != SourceSelectionKind::None || !Node.Children.empty())
1370b57cec5SDimitry Andric SelectionStack.back().Children.push_back(std::move(Node));
1380b57cec5SDimitry Andric }
1390b57cec5SDimitry Andric
selectionKindFor(CharSourceRange Range)1400b57cec5SDimitry Andric SourceSelectionKind selectionKindFor(CharSourceRange Range) {
1410b57cec5SDimitry Andric SourceLocation End = Range.getEnd();
1420b57cec5SDimitry Andric const SourceManager &SM = Context.getSourceManager();
1430b57cec5SDimitry Andric if (Range.isTokenRange())
1440b57cec5SDimitry Andric End = Lexer::getLocForEndOfToken(End, 0, SM, Context.getLangOpts());
1450b57cec5SDimitry Andric if (!SourceLocation::isPairOfFileLocations(Range.getBegin(), End))
1460b57cec5SDimitry Andric return SourceSelectionKind::None;
1470b57cec5SDimitry Andric if (!SelectionEnd.isValid()) {
1480b57cec5SDimitry Andric // Do a quick check when the selection is of length 0.
1490b57cec5SDimitry Andric if (SM.isPointWithin(SelectionBegin, Range.getBegin(), End))
1500b57cec5SDimitry Andric return SourceSelectionKind::ContainsSelection;
1510b57cec5SDimitry Andric return SourceSelectionKind::None;
1520b57cec5SDimitry Andric }
1530b57cec5SDimitry Andric bool HasStart = SM.isPointWithin(SelectionBegin, Range.getBegin(), End);
1540b57cec5SDimitry Andric bool HasEnd = SM.isPointWithin(SelectionEnd, Range.getBegin(), End);
1550b57cec5SDimitry Andric if (HasStart && HasEnd)
1560b57cec5SDimitry Andric return SourceSelectionKind::ContainsSelection;
1570b57cec5SDimitry Andric if (SM.isPointWithin(Range.getBegin(), SelectionBegin, SelectionEnd) &&
1580b57cec5SDimitry Andric SM.isPointWithin(End, SelectionBegin, SelectionEnd))
1590b57cec5SDimitry Andric return SourceSelectionKind::InsideSelection;
1600b57cec5SDimitry Andric // Ensure there's at least some overlap with the 'start'/'end' selection
1610b57cec5SDimitry Andric // types.
1620b57cec5SDimitry Andric if (HasStart && SelectionBegin != End)
1630b57cec5SDimitry Andric return SourceSelectionKind::ContainsSelectionStart;
1640b57cec5SDimitry Andric if (HasEnd && SelectionEnd != Range.getBegin())
1650b57cec5SDimitry Andric return SourceSelectionKind::ContainsSelectionEnd;
1660b57cec5SDimitry Andric
1670b57cec5SDimitry Andric return SourceSelectionKind::None;
1680b57cec5SDimitry Andric }
1690b57cec5SDimitry Andric
1700b57cec5SDimitry Andric const SourceLocation SelectionBegin, SelectionEnd;
1710b57cec5SDimitry Andric FileID TargetFile;
1720b57cec5SDimitry Andric const ASTContext &Context;
1730b57cec5SDimitry Andric std::vector<SelectedASTNode> SelectionStack;
1740b57cec5SDimitry Andric /// Controls whether we can traverse through the OpaqueValueExpr. This is
1750b57cec5SDimitry Andric /// typically enabled during the traversal of syntactic form for
1760b57cec5SDimitry Andric /// PseudoObjectExprs.
1770b57cec5SDimitry Andric bool LookThroughOpaqueValueExprs = false;
1780b57cec5SDimitry Andric };
1790b57cec5SDimitry Andric
1800b57cec5SDimitry Andric } // end anonymous namespace
1810b57cec5SDimitry Andric
182*bdd1243dSDimitry Andric std::optional<SelectedASTNode>
findSelectedASTNodes(const ASTContext & Context,SourceRange SelectionRange)1830b57cec5SDimitry Andric clang::tooling::findSelectedASTNodes(const ASTContext &Context,
1840b57cec5SDimitry Andric SourceRange SelectionRange) {
1850b57cec5SDimitry Andric assert(SelectionRange.isValid() &&
1860b57cec5SDimitry Andric SourceLocation::isPairOfFileLocations(SelectionRange.getBegin(),
1870b57cec5SDimitry Andric SelectionRange.getEnd()) &&
1880b57cec5SDimitry Andric "Expected a file range");
1890b57cec5SDimitry Andric FileID TargetFile =
1900b57cec5SDimitry Andric Context.getSourceManager().getFileID(SelectionRange.getBegin());
1910b57cec5SDimitry Andric assert(Context.getSourceManager().getFileID(SelectionRange.getEnd()) ==
1920b57cec5SDimitry Andric TargetFile &&
1930b57cec5SDimitry Andric "selection range must span one file");
1940b57cec5SDimitry Andric
1950b57cec5SDimitry Andric ASTSelectionFinder Visitor(SelectionRange, TargetFile, Context);
1960b57cec5SDimitry Andric Visitor.TraverseDecl(Context.getTranslationUnitDecl());
1970b57cec5SDimitry Andric return Visitor.getSelectedASTNode();
1980b57cec5SDimitry Andric }
1990b57cec5SDimitry Andric
selectionKindToString(SourceSelectionKind Kind)2000b57cec5SDimitry Andric static const char *selectionKindToString(SourceSelectionKind Kind) {
2010b57cec5SDimitry Andric switch (Kind) {
2020b57cec5SDimitry Andric case SourceSelectionKind::None:
2030b57cec5SDimitry Andric return "none";
2040b57cec5SDimitry Andric case SourceSelectionKind::ContainsSelection:
2050b57cec5SDimitry Andric return "contains-selection";
2060b57cec5SDimitry Andric case SourceSelectionKind::ContainsSelectionStart:
2070b57cec5SDimitry Andric return "contains-selection-start";
2080b57cec5SDimitry Andric case SourceSelectionKind::ContainsSelectionEnd:
2090b57cec5SDimitry Andric return "contains-selection-end";
2100b57cec5SDimitry Andric case SourceSelectionKind::InsideSelection:
2110b57cec5SDimitry Andric return "inside";
2120b57cec5SDimitry Andric }
2130b57cec5SDimitry Andric llvm_unreachable("invalid selection kind");
2140b57cec5SDimitry Andric }
2150b57cec5SDimitry Andric
dump(const SelectedASTNode & Node,llvm::raw_ostream & OS,unsigned Indent=0)2160b57cec5SDimitry Andric static void dump(const SelectedASTNode &Node, llvm::raw_ostream &OS,
2170b57cec5SDimitry Andric unsigned Indent = 0) {
2180b57cec5SDimitry Andric OS.indent(Indent * 2);
2190b57cec5SDimitry Andric if (const Decl *D = Node.Node.get<Decl>()) {
2200b57cec5SDimitry Andric OS << D->getDeclKindName() << "Decl";
2210b57cec5SDimitry Andric if (const auto *ND = dyn_cast<NamedDecl>(D))
222e8d8bef9SDimitry Andric OS << " \"" << ND->getDeclName() << '"';
2230b57cec5SDimitry Andric } else if (const Stmt *S = Node.Node.get<Stmt>()) {
2240b57cec5SDimitry Andric OS << S->getStmtClassName();
2250b57cec5SDimitry Andric }
2260b57cec5SDimitry Andric OS << ' ' << selectionKindToString(Node.SelectionKind) << "\n";
2270b57cec5SDimitry Andric for (const auto &Child : Node.Children)
2280b57cec5SDimitry Andric dump(Child, OS, Indent + 1);
2290b57cec5SDimitry Andric }
2300b57cec5SDimitry Andric
dump(llvm::raw_ostream & OS) const2310b57cec5SDimitry Andric void SelectedASTNode::dump(llvm::raw_ostream &OS) const { ::dump(*this, OS); }
2320b57cec5SDimitry Andric
2330b57cec5SDimitry Andric /// Returns true if the given node has any direct children with the following
2340b57cec5SDimitry Andric /// selection kind.
2350b57cec5SDimitry Andric ///
2360b57cec5SDimitry Andric /// Note: The direct children also include children of direct children with the
2370b57cec5SDimitry Andric /// "None" selection kind.
hasAnyDirectChildrenWithKind(const SelectedASTNode & Node,SourceSelectionKind Kind)2380b57cec5SDimitry Andric static bool hasAnyDirectChildrenWithKind(const SelectedASTNode &Node,
2390b57cec5SDimitry Andric SourceSelectionKind Kind) {
2400b57cec5SDimitry Andric assert(Kind != SourceSelectionKind::None && "invalid predicate!");
2410b57cec5SDimitry Andric for (const auto &Child : Node.Children) {
2420b57cec5SDimitry Andric if (Child.SelectionKind == Kind)
2430b57cec5SDimitry Andric return true;
2440b57cec5SDimitry Andric if (Child.SelectionKind == SourceSelectionKind::None)
2450b57cec5SDimitry Andric return hasAnyDirectChildrenWithKind(Child, Kind);
2460b57cec5SDimitry Andric }
2470b57cec5SDimitry Andric return false;
2480b57cec5SDimitry Andric }
2490b57cec5SDimitry Andric
2500b57cec5SDimitry Andric namespace {
2510b57cec5SDimitry Andric struct SelectedNodeWithParents {
2520b57cec5SDimitry Andric SelectedASTNode::ReferenceType Node;
2530b57cec5SDimitry Andric llvm::SmallVector<SelectedASTNode::ReferenceType, 8> Parents;
2540b57cec5SDimitry Andric
2550b57cec5SDimitry Andric /// Canonicalizes the given selection by selecting different related AST nodes
2560b57cec5SDimitry Andric /// when it makes sense to do so.
2570b57cec5SDimitry Andric void canonicalize();
2580b57cec5SDimitry Andric };
2590b57cec5SDimitry Andric
2600b57cec5SDimitry Andric enum SelectionCanonicalizationAction { KeepSelection, SelectParent };
2610b57cec5SDimitry Andric
2620b57cec5SDimitry Andric /// Returns the canonicalization action which should be applied to the
2630b57cec5SDimitry Andric /// selected statement.
2640b57cec5SDimitry Andric SelectionCanonicalizationAction
getSelectionCanonizalizationAction(const Stmt * S,const Stmt * Parent)2650b57cec5SDimitry Andric getSelectionCanonizalizationAction(const Stmt *S, const Stmt *Parent) {
2660b57cec5SDimitry Andric // Select the parent expression when:
2670b57cec5SDimitry Andric // - The string literal in ObjC string literal is selected, e.g.:
2680b57cec5SDimitry Andric // @"test" becomes @"test"
2690b57cec5SDimitry Andric // ~~~~~~ ~~~~~~~
2700b57cec5SDimitry Andric if (isa<StringLiteral>(S) && isa<ObjCStringLiteral>(Parent))
2710b57cec5SDimitry Andric return SelectParent;
2720b57cec5SDimitry Andric // The entire call should be selected when just the member expression
2730b57cec5SDimitry Andric // that refers to the method or the decl ref that refers to the function
2740b57cec5SDimitry Andric // is selected.
2750b57cec5SDimitry Andric // f.call(args) becomes f.call(args)
2760b57cec5SDimitry Andric // ~~~~ ~~~~~~~~~~~~
2770b57cec5SDimitry Andric // func(args) becomes func(args)
2780b57cec5SDimitry Andric // ~~~~ ~~~~~~~~~~
2790b57cec5SDimitry Andric else if (const auto *CE = dyn_cast<CallExpr>(Parent)) {
2800b57cec5SDimitry Andric if ((isa<MemberExpr>(S) || isa<DeclRefExpr>(S)) &&
2810b57cec5SDimitry Andric CE->getCallee()->IgnoreImpCasts() == S)
2820b57cec5SDimitry Andric return SelectParent;
2830b57cec5SDimitry Andric }
2840b57cec5SDimitry Andric // FIXME: Syntactic form -> Entire pseudo-object expr.
2850b57cec5SDimitry Andric return KeepSelection;
2860b57cec5SDimitry Andric }
2870b57cec5SDimitry Andric
2880b57cec5SDimitry Andric } // end anonymous namespace
2890b57cec5SDimitry Andric
canonicalize()2900b57cec5SDimitry Andric void SelectedNodeWithParents::canonicalize() {
2910b57cec5SDimitry Andric const Stmt *S = Node.get().Node.get<Stmt>();
2920b57cec5SDimitry Andric assert(S && "non statement selection!");
2930b57cec5SDimitry Andric const Stmt *Parent = Parents[Parents.size() - 1].get().Node.get<Stmt>();
2940b57cec5SDimitry Andric if (!Parent)
2950b57cec5SDimitry Andric return;
2960b57cec5SDimitry Andric
2970b57cec5SDimitry Andric // Look through the implicit casts in the parents.
2980b57cec5SDimitry Andric unsigned ParentIndex = 1;
2990b57cec5SDimitry Andric for (; (ParentIndex + 1) <= Parents.size() && isa<ImplicitCastExpr>(Parent);
3000b57cec5SDimitry Andric ++ParentIndex) {
3010b57cec5SDimitry Andric const Stmt *NewParent =
3020b57cec5SDimitry Andric Parents[Parents.size() - ParentIndex - 1].get().Node.get<Stmt>();
3030b57cec5SDimitry Andric if (!NewParent)
3040b57cec5SDimitry Andric break;
3050b57cec5SDimitry Andric Parent = NewParent;
3060b57cec5SDimitry Andric }
3070b57cec5SDimitry Andric
3080b57cec5SDimitry Andric switch (getSelectionCanonizalizationAction(S, Parent)) {
3090b57cec5SDimitry Andric case SelectParent:
3100b57cec5SDimitry Andric Node = Parents[Parents.size() - ParentIndex];
3110b57cec5SDimitry Andric for (; ParentIndex != 0; --ParentIndex)
3120b57cec5SDimitry Andric Parents.pop_back();
3130b57cec5SDimitry Andric break;
3140b57cec5SDimitry Andric case KeepSelection:
3150b57cec5SDimitry Andric break;
3160b57cec5SDimitry Andric }
3170b57cec5SDimitry Andric }
3180b57cec5SDimitry Andric
3190b57cec5SDimitry Andric /// Finds the set of bottom-most selected AST nodes that are in the selection
3200b57cec5SDimitry Andric /// tree with the specified selection kind.
3210b57cec5SDimitry Andric ///
3220b57cec5SDimitry Andric /// For example, given the following selection tree:
3230b57cec5SDimitry Andric ///
3240b57cec5SDimitry Andric /// FunctionDecl "f" contains-selection
3250b57cec5SDimitry Andric /// CompoundStmt contains-selection [#1]
3260b57cec5SDimitry Andric /// CallExpr inside
3270b57cec5SDimitry Andric /// ImplicitCastExpr inside
3280b57cec5SDimitry Andric /// DeclRefExpr inside
3290b57cec5SDimitry Andric /// IntegerLiteral inside
3300b57cec5SDimitry Andric /// IntegerLiteral inside
3310b57cec5SDimitry Andric /// FunctionDecl "f2" contains-selection
3320b57cec5SDimitry Andric /// CompoundStmt contains-selection [#2]
3330b57cec5SDimitry Andric /// CallExpr inside
3340b57cec5SDimitry Andric /// ImplicitCastExpr inside
3350b57cec5SDimitry Andric /// DeclRefExpr inside
3360b57cec5SDimitry Andric /// IntegerLiteral inside
3370b57cec5SDimitry Andric /// IntegerLiteral inside
3380b57cec5SDimitry Andric ///
3390b57cec5SDimitry Andric /// This function will find references to nodes #1 and #2 when searching for the
3400b57cec5SDimitry Andric /// \c ContainsSelection kind.
findDeepestWithKind(const SelectedASTNode & ASTSelection,llvm::SmallVectorImpl<SelectedNodeWithParents> & MatchingNodes,SourceSelectionKind Kind,llvm::SmallVectorImpl<SelectedASTNode::ReferenceType> & ParentStack)3410b57cec5SDimitry Andric static void findDeepestWithKind(
3420b57cec5SDimitry Andric const SelectedASTNode &ASTSelection,
3430b57cec5SDimitry Andric llvm::SmallVectorImpl<SelectedNodeWithParents> &MatchingNodes,
3440b57cec5SDimitry Andric SourceSelectionKind Kind,
3450b57cec5SDimitry Andric llvm::SmallVectorImpl<SelectedASTNode::ReferenceType> &ParentStack) {
3460b57cec5SDimitry Andric if (ASTSelection.Node.get<DeclStmt>()) {
3470b57cec5SDimitry Andric // Select the entire decl stmt when any of its child declarations is the
3480b57cec5SDimitry Andric // bottom-most.
3490b57cec5SDimitry Andric for (const auto &Child : ASTSelection.Children) {
3500b57cec5SDimitry Andric if (!hasAnyDirectChildrenWithKind(Child, Kind)) {
3510b57cec5SDimitry Andric MatchingNodes.push_back(SelectedNodeWithParents{
3520b57cec5SDimitry Andric std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}});
3530b57cec5SDimitry Andric return;
3540b57cec5SDimitry Andric }
3550b57cec5SDimitry Andric }
3560b57cec5SDimitry Andric } else {
3570b57cec5SDimitry Andric if (!hasAnyDirectChildrenWithKind(ASTSelection, Kind)) {
3580b57cec5SDimitry Andric // This node is the bottom-most.
3590b57cec5SDimitry Andric MatchingNodes.push_back(SelectedNodeWithParents{
3600b57cec5SDimitry Andric std::cref(ASTSelection), {ParentStack.begin(), ParentStack.end()}});
3610b57cec5SDimitry Andric return;
3620b57cec5SDimitry Andric }
3630b57cec5SDimitry Andric }
3640b57cec5SDimitry Andric // Search in the children.
3650b57cec5SDimitry Andric ParentStack.push_back(std::cref(ASTSelection));
3660b57cec5SDimitry Andric for (const auto &Child : ASTSelection.Children)
3670b57cec5SDimitry Andric findDeepestWithKind(Child, MatchingNodes, Kind, ParentStack);
3680b57cec5SDimitry Andric ParentStack.pop_back();
3690b57cec5SDimitry Andric }
3700b57cec5SDimitry Andric
findDeepestWithKind(const SelectedASTNode & ASTSelection,llvm::SmallVectorImpl<SelectedNodeWithParents> & MatchingNodes,SourceSelectionKind Kind)3710b57cec5SDimitry Andric static void findDeepestWithKind(
3720b57cec5SDimitry Andric const SelectedASTNode &ASTSelection,
3730b57cec5SDimitry Andric llvm::SmallVectorImpl<SelectedNodeWithParents> &MatchingNodes,
3740b57cec5SDimitry Andric SourceSelectionKind Kind) {
3750b57cec5SDimitry Andric llvm::SmallVector<SelectedASTNode::ReferenceType, 16> ParentStack;
3760b57cec5SDimitry Andric findDeepestWithKind(ASTSelection, MatchingNodes, Kind, ParentStack);
3770b57cec5SDimitry Andric }
3780b57cec5SDimitry Andric
379*bdd1243dSDimitry Andric std::optional<CodeRangeASTSelection>
create(SourceRange SelectionRange,const SelectedASTNode & ASTSelection)3800b57cec5SDimitry Andric CodeRangeASTSelection::create(SourceRange SelectionRange,
3810b57cec5SDimitry Andric const SelectedASTNode &ASTSelection) {
3820b57cec5SDimitry Andric // Code range is selected when the selection range is not empty.
3830b57cec5SDimitry Andric if (SelectionRange.getBegin() == SelectionRange.getEnd())
384*bdd1243dSDimitry Andric return std::nullopt;
3850b57cec5SDimitry Andric llvm::SmallVector<SelectedNodeWithParents, 4> ContainSelection;
3860b57cec5SDimitry Andric findDeepestWithKind(ASTSelection, ContainSelection,
3870b57cec5SDimitry Andric SourceSelectionKind::ContainsSelection);
3880b57cec5SDimitry Andric // We are looking for a selection in one body of code, so let's focus on
3890b57cec5SDimitry Andric // one matching result.
3900b57cec5SDimitry Andric if (ContainSelection.size() != 1)
391*bdd1243dSDimitry Andric return std::nullopt;
3920b57cec5SDimitry Andric SelectedNodeWithParents &Selected = ContainSelection[0];
3930b57cec5SDimitry Andric if (!Selected.Node.get().Node.get<Stmt>())
394*bdd1243dSDimitry Andric return std::nullopt;
3950b57cec5SDimitry Andric const Stmt *CodeRangeStmt = Selected.Node.get().Node.get<Stmt>();
3960b57cec5SDimitry Andric if (!isa<CompoundStmt>(CodeRangeStmt)) {
3970b57cec5SDimitry Andric Selected.canonicalize();
3980b57cec5SDimitry Andric return CodeRangeASTSelection(Selected.Node, Selected.Parents,
3990b57cec5SDimitry Andric /*AreChildrenSelected=*/false);
4000b57cec5SDimitry Andric }
4010b57cec5SDimitry Andric // FIXME (Alex L): First selected SwitchCase means that first case statement.
4020b57cec5SDimitry Andric // is selected actually
4030b57cec5SDimitry Andric // (See https://github.com/apple/swift-clang & CompoundStmtRange).
4040b57cec5SDimitry Andric
4050b57cec5SDimitry Andric // FIXME (Alex L): Tweak selection rules for compound statements, see:
4060b57cec5SDimitry Andric // https://github.com/apple/swift-clang/blob/swift-4.1-branch/lib/Tooling/
4070b57cec5SDimitry Andric // Refactor/ASTSlice.cpp#L513
4080b57cec5SDimitry Andric // The user selected multiple statements in a compound statement.
4090b57cec5SDimitry Andric Selected.Parents.push_back(Selected.Node);
4100b57cec5SDimitry Andric return CodeRangeASTSelection(Selected.Node, Selected.Parents,
4110b57cec5SDimitry Andric /*AreChildrenSelected=*/true);
4120b57cec5SDimitry Andric }
4130b57cec5SDimitry Andric
isFunctionLikeDeclaration(const Decl * D)4140b57cec5SDimitry Andric static bool isFunctionLikeDeclaration(const Decl *D) {
4150b57cec5SDimitry Andric // FIXME (Alex L): Test for BlockDecl.
4160b57cec5SDimitry Andric return isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D);
4170b57cec5SDimitry Andric }
4180b57cec5SDimitry Andric
isInFunctionLikeBodyOfCode() const4190b57cec5SDimitry Andric bool CodeRangeASTSelection::isInFunctionLikeBodyOfCode() const {
4200b57cec5SDimitry Andric bool IsPrevCompound = false;
4210b57cec5SDimitry Andric // Scan through the parents (bottom-to-top) and check if the selection is
4220b57cec5SDimitry Andric // contained in a compound statement that's a body of a function/method
4230b57cec5SDimitry Andric // declaration.
4240b57cec5SDimitry Andric for (const auto &Parent : llvm::reverse(Parents)) {
4250b57cec5SDimitry Andric const DynTypedNode &Node = Parent.get().Node;
4260b57cec5SDimitry Andric if (const auto *D = Node.get<Decl>()) {
4270b57cec5SDimitry Andric if (isFunctionLikeDeclaration(D))
4280b57cec5SDimitry Andric return IsPrevCompound;
4290b57cec5SDimitry Andric // Stop the search at any type declaration to avoid returning true for
4300b57cec5SDimitry Andric // expressions in type declarations in functions, like:
4310b57cec5SDimitry Andric // function foo() { struct X {
4320b57cec5SDimitry Andric // int m = /*selection:*/ 1 + 2 /*selection end*/; }; };
4330b57cec5SDimitry Andric if (isa<TypeDecl>(D))
4340b57cec5SDimitry Andric return false;
4350b57cec5SDimitry Andric }
4360b57cec5SDimitry Andric IsPrevCompound = Node.get<CompoundStmt>() != nullptr;
4370b57cec5SDimitry Andric }
4380b57cec5SDimitry Andric return false;
4390b57cec5SDimitry Andric }
4400b57cec5SDimitry Andric
getFunctionLikeNearestParent() const4410b57cec5SDimitry Andric const Decl *CodeRangeASTSelection::getFunctionLikeNearestParent() const {
4420b57cec5SDimitry Andric for (const auto &Parent : llvm::reverse(Parents)) {
4430b57cec5SDimitry Andric const DynTypedNode &Node = Parent.get().Node;
4440b57cec5SDimitry Andric if (const auto *D = Node.get<Decl>()) {
4450b57cec5SDimitry Andric if (isFunctionLikeDeclaration(D))
4460b57cec5SDimitry Andric return D;
4470b57cec5SDimitry Andric }
4480b57cec5SDimitry Andric }
4490b57cec5SDimitry Andric return nullptr;
4500b57cec5SDimitry Andric }
451