| //===--- ExtractionUtils.cpp - Extraction helper functions ----------------===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ExtractionUtils.h" |
| #include "clang/AST/Expr.h" |
| #include "clang/AST/ExprObjC.h" |
| #include "clang/AST/RecursiveASTVisitor.h" |
| #include "llvm/ADT/SmallPtrSet.h" |
| #include "llvm/Support/SaveAndRestore.h" |
| |
| using namespace clang; |
| |
| Optional<StringRef> tooling::extract::nameForExtractedVariable(const Expr *E) { |
| if (const auto *Call = dyn_cast<CallExpr>(E)) { |
| if (const auto *Fn = Call->getDirectCallee()) |
| return Fn->getName(); |
| } else if (const auto *Msg = dyn_cast<ObjCMessageExpr>(E)) { |
| if (const auto *M = Msg->getMethodDecl()) { |
| if (M->getSelector().isUnarySelector()) |
| return M->getSelector().getNameForSlot(0); |
| } |
| } else if (const auto *PRE = dyn_cast<ObjCPropertyRefExpr>(E)) { |
| if (PRE->isImplicitProperty()) { |
| if (const auto *M = PRE->getImplicitPropertyGetter()) |
| return M->getSelector().getNameForSlot(0); |
| } else if (const auto *Prop = PRE->getExplicitProperty()) |
| return Prop->getName(); |
| } |
| return None; |
| } |
| |
| namespace { |
| |
| /// Checks if a set of expressions is directly contained in some AST region. |
| class StmtReachabilityChecker |
| : public RecursiveASTVisitor<StmtReachabilityChecker> { |
| const llvm::SmallPtrSetImpl<const Stmt *> &Expressions; |
| unsigned Count = 0; |
| |
| StmtReachabilityChecker( |
| const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) |
| : Expressions(Expressions) {} |
| |
| bool areAllExpressionsReached() const { return Count == Expressions.size(); } |
| |
| public: |
| bool VisitStmt(const Stmt *S) { |
| if (Expressions.count(S)) { |
| ++Count; |
| if (areAllExpressionsReached()) |
| return false; |
| } |
| return true; |
| } |
| |
| static bool areAllExpressionsReachableFrom( |
| CompoundStmt *S, const llvm::SmallPtrSetImpl<const Stmt *> &Expressions) { |
| StmtReachabilityChecker Checker(Expressions); |
| Checker.TraverseStmt(S); |
| return Checker.areAllExpressionsReached(); |
| } |
| }; |
| |
| /// Figures out where the extracted variable should go. |
| class ExtractedVariableInsertionLocFinder |
| : public RecursiveASTVisitor<ExtractedVariableInsertionLocFinder> { |
| llvm::SmallPtrSet<const Stmt *, 4> Expressions; |
| llvm::SmallVector<std::pair<CompoundStmt *, const Stmt *>, 4> |
| InsertionCandidateStack; |
| bool IsPrevCompoundStmt = false; |
| |
| public: |
| SourceLocation Loc; |
| |
| /// Initializes the insertion location finder using the set of duplicate |
| /// \p Expressions from one function. |
| ExtractedVariableInsertionLocFinder(ArrayRef<const Expr *> Expressions) { |
| for (const Expr *E : Expressions) |
| this->Expressions.insert(E); |
| } |
| |
| bool TraverseStmt(Stmt *S) { |
| if (!S) |
| return RecursiveASTVisitor::TraverseStmt(S); |
| if (IsPrevCompoundStmt && !InsertionCandidateStack.empty()) |
| InsertionCandidateStack.back().second = S; |
| llvm::SaveAndRestore<bool> IsPrevCompoundStmtTracker(IsPrevCompoundStmt, |
| false); |
| if (auto *CS = dyn_cast<CompoundStmt>(S)) { |
| IsPrevCompoundStmt = true; |
| InsertionCandidateStack.emplace_back(CS, nullptr); |
| RecursiveASTVisitor::TraverseStmt(S); |
| InsertionCandidateStack.pop_back(); |
| return true; |
| } |
| return RecursiveASTVisitor::TraverseStmt(S); |
| } |
| |
| bool VisitStmt(const Stmt *S) { |
| if (Expressions.count(S)) { |
| // The insertion location should be in the first compound statement that |
| // includes all of the expressions as descendants as we want the new |
| // variable to be visible to all uses. |
| for (auto I = InsertionCandidateStack.rbegin(), |
| E = InsertionCandidateStack.rend(); |
| I != E; ++I) { |
| if (StmtReachabilityChecker::areAllExpressionsReachableFrom( |
| I->first, Expressions) && |
| I->second) { |
| Loc = I->second->getLocStart(); |
| break; |
| } |
| } |
| return false; |
| } |
| return true; |
| } |
| }; |
| |
| } // end anonymous namespace |
| |
| SourceLocation tooling::extract::locationForExtractedVariableDeclaration( |
| ArrayRef<const Expr *> Expressions, const Decl *ParentDecl, |
| const SourceManager &SM) { |
| ExtractedVariableInsertionLocFinder LocFinder(Expressions); |
| LocFinder.TraverseDecl(const_cast<Decl *>(ParentDecl)); |
| SourceLocation Result = LocFinder.Loc; |
| if (Result.isValid() && Result.isMacroID()) |
| return SM.getExpansionLoc(Result); |
| return Result; |
| } |