"git@repo.hca.bsc.es:rferrer/llvm-epi-0.8.git" did not exist on "27a3631bac2931fc0daa877e6015bebe599a0399"
Newer
Older
//==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file defines a DeadStores, a flow-sensitive checker that looks for
// stores to variables that are no longer live.
//
//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis
committed
#include "ClangSACheckers.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h"
Ted Kremenek
committed
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Ted Kremenek
committed
#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
Ted Kremenek
committed
#include "llvm/ADT/SmallPtrSet.h"
using namespace clang;
namespace {
Ted Kremenek
committed
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
// FIXME: Eventually migrate into its own file, and have it managed by
// AnalysisManager.
class ReachableCode {
const CFG &cfg;
llvm::BitVector reachable;
public:
ReachableCode(const CFG &cfg)
: cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
void computeReachableBlocks();
bool isReachable(const CFGBlock *block) const {
return reachable[block->getBlockID()];
}
};
}
void ReachableCode::computeReachableBlocks() {
if (!cfg.getNumBlockIDs())
return;
llvm::SmallVector<const CFGBlock*, 10> worklist;
worklist.push_back(&cfg.getEntry());
while (!worklist.empty()) {
const CFGBlock *block = worklist.back();
worklist.pop_back();
llvm::BitVector::reference isReachable = reachable[block->getBlockID()];
if (isReachable)
continue;
isReachable = true;
for (CFGBlock::const_succ_iterator i = block->succ_begin(),
e = block->succ_end(); i != e; ++i)
if (const CFGBlock *succ = *i)
worklist.push_back(succ);
}
}
namespace {
Kovarththanan Rajaratnam
committed
class DeadStoreObs : public LiveVariables::ObserverTy {
Ted Kremenek
committed
const CFG &cfg;
ASTContext &Ctx;
Ted Kremenek
committed
BugReporter& BR;
Ted Kremenek
committed
llvm::SmallPtrSet<VarDecl*, 20> Escaped;
Ted Kremenek
committed
llvm::OwningPtr<ReachableCode> reachableCode;
const CFGBlock *currentBlock;
Ted Kremenek
committed
enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
public:
Ted Kremenek
committed
DeadStoreObs(const CFG &cfg, ASTContext &ctx,
BugReporter& br, ParentMap& parents,
Ted Kremenek
committed
llvm::SmallPtrSet<VarDecl*, 20> &escaped)
Ted Kremenek
committed
: cfg(cfg), Ctx(ctx), BR(br), Parents(parents),
Escaped(escaped), currentBlock(0) {}
virtual ~DeadStoreObs() {}
Ted Kremenek
committed
Ted Kremenek
committed
void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) {
Ted Kremenek
committed
if (Escaped.count(V))
return;
Ted Kremenek
committed
// Compute reachable blocks within the CFG for trivial cases
// where a bogus dead store can be reported because itself is unreachable.
if (!reachableCode.get()) {
reachableCode.reset(new ReachableCode(cfg));
reachableCode->computeReachableBlocks();
}
if (!reachableCode->isReachable(currentBlock))
return;
Ted Kremenek
committed
Ted Kremenek
committed
const std::string &name = V->getNameAsString();
Ted Kremenek
committed
const char* BugType = 0;
std::string msg;
Ted Kremenek
committed
switch (dsk) {
default:
assert(false && "Impossible dead store type.");
Ted Kremenek
committed
case DeadInit:
Ted Kremenek
committed
msg = "Value stored to '" + name +
"' during its initialization is never read";
break;
Ted Kremenek
committed
case DeadIncrement:
Ted Kremenek
committed
case Standard:
if (!BugType) BugType = "Dead assignment";
Ted Kremenek
committed
msg = "Value stored to '" + name + "' is never read";
break;
Ted Kremenek
committed
case Enclosing:
// Don't report issues in this case, e.g.: "if (x = foo())",
// where 'x' is unused later. We have yet to see a case where
// this is a real bug.
return;
BR.EmitBasicReport(BugType, "Dead store", msg, L, R);
void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
Ted Kremenek
committed
DeadStoreKind dsk,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
if (!VD->hasLocalStorage())
return;
// Reference types confuse the dead stores checker. Skip them
// for now.
if (VD->getType()->getAs<ReferenceType>())
return;
if (!Live(VD, AD) &&
Ted Kremenek
committed
!(VD->getAttr<UnusedAttr>() || VD->getAttr<BlocksAttr>()))
Ted Kremenek
committed
Report(VD, dsk, Ex->getSourceRange().getBegin(),
Ted Kremenek
committed
void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
Ted Kremenek
committed
CheckVarDecl(VD, DR, Val, dsk, AD, Live);
}
Ted Kremenek
committed
bool isIncrement(VarDecl* VD, BinaryOperator* B) {
if (B->isCompoundAssignmentOp())
return true;
Ted Kremenek
committed
Expr* RHS = B->getRHS()->IgnoreParenCasts();
BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
Ted Kremenek
committed
if (!BRHS)
return false;
Ted Kremenek
committed
DeclRefExpr *DR;
Ted Kremenek
committed
if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
if (DR->getDecl() == VD)
return true;
Ted Kremenek
committed
if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
if (DR->getDecl() == VD)
return true;
Ted Kremenek
committed
return false;
Ted Kremenek
committed
virtual void ObserveStmt(Stmt* S, const CFGBlock *block,
const LiveVariables::AnalysisDataTy& AD,
const LiveVariables::ValTy& Live) {
Ted Kremenek
committed
currentBlock = block;
// Skip statements in macros.
if (S->getLocStart().isMacroID())
return;
Ted Kremenek
committed
// Only cover dead stores from regular assignments. ++/-- dead stores
// have never flagged a real bug.
if (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
if (!B->isAssignmentOp()) return; // Skip non-assignments.
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
Ted Kremenek
committed
// Special case: check for assigning null to a pointer.
// This is a common form of defensive programming.
QualType T = VD->getType();
if (T->isPointerType() || T->isObjCObjectPointerType()) {
if (B->getRHS()->isNullPointerConstant(Ctx,
Expr::NPC_ValueDependentIsNull))
return;
Ted Kremenek
committed
}
Expr* RHS = B->getRHS()->IgnoreParenCasts();
// Special case: self-assignments. These are often used to shut up
// "unused variable" compiler warnings.
if (DeclRefExpr* RhsDR = dyn_cast<DeclRefExpr>(RHS))
if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
return;
// Otherwise, issue a warning.
Ted Kremenek
committed
DeadStoreKind dsk = Parents.isConsumedExpr(B)
Ted Kremenek
committed
: (isIncrement(VD,B) ? DeadIncrement : Standard);
Ted Kremenek
committed
CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live);
}
else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
Ted Kremenek
committed
if (!U->isIncrementOp() || U->isPrefix())
Ted Kremenek
committed
Stmt *parent = Parents.getParentIgnoreParenCasts(U);
if (!parent || !isa<ReturnStmt>(parent))
Ted Kremenek
committed
return;
Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(Ex))
Ted Kremenek
committed
CheckDeclRef(DR, U, DeadIncrement, AD, Live);
else if (DeclStmt* DS = dyn_cast<DeclStmt>(S))
// Iterate through the decls. Warn if any initializers are complex
// expressions that are not live (never used).
Ted Kremenek
committed
for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end();
DI != DE; ++DI) {
Ted Kremenek
committed
VarDecl* V = dyn_cast<VarDecl>(*DI);
if (!V)
continue;
if (V->hasLocalStorage()) {
// Reference types confuse the dead stores checker. Skip them
// for now.
if (V->getType()->getAs<ReferenceType>())
return;
if (Expr* E = V->getInit()) {
while (ExprWithCleanups *exprClean = dyn_cast<ExprWithCleanups>(E))
E = exprClean->getSubExpr();
// Don't warn on C++ objects (yet) until we can show that their
// constructors/destructors don't have side effects.
if (isa<CXXConstructExpr>(E))
return;
Ted Kremenek
committed
// A dead initialization is a variable that is dead after it
// is initialized. We don't flag warnings for those variables
// marked 'unused'.
Argyrios Kyrtzidis
committed
if (!Live(V, AD) && V->getAttr<UnusedAttr>() == 0) {
// Special case: check for initializations with constants.
//
// e.g. : int x = 0;
//
// If x is EVER assigned a new value later, don't issue
// a warning. This is because such initialization can be
// due to defensive programming.
if (E->isConstantInitializer(Ctx, false))
Ted Kremenek
committed
return;
Ted Kremenek
committed
if (DeclRefExpr *DRE=dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
if (VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
// Special case: check for initialization from constant
// variables.
//
// e.g. extern const int MyConstant;
// int x = MyConstant;
//
Ted Kremenek
committed
if (VD->hasGlobalStorage() &&
VD->getType().isConstQualified())
return;
// Special case: check for initialization from scalar
// parameters. This is often a form of defensive
// programming. Non-scalars are still an error since
// because it more likely represents an actual algorithmic
// bug.
if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
return;
}
Ted Kremenek
committed
Report(V, DeadInit, V->getLocation(), E->getSourceRange());
}
}
}
};
} // end anonymous namespace
Ted Kremenek
committed
//===----------------------------------------------------------------------===//
// Driver function to invoke the Dead-Stores checker on a CFG.
//===----------------------------------------------------------------------===//
Ted Kremenek
committed
namespace {
Kovarththanan Rajaratnam
committed
class FindEscaped : public CFGRecStmtDeclVisitor<FindEscaped>{
Ted Kremenek
committed
CFG *cfg;
public:
FindEscaped(CFG *c) : cfg(c) {}
Ted Kremenek
committed
CFG& getCFG() { return *cfg; }
Ted Kremenek
committed
llvm::SmallPtrSet<VarDecl*, 20> Escaped;
void VisitUnaryOperator(UnaryOperator* U) {
// Check for '&'. Any VarDecl whose value has its address-taken we
// treat as escaped.
Expr* E = U->getSubExpr()->IgnoreParenCasts();
John McCall
committed
if (U->getOpcode() == UO_AddrOf)
Ted Kremenek
committed
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(E))
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl())) {
Escaped.insert(VD);
return;
}
Visit(E);
}
};
} // end anonymous namespace
Ted Kremenek
committed
Argyrios Kyrtzidis
committed
//===----------------------------------------------------------------------===//
// DeadStoresChecker
//===----------------------------------------------------------------------===//
namespace {
class DeadStoresChecker : public Checker<check::ASTCodeBody> {
Argyrios Kyrtzidis
committed
public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
if (LiveVariables *L = mgr.getLiveVariables(D)) {
CFG &cfg = *mgr.getCFG(D);
ParentMap &pmap = mgr.getParentMap(D);
FindEscaped FS(&cfg);
FS.getCFG().VisitBlockStmts(FS);
DeadStoreObs A(cfg, BR.getContext(), BR, pmap, FS.Escaped);
L->runOnAllBlocks(cfg, &A);
}
}
};
}
void ento::registerDeadStoresChecker(CheckerManager &mgr) {
mgr.registerChecker<DeadStoresChecker>();