diff options
author | Ted Kremenek <kremenek@apple.com> | 2009-03-31 23:00:32 +0000 |
---|---|---|
committer | Ted Kremenek <kremenek@apple.com> | 2009-03-31 23:00:32 +0000 |
commit | 3106198188ac6eb2753f1764d5c28376b0b76351 (patch) | |
tree | ed28bbeaef6af8cb9e82b1ae0deac13635c6118f /lib/Analysis/BugReporter.cpp | |
parent | 35f38a2c22d68c22e2dbe8e9ee84c120c8f327bb (diff) |
More code reshuffling. No functionality change.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68157 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/BugReporter.cpp')
-rw-r--r-- | lib/Analysis/BugReporter.cpp | 930 |
1 files changed, 465 insertions, 465 deletions
diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp index 8a37d1a3c1..1d1c2fa7d0 100644 --- a/lib/Analysis/BugReporter.cpp +++ b/lib/Analysis/BugReporter.cpp @@ -31,7 +31,7 @@ using namespace clang; //===----------------------------------------------------------------------===// -// static functions. +// Helper routines for walking the ExplodedGraph and fetching statements. //===----------------------------------------------------------------------===// static inline Stmt* GetStmt(ProgramPoint P) { @@ -99,7 +99,7 @@ static inline Stmt* GetCurrentOrNextStmt(const ExplodedNode<GRState>* N) { } //===----------------------------------------------------------------------===// -// Diagnostics for 'execution continues on line XXX'. +// PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// typedef llvm::DenseMap<const ExplodedNode<GRState>*, @@ -252,270 +252,9 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { } //===----------------------------------------------------------------------===// -// 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. +// ScanNotableSymbols: closure-like callback for scanning Store bindings. //===----------------------------------------------------------------------===// -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)); -} - static const VarDecl* GetMostRecentVarDeclBinding(const ExplodedNode<GRState>* N, GRStateManager& VMgr, SVal X) { @@ -550,8 +289,8 @@ GetMostRecentVarDeclBinding(const ExplodedNode<GRState>* N, namespace { class VISIBILITY_HIDDEN NotableSymbolHandler - : public StoreManager::BindingsHandler { - +: public StoreManager::BindingsHandler { + SymbolRef Sym; const GRState* PrevSt; const Stmt* S; @@ -559,19 +298,19 @@ class VISIBILITY_HIDDEN NotableSymbolHandler const ExplodedNode<GRState>* Pred; PathDiagnostic& PD; BugReporter& BR; - + public: 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) {} - + : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} + bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, SVal V) { - + SymbolRef ScanSym = V.getAsSymbol(); - + if (ScanSym != Sym) return true; @@ -650,200 +389,52 @@ static void HandleNotableSymbol(const ExplodedNode<GRState>* N, namespace { class VISIBILITY_HIDDEN ScanNotableSymbols - : public StoreManager::BindingsHandler { - +: public StoreManager::BindingsHandler { + 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) {} + : 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; } }; } // end anonymous namespace -/// 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. - const FullSourceLoc Loc = I->getLocation(); - - // 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; - } - - 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; - } - - // We aren't in the same group. Are we descending into a new macro - // or are part of an old one? - PathDiagnosticMacroPiece *MacroGroup = 0; - - 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; - } - - MacroStack.pop_back(); - } - - if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { - // Create a new macro group and add it to the stack. - PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); - - if (MacroGroup) - MacroGroup->push_back(NewGroup); - else { - assert(InstantiationLoc.isFileID()); - Pieces.push_back(NewGroup); - } - - MacroGroup = NewGroup; - MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc)); - } - - // Finally, add the PathDiagnosticPiece to the group. - MacroGroup->push_back(&*I); - } - - // Now take the pieces and construct a new PathDiagnostic. - PD.resetPath(false); - - 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); - } -} - -static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode<GRState> *N); - -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: - 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()); -} +//===----------------------------------------------------------------------===// +// "Minimal" path diagnostic generation algorithm. +//===----------------------------------------------------------------------===// 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()); - + ? NULL : *(N->pred_begin()); while (NextNode) { N = NextNode; NextNode = GetPredecessorNode(N); @@ -876,7 +467,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); os << "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getInstantiationLineNumber(); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, os.str())); break; @@ -886,19 +477,19 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, // Figure out what case arm we took. std::string sbuf; llvm::raw_string_ostream os(sbuf); - + if (Stmt* S = Dst->getLabel()) { PathDiagnosticLocation End(S, SMgr); - + switch (S->getStmtClass()) { default: os << "No cases match in the switch statement. " - "Control jumps to line " - << End.asLocation().getInstantiationLineNumber(); + "Control jumps to line " + << End.asLocation().getInstantiationLineNumber(); break; case Stmt::DefaultStmtClass: os << "Control jumps to the 'default' case at line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getInstantiationLineNumber(); break; case Stmt::CaseStmtClass: { @@ -913,16 +504,16 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, // FIXME: Maybe this should be an assertion. Are there cases // were it is not an EnumConstantDecl? EnumConstantDecl* D = - dyn_cast<EnumConstantDecl>(DR->getDecl()); - + dyn_cast<EnumConstantDecl>(DR->getDecl()); + if (D) { GetRawInt = false; os << D->getNameAsString(); } } - - if (GetRawInt) { + if (GetRawInt) { + // Not an enum. Expr* CondE = cast<SwitchStmt>(T)->getCond(); unsigned bits = Ctx.getTypeSize(CondE->getType()); @@ -937,7 +528,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } os << ":' at line " - << End.asLocation().getInstantiationLineNumber(); + << End.asLocation().getInstantiationLineNumber(); break; } } @@ -963,13 +554,13 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os.str())); break; } - - // Determine control-flow for ternary '?'. + + // Determine control-flow for ternary '?'. case Stmt::ConditionalOperatorClass: { std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "'?' condition is "; - + if (*(Src->succ_begin()+1) == Dst) os << "false"; else @@ -985,7 +576,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, break; } - // Determine control-flow for short-circuited '&&' and '||'. + // Determine control-flow for short-circuited '&&' and '||'. case Stmt::BinaryOperatorClass: { if (!PDB.supportsLogicalOpControlFlow()) break; @@ -994,7 +585,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, std::string sbuf; llvm::raw_string_ostream os(sbuf); os << "Left side of '"; - + if (B->getOpcode() == BinaryOperator::LAnd) { os << "&&" << "' is "; @@ -1032,7 +623,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os.str())); } } - + break; } @@ -1043,7 +634,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, os << "Loop condition is true. "; PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - + if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); @@ -1055,9 +646,9 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, if (const Stmt *S = End.asStmt()) End = PDB.getEnclosingStmtLocation(S); - + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Loop condition is false. Exiting loop")); + "Loop condition is false. Exiting loop")); } break; @@ -1068,12 +659,12 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, 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())); } @@ -1083,7 +674,7 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, End = PDB.getEnclosingStmtLocation(S); PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Loop condition is true. Entering loop body")); + "Loop condition is true. Entering loop body")); } break; @@ -1091,28 +682,28 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, 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, - "Taking false branch")); + "Taking false branch")); else PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Taking true branch")); + "Taking true branch")); break; } } } - + if (PathDiagnosticPiece* p = - PDB.getReport().VisitNode(N, NextNode, PDB.getGraph(), - PDB.getBugReporter(), - PDB.getNodeMapClosure())) { - PD.push_front(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 @@ -1123,6 +714,415 @@ static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, } } +//===----------------------------------------------------------------------===// +// 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), + |