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"
|
|
|
|
#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"
|
2008-04-03 12:42:52 +08:00
|
|
|
#include <sstream>
|
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
|
|
|
BugReporter::~BugReporter() {}
|
2008-07-03 05:24:01 +08:00
|
|
|
GRBugReporter::~GRBugReporter() {}
|
|
|
|
BugReporterData::~BugReporterData() {}
|
2008-04-10 05:41:14 +08:00
|
|
|
BugType::~BugType() {}
|
|
|
|
BugReport::~BugReport() {}
|
2008-04-12 02:40:29 +08:00
|
|
|
RangedBugReport::~RangedBugReport() {}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedGraph<GRState>& GRBugReporter::getGraph() {
|
2008-05-23 07:45:19 +08:00
|
|
|
return Eng.getGraph();
|
|
|
|
}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
GRStateManager& GRBugReporter::getStateManager() {
|
2008-05-23 07:45:19 +08:00
|
|
|
return Eng.getStateManager();
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
static inline Stmt* GetStmt(const ProgramPoint& P) {
|
|
|
|
if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
|
|
|
|
return PS->getStmt();
|
|
|
|
}
|
|
|
|
else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
|
|
|
|
return BE->getSrc()->getTerminator();
|
|
|
|
}
|
|
|
|
else if (const BlockEntrance* BE = dyn_cast<BlockEntrance>(&P)) {
|
|
|
|
return BE->getFirstStmt();
|
|
|
|
}
|
|
|
|
|
|
|
|
assert (false && "Unsupported ProgramPoint.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-04-08 07:35:17 +08:00
|
|
|
static inline Stmt* GetStmt(const CFGBlock* B) {
|
2008-05-07 02:11:09 +08:00
|
|
|
if (B->empty())
|
2008-04-26 03:01:27 +08:00
|
|
|
return const_cast<Stmt*>(B->getTerminator());
|
|
|
|
else
|
|
|
|
return (*B)[0];
|
2008-04-08 07:35:17 +08:00
|
|
|
}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
static inline ExplodedNode<GRState>*
|
|
|
|
GetNextNode(ExplodedNode<GRState>* N) {
|
2008-04-18 07:44:37 +08:00
|
|
|
return N->pred_empty() ? NULL : *(N->pred_begin());
|
|
|
|
}
|
2008-04-26 03:01:27 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
static Stmt* GetLastStmt(ExplodedNode<GRState>* N) {
|
2008-04-18 07:44:37 +08:00
|
|
|
assert (isa<BlockEntrance>(N->getLocation()));
|
|
|
|
|
|
|
|
for (N = GetNextNode(N); N; N = GetNextNode(N)) {
|
|
|
|
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
|
|
|
if (PostStmt* PS = dyn_cast<PostStmt>(&P))
|
|
|
|
return PS->getStmt();
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-05-07 02:11:09 +08:00
|
|
|
static void ExecutionContinues(std::ostringstream& os, SourceManager& SMgr,
|
|
|
|
Stmt* S) {
|
2008-05-02 06:50:36 +08:00
|
|
|
|
|
|
|
if (!S)
|
|
|
|
return;
|
2008-05-07 02:11:09 +08:00
|
|
|
|
|
|
|
// Slow, but probably doesn't matter.
|
|
|
|
if (os.str().empty())
|
|
|
|
os << ' ';
|
|
|
|
|
|
|
|
os << "Execution continues on line "
|
2008-05-02 06:50:36 +08:00
|
|
|
<< SMgr.getLogicalLineNumber(S->getLocStart()) << '.';
|
|
|
|
}
|
2008-05-07 02:11:09 +08:00
|
|
|
|
|
|
|
|
|
|
|
static inline void ExecutionContinues(std::ostringstream& os,
|
|
|
|
SourceManager& SMgr,
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* N) {
|
2008-05-07 02:11:09 +08:00
|
|
|
|
|
|
|
ExecutionContinues(os, SMgr, GetStmt(N->getLocation()));
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void ExecutionContinues(std::ostringstream& os,
|
|
|
|
SourceManager& SMgr,
|
|
|
|
const CFGBlock* B) {
|
2008-04-18 07:44:37 +08:00
|
|
|
|
2008-05-07 02:11:09 +08:00
|
|
|
ExecutionContinues(os, SMgr, GetStmt(B));
|
|
|
|
}
|
|
|
|
|
2008-05-02 06:50:36 +08:00
|
|
|
|
|
|
|
Stmt* BugReport::getStmt(BugReporter& BR) const {
|
|
|
|
|
2008-05-03 07:21:21 +08:00
|
|
|
ProgramPoint ProgP = EndNode->getLocation();
|
2008-04-18 07:44:37 +08:00
|
|
|
Stmt *S = NULL;
|
|
|
|
|
|
|
|
if (BlockEntrance* BE = dyn_cast<BlockEntrance>(&ProgP))
|
2008-07-03 13:26:14 +08:00
|
|
|
if (BE->getBlock() == &BR.getCFG()->getExit())
|
2008-05-03 07:21:21 +08:00
|
|
|
S = GetLastStmt(EndNode);
|
2008-04-18 07:44:37 +08:00
|
|
|
if (!S)
|
2008-05-02 06:50:36 +08:00
|
|
|
S = GetStmt(ProgP);
|
|
|
|
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
|
|
|
|
PathDiagnosticPiece*
|
|
|
|
BugReport::getEndPath(BugReporter& BR,
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* EndPathNode) {
|
2008-05-02 06:50:36 +08:00
|
|
|
|
|
|
|
Stmt* S = getStmt(BR);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
if (!S)
|
|
|
|
return NULL;
|
|
|
|
|
2008-05-02 07:13:35 +08:00
|
|
|
FullSourceLoc L(S->getLocStart(), BR.getContext().getSourceManager());
|
|
|
|
PathDiagnosticPiece* P = new PathDiagnosticPiece(L, getDescription());
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-04 02:00:37 +08:00
|
|
|
const SourceRange *Beg, *End;
|
2008-05-02 06:50:36 +08:00
|
|
|
getRanges(BR, Beg, End);
|
|
|
|
|
|
|
|
for (; Beg != End; ++Beg)
|
|
|
|
P->addRange(*Beg);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
return P;
|
|
|
|
}
|
|
|
|
|
2008-05-02 06:50:36 +08:00
|
|
|
void BugReport::getRanges(BugReporter& BR, const SourceRange*& beg,
|
|
|
|
const SourceRange*& end) {
|
|
|
|
|
|
|
|
if (Expr* E = dyn_cast_or_null<Expr>(getStmt(BR))) {
|
|
|
|
R = E->getSourceRange();
|
|
|
|
beg = &R;
|
|
|
|
end = beg+1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
beg = end = 0;
|
2008-04-04 01:57:38 +08:00
|
|
|
}
|
|
|
|
|
2008-04-15 01:39:48 +08:00
|
|
|
FullSourceLoc BugReport::getLocation(SourceManager& Mgr) {
|
|
|
|
|
2008-05-03 07:21:21 +08:00
|
|
|
if (!EndNode)
|
2008-04-15 01:39:48 +08:00
|
|
|
return FullSourceLoc();
|
|
|
|
|
2008-05-03 07:21:21 +08:00
|
|
|
Stmt* S = GetStmt(EndNode->getLocation());
|
2008-04-15 01:39:48 +08:00
|
|
|
|
|
|
|
if (!S)
|
|
|
|
return FullSourceLoc();
|
|
|
|
|
|
|
|
return FullSourceLoc(S->getLocStart(), Mgr);
|
|
|
|
}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
PathDiagnosticPiece* BugReport::VisitNode(ExplodedNode<GRState>* N,
|
|
|
|
ExplodedNode<GRState>* PrevN,
|
|
|
|
ExplodedGraph<GRState>& G,
|
2008-04-18 11:39:05 +08:00
|
|
|
BugReporter& BR) {
|
2008-04-10 05:41:14 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
static std::pair<ExplodedGraph<GRState>*, ExplodedNode<GRState>*>
|
|
|
|
MakeReportGraph(ExplodedGraph<GRState>* G, ExplodedNode<GRState>* N) {
|
2008-04-15 01:39:48 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
llvm::OwningPtr<ExplodedGraph<GRState> > GTrim(G->Trim(&N, &N+1));
|
2008-04-24 07:02:12 +08:00
|
|
|
|
2008-04-25 09:25:15 +08:00
|
|
|
// Find the error node in the trimmed graph.
|
2008-04-03 12:59:14 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* NOld = N;
|
2008-04-25 09:25:15 +08:00
|
|
|
N = 0;
|
2008-04-03 12:59:14 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
for (ExplodedGraph<GRState>::node_iterator
|
2008-04-18 07:44:37 +08:00
|
|
|
I = GTrim->nodes_begin(), E = GTrim->nodes_end(); I != E; ++I) {
|
2008-04-03 12:59:14 +08:00
|
|
|
|
2008-04-25 09:25:15 +08:00
|
|
|
if (I->getState() == NOld->getState() &&
|
|
|
|
I->getLocation() == NOld->getLocation()) {
|
2008-04-24 07:02:12 +08:00
|
|
|
N = &*I;
|
2008-04-03 12:59:14 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-04-24 07:02:12 +08:00
|
|
|
assert(N);
|
|
|
|
|
|
|
|
// Create a new graph with a single path.
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
G = new ExplodedGraph<GRState>(GTrim->getCFG(), GTrim->getCodeDecl(),
|
2008-04-24 07:02:12 +08:00
|
|
|
GTrim->getContext());
|
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
// Sometimes TrimGraph can contain a cycle. Perform a reverse DFS
|
|
|
|
// to the root node, and then construct a new graph that contains only
|
|
|
|
// a single path.
|
|
|
|
llvm::DenseMap<void*,unsigned> Visited;
|
2008-08-13 12:27:00 +08:00
|
|
|
llvm::SmallVector<ExplodedNode<GRState>*, 10> WS;
|
2008-06-18 13:34:07 +08:00
|
|
|
WS.push_back(N);
|
|
|
|
unsigned cnt = 0;
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* Root = 0;
|
2008-06-18 03:14:06 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
while (!WS.empty()) {
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* Node = WS.back();
|
2008-06-18 13:34:07 +08:00
|
|
|
WS.pop_back();
|
|
|
|
|
|
|
|
if (Visited.find(Node) != Visited.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Visited[Node] = cnt++;
|
|
|
|
|
|
|
|
if (Node->pred_empty()) {
|
|
|
|
Root = Node;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
for (ExplodedNode<GRState>::pred_iterator I=Node->pred_begin(),
|
2008-06-18 13:34:07 +08:00
|
|
|
E=Node->pred_end(); I!=E; ++I)
|
|
|
|
WS.push_back(*I);
|
|
|
|
}
|
2008-04-24 07:02:12 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
assert (Root);
|
|
|
|
|
|
|
|
// Now walk from the root down the DFS path, always taking the successor
|
|
|
|
// with the lowest number.
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState> *Last = 0, *First = 0;
|
2008-06-18 03:14:06 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
for ( N = Root ;;) {
|
2008-04-24 07:02:12 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
// Lookup the number associated with the current node.
|
|
|
|
llvm::DenseMap<void*,unsigned>::iterator I=Visited.find(N);
|
|
|
|
assert (I != Visited.end());
|
2008-04-18 07:44:37 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
// Create the equivalent node in the new graph with the same state
|
|
|
|
// and location.
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* NewN =
|
2008-06-18 13:34:07 +08:00
|
|
|
G->getNode(N->getLocation(), N->getState());
|
|
|
|
|
|
|
|
// Link up the new node with the previous node.
|
|
|
|
if (Last)
|
|
|
|
NewN->addPredecessor(Last);
|
2008-06-18 03:14:06 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
Last = NewN;
|
|
|
|
|
|
|
|
// Are we at the final node?
|
|
|
|
if (I->second == 0) {
|
|
|
|
First = NewN;
|
2008-06-18 03:14:06 +08:00
|
|
|
break;
|
2008-06-18 13:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find the next successor node. We choose the node that is marked
|
|
|
|
// with the lowest DFS number.
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>::succ_iterator SI = N->succ_begin();
|
|
|
|
ExplodedNode<GRState>::succ_iterator SE = N->succ_end();
|
2008-06-18 03:14:06 +08:00
|
|
|
N = 0;
|
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
for (unsigned MinVal = 0; SI != SE; ++SI) {
|
2008-06-18 03:14:06 +08:00
|
|
|
|
2008-06-18 13:34:07 +08:00
|
|
|
I = Visited.find(*SI);
|
|
|
|
|
|
|
|
if (I == Visited.end())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!N || I->second < MinVal) {
|
|
|
|
N = *SI;
|
|
|
|
MinVal = I->second;
|
2008-06-18 03:14:06 +08:00
|
|
|
}
|
2008-06-18 13:34:07 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
assert (N);
|
2008-04-24 07:02:12 +08:00
|
|
|
}
|
2008-06-18 13:34:07 +08:00
|
|
|
|
|
|
|
assert (First);
|
2008-04-24 07:04:32 +08:00
|
|
|
return std::make_pair(G, First);
|
2008-04-24 07:02:12 +08:00
|
|
|
}
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
static VarDecl* GetMostRecentVarDeclBinding(ExplodedNode<GRState>* N,
|
|
|
|
GRStateManager& VMgr,
|
2008-10-17 13:57:07 +08:00
|
|
|
SVal X) {
|
2008-05-23 07:45:19 +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;
|
|
|
|
|
2008-10-17 13:57:07 +08:00
|
|
|
SVal Y = VMgr.GetSVal(N->getState(), DR);
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
if (X != Y)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
VarDecl* VD = dyn_cast<VarDecl>(DR->getDecl());
|
|
|
|
|
|
|
|
if (!VD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
return VD;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2008-10-04 13:50:14 +08:00
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN NotableSymbolHandler
|
|
|
|
: public StoreManager::BindingsHandler {
|
|
|
|
|
|
|
|
SymbolID Sym;
|
|
|
|
const GRState* PrevSt;
|
|
|
|
Stmt* S;
|
|
|
|
GRStateManager& VMgr;
|
|
|
|
ExplodedNode<GRState>* Pred;
|
|
|
|
PathDiagnostic& PD;
|
|
|
|
BugReporter& BR;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
|
|
|
NotableSymbolHandler(SymbolID sym, const GRState* prevst, Stmt* s,
|
|
|
|
GRStateManager& vmgr, ExplodedNode<GRState>* pred,
|
|
|
|
PathDiagnostic& pd, BugReporter& br)
|
|
|
|
: Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {}
|
|
|
|
|
2008-10-17 13:57:07 +08:00
|
|
|
bool HandleBinding(StoreManager& SMgr, Store store, MemRegion* R, SVal V) {
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
SymbolID ScanSym;
|
|
|
|
|
2008-10-17 13:57:07 +08:00
|
|
|
if (loc::SymbolVal* SV = dyn_cast<loc::SymbolVal>(&V))
|
2008-05-23 07:45:19 +08:00
|
|
|
ScanSym = SV->getSymbol();
|
2008-10-17 13:57:07 +08:00
|
|
|
else if (nonloc::SymbolVal* SV = dyn_cast<nonloc::SymbolVal>(&V))
|
2008-05-23 07:45:19 +08:00
|
|
|
ScanSym = SV->getSymbol();
|
|
|
|
else
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
if (ScanSym != Sym)
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
2008-05-23 07:45:19 +08:00
|
|
|
|
2008-10-04 13:50:14 +08:00
|
|
|
// Check if the previous state has this binding.
|
2008-10-17 13:57:07 +08:00
|
|
|
SVal X = VMgr.GetSVal(PrevSt, loc::MemRegionVal(R));
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
if (X == V) // Same binding?
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
// 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 (BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
|
|
|
|
if (!B->isAssignmentOp())
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
// What variable did we assign to?
|
|
|
|
DeclRefExpr* DR = dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenCasts());
|
|
|
|
|
|
|
|
if (!DR)
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
VD = dyn_cast<VarDecl>(DR->getDecl());
|
|
|
|
}
|
2008-10-07 02:37:46 +08:00
|
|
|
else if (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());
|
|
|
|
}
|
2008-10-04 13:50:14 +08:00
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
if (!VD)
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
// What is the most recently referenced variable with this binding?
|
|
|
|
VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V);
|
2008-10-04 13:50:14 +08:00
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
if (!MostRecent)
|
2008-10-04 13:50:14 +08:00
|
|
|
return true;
|
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
// Create the diagnostic.
|
|
|
|
|
|
|
|
FullSourceLoc L(S->getLocStart(), BR.getSourceManager());
|
2008-10-04 13:50:14 +08:00
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
if (VD->getType()->isPointerLikeType()) {
|
|
|
|
std::string msg = "'" + std::string(VD->getName()) +
|
2008-10-04 13:50:14 +08:00
|
|
|
"' now aliases '" + MostRecent->getName() + "'";
|
2008-05-23 07:45:19 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, msg));
|
|
|
|
}
|
2008-10-04 13:50:14 +08:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
2008-05-23 07:45:19 +08:00
|
|
|
}
|
|
|
|
|
2008-10-04 13:50:14 +08:00
|
|
|
static void HandleNotableSymbol(ExplodedNode<GRState>* N, Stmt* S,
|
|
|
|
SymbolID Sym, BugReporter& BR,
|
|
|
|
PathDiagnostic& PD) {
|
|
|
|
|
|
|
|
ExplodedNode<GRState>* Pred = N->pred_empty() ? 0 : *N->pred_begin();
|
|
|
|
const GRState* PrevSt = Pred ? Pred->getState() : 0;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class VISIBILITY_HIDDEN ScanNotableSymbols
|
|
|
|
: public StoreManager::BindingsHandler {
|
|
|
|
|
|
|
|
llvm::SmallSet<SymbolID, 10> AlreadyProcessed;
|
|
|
|
ExplodedNode<GRState>* N;
|
|
|
|
Stmt* S;
|
|
|
|
GRBugReporter& BR;
|
|
|
|
PathDiagnostic& PD;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ScanNotableSymbols(ExplodedNode<GRState>* n, Stmt* s, GRBugReporter& br,
|
|
|
|
PathDiagnostic& pd)
|
|
|
|
: N(n), S(s), BR(br), PD(pd) {}
|
|
|
|
|
2008-10-17 13:57:07 +08:00
|
|
|
bool HandleBinding(StoreManager& SMgr, Store store, MemRegion* R, SVal V) {
|
2008-10-04 13:50:14 +08:00
|
|
|
SymbolID ScanSym;
|
|
|
|
|
2008-10-17 13:57:07 +08:00
|
|
|
if (loc::SymbolVal* SV = dyn_cast<loc::SymbolVal>(&V))
|
2008-10-04 13:50:14 +08:00
|
|
|
ScanSym = SV->getSymbol();
|
2008-10-17 13:57:07 +08:00
|
|
|
else if (nonloc::SymbolVal* SV = dyn_cast<nonloc::SymbolVal>(&V))
|
2008-10-04 13:50:14 +08:00
|
|
|
ScanSym = SV->getSymbol();
|
|
|
|
else
|
|
|
|
return true;
|
|
|
|
|
|
|
|
assert (ScanSym.isInitialized());
|
|
|
|
|
|
|
|
if (!BR.isNotable(ScanSym))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (AlreadyProcessed.count(ScanSym))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
AlreadyProcessed.insert(ScanSym);
|
|
|
|
|
|
|
|
HandleNotableSymbol(N, S, ScanSym, BR, PD);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2008-07-03 05:24:01 +08:00
|
|
|
void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
|
|
|
|
BugReport& R) {
|
2008-04-24 07:02:12 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* N = R.getEndNode();
|
2008-04-24 07:02:12 +08:00
|
|
|
|
|
|
|
if (!N) return;
|
|
|
|
|
|
|
|
// Construct a new graph that contains only a single path from the error
|
|
|
|
// node to a root.
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
const std::pair<ExplodedGraph<GRState>*,ExplodedNode<GRState>*>
|
2008-04-24 07:02:12 +08:00
|
|
|
GPair = MakeReportGraph(&getGraph(), N);
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
llvm::OwningPtr<ExplodedGraph<GRState> > ReportGraph(GPair.first);
|
2008-04-24 07:02:12 +08:00
|
|
|
assert(GPair.second->getLocation() == N->getLocation());
|
|
|
|
N = GPair.second;
|
|
|
|
|
|
|
|
// Start building the path diagnostic...
|
|
|
|
|
2008-04-18 07:44:37 +08:00
|
|
|
if (PathDiagnosticPiece* Piece = R.getEndPath(*this, N))
|
|
|
|
PD.push_back(Piece);
|
|
|
|
else
|
|
|
|
return;
|
2008-04-09 08:20:43 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* NextNode = N->pred_empty()
|
2008-04-09 08:20:43 +08:00
|
|
|
? NULL : *(N->pred_begin());
|
|
|
|
|
2008-07-03 05:24:01 +08:00
|
|
|
ASTContext& Ctx = getContext();
|
2008-04-18 07:44:37 +08:00
|
|
|
SourceManager& SMgr = Ctx.getSourceManager();
|
|
|
|
|
2008-04-09 08:20:43 +08:00
|
|
|
while (NextNode) {
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* LastNode = N;
|
2008-04-09 08:20:43 +08:00
|
|
|
N = NextNode;
|
2008-04-18 07:44:37 +08:00
|
|
|
NextNode = GetNextNode(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;
|
|
|
|
|
|
|
|
FullSourceLoc L(T->getLocStart(), SMgr);
|
|
|
|
|
|
|
|
switch (T->getStmtClass()) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Stmt::GotoStmtClass:
|
|
|
|
case Stmt::IndirectGotoStmtClass: {
|
|
|
|
|
|
|
|
Stmt* S = GetStmt(LastNode->getLocation());
|
|
|
|
|
|
|
|
if (!S)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
|
|
|
os << "Control jumps to line "
|
2008-04-08 07:35:17 +08:00
|
|
|
<< SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, os.str()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Stmt::SwitchStmtClass: {
|
|
|
|
|
|
|
|
// Figure out what case arm we took.
|
2008-04-24 07:35:07 +08:00
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
std::ostringstream os;
|
2008-04-24 07:35:07 +08:00
|
|
|
|
|
|
|
if (Stmt* S = Dst->getLabel())
|
|
|
|
switch (S->getStmtClass()) {
|
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
default:
|
2008-04-24 07:35:07 +08:00
|
|
|
assert(false && "Not a valid switch label.");
|
|
|
|
continue;
|
|
|
|
|
2008-04-23 06:29:46 +08:00
|
|
|
case Stmt::DefaultStmtClass: {
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
os << "Control jumps to the 'default' case at line "
|
2008-04-09 08:20:43 +08:00
|
|
|
<< SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case Stmt::CaseStmtClass: {
|
|
|
|
|
|
|
|
os << "Control jumps to 'case ";
|
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
CaseStmt* Case = cast<CaseStmt>(S);
|
|
|
|
Expr* LHS = Case->getLHS()->IgnoreParenCasts();
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
// Determine if it is an enum.
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
bool GetRawInt = true;
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
if (DeclRefExpr* DR = dyn_cast<DeclRefExpr>(LHS)) {
|
|
|
|
|
|
|
|
// FIXME: Maybe this should be an assertion. Are there cases
|
|
|
|
// were it is not an EnumConstantDecl?
|
2008-11-18 14:07:40 +08:00
|
|
|
EnumConstantDecl* D = dyn_cast<EnumConstantDecl>(DR->getDecl());
|
2008-04-24 07:35:07 +08:00
|
|
|
if (D) {
|
|
|
|
GetRawInt = false;
|
|
|
|
os << D->getName();
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2008-04-24 07:35:07 +08:00
|
|
|
|
|
|
|
if (GetRawInt) {
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
// Not an enum.
|
|
|
|
Expr* CondE = cast<SwitchStmt>(T)->getCond();
|
|
|
|
unsigned bits = Ctx.getTypeSize(CondE->getType());
|
|
|
|
llvm::APSInt V(bits, false);
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
if (!LHS->isIntegerConstantExpr(V, Ctx, 0, true)) {
|
|
|
|
assert (false && "Case condition must be constant.");
|
2008-04-03 12:42:52 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2008-08-24 06:23:37 +08:00
|
|
|
llvm::raw_os_ostream OS(os);
|
|
|
|
OS << V;
|
2008-04-26 03:01:27 +08:00
|
|
|
}
|
2008-04-24 07:35:07 +08:00
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
os << ":' at line "
|
2008-04-24 07:35:07 +08:00
|
|
|
<< SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
2008-04-25 09:29:56 +08:00
|
|
|
else {
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "'Default' branch taken. ";
|
2008-04-26 03:01:27 +08:00
|
|
|
ExecutionContinues(os, SMgr, LastNode);
|
2008-04-25 09:29:56 +08:00
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, os.str()));
|
|
|
|
break;
|
|
|
|
}
|
2008-04-26 03:01:27 +08:00
|
|
|
|
|
|
|
case Stmt::BreakStmtClass:
|
|
|
|
case Stmt::ContinueStmtClass: {
|
|
|
|
std::ostringstream os;
|
|
|
|
ExecutionContinues(os, SMgr, LastNode);
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, os.str()));
|
|
|
|
break;
|
|
|
|
}
|
2008-04-08 07:35:17 +08:00
|
|
|
|
|
|
|
case Stmt::ConditionalOperatorClass: {
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
os << "'?' condition evaluates to ";
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()+1) == Dst)
|
|
|
|
os << "false.";
|
|
|
|
else
|
|
|
|
os << "true.";
|
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, os.str()));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-08 07:35:17 +08:00
|
|
|
case Stmt::DoStmtClass: {
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()) == Dst) {
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "Loop condition is true. ";
|
2008-05-07 02:11:09 +08:00
|
|
|
ExecutionContinues(os, SMgr, Dst);
|
2008-04-08 07:35:17 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, os.str()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L,
|
|
|
|
"Loop condition is false. Exiting loop."));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
case Stmt::WhileStmtClass:
|
2008-04-08 07:35:17 +08:00
|
|
|
case Stmt::ForStmtClass: {
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
|
|
|
|
std::ostringstream os;
|
|
|
|
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "Loop condition is false. ";
|
2008-05-07 02:11:09 +08:00
|
|
|
ExecutionContinues(os, SMgr, Dst);
|
2008-04-08 07:35:17 +08:00
|
|
|
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, os.str()));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L,
|
|
|
|
"Loop condition is true. Entering loop body."));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
case Stmt::IfStmtClass: {
|
|
|
|
|
|
|
|
if (*(Src->succ_begin()+1) == Dst)
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
|
|
|
|
else
|
|
|
|
PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2008-04-09 08:20:43 +08:00
|
|
|
}
|
2008-04-24 07:35:07 +08:00
|
|
|
|
|
|
|
if (PathDiagnosticPiece* p = R.VisitNode(N, NextNode, *ReportGraph, *this))
|
2008-05-23 07:45:19 +08:00
|
|
|
PD.push_front(p);
|
|
|
|
|
2008-10-04 13:50:14 +08:00
|
|
|
if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
|
|
|
|
// Scan the region bindings, and see if a "notable" symbol has a new
|
2008-05-23 07:45:19 +08:00
|
|
|
// lval binding.
|
2008-10-04 13:50:14 +08:00
|
|
|
ScanNotableSymbols SNS(N, PS->getStmt(), *this, PD);
|
|
|
|
getStateManager().iterBindings(N->getState(), SNS);
|
2008-05-23 07:45:19 +08:00
|
|
|
}
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-05-23 07:45:19 +08:00
|
|
|
|
2008-04-19 04:54:29 +08:00
|
|
|
bool BugTypeCacheLocation::isCached(BugReport& R) {
|
|
|
|
|
2008-08-13 12:27:00 +08:00
|
|
|
ExplodedNode<GRState>* N = R.getEndNode();
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-04-18 10:24:50 +08:00
|
|
|
if (!N)
|
|
|
|
return false;
|
2008-04-19 04:54:29 +08:00
|
|
|
|
|
|
|
// Cache the location of the error. Don't emit the same
|
2008-04-03 12:42:52 +08:00
|
|
|
// warning for the same error type that occurs at the same program
|
|
|
|
// location but along a different path.
|
|
|
|
|
2008-05-17 02:33:14 +08:00
|
|
|
return isCached(N->getLocation());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BugTypeCacheLocation::isCached(ProgramPoint P) {
|
2008-09-17 02:44:52 +08:00
|
|
|
if (CachedErrors.count(P))
|
2008-04-03 12:42:52 +08:00
|
|
|
return true;
|
|
|
|
|
2008-09-17 02:44:52 +08:00
|
|
|
CachedErrors.insert(P);
|
2008-04-03 12:42:52 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2008-04-18 09:56:37 +08:00
|
|
|
void BugReporter::EmitWarning(BugReport& R) {
|
|
|
|
|
2008-04-19 04:54:29 +08:00
|
|
|
if (R.getBugType().isCached(R))
|
2008-04-03 12:42:52 +08:00
|
|
|
return;
|
2008-04-18 09:56:37 +08:00
|
|
|
|
2008-09-20 12:23:38 +08:00
|
|
|
llvm::OwningPtr<PathDiagnostic> D(new PathDiagnostic(R.getName(),
|
|
|
|
R.getCategory()));
|
2008-04-23 00:15:03 +08:00
|
|
|
GeneratePathDiagnostic(*D.get(), R);
|
2008-05-01 07:47:44 +08:00
|
|
|
|
|
|
|
// Get the meta data.
|
|
|
|
|
|
|
|
std::pair<const char**, const char**> Meta = R.getExtraDescriptiveText();
|
|
|
|
|
|
|
|
for (const char** s = Meta.first; s != Meta.second; ++s)
|
|
|
|
D->addMeta(*s);
|
2008-04-18 09:56:37 +08:00
|
|
|
|
|
|
|
// Emit a full diagnostic for the path if we have a PathDiagnosticClient.
|
2008-04-03 15:33:55 +08:00
|
|
|
|
2008-07-03 05:24:01 +08:00
|
|
|
PathDiagnosticClient* PD = getPathDiagnosticClient();
|
|
|
|
|
2008-04-23 00:15:03 +08:00
|
|
|
if (PD && !D->empty()) {
|
|
|
|
PD->HandlePathDiagnostic(D.take());
|
2008-04-18 09:56:37 +08:00
|
|
|
return;
|
|
|
|
}
|
2008-04-15 01:39:48 +08:00
|
|
|
|
2008-04-18 09:56:37 +08:00
|
|
|
// We don't have a PathDiagnosticClient, but we can still emit a single
|
|
|
|
// line diagnostic. Determine the location.
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2008-07-03 05:24:01 +08:00
|
|
|
FullSourceLoc L = D->empty() ? R.getLocation(getSourceManager())
|
2008-04-23 00:15:03 +08:00
|
|
|
: D->back()->getLocation();
|
2008-04-15 02:06:42 +08:00
|
|
|
|
|
|
|
|
2008-04-18 09:56:37 +08:00
|
|
|
// Determine the range.
|
2008-04-15 02:06:42 +08:00
|
|
|
|
2008-04-18 09:56:37 +08:00
|
|
|
const SourceRange *Beg, *End;
|
2008-07-15 01:40:50 +08:00
|
|
|
|
2008-04-23 00:15:03 +08:00
|
|
|
if (!D->empty()) {
|
|
|
|
Beg = D->back()->ranges_begin();
|
|
|
|
End = D->back()->ranges_end();
|
2008-04-15 02:06:42 +08:00
|
|
|
}
|
2008-07-15 01:40:50 +08:00
|
|
|
else
|
2008-05-02 06:50:36 +08:00
|
|
|
R.getRanges(*this, Beg, End);
|
2008-04-18 09:56:37 +08:00
|
|
|
|
2008-04-19 06:56:53 +08:00
|
|
|
if (PD) {
|
|
|
|
PathDiagnosticPiece* piece = new PathDiagnosticPiece(L, R.getDescription());
|
|
|
|
|
|
|
|
for ( ; Beg != End; ++Beg)
|
|
|
|
piece->addRange(*Beg);
|
|
|
|
|
2008-07-15 01:40:50 +08:00
|
|
|
D->push_back(piece);
|
2008-04-23 00:15:03 +08:00
|
|
|
PD->HandlePathDiagnostic(D.take());
|
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
|
|
|
return;
|
2008-04-19 06:56:53 +08:00
|
|
|
}
|
|
|
|
else {
|
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
|
|
|
std::string str;
|
2008-07-15 01:40:50 +08:00
|
|
|
|
2008-04-23 00:15:03 +08:00
|
|
|
if (D->empty())
|
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
|
|
|
str = R.getDescription();
|
2008-04-19 06:56:53 +08:00
|
|
|
else
|
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
|
|
|
str = D->back()->getString();
|
2008-07-15 01:40:50 +08:00
|
|
|
|
2008-07-03 05:24:01 +08:00
|
|
|
Diagnostic& Diag = getDiagnostic();
|
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
|
|
|
unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, str.c_str());
|
|
|
|
|
|
|
|
switch (End-Beg) {
|
|
|
|
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
|
|
|
}
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
2008-09-20 12:23:38 +08:00
|
|
|
SimpleBugType BT(name, category, 0);
|
2008-07-15 01:40:50 +08:00
|
|
|
DiagCollector C(BT);
|
|
|
|
Diagnostic& Diag = getDiagnostic();
|
2008-11-18 14:07:40 +08:00
|
|
|
|
|
|
|
DiagnosticClient *OldClient = Diag.getClient();
|
|
|
|
Diag.setClient(&C);
|
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);
|
|
|
|
unsigned DiagID = Diag.getCustomDiagID(Diagnostic::Warning, str);
|
|
|
|
|
|
|
|
switch (NumRanges) {
|
|
|
|
default: assert(0 && "Don't handle this many ranges yet!");
|
|
|
|
case 0: Diag.Report(L, DiagID); break;
|
|
|
|
case 1: Diag.Report(L, DiagID) << RBeg[0]; break;
|
|
|
|
case 2: Diag.Report(L, DiagID) << RBeg[0] << RBeg[1]; break;
|
|
|
|
case 3: Diag.Report(L, DiagID) << RBeg[0] << RBeg[1] << RBeg[2]; break;
|
|
|
|
}
|
|
|
|
|
2008-11-18 14:07:40 +08:00
|
|
|
Diag.setClient(OldClient);
|
2008-07-15 01:40:50 +08:00
|
|
|
|
|
|
|
for (DiagCollector::iterator I = C.begin(), E = C.end(); I != E; ++I)
|
|
|
|
EmitWarning(*I);
|
|
|
|
}
|
|
|
|
|