2008-04-03 12:42:52 +08:00
|
|
|
// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- 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 BugReporter, a utility class for generating
|
|
|
|
// PathDiagnostics for analyses based on GRSimpleVals.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "clang/Analysis/PathSensitive/BugReporter.h"
|
2008-04-10 05:41:14 +08:00
|
|
|
#include "clang/Analysis/PathSensitive/GRExprEngine.h"
|
2008-04-03 12:42:52 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
|
|
|
#include "clang/Basic/SourceLocation.h"
|
|
|
|
#include "clang/AST/ASTContext.h"
|
|
|
|
#include "clang/AST/CFG.h"
|
|
|
|
#include "clang/AST/Expr.h"
|
2009-03-28 04:55:39 +08:00
|
|
|
#include "clang/AST/ParentMap.h"
|
2008-04-03 12:42:52 +08:00
|
|
|
#include "clang/Analysis/ProgramPoint.h"
|
|
|
|
#include "clang/Analysis/PathDiagnostic.h"
|
2008-08-24 06:23:37 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2008-06-18 13:34:07 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
2009-02-05 07:49:09 +08:00
|
|
|
#include "llvm/ADT/STLExtras.h"
|
2009-03-28 04:55:39 +08:00
|
|
|
#include "llvm/ADT/OwningPtr.h"
|
2009-03-13 07:41:59 +08:00
|
|
|
#include <queue>
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// Helper routines for walking the ExplodedGraph and fetching statements.
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
static inline Stmt* GetStmt(ProgramPoint P) {
|
|
|
|
if (const PostStmt* PS = dyn_cast<PostStmt>(&P))
|
2008-04-03 12:42:52 +08:00
|
|
|
return PS->getStmt();
|
2009-02-24 06:44:26 +08:00
|
|
|
else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P))
|
2008-04-03 12:42:52 +08:00
|
|
|
return BE->getSrc()->getTerminator();
|
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return 0;
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
static inline const ExplodedNode<GRState>*
|
|
|
|
GetPredecessorNode(const ExplodedNode<GRState>* N) {
|
|
|
|
return N->pred_empty() ? NULL : *(N->pred_begin());
|
2008-04-08 07:35:17 +08:00
|
|
|
}
|
|
|
|
|
2009-01-24 08:55:43 +08:00
|
|
|
static inline const ExplodedNode<GRState>*
|
2009-02-24 06:44:26 +08:00
|
|
|
GetSuccessorNode(const ExplodedNode<GRState>* N) {
|
|
|
|
return N->succ_empty() ? NULL : *(N->succ_begin());
|
2008-04-18 07:44:37 +08:00
|
|
|
}
|
2008-04-26 03:01:27 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
static Stmt* GetPreviousStmt(const ExplodedNode<GRState>* N) {
|
|
|
|
for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N))
|
|
|
|
if (Stmt *S = GetStmt(N->getLocation()))
|
|
|
|
return S;
|
2008-04-18 07:44:37 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static Stmt* GetNextStmt(const ExplodedNode<GRState>* N) {
|
|
|
|
for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N))
|
2009-03-29 01:33:57 +08:00
|
|
|
if (Stmt *S = GetStmt(N->getLocation())) {
|
|
|
|
// Check if the statement is '?' or '&&'/'||'. These are "merges",
|
|
|
|
// not actual statement points.
|
|
|
|
switch (S->getStmtClass()) {
|
|
|
|
case Stmt::ChooseExprClass:
|
|
|
|
case Stmt::ConditionalOperatorClass: continue;
|
|
|
|
case Stmt::BinaryOperatorClass: {
|
|
|
|
BinaryOperator::Opcode Op = cast<BinaryOperator>(S)->getOpcode();
|
|
|
|
if (Op == BinaryOperator::LAnd || Op == BinaryOperator::LOr)
|
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-02-24 06:44:26 +08:00
|
|
|
return S;
|
2009-03-29 01:33:57 +08:00
|
|
|
}
|
2008-04-18 07:44:37 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return 0;
|
2008-04-18 07:44:37 +08:00
|
|
|
}
|
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
static inline Stmt* GetCurrentOrPreviousStmt(const ExplodedNode<GRState>* N) {
|
|
|
|
if (Stmt *S = GetStmt(N->getLocation()))
|
|
|
|
return S;
|
|
|
|
|
|
|
|
return GetPreviousStmt(N);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline Stmt* GetCurrentOrNextStmt(const ExplodedNode<GRState>* N) {
|
|
|
|
if (Stmt *S = GetStmt(N->getLocation()))
|
|
|
|
return S;
|
|
|
|
|
|
|
|
return GetNextStmt(N);
|
2009-01-24 08:55:43 +08:00
|
|
|
}
|
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// PathDiagnosticBuilder and its associated routines and helper objects.
|
2009-02-24 06:44:26 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-02-24 07:13:51 +08:00
|
|
|
|
2009-04-01 04:22:36 +08:00
|
|
|
typedef llvm::DenseMap<const ExplodedNode<GRState>*,
|
|
|
|
const ExplodedNode<GRState>*> NodeBackMap;
|
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
namespace {
|
2009-04-01 04:22:36 +08:00
|
|
|
class VISIBILITY_HIDDEN NodeMapClosure : public BugReport::NodeResolver {
|
|
|
|
NodeBackMap& M;
|
|
|
|
public:
|
|
|
|
NodeMapClosure(NodeBackMap *m) : M(*m) {}
|
|
|
|
~NodeMapClosure() {}
|
|
|
|
|
|
|
|
const ExplodedNode<GRState>* getOriginalNode(const ExplodedNode<GRState>* N) {
|
|
|
|
NodeBackMap::iterator I = M.find(N);
|
|
|
|
return I == M.end() ? 0 : I->second;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
class VISIBILITY_HIDDEN PathDiagnosticBuilder {
|
2009-04-01 04:22:36 +08:00
|
|
|
GRBugReporter &BR;
|
2009-03-27 13:06:10 +08:00
|
|
|
SourceManager &SMgr;
|
2009-04-01 04:22:36 +08:00
|
|
|
ExplodedGraph<GRState> *ReportGraph;
|
|
|
|
BugReport *R;
|
2009-03-27 13:06:10 +08:00
|
|
|
const Decl& CodeDecl;
|
|
|
|
PathDiagnosticClient *PDC;
|
2009-03-28 04:55:39 +08:00
|
|
|
llvm::OwningPtr<ParentMap> PM;
|
2009-04-01 04:22:36 +08:00
|
|
|
NodeMapClosure NMC;
|
2009-03-27 13:06:10 +08:00
|
|
|
public:
|
2009-04-01 04:22:36 +08:00
|
|
|
PathDiagnosticBuilder(GRBugReporter &br, ExplodedGraph<GRState> *reportGraph,
|
|
|
|
BugReport *r, NodeBackMap *Backmap,
|
|
|
|
const Decl& codedecl, PathDiagnosticClient *pdc)
|
|
|
|
: BR(br), SMgr(BR.getSourceManager()), ReportGraph(reportGraph), R(r),
|
|
|
|
CodeDecl(codedecl), PDC(pdc), NMC(Backmap) {}
|
2009-03-27 13:06:10 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation ExecutionContinues(const ExplodedNode<GRState>* N);
|
2009-03-27 13:06:10 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os,
|
|
|
|
const ExplodedNode<GRState>* N);
|
|
|
|
|
|
|
|
ParentMap& getParentMap() {
|
|
|
|
if (PM.get() == 0) PM.reset(new ParentMap(CodeDecl.getBody()));
|
|
|
|
return *PM.get();
|
|
|
|
}
|
2009-03-27 13:06:10 +08:00
|
|
|
|
2009-04-02 01:18:21 +08:00
|
|
|
const Stmt *getParent(const Stmt *S) {
|
|
|
|
return getParentMap().getParent(S);
|
|
|
|
}
|
|
|
|
|
2009-04-02 01:52:26 +08:00
|
|
|
const CFG& getCFG() {
|
|
|
|
return *BR.getCFG();
|
|
|
|
}
|
|
|
|
|
|
|
|
const Decl& getCodeDecl() {
|
|
|
|
return BR.getStateManager().getCodeDecl();
|
|
|
|
}
|
|
|
|
|
2009-04-01 04:22:36 +08:00
|
|
|
ExplodedGraph<GRState>& getGraph() { return *ReportGraph; }
|
|
|
|
NodeMapClosure& getNodeMapClosure() { return NMC; }
|
|
|
|
ASTContext& getContext() { return BR.getContext(); }
|
|
|
|
SourceManager& getSourceManager() { return SMgr; }
|
|
|
|
BugReport& getReport() { return *R; }
|
|
|
|
GRBugReporter& getBugReporter() { return BR; }
|
|
|
|
GRStateManager& getStateManager() { return BR.getStateManager(); }
|
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
|
|
|
|
|
2009-04-01 14:13:56 +08:00
|
|
|
PathDiagnosticLocation
|
|
|
|
getEnclosingStmtLocation(const PathDiagnosticLocation &L) {
|
|
|
|
if (const Stmt *S = L.asStmt())
|
|
|
|
return getEnclosingStmtLocation(S);
|
|
|
|
|
|
|
|
return L;
|
|
|
|
}
|
|
|
|
|
2009-04-01 04:22:36 +08:00
|
|
|
PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const {
|
|
|
|
return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive;
|
|
|
|
}
|
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
bool supportsLogicalOpControlFlow() const {
|
|
|
|
return PDC ? PDC->supportsLogicalOpControlFlow() : true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation
|
|
|
|
PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode<GRState>* N) {
|
|
|
|
if (Stmt *S = GetNextStmt(N))
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
|
|
|
|
return FullSourceLoc(CodeDecl.getBody()->getRBracLoc(), SMgr);
|
2009-03-13 02:41:53 +08:00
|
|
|
}
|
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation
|
2009-03-27 13:06:10 +08:00
|
|
|
PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream& os,
|
|
|
|
const ExplodedNode<GRState>* N) {
|
|
|
|
|
2008-05-07 02:11:09 +08:00
|
|
|
// Slow, but probably doesn't matter.
|
2009-02-24 06:44:26 +08:00
|
|
|
if (os.str().empty())
|
|
|
|
os << ' ';
|
2008-05-07 02:11:09 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
const PathDiagnosticLocation &Loc = ExecutionContinues(N);
|
2009-03-13 02:41:53 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
if (Loc.asStmt())
|
2009-02-24 06:44:26 +08:00
|
|
|
os << "Execution continues on line "
|
2009-03-28 04:55:39 +08:00
|
|
|
<< SMgr.getInstantiationLineNumber(Loc.asLocation()) << '.';
|
2009-02-24 06:44:26 +08:00
|
|
|
else
|
2009-02-24 07:13:51 +08:00
|
|
|
os << "Execution jumps to the end of the "
|
2009-03-27 13:06:10 +08:00
|
|
|
<< (isa<ObjCMethodDecl>(CodeDecl) ? "method" : "function") << '.';
|
2009-03-13 02:41:53 +08:00
|
|
|
|
|
|
|
return Loc;
|
2008-05-07 02:11:09 +08:00
|
|
|
}
|
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
PathDiagnosticLocation
|
|
|
|
PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
|
|
|
|
assert(S && "Null Stmt* passed to getEnclosingStmtLocation");
|
|
|
|
ParentMap &P = getParentMap();
|
2009-04-01 14:13:56 +08:00
|
|
|
|
|
|
|
while (isa<DeclStmt>(S) || isa<Expr>(S)) {
|
2009-03-28 05:16:25 +08:00
|
|
|
const Stmt *Parent = P.getParent(S);
|
|
|
|
|
2009-03-28 11:37:59 +08:00
|
|
|
if (!Parent)
|
|
|
|
break;
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2009-03-28 11:37:59 +08:00
|
|
|
switch (Parent->getStmtClass()) {
|
2009-04-01 14:13:56 +08:00
|
|
|
case Stmt::BinaryOperatorClass: {
|
|
|
|
const BinaryOperator *B = cast<BinaryOperator>(Parent);
|
|
|
|
if (B->isLogicalOp())
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::CompoundStmtClass:
|
|
|
|
case Stmt::StmtExprClass:
|
2009-03-28 12:08:14 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
case Stmt::ChooseExprClass:
|
|
|
|
// Similar to '?' if we are referring to condition, just have the edge
|
|
|
|
// point to the entire choose expression.
|
|
|
|
if (cast<ChooseExpr>(Parent)->getCond() == S)
|
|
|
|
return PathDiagnosticLocation(Parent, SMgr);
|
|
|
|
else
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
case Stmt::ConditionalOperatorClass:
|
|
|
|
// For '?', if we are referring to condition, just have the edge point
|
|
|
|
// to the entire '?' expression.
|
|
|
|
if (cast<ConditionalOperator>(Parent)->getCond() == S)
|
|
|
|
return PathDiagnosticLocation(Parent, SMgr);
|
|
|
|
else
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::DoStmtClass:
|
|
|
|
if (cast<DoStmt>(Parent)->getCond() != S)
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
break;
|
|
|
|
case Stmt::ForStmtClass:
|
|
|
|
if (cast<ForStmt>(Parent)->getBody() == S)
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
break;
|
|
|
|
case Stmt::IfStmtClass:
|
|
|
|
if (cast<IfStmt>(Parent)->getCond() != S)
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
break;
|
|
|
|
case Stmt::ObjCForCollectionStmtClass:
|
|
|
|
if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S)
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
break;
|
|
|
|
case Stmt::WhileStmtClass:
|
|
|
|
if (cast<WhileStmt>(Parent)->getCond() != S)
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
S = Parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(S && "Cannot have null Stmt for PathDiagnosticLocation");
|
|
|
|
return PathDiagnosticLocation(S, SMgr);
|
|
|
|
}
|
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// ScanNotableSymbols: closure-like callback for scanning Store bindings.
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
static const VarDecl*
|
|
|
|
GetMostRecentVarDeclBinding(const ExplodedNode<GRState>* N,
|
|
|
|
GRStateManager& VMgr, SVal X) {
|
2008-04-18 07:44:37 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) {
|
|
|
|
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
|
|
|
if (!isa<PostStmt>(P))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
DeclRefExpr* DR = dyn_cast<DeclRefExpr>(cast<PostStmt>(P).getStmt());
|
|
|
|
|
|
|
|
if (!DR)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
SVal Y = VMgr.GetSVal(N->getState(), DR);
|
|
|
|
|
|
|
|
if (X != Y)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl());
|
|
|
|
|
|
|
|
if (!VD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return VD;
|
2009-02-05 07:49:09 +08:00
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
return 0;
|
2008-05-02 06:50:36 +08:00
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN NotableSymbolHandler
|
|
|
|
: public StoreManager::BindingsHandler {
|
2008-05-02 06:50:36 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
SymbolRef Sym;
|
|
|
|
const GRState* PrevSt;
|
|
|
|
const Stmt* S;
|
|
|
|
GRStateManager& VMgr;
|
|
|
|
const ExplodedNode<GRState>* Pred;
|
|
|
|
PathDiagnostic& PD;
|
|
|
|
BugReporter& BR;
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
public:
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s,
|
|
|
|
GRStateManager& vmgr, const ExplodedNode<GRState>* pred,
|
|
|
|
PathDiagnostic& pd, BugReporter& br)
|
|
|
|
: Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R,
|
|
|
|
SVal V) {
|
|
|
|
|
|
|
|
SymbolRef ScanSym = V.getAsSymbol();
|
|
|
|
|
|
|
|
if (ScanSym != Sym)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Check if the previous state has this binding.
|
|
|
|
SVal X = VMgr.GetSVal(PrevSt, loc::MemRegionVal(R));
|
|
|
|
|
|
|
|
if (X == V) // Same binding?
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Different binding. Only handle assignments for now. We don't pull
|
|
|
|
// this check out of the loop because we will eventually handle other
|
|
|
|
// cases.
|
|
|
|
|
|
|
|
VarDecl *VD = 0;
|
|
|
|
|
|
|
|
if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
|
|
|
|
if (!B->isAssignmentOp())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// What variable did we assign to?
|
|
|
|
DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts());
|
|
|
|
|
|
|
|
if (!DR)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
VD = dyn_cast<VarDecl>(DR->getDecl());
|
|
|
|
}
|
|
|
|
else if (const DeclStmt* DS = dyn_cast<DeclStmt>(S)) {
|
|
|
|
// FIXME: Eventually CFGs won't have DeclStmts. Right now we
|
|
|
|
// assume that each DeclStmt has a single Decl. This invariant
|
|
|
|
// holds by contruction in the CFG.
|
|
|
|
VD = dyn_cast<VarDecl>(*DS->decl_begin());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!VD)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// What is the most recently referenced variable with this binding?
|
|
|
|
const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V);
|
|
|
|
|
|
|
|
if (!MostRecent)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Create the diagnostic.
|
|
|
|
FullSourceLoc L(S->getLocStart(), BR.getSourceManager());
|
|
|
|
|
|
|
|
if (Loc::IsLocType(VD->getType())) {
|
|
|
|
std::string msg = "'" + std::string(VD->getNameAsString()) +
|
|
|
|
"' now aliases '" + MostRecent->getNameAsString() + "'";
|
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticEventPiece(L, msg));
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
static void HandleNotableSymbol(const ExplodedNode<GRState>* N,
|
|
|
|
const Stmt* S,
|
|
|
|
SymbolRef Sym, BugReporter& BR,
|
|
|
|
PathDiagnostic& PD) {
|
2009-02-05 07:49:09 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
const ExplodedNode<GRState>* Pred = N->pred_empty() ? 0 : *N->pred_begin();
|
|
|
|
const GRState* PrevSt = Pred ? Pred->getState() : 0;
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (!PrevSt)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Look at the region bindings of the current state that map to the
|
|
|
|
// specified symbol. Are any of them not in the previous state?
|
|
|
|
GRStateManager& VMgr = cast<GRBugReporter>(BR).getStateManager();
|
|
|
|
NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR);
|
|
|
|
cast<GRBugReporter>(BR).getStateManager().iterBindings(N->getState(), H);
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN ScanNotableSymbols
|
|
|
|
: public StoreManager::BindingsHandler {
|
2008-05-02 06:50:36 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
llvm::SmallSet<SymbolRef, 10> AlreadyProcessed;
|
|
|
|
const ExplodedNode<GRState>* N;
|
|
|
|
Stmt* S;
|
|
|
|
GRBugReporter& BR;
|
|
|
|
PathDiagnostic& PD;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ScanNotableSymbols(const ExplodedNode<GRState>* n, Stmt* s, GRBugReporter& br,
|
|
|
|
PathDiagnostic& pd)
|
|
|
|
: N(n), S(s), BR(br), PD(pd) {}
|
|
|
|
|
|
|
|
bool HandleBinding(StoreManager& SMgr, Store store,
|
|
|
|
const MemRegion* R, SVal V) {
|
|
|
|
|
|
|
|
SymbolRef ScanSym = V.getAsSymbol();
|
|
|
|
|
|
|
|
if (!ScanSym)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!BR.isNotable(ScanSym))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (AlreadyProcessed.count(ScanSym))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
AlreadyProcessed.insert(ScanSym);
|
|
|
|
|
|
|
|
HandleNotableSymbol(N, S, ScanSym, BR, PD);
|
|
|
|
return true;
|
2008-05-02 06:50:36 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
2008-04-10 05:41:14 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// "Minimal" path diagnostic generation algorithm.
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
|
|
|
|
PathDiagnosticBuilder &PDB,
|
|
|
|
const ExplodedNode<GRState> *N) {
|
|
|
|
ASTContext& Ctx = PDB.getContext();
|
|
|
|
SourceManager& SMgr = PDB.getSourceManager();
|
|
|
|
const ExplodedNode<GRState>* NextNode = N->pred_empty()
|
|
|
|
? NULL : *(N->pred_begin());
|
|
|
|
while (NextNode) {
|
|
|
|
N = NextNode;
|
|
|
|
NextNode = GetPredecessorNode(N);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
|
|
|
if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
|
|
|
|
CFGBlock* Src = BE->getSrc();
|
|
|
|
CFGBlock* Dst = BE->getDst();
|
|
|
|
Stmt* T = Src->getTerminator();
|
|
|
|
|
|
|
|
if (!T)
|
|
|
|
continue;
|
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
FullSourceLoc Start(T->getLocStart(), SMgr);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
switch (T->getStmtClass()) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Stmt::GotoStmtClass:
|
2009-03-13 02:41:53 +08:00
|
|
|
case Stmt::IndirectGotoStmtClass: {
|
2009-02-24 06:44:26 +08:00
|
|
|
Stmt* S = GetNextStmt(N);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
if (!S)
|
|
|
|
continue;
|
|
|
|
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
2009-03-13 02:41:53 +08:00
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-03-28 05:16:25 +08:00
|
|
|
const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
os << "Control jumps to line "
|
2009-04-01 07:00:32 +08:00
|
|
|
<< End.asLocation().getInstantiationLineNumber();
|
2009-03-13 02:41:53 +08:00
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
2008-04-03 12:42:52 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-02-11 07:56:07 +08:00
|
|
|
case Stmt::SwitchStmtClass: {
|
2008-04-03 12:42:52 +08:00
|
|
|
// Figure out what case arm we took.
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
if (Stmt* S = Dst->getLabel()) {
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End(S, SMgr);
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
switch (S->getStmtClass()) {
|
2009-03-13 02:41:53 +08:00
|
|
|
default:
|
|
|
|
os << "No cases match in the switch statement. "
|
2009-04-01 07:00:32 +08:00
|
|
|
"Control jumps to line "
|
|
|
|
<< End.asLocation().getInstantiationLineNumber();
|
2009-03-13 02:41:53 +08:00
|
|
|
break;
|
|
|
|
case Stmt::DefaultStmtClass:
|
|
|
|
os << "Control jumps to the 'default' case at line "
|
2009-04-01 07:00:32 +08:00
|
|
|
<< End.asLocation().getInstantiationLineNumber();
|
2009-03-13 02:41:53 +08:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Stmt::CaseStmtClass: {
|
|
|
|
os << "Control jumps to 'case ";
|
|
|
|
CaseStmt* Case = cast<CaseStmt>(S);
|
|
|
|
Expr* LHS = Case->getLHS()->IgnoreParenCasts();
|
|
|
|
|
|
|
|
// Determine if it is an enum.
|
|
|
|
bool GetRawInt = true;
|
|
|
|
|
|
|
|
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) {
|
|
|
|
// FIXME: Maybe this should be an assertion. Are there cases
|
|
|
|
// were it is not an EnumConstantDecl?
|
|
|
|
EnumConstantDecl* D =
|
2009-04-01 07:00:32 +08:00
|
|
|
dyn_cast<EnumConstantDecl>(DR->getDecl());
|
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
if (D) {
|
|
|
|
GetRawInt = false;
|
|
|
|
os << D->getNameAsString();
|
|
|
|
}
|
2008-04-24 07:35:07 +08:00
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (GetRawInt) {
|
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
// Not an enum.
|
|
|
|
Expr* CondE = cast<SwitchStmt>(T)->getCond();
|
|
|
|
unsigned bits = Ctx.getTypeSize(CondE->getType());
|
|
|
|
llvm::APSInt V(bits, false);
|
|
|
|
|
|
|
|
if (!LHS->isIntegerConstantExpr(V, Ctx, 0, true)) {
|
|
|
|
assert (false && "Case condition must be constant.");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
os << V;
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
os << ":' at line "
|
2009-04-01 07:00:32 +08:00
|
|
|
<< End.asLocation().getInstantiationLineNumber();
|
2009-03-13 02:41:53 +08:00
|
|
|
break;
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2009-03-28 04:55:39 +08:00
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2008-04-25 09:29:56 +08:00
|
|
|
else {
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "'Default' branch taken. ";
|
2009-03-28 04:55:39 +08:00
|
|
|
const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N);
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
2008-04-25 09:29:56 +08:00
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2008-04-26 03:01:27 +08:00
|
|
|
|
|
|
|
case Stmt::BreakStmtClass:
|
|
|
|
case Stmt::ContinueStmtClass: {
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
|
2009-03-13 02:41:53 +08:00
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
2008-04-26 03:01:27 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
// Determine control-flow for ternary '?'.
|
2008-04-08 07:35:17 +08:00
|
|
|
case Stmt::ConditionalOperatorClass: {
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-03-28 12:08:14 +08:00
|
|
|
os << "'?' condition is ";
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2008-04-08 07:35:17 +08:00
|
|
|
if (*(Src->succ_begin()+1) == Dst)
|
2009-03-13 02:41:53 +08:00
|
|
|
os << "false";
|
2008-04-08 07:35:17 +08:00
|
|
|
else
|
2009-03-13 02:41:53 +08:00
|
|
|
os << "true";
|
2008-04-08 07:35:17 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2009-03-27 13:06:10 +08:00
|
|
|
|
2009-03-28 12:08:14 +08:00
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Determine control-flow for short-circuited '&&' and '||'.
|
2009-03-27 13:06:10 +08:00
|
|
|
case Stmt::BinaryOperatorClass: {
|
|
|
|
if (!PDB.supportsLogicalOpControlFlow())
|
|
|
|
break;
|
2009-03-13 02:41:53 +08:00
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
BinaryOperator *B = cast<BinaryOperator>(T);
|
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
|
|
|
os << "Left side of '";
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
if (B->getOpcode() == BinaryOperator::LAnd) {
|
2009-03-29 01:33:57 +08:00
|
|
|
os << "&&" << "' is ";
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
os << "false";
|
|
|
|
PathDiagnosticLocation End(B->getLHS(), SMgr);
|
|
|
|
PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr);
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
os << "true";
|
|
|
|
PathDiagnosticLocation Start(B->getLHS(), SMgr);
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
}
|
2009-03-27 13:06:10 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
assert(B->getOpcode() == BinaryOperator::LOr);
|
2009-03-29 01:33:57 +08:00
|
|
|
os << "||" << "' is ";
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
os << "false";
|
|
|
|
PathDiagnosticLocation Start(B->getLHS(), SMgr);
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
os << "true";
|
|
|
|
PathDiagnosticLocation End(B->getLHS(), SMgr);
|
|
|
|
PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr);
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
}
|
2009-03-27 13:06:10 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2008-04-08 07:35:17 +08:00
|
|
|
break;
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
case Stmt::DoStmtClass: {
|
2008-04-08 07:35:17 +08:00
|
|
|
if (*(Src->succ_begin()) == Dst) {
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2008-04-08 07:35:17 +08:00
|
|
|
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "Loop condition is true. ";
|
2009-03-28 05:16:25 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
}
|
|
|
|
else {
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2009-03-28 05:16:25 +08:00
|
|
|
|
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
"Loop condition is false. Exiting loop"));
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Stmt::WhileStmtClass:
|
|
|
|
case Stmt::ForStmtClass: {
|
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
|
|
|
|
|
|
|
os << "Loop condition is false. ";
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
|
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
|
|
|
os.str()));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
2009-04-01 14:13:56 +08:00
|
|
|
"Loop condition is true. Entering loop body"));
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Stmt::IfStmtClass: {
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
|
|
|
|
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()+1) == Dst)
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
2009-04-01 14:13:56 +08:00
|
|
|
"Taking false branch"));
|
2009-04-01 07:00:32 +08:00
|
|
|
else
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(Start, End,
|
2009-04-01 14:13:56 +08:00
|
|
|
"Taking true branch"));
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PathDiagnosticPiece* p =
|
|
|
|
PDB.getReport().VisitNode(N, NextNode, PDB.getGraph(),
|
|
|
|
PDB.getBugReporter(),
|
|
|
|
PDB.getNodeMapClosure())) {
|
|
|
|
PD.push_front(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
|
|
|
|
// Scan the region bindings, and see if a "notable" symbol has a new
|
|
|
|
// lval binding.
|
|
|
|
ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD);
|
|
|
|
PDB.getStateManager().iterBindings(N->getState(), SNS);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-01 14:13:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// "Extensive" PathDiagnostic generation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
static bool IsControlFlowExpr(const Stmt *S) {
|
|
|
|
const Expr *E = dyn_cast<Expr>(S);
|
|
|
|
|
|
|
|
if (!E)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
E = E->IgnoreParenCasts();
|
|
|
|
|
|
|
|
if (isa<ConditionalOperator>(E))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E))
|
|
|
|
if (B->isLogicalOp())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void GenExtAddEdge(PathDiagnostic& PD,
|
|
|
|
PathDiagnosticBuilder &PDB,
|
|
|
|
PathDiagnosticLocation NewLoc,
|
|
|
|
PathDiagnosticLocation &PrevLoc,
|
2009-04-02 01:18:21 +08:00
|
|
|
bool allowBlockJump = false) {
|
2009-04-01 14:13:56 +08:00
|
|
|
|
|
|
|
if (const Stmt *S = NewLoc.asStmt()) {
|
|
|
|
if (IsControlFlowExpr(S))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!PrevLoc.isValid()) {
|
|
|
|
PrevLoc = NewLoc;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NewLoc == PrevLoc)
|
|
|
|
return;
|
2009-04-02 01:18:21 +08:00
|
|
|
|
|
|
|
// Are we jumping between statements with the same compound statement?
|
|
|
|
if (!allowBlockJump)
|
|
|
|
if (const Stmt *PS = PrevLoc.asStmt())
|
|
|
|
if (const Stmt *NS = NewLoc.asStmt()) {
|
|
|
|
const Stmt *parentPS = PDB.getParent(PS);
|
|
|
|
if (isa<CompoundStmt>(parentPS) && parentPS == PDB.getParent(NS))
|
|
|
|
return;
|
|
|
|
}
|
2009-04-01 14:13:56 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticControlFlowPiece(NewLoc, PrevLoc));
|
2009-04-02 01:18:21 +08:00
|
|
|
PrevLoc = NewLoc;
|
2009-04-01 14:13:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static bool IsNestedDeclStmt(const Stmt *S, ParentMap &PM) {
|
|
|
|
const DeclStmt *DS = dyn_cast<DeclStmt>(S);
|
|
|
|
|
|
|
|
if (!DS)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const Stmt *Parent = PM.getParent(DS);
|
|
|
|
if (!Parent)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (const ForStmt *FS = dyn_cast<ForStmt>(Parent))
|
|
|
|
return FS->getInit() == DS;
|
|
|
|
|
2009-04-02 02:48:52 +08:00
|
|
|
// FIXME: In the future IfStmt/WhileStmt may contain DeclStmts in their
|
|
|
|
// condition.
|
2009-04-01 14:13:56 +08:00
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
|
|
|
|
PathDiagnosticBuilder &PDB,
|
|
|
|
const ExplodedNode<GRState> *N) {
|
|
|
|
|
|
|
|
SourceManager& SMgr = PDB.getSourceManager();
|
|
|
|
const ExplodedNode<GRState>* NextNode = N->pred_empty()
|
|
|
|
? NULL : *(N->pred_begin());
|
|
|
|
|
|
|
|
PathDiagnosticLocation PrevLoc;
|
|
|
|
|
|
|
|
while (NextNode) {
|
|
|
|
N = NextNode;
|
|
|
|
NextNode = GetPredecessorNode(N);
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
|
|
|
// Block edges.
|
|
|
|
if (const BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
|
|
|
|
const CFGBlock &Blk = *BE->getSrc();
|
2009-04-02 01:52:26 +08:00
|
|
|
|
|
|
|
// Add a special edge for the entrance into the function/method.
|
|
|
|
if (&Blk == &PDB.getCFG().getEntry()) {
|
|
|
|
FullSourceLoc L = FullSourceLoc(PDB.getCodeDecl().getLocation(), SMgr);
|
|
|
|
GenExtAddEdge(PD, PDB, L.getSpellingLoc(), PrevLoc);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-04-01 14:13:56 +08:00
|
|
|
if (const Stmt *Term = Blk.getTerminator()) {
|
2009-04-02 02:48:52 +08:00
|
|
|
const Stmt *Cond = Blk.getTerminatorCondition();
|
2009-04-01 14:13:56 +08:00
|
|
|
if (!Cond || !IsControlFlowExpr(Cond)) {
|
2009-04-02 02:48:52 +08:00
|
|
|
// For terminators that are control-flow expressions like '&&', '?',
|
|
|
|
// have the condition be the anchor point for the control-flow edge
|
|
|
|
// instead of the terminator.
|
|
|
|
const Stmt *X = isa<Expr>(Term) ? (Cond ? Cond : Term) : Term;
|
|
|
|
GenExtAddEdge(PD, PDB, PathDiagnosticLocation(X, SMgr), PrevLoc,true);
|
2009-04-01 14:13:56 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only handle blocks with more than 1 statement here, as the blocks
|
|
|
|
// with one statement are handled at BlockEntrances.
|
|
|
|
if (Blk.size() > 1) {
|
|
|
|
const Stmt *S = *Blk.rbegin();
|
|
|
|
|
|
|
|
// We don't add control-flow edges for DeclStmt's that appear in
|
|
|
|
// the condition of if/while/for or are control-flow merge expressions.
|
|
|
|
if (!IsControlFlowExpr(S) && !IsNestedDeclStmt(S, PDB.getParentMap())) {
|
|
|
|
GenExtAddEdge(PD, PDB, PathDiagnosticLocation(S, SMgr), PrevLoc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (const BlockEntrance *BE = dyn_cast<BlockEntrance>(&P)) {
|
|
|
|
if (const Stmt* S = BE->getFirstStmt()) {
|
|
|
|
if (!IsControlFlowExpr(S) && !IsNestedDeclStmt(S, PDB.getParentMap())) {
|
2009-04-02 01:18:21 +08:00
|
|
|
if (PrevLoc.isValid()) {
|
2009-04-02 01:52:26 +08:00
|
|
|
// Are we jumping within the same enclosing statement?
|
2009-04-02 01:18:21 +08:00
|
|
|
if (PDB.getEnclosingStmtLocation(S) ==
|
|
|
|
PDB.getEnclosingStmtLocation(PrevLoc))
|
2009-04-01 14:13:56 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
GenExtAddEdge(PD, PDB, PDB.getEnclosingStmtLocation(S), PrevLoc);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PathDiagnosticPiece* p =
|
|
|
|
PDB.getReport().VisitNode(N, NextNode, PDB.getGraph(),
|
|
|
|
PDB.getBugReporter(), PDB.getNodeMapClosure());
|
|
|
|
|
|
|
|
if (p) {
|
2009-04-02 02:48:52 +08:00
|
|
|
GenExtAddEdge(PD, PDB, p->getLocation(), PrevLoc, true);
|
2009-04-01 14:13:56 +08:00
|
|
|
PD.push_front(p);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Methods for BugType and subclasses.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
BugType::~BugType() {}
|
|
|
|
void BugType::FlushReports(BugReporter &BR) {}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Methods for BugReport and subclasses.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
BugReport::~BugReport() {}
|
|
|
|
RangedBugReport::~RangedBugReport() {}
|
|
|
|
|
|
|
|
Stmt* BugReport::getStmt(BugReporter& BR) const {
|
|
|
|
ProgramPoint ProgP = EndNode->getLocation();
|
|
|
|
Stmt *S = NULL;
|
|
|
|
|
|
|
|
if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP)) {
|
|
|
|
if (BE->getBlock() == &BR.getCFG()->getExit()) S = GetPreviousStmt(EndNode);
|
|
|
|
}
|
|
|
|
if (!S) S = GetStmt(ProgP);
|
|
|
|
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
|
|
|
|
PathDiagnosticPiece*
|
|
|
|
BugReport::getEndPath(BugReporter& BR,
|
|
|
|
const ExplodedNode<GRState>* EndPathNode) {
|
|
|
|
|
|
|
|
Stmt* S = getStmt(BR);
|
|
|
|
|
|
|
|
if (!S)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
FullSourceLoc L(S->getLocStart(), BR.getContext().getSourceManager());
|
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription());
|
|
|
|
|
|
|
|
const SourceRange *Beg, *End;
|
|
|
|
getRanges(BR, Beg, End);
|
|
|
|
|
|
|
|
for (; Beg != End; ++Beg)
|
|
|
|
P->addRange(*Beg);
|
|
|
|
|
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
|
|
|
void BugReport::getRanges(BugReporter& BR, const SourceRange*& beg,
|
|
|
|
const SourceRange*& end) {
|
|
|
|
|
|
|
|
if (Expr* E = dyn_cast_or_null<Expr>(getStmt(BR))) {
|
|
|
|
R = E->getSourceRange();
|
|
|
|
assert(R.isValid());
|
|
|
|
beg = &R;
|
|
|
|
end = beg+1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
beg = end = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
SourceLocation BugReport::getLocation() const {
|
|
|
|
if (EndNode)
|
|
|
|
if (Stmt* S = GetCurrentOrPreviousStmt(EndNode)) {
|
|
|
|
// For member expressions, return the location of the '.' or '->'.
|
|
|
|
if (MemberExpr* ME = dyn_cast<MemberExpr>(S))
|
|
|
|
return ME->getMemberLoc();
|
|
|
|
|
|
|
|
return S->getLocStart();
|
|
|
|
}
|
|
|
|
|
|
|
|
return FullSourceLoc();
|
|
|
|
}
|
|
|
|
|
|
|
|
PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode<GRState>* N,
|
|
|
|
const ExplodedNode<GRState>* PrevN,
|
|
|
|
const ExplodedGraph<GRState>& G,
|
|
|
|
BugReporter& BR,
|
|
|
|
NodeResolver &NR) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Methods for BugReporter and subclasses.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
BugReportEquivClass::~BugReportEquivClass() {
|
|
|
|
for (iterator I=begin(), E=end(); I!=E; ++I) delete *I;
|
|
|
|
}
|
|
|
|
|
|
|
|
GRBugReporter::~GRBugReporter() { FlushReports(); }
|
|
|
|
BugReporterData::~BugReporterData() {}
|
|
|
|
|
|
|
|
ExplodedGraph<GRState>&
|
|
|
|
GRBugReporter::getGraph() { return Eng.getGraph(); }
|
|
|
|
|
|
|
|
GRStateManager&
|
|
|
|
GRBugReporter::getStateManager() { return Eng.getStateManager(); }
|
|
|
|
|
|
|
|
BugReporter::~BugReporter() { FlushReports(); }
|
|
|
|
|
|
|
|
void BugReporter::FlushReports() {
|
|
|
|
if (BugTypes.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// First flush the warnings for each BugType. This may end up creating new
|
|
|
|
// warnings and new BugTypes. Because ImmutableSet is a functional data
|
|
|
|
// structure, we do not need to worry about the iterators being invalidated.
|
|
|
|
for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I)
|
|
|
|
const_cast<BugType*>(*I)->FlushReports(*this);
|
|
|
|
|
|
|
|
// Iterate through BugTypes a second time. BugTypes may have been updated
|
|
|
|
// with new BugType objects and new warnings.
|
|
|
|
for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) {
|
|
|
|
BugType *BT = const_cast<BugType*>(*I);
|
|
|
|
|
|
|
|
typedef llvm::FoldingSet<BugReportEquivClass> SetTy;
|
|
|
|
SetTy& EQClasses = BT->EQClasses;
|
|
|
|
|
|
|
|
for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){
|
|
|
|
BugReportEquivClass& EQ = *EI;
|
|
|
|
FlushReport(EQ);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete the BugType object. This will also delete the equivalence
|
|
|
|
// classes.
|
|
|
|
delete BT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all references to the BugType objects.
|
|
|
|
BugTypes = F.GetEmptySet();
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PathDiagnostics generation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
static std::pair<std::pair<ExplodedGraph<GRState>*, NodeBackMap*>,
|
|
|
|
std::pair<ExplodedNode<GRState>*, unsigned> >
|
|
|
|
MakeReportGraph(const ExplodedGraph<GRState>* G,
|
|
|
|
const ExplodedNode<GRState>** NStart,
|
|
|
|
const ExplodedNode<GRState>** NEnd) {
|
|
|
|
|
|
|
|
// Create the trimmed graph. It will contain the shortest paths from the
|
|
|
|
// error nodes to the root. In the new graph we should only have one
|
|
|
|
// error node unless there are two or more error nodes with the same minimum
|
|
|
|
// path length.
|
|
|
|
ExplodedGraph<GRState>* GTrim;
|
|
|
|
InterExplodedGraphMap<GRState>* NMap;
|
|
|
|
|
|
|
|
llvm::DenseMap<const void*, const void*> InverseMap;
|
|
|
|
llvm::tie(GTrim, NMap) = G->Trim(NStart, NEnd, &InverseMap);
|
|
|
|
|
|
|
|
// Create owning pointers for GTrim and NMap just to ensure that they are
|
|
|
|
// released when this function exists.
|
|
|
|
llvm::OwningPtr<ExplodedGraph<GRState> > AutoReleaseGTrim(GTrim);
|
|
|
|
llvm::OwningPtr<InterExplodedGraphMap<GRState> > AutoReleaseNMap(NMap);
|
|
|
|
|
|
|
|
// Find the (first) error node in the trimmed graph. We just need to consult
|
|
|
|
// the node map (NMap) which maps from nodes in the original graph to nodes
|
|
|
|
// in the new graph.
|
|
|
|
const ExplodedNode<GRState>* N = 0;
|
|
|
|
unsigned NodeIndex = 0;
|
|
|
|
|
|
|
|
for (const ExplodedNode<GRState>** I = NStart; I != NEnd; ++I)
|
|
|
|
if ((N = NMap->getMappedNode(*I))) {
|
|
|
|
NodeIndex = (I - NStart) / sizeof(*I);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(N && "No error node found in the trimmed graph.");
|
|
|
|
|
|
|
|
// Create a new (third!) graph with a single path. This is the graph
|
|
|
|
// that will be returned to the caller.
|
|
|
|
ExplodedGraph<GRState> *GNew =
|
|
|
|
new ExplodedGraph<GRState>(GTrim->getCFG(), GTrim->getCodeDecl(),
|
|
|
|
GTrim->getContext());
|
|
|
|
|
|
|
|
// Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS
|
|
|
|
// to the root node, and then construct a new graph that contains only
|
|
|
|
// a single path.
|
|
|
|
llvm::DenseMap<const void*,unsigned> Visited;
|
|
|
|
std::queue<const ExplodedNode<GRState>*> WS;
|
|
|
|
WS.push(N);
|
|
|
|
|
|
|
|
unsigned cnt = 0;
|
|
|
|
const ExplodedNode<GRState>* Root = 0;
|
|
|
|
|
|
|
|
while (!WS.empty()) {
|
|
|
|
const ExplodedNode<GRState>* Node = WS.front();
|
|
|
|
WS.pop();
|
|
|
|
|
|
|
|
if (Visited.find(Node) != Visited.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Visited[Node] = cnt++;
|
|
|
|
|
|
|
|
if (Node->pred_empty()) {
|
|
|
|
Root = Node;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (ExplodedNode<GRState>::const_pred_iterator I=Node->pred_begin(),
|
|
|
|
E=Node->pred_end(); I!=E; ++I)
|
|
|
|
WS.push(*I);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert (Root);
|
|
|
|
|
|
|
|
// Now walk from the root down the BFS path, always taking the successor
|
|
|
|
// with the lowest number.
|
|
|
|
ExplodedNode<GRState> *Last = 0, *First = 0;
|
|
|
|
NodeBackMap *BM = new NodeBackMap();
|
|
|
|
|
|
|
|
for ( N = Root ;;) {
|
|
|
|
// Lookup the number associated with the current node.
|
|
|
|
llvm::DenseMap<const void*,unsigned>::iterator I = Visited.find(N);
|
|
|
|
assert (I != Visited.end());
|
|
|
|
|
|
|
|
// Create the equivalent node in the new graph with the same state
|
|
|
|
// and location.
|
|
|
|
ExplodedNode<GRState>* NewN =
|
|
|
|
GNew->getNode(N->getLocation(), N->getState());
|
|
|
|
|
|
|
|
// Store the mapping to the original node.
|
|
|
|
llvm::DenseMap<const void*, const void*>::iterator IMitr=InverseMap.find(N);
|
|
|
|
assert(IMitr != InverseMap.end() && "No mapping to original node.");
|
|
|
|
(*BM)[NewN] = (const ExplodedNode<GRState>*) IMitr->second;
|
|
|
|
|
|
|
|
// Link up the new node with the previous node.
|
|
|
|
if (Last)
|
|
|
|
NewN->addPredecessor(Last);
|
|
|
|
|
|
|
|
Last = NewN;
|
|
|
|
|
|
|
|
// Are we at the final node?
|
|
|
|
if (I->second == 0) {
|
|
|
|
First = NewN;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the next successor node. We choose the node that is marked
|
|
|
|
// with the lowest DFS number.
|
|
|
|
ExplodedNode<GRState>::const_succ_iterator SI = N->succ_begin();
|
|
|
|
ExplodedNode<GRState>::const_succ_iterator SE = N->succ_end();
|
|
|
|
N = 0;
|
|
|
|
|
|
|
|
for (unsigned MinVal = 0; SI != SE; ++SI) {
|
|
|
|
|
|
|
|
I = Visited.find(*SI);
|
|
|
|
|
|
|
|
if (I == Visited.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!N || I->second < MinVal) {
|
|
|
|
N = *SI;
|
|
|
|
MinVal = I->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert (N);
|
|
|
|
}
|
|
|
|
|
|
|
|
assert (First);
|
|
|
|
return std::make_pair(std::make_pair(GNew, BM),
|
|
|
|
std::make_pair(First, NodeIndex));
|
|
|
|
}
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object
|
|
|
|
/// and collapses PathDiagosticPieces that are expanded by macros.
|
|
|
|
static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) {
|
|
|
|
typedef std::vector<std::pair<PathDiagnosticMacroPiece*, SourceLocation> >
|
|
|
|
MacroStackTy;
|
|
|
|
|
|
|
|
typedef std::vector<PathDiagnosticPiece*>
|
|
|
|
PiecesTy;
|
|
|
|
|
|
|
|
MacroStackTy MacroStack;
|
|
|
|
PiecesTy Pieces;
|
|
|
|
|
|
|
|
for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) {
|
|
|
|
// Get the location of the PathDiagnosticPiece.
|
2009-04-01 14:13:56 +08:00
|
|
|
const FullSourceLoc Loc = I->getLocation().asLocation();
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
// Determine the instantiation location, which is the location we group
|
|
|
|
// related PathDiagnosticPieces.
|
|
|
|
SourceLocation InstantiationLoc = Loc.isMacroID() ?
|
|
|
|
SM.getInstantiationLoc(Loc) :
|
|
|
|
SourceLocation();
|
|
|
|
|
|
|
|
if (Loc.isFileID()) {
|
|
|
|
MacroStack.clear();
|
|
|
|
Pieces.push_back(&*I);
|
|
|
|
continue;
|
|
|
|
}
|
2008-04-08 07:35:17 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
assert(Loc.isMacroID());
|
|
|
|
|
|
|
|
// Is the PathDiagnosticPiece within the same macro group?
|
|
|
|
if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
|
|
|
|
MacroStack.back().first->push_back(&*I);
|
|
|
|
continue;
|
|
|
|
}
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// We aren't in the same group. Are we descending into a new macro
|
|
|
|
// or are part of an old one?
|
|
|
|
PathDiagnosticMacroPiece *MacroGroup = 0;
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
|
|
|
|
SM.getInstantiationLoc(Loc) :
|
|
|
|
SourceLocation();
|
|
|
|
|
|
|
|
// Walk the entire macro stack.
|
|
|
|
while (!MacroStack.empty()) {
|
|
|
|
if (InstantiationLoc == MacroStack.back().second) {
|
|
|
|
MacroGroup = MacroStack.back().first;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ParentInstantiationLoc == MacroStack.back().second) {
|
|
|
|
MacroGroup = MacroStack.back().first;
|
|
|
|
break;
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
MacroStack.pop_back();
|
2008-04-09 08:20:43 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
|
|
|
|
// Create a new macro group and add it to the stack.
|
|
|
|
PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc);
|
2008-04-24 07:35:07 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (MacroGroup)
|
|
|
|
MacroGroup->push_back(NewGroup);
|
|
|
|
else {
|
|
|
|
assert(InstantiationLoc.isFileID());
|
|
|
|
Pieces.push_back(NewGroup);
|
|
|
|
}
|
|
|
|
|
|
|
|
MacroGroup = NewGroup;
|
|
|
|
MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
|
2009-04-01 04:22:36 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
// Finally, add the PathDiagnosticPiece to the group.
|
|
|
|
MacroGroup->push_back(&*I);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now take the pieces and construct a new PathDiagnostic.
|
|
|
|
PD.resetPath(false);
|
2008-05-23 07:45:19 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) {
|
|
|
|
if (PathDiagnosticMacroPiece *MP=dyn_cast<PathDiagnosticMacroPiece>(*I))
|
|
|
|
if (!MP->containsEvent()) {
|
|
|
|
delete MP;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
PD.push_back(*I);
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
|
|
|
|
BugReportEquivClass& EQ) {
|
|
|
|
|
|
|
|
std::vector<const ExplodedNode<GRState>*> Nodes;
|
|
|
|
|
|
|
|
for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) {
|
|
|
|
const ExplodedNode<GRState>* N = I->getEndNode();
|
|
|
|
if (N) Nodes.push_back(N);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Nodes.empty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Construct a new graph that contains only a single path from the error
|
|
|
|
// node to a root.
|
|
|
|
const std::pair<std::pair<ExplodedGraph<GRState>*, NodeBackMap*>,
|
|
|
|
std::pair<ExplodedNode<GRState>*, unsigned> >&
|
|
|
|
GPair = MakeReportGraph(&getGraph(), &Nodes[0], &Nodes[0] + Nodes.size());
|
|
|
|
|
|
|
|
// Find the BugReport with the original location.
|
|
|
|
BugReport *R = 0;
|
|
|
|
unsigned i = 0;
|
|
|
|
for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I, ++i)
|
|
|
|
if (i == GPair.second.second) { R = *I; break; }
|
|
|
|
|
|
|
|
assert(R && "No original report found for sliced graph.");
|
|
|
|
|
|
|
|
llvm::OwningPtr<ExplodedGraph<GRState> > ReportGraph(GPair.first.first);
|
|
|
|
llvm::OwningPtr<NodeBackMap> BackMap(GPair.first.second);
|
|
|
|
const ExplodedNode<GRState> *N = GPair.second.first;
|
|
|
|
|
|
|
|
// Start building the path diagnostic...
|
|
|
|
if (PathDiagnosticPiece* Piece = R->getEndPath(*this, N))
|
|
|
|
PD.push_back(Piece);
|
|
|
|
else
|
|
|
|
return;
|
|
|
|
|
|
|
|
PathDiagnosticBuilder PDB(*this, ReportGraph.get(), R, BackMap.get(),
|
|
|
|
getStateManager().getCodeDecl(),
|
|
|
|
getPathDiagnosticClient());
|
|
|
|
|
|
|
|
switch (PDB.getGenerationScheme()) {
|
|
|
|
case PathDiagnosticClient::Extensive:
|
2009-04-01 14:13:56 +08:00
|
|
|
GenerateExtensivePathDiagnostic(PD,PDB, N);
|
|
|
|
break;
|
2009-04-01 07:00:32 +08:00
|
|
|
case PathDiagnosticClient::Minimal:
|
|
|
|
GenerateMinimalPathDiagnostic(PD, PDB, N);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// After constructing the full PathDiagnostic, do a pass over it to compact
|
|
|
|
// PathDiagnosticPieces that occur within a macro.
|
|
|
|
CompactPathDiagnostic(PD, PDB.getSourceManager());
|
|
|
|
}
|
2008-05-23 07:45:19 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
void BugReporter::Register(BugType *BT) {
|
|
|
|
BugTypes = F.Add(BugTypes, BT);
|
2008-05-17 02:33:14 +08:00
|
|
|
}
|
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
void BugReporter::EmitReport(BugReport* R) {
|
|
|
|
// Compute the bug report's hash to determine its equivalence class.
|
|
|
|
llvm::FoldingSetNodeID ID;
|
|
|
|
R->Profile(ID);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
// Lookup the equivance class. If there isn't one, create it.
|
|
|
|
BugType& BT = R->getBugType();
|
|
|
|
Register(&BT);
|
|
|
|
void *InsertPos;
|
|
|
|
BugReportEquivClass* EQ = BT.EQClasses.FindNodeOrInsertPos(ID, InsertPos);
|
|
|
|
|
|
|
|
if (!EQ) {
|
|
|
|
EQ = new BugReportEquivClass(R);
|
|
|
|
BT.EQClasses.InsertNode(EQ, InsertPos);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
EQ->AddReport(R);
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
|
|
|
|
assert(!EQ.Reports.empty());
|
|
|
|
BugReport &R = **EQ.begin();
|
|
|
|
|
|
|
|
// FIXME: Make sure we use the 'R' for the path that was actually used.
|
|
|
|
// Probably doesn't make a difference in practice.
|
|
|
|
BugType& BT = R.getBugType();
|
|
|
|
|
|
|
|
llvm::OwningPtr<PathDiagnostic> D(new PathDiagnostic(R.getBugType().getName(),
|
|
|
|
R.getDescription(),
|
|
|
|
BT.getCategory()));
|
|
|
|
GeneratePathDiagnostic(*D.get(), EQ);
|
2008-05-01 07:47:44 +08:00
|
|
|
|
|
|
|
// Get the meta data.
|
|
|
|
std::pair<const char**, const char**> Meta = R.getExtraDescriptiveText();
|
2009-01-24 08:55:43 +08:00
|
|
|
for (const char** s = Meta.first; s != Meta.second; ++s) D->addMeta(*s);
|
2008-04-18 09:56:37 +08:00
|
|
|
|
2009-01-24 08:55:43 +08:00
|
|
|
// Emit a summary diagnostic to the regular Diagnostics engine.
|
2008-07-03 05:24:01 +08:00
|
|
|
PathDiagnosticClient* PD = getPathDiagnosticClient();
|
2009-01-24 08:55:43 +08:00
|
|
|
const SourceRange *Beg = 0, *End = 0;
|
|
|
|
R.getRanges(*this, Beg, End);
|
|
|
|
Diagnostic& Diag = getDiagnostic();
|
2009-02-05 07:49:09 +08:00
|
|
|
FullSourceLoc L(R.getLocation(), getSourceManager());
|
2009-02-08 06:36:41 +08:00
|
|
|
unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
|
|
|
|
R.getDescription().c_str());
|
This reworks some of the Diagnostic interfaces a bit to change how diagnostics
are formed. In particular, a diagnostic with all its strings and ranges is now
packaged up and sent to DiagnosticClients as a DiagnosticInfo instead of as a
ton of random stuff. This has the benefit of simplifying the interface, making
it more extensible, and allowing us to do more checking for things like access
past the end of the various arrays passed in.
In addition to introducing DiagnosticInfo, this also substantially changes how
Diagnostic::Report works. Instead of being passed in all of the info required
to issue a diagnostic, Report now takes only the required info (a location and
ID) and returns a fresh DiagnosticInfo *by value*. The caller is then free to
stuff strings and ranges into the DiagnosticInfo with the << operator. When
the dtor runs on the DiagnosticInfo object (which should happen at the end of
the statement), the diagnostic is actually emitted with all of the accumulated
information. This is a somewhat tricky dance, but it means that the
accumulated DiagnosticInfo is allowed to keep pointers to other expression
temporaries without those pointers getting invalidated.
This is just the minimal change to get this stuff working, but this will allow
us to eliminate the zillions of variant "Diag" methods scattered throughout
(e.g.) sema. For example, instead of calling:
Diag(BuiltinLoc, diag::err_overload_no_match, typeNames,
SourceRange(BuiltinLoc, RParenLoc));
We will soon be able to just do:
Diag(BuiltinLoc, diag::err_overload_no_match)
<< typeNames << SourceRange(BuiltinLoc, RParenLoc));
This scales better to support arbitrary types being passed in (not just
strings) in a type-safe way. Go operator overloading?!
llvm-svn: 59502
2008-11-18 15:04:44 +08:00
|
|
|
|
2009-01-24 08:55:43 +08:00
|
|
|
switch (End-Beg) {
|
This reworks some of the Diagnostic interfaces a bit to change how diagnostics
are formed. In particular, a diagnostic with all its strings and ranges is now
packaged up and sent to DiagnosticClients as a DiagnosticInfo instead of as a
ton of random stuff. This has the benefit of simplifying the interface, making
it more extensible, and allowing us to do more checking for things like access
past the end of the various arrays passed in.
In addition to introducing DiagnosticInfo, this also substantially changes how
Diagnostic::Report works. Instead of being passed in all of the info required
to issue a diagnostic, Report now takes only the required info (a location and
ID) and returns a fresh DiagnosticInfo *by value*. The caller is then free to
stuff strings and ranges into the DiagnosticInfo with the << operator. When
the dtor runs on the DiagnosticInfo object (which should happen at the end of
the statement), the diagnostic is actually emitted with all of the accumulated
information. This is a somewhat tricky dance, but it means that the
accumulated DiagnosticInfo is allowed to keep pointers to other expression
temporaries without those pointers getting invalidated.
This is just the minimal change to get this stuff working, but this will allow
us to eliminate the zillions of variant "Diag" methods scattered throughout
(e.g.) sema. For example, instead of calling:
Diag(BuiltinLoc, diag::err_overload_no_match, typeNames,
SourceRange(BuiltinLoc, RParenLoc));
We will soon be able to just do:
Diag(BuiltinLoc, diag::err_overload_no_match)
<< typeNames << SourceRange(BuiltinLoc, RParenLoc));
This scales better to support arbitrary types being passed in (not just
strings) in a type-safe way. Go operator overloading?!
llvm-svn: 59502
2008-11-18 15:04:44 +08:00
|
|
|
default: assert(0 && "Don't handle this many ranges yet!");
|
|
|
|
case 0: Diag.Report(L, ErrorDiag); break;
|
|
|
|
case 1: Diag.Report(L, ErrorDiag) << Beg[0]; break;
|
|
|
|
case 2: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1]; break;
|
|
|
|
case 3: Diag.Report(L, ErrorDiag) << Beg[0] << Beg[1] << Beg[2]; break;
|
2008-04-19 06:56:53 +08:00
|
|
|
}
|
2009-01-24 08:55:43 +08:00
|
|
|
|
|
|
|
// Emit a full diagnostic for the path if we have a PathDiagnosticClient.
|
|
|
|
if (!PD)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (D->empty()) {
|
2009-03-07 07:58:11 +08:00
|
|
|
PathDiagnosticPiece* piece =
|
|
|
|
new PathDiagnosticEventPiece(L, R.getDescription());
|
|
|
|
|
2009-01-24 08:55:43 +08:00
|
|
|
for ( ; Beg != End; ++Beg) piece->addRange(*Beg);
|
|
|
|
D->push_back(piece);
|
|
|
|
}
|
|
|
|
|
|
|
|
PD->HandlePathDiagnostic(D.take());
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2008-07-15 01:40:50 +08:00
|
|
|
|
2008-09-20 12:23:38 +08:00
|
|
|
void BugReporter::EmitBasicReport(const char* name, const char* str,
|
|
|
|
SourceLocation Loc,
|
|
|
|
SourceRange* RBeg, unsigned NumRanges) {
|
|
|
|
EmitBasicReport(name, "", str, Loc, RBeg, NumRanges);
|
|
|
|
}
|
2009-02-05 07:49:09 +08:00
|
|
|
|
2008-09-20 12:23:38 +08:00
|
|
|
void BugReporter::EmitBasicReport(const char* name, const char* category,
|
|
|
|
const char* str, SourceLocation Loc,
|
|
|
|
SourceRange* RBeg, unsigned NumRanges) {
|
2008-07-15 01:40:50 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
// 'BT' will be owned by BugReporter as soon as we call 'EmitReport'.
|
|
|
|
BugType *BT = new BugType(name, category);
|
This reworks some of the Diagnostic interfaces a bit to change how diagnostics
are formed. In particular, a diagnostic with all its strings and ranges is now
packaged up and sent to DiagnosticClients as a DiagnosticInfo instead of as a
ton of random stuff. This has the benefit of simplifying the interface, making
it more extensible, and allowing us to do more checking for things like access
past the end of the various arrays passed in.
In addition to introducing DiagnosticInfo, this also substantially changes how
Diagnostic::Report works. Instead of being passed in all of the info required
to issue a diagnostic, Report now takes only the required info (a location and
ID) and returns a fresh DiagnosticInfo *by value*. The caller is then free to
stuff strings and ranges into the DiagnosticInfo with the << operator. When
the dtor runs on the DiagnosticInfo object (which should happen at the end of
the statement), the diagnostic is actually emitted with all of the accumulated
information. This is a somewhat tricky dance, but it means that the
accumulated DiagnosticInfo is allowed to keep pointers to other expression
temporaries without those pointers getting invalidated.
This is just the minimal change to get this stuff working, but this will allow
us to eliminate the zillions of variant "Diag" methods scattered throughout
(e.g.) sema. For example, instead of calling:
Diag(BuiltinLoc, diag::err_overload_no_match, typeNames,
SourceRange(BuiltinLoc, RParenLoc));
We will soon be able to just do:
Diag(BuiltinLoc, diag::err_overload_no_match)
<< typeNames << SourceRange(BuiltinLoc, RParenLoc));
This scales better to support arbitrary types being passed in (not just
strings) in a type-safe way. Go operator overloading?!
llvm-svn: 59502
2008-11-18 15:04:44 +08:00
|
|
|
FullSourceLoc L = getContext().getFullLoc(Loc);
|
2009-02-05 07:49:09 +08:00
|
|
|
RangedBugReport *R = new DiagBugReport(*BT, str, L);
|
|
|
|
for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg);
|
|
|
|
EmitReport(R);
|
2009-01-24 04:28:53 +08:00
|
|
|
}
|