#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Basic/Diagnostic.h"
#include "clang/AST/ASTContext.h"
+#include "clang/AST/ParentMap.h"
#include "llvm/Support/Compiler.h"
using namespace clang;
namespace {
-
+
class VISIBILITY_HIDDEN DeadStoreObs : public LiveVariables::ObserverTy {
ASTContext &Ctx;
Diagnostic &Diags;
DiagnosticClient &Client;
+ ParentMap& Parents;
+
public:
- DeadStoreObs(ASTContext &ctx, Diagnostic &diags, DiagnosticClient &client)
- : Ctx(ctx), Diags(diags), Client(client) {}
+ DeadStoreObs(ASTContext &ctx, Diagnostic &diags, DiagnosticClient &client,
+ ParentMap& parents)
+ : Ctx(ctx), Diags(diags), Client(client), Parents(parents) {}
virtual ~DeadStoreObs() {}
- unsigned GetDiag(VarDecl* VD) {
- std::string msg = "value stored to '" + std::string(VD->getName()) +
- "' is never used";
+ unsigned GetDiag(VarDecl* VD, bool inEnclosing = false) {
+ std::string name(VD->getName());
+
+ std::string msg = inEnclosing
+ ? "Although the value stored to '" + name +
+ "' is used in the enclosing expression, the value is never actually read"
+ " from '" + name + "'"
+ : "Value stored to '" + name + "' is never read";
- return Diags.getCustomDiagID(Diagnostic::Warning, msg.c_str());
-
+ return Diags.getCustomDiagID(Diagnostic::Warning, msg.c_str());
+ }
+
+ void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val,
+ bool hasEnclosing,
+ const LiveVariables::AnalysisDataTy& AD,
+ const LiveVariables::ValTy& Live) {
+
+ if (VD->hasLocalStorage() && !Live(VD, AD)) {
+ SourceRange R = Val->getSourceRange();
+ Diags.Report(&Client,
+ Ctx.getFullLoc(Ex->getSourceRange().getBegin()),
+ GetDiag(VD, hasEnclosing), 0, 0, &R, 1);
+ }
}
void CheckDeclRef(DeclRefExpr* DR, Expr* Val,
const LiveVariables::ValTy& Live) {
if (VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl()))
- if (VD->hasLocalStorage() && !Live(VD, AD)) {
- SourceRange R = Val->getSourceRange();
- Diags.Report(&Client,
- Ctx.getFullLoc(DR->getSourceRange().getBegin()),
- GetDiag(VD), 0, 0, &R, 1);
- }
+ CheckVarDecl(VD, DR, Val, false, AD, Live);
}
virtual void ObserveStmt(Stmt* S,
if (!B->isAssignmentOp()) return; // Skip non-assignments.
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()))
- CheckDeclRef(DR, B->getRHS(), AD, Live);
+ if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+
+ // Special case: check for assigning null to a pointer. This
+ // is a common form of defensive programming.
+ // FIXME: Make this optional?
+
+ Expr* Val = B->getRHS();
+ llvm::APSInt Result(Ctx.getTypeSize(Val->getType()));
+
+ if (VD->getType()->isPointerType() &&
+ Val->IgnoreParenCasts()->isIntegerConstantExpr(Result, Ctx, 0))
+ if (Result == 0)
+ return;
+
+ CheckVarDecl(VD, DR, Val, Parents.isSubExpr(B), AD, Live);
+ }
}
else if (UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
if (!U->isIncrementOp())
// Driver function to invoke the Dead-Stores checker on a CFG.
//===----------------------------------------------------------------------===//
-void clang::CheckDeadStores(CFG& cfg, ASTContext &Ctx, Diagnostic &Diags) {
+void clang::CheckDeadStores(CFG& cfg, ASTContext &Ctx,
+ ParentMap& Parents, Diagnostic &Diags) {
LiveVariables L(cfg);
L.runOnCFG(cfg);
- DeadStoreObs A(Ctx, Diags, Diags.getClient());
+ DeadStoreObs A(Ctx, Diags, Diags.getClient(), Parents);
L.runOnAllBlocks(cfg, &A);
}
// Run the dead store checker and collect the diagnostics.
DiagCollector C(*this);
- DeadStoreObs A(BR.getContext(), BR.getDiagnostic(), C);
- GRExprEngine& Eng = BR.getEngine();
- Eng.getLiveness().runOnAllBlocks(Eng.getCFG(), &A);
+ DeadStoreObs A(BR.getContext(), BR.getDiagnostic(), C, BR.getParentMap());
+
+ GRExprEngine& Eng = BR.getEngine();
+ Eng.getLiveness().runOnAllBlocks(BR.getCFG(), &A);
// Emit the bug reports.
//
//===----------------------------------------------------------------------===//
+#include "clang/AST/ParentMap.h"
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
#include "clang/Analysis/PathSensitive/BugReporter.h"
#include "clang/Basic/SourceManager.h"
GRExprEngine::GRExprEngine(CFG& cfg, Decl& CD, ASTContext& Ctx)
: CoreEngine(cfg, CD, Ctx, *this),
G(CoreEngine.getGraph()),
+ Parents(0),
Liveness(G.getCFG()),
Builder(NULL),
StateMgr(G.getContext(), G.getAllocator()),
Liveness.runOnAllBlocks(G.getCFG(), NULL, true);
}
-GRExprEngine::~GRExprEngine() {
+GRExprEngine::~GRExprEngine() {
for (BugTypeSet::iterator I = BugTypes.begin(), E = BugTypes.end(); I!=E; ++I)
delete *I;
delete *I;
delete [] NSExceptionInstanceRaiseSelectors;
+
+ delete Parents;
+}
+
+ParentMap& GRExprEngine::getParentMap() {
+ if (!Parents) {
+ Stmt* Body = getGraph().getCodeDecl().getCodeBody();
+ Parents = new ParentMap(Body);
+ }
+
+ return *Parents;
}
//===----------------------------------------------------------------------===//