//=- ReachableCodePathInsensitive.cpp ---------------------------*- C++ --*-==// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file implements a flow-sensitive, path-insensitive analysis of // determining reachable blocks within a CFG. // //===----------------------------------------------------------------------===// #include "llvm/ADT/BitVector.h" #include "llvm/ADT/SmallVector.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/StmtCXX.h" #include "clang/Analysis/Analyses/ReachableCode.h" #include "clang/Analysis/CFG.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/SourceManager.h" using namespace clang; static SourceLocation GetUnreachableLoc(const CFGBlock &b, SourceRange &R1, SourceRange &R2) { const Stmt *S = 0; unsigned sn = 0; R1 = R2 = SourceRange(); if (sn < b.size()) { const CFGStmt *CS = b[sn].getAs(); if (!CS) return SourceLocation(); S = CS->getStmt(); } else if (b.getTerminator()) S = b.getTerminator(); else return SourceLocation(); if (const Expr *Ex = dyn_cast(S)) S = Ex->IgnoreParenImpCasts(); switch (S->getStmtClass()) { case Expr::BinaryOperatorClass: { const BinaryOperator *BO = cast(S); if (BO->getOpcode() == BO_Comma) { if (sn+1 < b.size()) return b[sn+1].getAs()->getStmt()->getLocStart(); const CFGBlock *n = &b; while (1) { if (n->getTerminator()) return n->getTerminator()->getLocStart(); if (n->succ_size() != 1) return SourceLocation(); n = n[0].succ_begin()[0]; if (n->pred_size() != 1) return SourceLocation(); if (!n->empty()) return n[0][0].getAs()->getStmt()->getLocStart(); } } R1 = BO->getLHS()->getSourceRange(); R2 = BO->getRHS()->getSourceRange(); return BO->getOperatorLoc(); } case Expr::UnaryOperatorClass: { const UnaryOperator *UO = cast(S); R1 = UO->getSubExpr()->getSourceRange(); return UO->getOperatorLoc(); } case Expr::CompoundAssignOperatorClass: { const CompoundAssignOperator *CAO = cast(S); R1 = CAO->getLHS()->getSourceRange(); R2 = CAO->getRHS()->getSourceRange(); return CAO->getOperatorLoc(); } case Expr::BinaryConditionalOperatorClass: case Expr::ConditionalOperatorClass: { const AbstractConditionalOperator *CO = cast(S); return CO->getQuestionLoc(); } case Expr::MemberExprClass: { const MemberExpr *ME = cast(S); R1 = ME->getSourceRange(); return ME->getMemberLoc(); } case Expr::ArraySubscriptExprClass: { const ArraySubscriptExpr *ASE = cast(S); R1 = ASE->getLHS()->getSourceRange(); R2 = ASE->getRHS()->getSourceRange(); return ASE->getRBracketLoc(); } case Expr::CStyleCastExprClass: { const CStyleCastExpr *CSC = cast(S); R1 = CSC->getSubExpr()->getSourceRange(); return CSC->getLParenLoc(); } case Expr::CXXFunctionalCastExprClass: { const CXXFunctionalCastExpr *CE = cast (S); R1 = CE->getSubExpr()->getSourceRange(); return CE->getTypeBeginLoc(); } case Stmt::CXXTryStmtClass: { return cast(S)->getHandler(0)->getCatchLoc(); } case Expr::ObjCBridgedCastExprClass: { const ObjCBridgedCastExpr *CSC = cast(S); R1 = CSC->getSubExpr()->getSourceRange(); return CSC->getLParenLoc(); } default: ; } R1 = S->getSourceRange(); return S->getLocStart(); } static SourceLocation MarkLiveTop(const CFGBlock *Start, llvm::BitVector &reachable, SourceManager &SM) { // Prep work worklist. llvm::SmallVector WL; WL.push_back(Start); SourceRange R1, R2; SourceLocation top = GetUnreachableLoc(*Start, R1, R2); bool FromMainFile = false; bool FromSystemHeader = false; bool TopValid = false; if (top.isValid()) { FromMainFile = SM.isFromMainFile(top); FromSystemHeader = SM.isInSystemHeader(top); TopValid = true; } // Solve CFGBlock::FilterOptions FO; FO.IgnoreDefaultsWithCoveredEnums = 1; while (!WL.empty()) { const CFGBlock *item = WL.back(); WL.pop_back(); SourceLocation c = GetUnreachableLoc(*item, R1, R2); if (c.isValid() && (!TopValid || (SM.isFromMainFile(c) && !FromMainFile) || (FromSystemHeader && !SM.isInSystemHeader(c)) || SM.isBeforeInTranslationUnit(c, top))) { top = c; FromMainFile = SM.isFromMainFile(top); FromSystemHeader = SM.isInSystemHeader(top); } reachable.set(item->getBlockID()); for (CFGBlock::filtered_succ_iterator I = item->filtered_succ_start_end(FO); I.hasMore(); ++I) if (const CFGBlock *B = *I) { unsigned blockID = B->getBlockID(); if (!reachable[blockID]) { reachable.set(blockID); WL.push_back(B); } } } return top; } static int LineCmp(const void *p1, const void *p2) { SourceLocation *Line1 = (SourceLocation *)p1; SourceLocation *Line2 = (SourceLocation *)p2; return !(*Line1 < *Line2); } namespace { struct ErrLoc { SourceLocation Loc; SourceRange R1; SourceRange R2; ErrLoc(SourceLocation l, SourceRange r1, SourceRange r2) : Loc(l), R1(r1), R2(r2) { } }; } namespace clang { namespace reachable_code { /// ScanReachableFromBlock - Mark all blocks reachable from Start. /// Returns the total number of blocks that were marked reachable. unsigned ScanReachableFromBlock(const CFGBlock &Start, llvm::BitVector &Reachable) { unsigned count = 0; llvm::SmallVector WL; // Prep work queue Reachable.set(Start.getBlockID()); ++count; WL.push_back(&Start); // Find the reachable blocks from 'Start'. CFGBlock::FilterOptions FO; FO.IgnoreDefaultsWithCoveredEnums = 1; while (!WL.empty()) { const CFGBlock *item = WL.back(); WL.pop_back(); // Look at the successors and mark then reachable. for (CFGBlock::filtered_succ_iterator I= item->filtered_succ_start_end(FO); I.hasMore(); ++I) if (const CFGBlock *B = *I) { unsigned blockID = B->getBlockID(); if (!Reachable[blockID]) { Reachable.set(blockID); ++count; WL.push_back(B); } } } return count; } void FindUnreachableCode(AnalysisContext &AC, Callback &CB) { CFG *cfg = AC.getCFG(); if (!cfg) return; // Scan for reachable blocks. llvm::BitVector reachable(cfg->getNumBlockIDs()); unsigned numReachable = ScanReachableFromBlock(cfg->getEntry(), reachable); // If there are no unreachable blocks, we're done. if (numReachable == cfg->getNumBlockIDs()) return; SourceRange R1, R2; llvm::SmallVector lines; bool AddEHEdges = AC.getAddEHEdges(); // First, give warnings for blocks with no predecessors, as they // can't be part of a loop. for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { CFGBlock &b = **I; if (!reachable[b.getBlockID()]) { if (b.pred_empty()) { if (!AddEHEdges && dyn_cast_or_null(b.getTerminator().getStmt())) { // When not adding EH edges from calls, catch clauses // can otherwise seem dead. Avoid noting them as dead. numReachable += ScanReachableFromBlock(b, reachable); continue; } SourceLocation c = GetUnreachableLoc(b, R1, R2); if (!c.isValid()) { // Blocks without a location can't produce a warning, so don't mark // reachable blocks from here as live. reachable.set(b.getBlockID()); ++numReachable; continue; } lines.push_back(ErrLoc(c, R1, R2)); // Avoid excessive errors by marking everything reachable from here numReachable += ScanReachableFromBlock(b, reachable); } } } if (numReachable < cfg->getNumBlockIDs()) { // And then give warnings for the tops of loops. for (CFG::iterator I = cfg->begin(), E = cfg->end(); I != E; ++I) { CFGBlock &b = **I; if (!reachable[b.getBlockID()]) // Avoid excessive errors by marking everything reachable from here lines.push_back(ErrLoc(MarkLiveTop(&b, reachable, AC.getASTContext().getSourceManager()), SourceRange(), SourceRange())); } } llvm::array_pod_sort(lines.begin(), lines.end(), LineCmp); for (llvm::SmallVectorImpl::iterator I=lines.begin(), E=lines.end(); I != E; ++I) if (I->Loc.isValid()) CB.HandleUnreachable(I->Loc, I->R1, I->R2); } }} // end namespace clang::reachable_code