From: Ted Kremenek Date: Tue, 31 Mar 2009 23:00:32 +0000 (+0000) Subject: More code reshuffling. No functionality change. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3106198188ac6eb2753f1764d5c28376b0b76351;p=clang More code reshuffling. No functionality change. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@68157 91177308-0d34-0410-b5e6-96231b3b80d8 --- 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* N) { } //===----------------------------------------------------------------------===// -// Diagnostics for 'execution continues on line XXX'. +// PathDiagnosticBuilder and its associated routines and helper objects. //===----------------------------------------------------------------------===// typedef llvm::DenseMap*, @@ -252,601 +252,192 @@ PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { } //===----------------------------------------------------------------------===// -// Methods for BugType and subclasses. -//===----------------------------------------------------------------------===// -BugType::~BugType() {} -void BugType::FlushReports(BugReporter &BR) {} - -//===----------------------------------------------------------------------===// -// Methods for BugReport and subclasses. +// ScanNotableSymbols: closure-like callback for scanning Store bindings. //===----------------------------------------------------------------------===// -BugReport::~BugReport() {} -RangedBugReport::~RangedBugReport() {} -Stmt* BugReport::getStmt(BugReporter& BR) const { - ProgramPoint ProgP = EndNode->getLocation(); - Stmt *S = NULL; +static const VarDecl* +GetMostRecentVarDeclBinding(const ExplodedNode* N, + GRStateManager& VMgr, SVal X) { - if (BlockEntrance* BE = dyn_cast(&ProgP)) { - if (BE->getBlock() == &BR.getCFG()->getExit()) S = GetPreviousStmt(EndNode); + for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { + + ProgramPoint P = N->getLocation(); + + if (!isa(P)) + continue; + + DeclRefExpr* DR = dyn_cast(cast(P).getStmt()); + + if (!DR) + continue; + + SVal Y = VMgr.GetSVal(N->getState(), DR); + + if (X != Y) + continue; + + VarDecl* VD = dyn_cast(DR->getDecl()); + + if (!VD) + continue; + + return VD; } - if (!S) S = GetStmt(ProgP); - return S; + return 0; } -PathDiagnosticPiece* -BugReport::getEndPath(BugReporter& BR, - const ExplodedNode* EndPathNode) { +namespace { +class VISIBILITY_HIDDEN NotableSymbolHandler +: public StoreManager::BindingsHandler { - Stmt* S = getStmt(BR); + SymbolRef Sym; + const GRState* PrevSt; + const Stmt* S; + GRStateManager& VMgr; + const ExplodedNode* Pred; + PathDiagnostic& PD; + BugReporter& BR; - if (!S) - return NULL; +public: - FullSourceLoc L(S->getLocStart(), BR.getContext().getSourceManager()); - PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription()); + NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, + GRStateManager& vmgr, const ExplodedNode* pred, + PathDiagnostic& pd, BugReporter& br) + : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} - const SourceRange *Beg, *End; - getRanges(BR, Beg, End); + 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(S)) { + if (!B->isAssignmentOp()) + return true; + + // What variable did we assign to? + DeclRefExpr* DR = dyn_cast(B->getLHS()->IgnoreParenCasts()); + + if (!DR) + return true; + + VD = dyn_cast(DR->getDecl()); + } + else if (const DeclStmt* DS = dyn_cast(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(*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* N, + const Stmt* S, + SymbolRef Sym, BugReporter& BR, + PathDiagnostic& PD) { - for (; Beg != End; ++Beg) - P->addRange(*Beg); + const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin(); + const GRState* PrevSt = Pred ? Pred->getState() : 0; - return P; + 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(BR).getStateManager(); + NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); + cast(BR).getStateManager().iterBindings(N->getState(), H); } -void BugReport::getRanges(BugReporter& BR, const SourceRange*& beg, - const SourceRange*& end) { +namespace { +class VISIBILITY_HIDDEN ScanNotableSymbols +: public StoreManager::BindingsHandler { - if (Expr* E = dyn_cast_or_null(getStmt(BR))) { - R = E->getSourceRange(); - assert(R.isValid()); - beg = &R; - end = beg+1; + llvm::SmallSet AlreadyProcessed; + const ExplodedNode* N; + Stmt* S; + GRBugReporter& BR; + PathDiagnostic& PD; + +public: + ScanNotableSymbols(const ExplodedNode* 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; } - 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(S)) - return ME->getMemberLoc(); - - return S->getLocStart(); - } - - return FullSourceLoc(); -} - -PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, - const ExplodedNode* PrevN, - const ExplodedGraph& G, - BugReporter& BR, - NodeResolver &NR) { - return NULL; -} +}; +} // end anonymous namespace //===----------------------------------------------------------------------===// -// Methods for BugReporter and subclasses. +// "Minimal" path diagnostic generation algorithm. //===----------------------------------------------------------------------===// -BugReportEquivClass::~BugReportEquivClass() { - for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; -} - -GRBugReporter::~GRBugReporter() { FlushReports(); } -BugReporterData::~BugReporterData() {} - -ExplodedGraph& -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(*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(*I); - - typedef llvm::FoldingSet 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*, NodeBackMap*>, - std::pair*, unsigned> > -MakeReportGraph(const ExplodedGraph* G, - const ExplodedNode** NStart, - const ExplodedNode** 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* GTrim; - InterExplodedGraphMap* NMap; - - llvm::DenseMap 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 > AutoReleaseGTrim(GTrim); - llvm::OwningPtr > 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* N = 0; - unsigned NodeIndex = 0; - - for (const ExplodedNode** 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 *GNew = - new ExplodedGraph(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 Visited; - std::queue*> WS; - WS.push(N); - - unsigned cnt = 0; - const ExplodedNode* Root = 0; - - while (!WS.empty()) { - const ExplodedNode* Node = WS.front(); - WS.pop(); - - if (Visited.find(Node) != Visited.end()) - continue; - - Visited[Node] = cnt++; - - if (Node->pred_empty()) { - Root = Node; - break; - } - - for (ExplodedNode::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 *Last = 0, *First = 0; - NodeBackMap *BM = new NodeBackMap(); - - for ( N = Root ;;) { - // Lookup the number associated with the current node. - llvm::DenseMap::iterator I = Visited.find(N); - assert (I != Visited.end()); - - // Create the equivalent node in the new graph with the same state - // and location. - ExplodedNode* NewN = - GNew->getNode(N->getLocation(), N->getState()); - - // Store the mapping to the original node. - llvm::DenseMap::iterator IMitr=InverseMap.find(N); - assert(IMitr != InverseMap.end() && "No mapping to original node."); - (*BM)[NewN] = (const ExplodedNode*) 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::const_succ_iterator SI = N->succ_begin(); - ExplodedNode::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* N, - GRStateManager& VMgr, SVal X) { - - for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { - - ProgramPoint P = N->getLocation(); - - if (!isa(P)) - continue; - - DeclRefExpr* DR = dyn_cast(cast(P).getStmt()); - - if (!DR) - continue; - - SVal Y = VMgr.GetSVal(N->getState(), DR); - - if (X != Y) - continue; - - VarDecl* VD = dyn_cast(DR->getDecl()); - - if (!VD) - continue; - - return VD; - } - - return 0; -} - -namespace { -class VISIBILITY_HIDDEN NotableSymbolHandler - : public StoreManager::BindingsHandler { - - SymbolRef Sym; - const GRState* PrevSt; - const Stmt* S; - GRStateManager& VMgr; - const ExplodedNode* Pred; - PathDiagnostic& PD; - BugReporter& BR; - -public: - - NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, - GRStateManager& vmgr, const ExplodedNode* pred, - PathDiagnostic& pd, BugReporter& 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; - - // 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(S)) { - if (!B->isAssignmentOp()) - return true; - - // What variable did we assign to? - DeclRefExpr* DR = dyn_cast(B->getLHS()->IgnoreParenCasts()); - - if (!DR) - return true; - - VD = dyn_cast(DR->getDecl()); - } - else if (const DeclStmt* DS = dyn_cast(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(*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* N, - const Stmt* S, - SymbolRef Sym, BugReporter& BR, - PathDiagnostic& PD) { - - const ExplodedNode* 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(BR).getStateManager(); - NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); - cast(BR).getStateManager().iterBindings(N->getState(), H); -} - -namespace { -class VISIBILITY_HIDDEN ScanNotableSymbols - : public StoreManager::BindingsHandler { - - llvm::SmallSet AlreadyProcessed; - const ExplodedNode* N; - Stmt* S; - GRBugReporter& BR; - PathDiagnostic& PD; - -public: - ScanNotableSymbols(const ExplodedNode* 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; - } -}; -} // 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 > - MacroStackTy; - - typedef std::vector - 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(*I)) - if (!MP->containsEvent()) { - delete MP; - continue; - } - - PD.push_back(*I); - } -} - -static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N); - -void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, - BugReportEquivClass& EQ) { - - std::vector*> Nodes; - - for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { - const ExplodedNode* 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*, NodeBackMap*>, - std::pair*, 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 > ReportGraph(GPair.first.first); - llvm::OwningPtr BackMap(GPair.first.second); - const ExplodedNode *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()); -} - -static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, - PathDiagnosticBuilder &PDB, - const ExplodedNode *N) { - - - ASTContext& Ctx = PDB.getContext(); - SourceManager& SMgr = PDB.getSourceManager(); - const ExplodedNode* NextNode = N->pred_empty() - ? NULL : *(N->pred_begin()); - - while (NextNode) { - N = NextNode; - NextNode = GetPredecessorNode(N); +static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, + PathDiagnosticBuilder &PDB, + const ExplodedNode *N) { + ASTContext& Ctx = PDB.getContext(); + SourceManager& SMgr = PDB.getSourceManager(); + const ExplodedNode* NextNode = N->pred_empty() + ? NULL : *(N->pred_begin()); + while (NextNode) { + N = NextNode; + NextNode = GetPredecessorNode(N); ProgramPoint P = N->getLocation(); @@ -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(DR->getDecl()); - + dyn_cast(DR->getDecl()); + if (D) { GetRawInt = false; os << D->getNameAsString(); } } - - if (GetRawInt) { + if (GetRawInt) { + // Not an enum. Expr* CondE = cast(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,74 +646,483 @@ 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")); + } + + 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, + "Loop condition is true. Entering loop body")); + } + + 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, + "Taking false branch")); + else + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Taking true branch")); + + 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(&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); + } + } +} + +//===----------------------------------------------------------------------===// +// 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(&ProgP)) { + if (BE->getBlock() == &BR.getCFG()->getExit()) S = GetPreviousStmt(EndNode); + } + if (!S) S = GetStmt(ProgP); + + return S; +} + +PathDiagnosticPiece* +BugReport::getEndPath(BugReporter& BR, + const ExplodedNode* 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(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(S)) + return ME->getMemberLoc(); + + return S->getLocStart(); + } + + return FullSourceLoc(); +} + +PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + const ExplodedGraph& 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& +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(*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(*I); + + typedef llvm::FoldingSet 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*, NodeBackMap*>, + std::pair*, unsigned> > +MakeReportGraph(const ExplodedGraph* G, + const ExplodedNode** NStart, + const ExplodedNode** 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* GTrim; + InterExplodedGraphMap* NMap; + + llvm::DenseMap 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 > AutoReleaseGTrim(GTrim); + llvm::OwningPtr > 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* N = 0; + unsigned NodeIndex = 0; + + for (const ExplodedNode** 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 *GNew = + new ExplodedGraph(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 Visited; + std::queue*> WS; + WS.push(N); + + unsigned cnt = 0; + const ExplodedNode* Root = 0; + + while (!WS.empty()) { + const ExplodedNode* Node = WS.front(); + WS.pop(); + + if (Visited.find(Node) != Visited.end()) + continue; + + Visited[Node] = cnt++; + + if (Node->pred_empty()) { + Root = Node; + break; + } + + for (ExplodedNode::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 *Last = 0, *First = 0; + NodeBackMap *BM = new NodeBackMap(); + + for ( N = Root ;;) { + // Lookup the number associated with the current node. + llvm::DenseMap::iterator I = Visited.find(N); + assert (I != Visited.end()); + + // Create the equivalent node in the new graph with the same state + // and location. + ExplodedNode* NewN = + GNew->getNode(N->getLocation(), N->getState()); + + // Store the mapping to the original node. + llvm::DenseMap::iterator IMitr=InverseMap.find(N); + assert(IMitr != InverseMap.end() && "No mapping to original node."); + (*BM)[NewN] = (const ExplodedNode*) 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::const_succ_iterator SI = N->succ_begin(); + ExplodedNode::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)); +} - 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); +/// 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 > + MacroStackTy; + + typedef std::vector + 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; + } - os << "Loop condition is false. "; - PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); - if (const Stmt *S = End.asStmt()) - End = PDB.getEnclosingStmtLocation(S); + 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; + } - 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, - "Loop condition is true. Entering loop body")); - } - - break; - } - - case Stmt::IfStmtClass: { - PathDiagnosticLocation End = PDB.ExecutionContinues(N); + // We aren't in the same group. Are we descending into a new macro + // or are part of an old one? + PathDiagnosticMacroPiece *MacroGroup = 0; - 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")); - else - PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, - "Taking true branch")); - - break; - } + 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 (PathDiagnosticPiece* p = - PDB.getReport().VisitNode(N, NextNode, PDB.getGraph(), - PDB.getBugReporter(), - PDB.getNodeMapClosure())) { - PD.push_front(p); + 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); - if (const PostStmt* PS = dyn_cast(&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); - } + for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) { + if (PathDiagnosticMacroPiece *MP=dyn_cast(*I)) + if (!MP->containsEvent()) { + delete MP; + continue; + } + + PD.push_back(*I); } } +void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, + BugReportEquivClass& EQ) { + + std::vector*> Nodes; + + for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { + const ExplodedNode* 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*, NodeBackMap*>, + std::pair*, 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 > ReportGraph(GPair.first.first); + llvm::OwningPtr BackMap(GPair.first.second); + const ExplodedNode *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()); +} void BugReporter::Register(BugType *BT) { BugTypes = F.Add(BugTypes, BT);