llvm-project/clang/lib/Analysis/ReachableCode.cpp
Chandler Carruth b35635e942 Remove a kludge from analysis based warnings that used to detect
temporaries with no-return destructors. The CFG now properly supports
temporaries and implicit destructors which both makes this kludge no
longer work, and conveniently removes the need for it.

Turn on CFG handling of implicit destructors and initializers. Several
ad-hoc benchmarks don't indicate any measurable performance impact from
growing the CFG, and it fixes real correctness problems with warnings.

As a result of turning on these CFG elements, we started to tickle an
inf-loop in the unreachable code logic used for warnings. The fix is
trivial.

llvm-svn: 123056
2011-01-08 06:54:40 +00:00

289 lines
8.9 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/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()) {
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::ConditionalOperatorClass: {
const ConditionalOperator *CO = cast<ConditionalOperator>(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();
}
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