forked from OSchip/llvm-project
297 lines
9.2 KiB
C++
297 lines
9.2 KiB
C++
//=- 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<CFGStmt>();
|
|
if (!CS)
|
|
return SourceLocation();
|
|
|
|
S = CS->getStmt();
|
|
} else if (b.getTerminator())
|
|
S = b.getTerminator();
|
|
else
|
|
return SourceLocation();
|
|
|
|
if (const Expr *Ex = dyn_cast<Expr>(S))
|
|
S = Ex->IgnoreParenImpCasts();
|
|
|
|
switch (S->getStmtClass()) {
|
|
case Expr::BinaryOperatorClass: {
|
|
const BinaryOperator *BO = cast<BinaryOperator>(S);
|
|
if (BO->getOpcode() == BO_Comma) {
|
|
if (sn+1 < b.size())
|
|
return b[sn+1].getAs<CFGStmt>()->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<CFGStmt>()->getStmt()->getLocStart();
|
|
}
|
|
}
|
|
R1 = BO->getLHS()->getSourceRange();
|
|
R2 = BO->getRHS()->getSourceRange();
|
|
return BO->getOperatorLoc();
|
|
}
|
|
case Expr::UnaryOperatorClass: {
|
|
const UnaryOperator *UO = cast<UnaryOperator>(S);
|
|
R1 = UO->getSubExpr()->getSourceRange();
|
|
return UO->getOperatorLoc();
|
|
}
|
|
case Expr::CompoundAssignOperatorClass: {
|
|
const CompoundAssignOperator *CAO = cast<CompoundAssignOperator>(S);
|
|
R1 = CAO->getLHS()->getSourceRange();
|
|
R2 = CAO->getRHS()->getSourceRange();
|
|
return CAO->getOperatorLoc();
|
|
}
|
|
case Expr::BinaryConditionalOperatorClass:
|
|
case Expr::ConditionalOperatorClass: {
|
|
const AbstractConditionalOperator *CO =
|
|
cast<AbstractConditionalOperator>(S);
|
|
return CO->getQuestionLoc();
|
|
}
|
|
case Expr::MemberExprClass: {
|
|
const MemberExpr *ME = cast<MemberExpr>(S);
|
|
R1 = ME->getSourceRange();
|
|
return ME->getMemberLoc();
|
|
}
|
|
case Expr::ArraySubscriptExprClass: {
|
|
const ArraySubscriptExpr *ASE = cast<ArraySubscriptExpr>(S);
|
|
R1 = ASE->getLHS()->getSourceRange();
|
|
R2 = ASE->getRHS()->getSourceRange();
|
|
return ASE->getRBracketLoc();
|
|
}
|
|
case Expr::CStyleCastExprClass: {
|
|
const CStyleCastExpr *CSC = cast<CStyleCastExpr>(S);
|
|
R1 = CSC->getSubExpr()->getSourceRange();
|
|
return CSC->getLParenLoc();
|
|
}
|
|
case Expr::CXXFunctionalCastExprClass: {
|
|
const CXXFunctionalCastExpr *CE = cast <CXXFunctionalCastExpr>(S);
|
|
R1 = CE->getSubExpr()->getSourceRange();
|
|
return CE->getTypeBeginLoc();
|
|
}
|
|
case Stmt::CXXTryStmtClass: {
|
|
return cast<CXXTryStmt>(S)->getHandler(0)->getCatchLoc();
|
|
}
|
|
case Expr::ObjCBridgedCastExprClass: {
|
|
const ObjCBridgedCastExpr *CSC = cast<ObjCBridgedCastExpr>(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<const CFGBlock*, 32> 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<const CFGBlock*, 32> 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<ErrLoc, 24> 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<CXXTryStmt>(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<ErrLoc>::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
|