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
|
2009-06-26 08:05:51 +08:00
|
|
|
// PathDiagnostics.
|
2008-04-03 12:42:52 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-03-16 05:41:53 +08:00
|
|
|
#define DEBUG_TYPE "BugReporter"
|
|
|
|
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
|
2008-04-03 12:42:52 +08:00
|
|
|
#include "clang/AST/ASTContext.h"
|
2012-01-28 20:06:22 +08:00
|
|
|
#include "clang/AST/DeclObjC.h"
|
2008-04-03 12:42:52 +08:00
|
|
|
#include "clang/AST/Expr.h"
|
2009-03-28 04:55:39 +08:00
|
|
|
#include "clang/AST/ParentMap.h"
|
2009-04-26 09:32:48 +08:00
|
|
|
#include "clang/AST/StmtObjC.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Analysis/CFG.h"
|
2008-04-03 12:42:52 +08:00
|
|
|
#include "clang/Analysis/ProgramPoint.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/Basic/SourceManager.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-02-10 09:03:03 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
2008-06-18 13:34:07 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
2012-02-08 12:32:34 +08:00
|
|
|
#include "llvm/ADT/IntrusiveRefCntPtr.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "llvm/ADT/OwningPtr.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
|
|
#include "llvm/ADT/SmallString.h"
|
2013-03-16 05:41:53 +08:00
|
|
|
#include "llvm/ADT/Statistic.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2009-03-13 07:41:59 +08:00
|
|
|
#include <queue>
|
2008-04-03 12:42:52 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2013-03-16 05:41:53 +08:00
|
|
|
STATISTIC(MaxBugClassSize,
|
|
|
|
"The maximum number of bug reports in the same equivalence class");
|
|
|
|
STATISTIC(MaxValidBugClassSize,
|
|
|
|
"The maximum number of bug reports in the same equivalence class "
|
|
|
|
"where at least one report is valid (not suppressed)");
|
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
BugReporterVisitor::~BugReporterVisitor() {}
|
2010-03-21 02:01:57 +08:00
|
|
|
|
2011-12-20 10:48:34 +08:00
|
|
|
void BugReporterContext::anchor() {}
|
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// Helper routines for walking the ExplodedGraph and fetching statements.
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2008-04-03 12:42:52 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
static inline const Stmt *GetStmt(const ProgramPoint &P) {
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<StmtPoint> SP = P.getAs<StmtPoint>())
|
2009-08-18 09:05:30 +08:00
|
|
|
return SP->getStmt();
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<BlockEdge> BE = P.getAs<BlockEdge>())
|
2008-04-03 12:42:52 +08:00
|
|
|
return BE->getSrc()->getTerminator();
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<CallEnter> CE = P.getAs<CallEnter>())
|
2012-07-11 06:07:52 +08:00
|
|
|
return CE->getCallExpr();
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>())
|
2012-07-11 06:07:52 +08:00
|
|
|
return CEE->getCalleeContext()->getCallSite();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return 0;
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
static inline const ExplodedNode*
|
2011-08-13 07:37:29 +08:00
|
|
|
GetPredecessorNode(const ExplodedNode *N) {
|
2009-02-24 06:44:26 +08:00
|
|
|
return N->pred_empty() ? NULL : *(N->pred_begin());
|
2008-04-08 07:35:17 +08:00
|
|
|
}
|
|
|
|
|
2009-08-06 09:32:16 +08:00
|
|
|
static inline const ExplodedNode*
|
2011-08-13 07:37:29 +08:00
|
|
|
GetSuccessorNode(const ExplodedNode *N) {
|
2009-02-24 06:44:26 +08:00
|
|
|
return N->succ_empty() ? NULL : *(N->succ_begin());
|
2008-04-18 07:44:37 +08:00
|
|
|
}
|
2008-04-26 03:01:27 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
static const Stmt *GetPreviousStmt(const ExplodedNode *N) {
|
2009-02-24 06:44:26 +08:00
|
|
|
for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N))
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const Stmt *S = GetStmt(N->getLocation()))
|
2009-02-24 06:44:26 +08:00
|
|
|
return S;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
static const Stmt *GetNextStmt(const ExplodedNode *N) {
|
2009-02-24 06:44:26 +08:00
|
|
|
for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N))
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const Stmt *S = GetStmt(N->getLocation())) {
|
2009-03-29 01:33:57 +08:00
|
|
|
// Check if the statement is '?' or '&&'/'||'. These are "merges",
|
|
|
|
// not actual statement points.
|
|
|
|
switch (S->getStmtClass()) {
|
|
|
|
case Stmt::ChooseExprClass:
|
2011-02-17 18:25:35 +08:00
|
|
|
case Stmt::BinaryConditionalOperatorClass: continue;
|
2009-03-29 01:33:57 +08:00
|
|
|
case Stmt::ConditionalOperatorClass: continue;
|
|
|
|
case Stmt::BinaryOperatorClass: {
|
2010-08-25 19:45:40 +08:00
|
|
|
BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
|
|
|
|
if (Op == BO_LAnd || Op == BO_LOr)
|
2009-03-29 01:33:57 +08:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-02-24 06:44:26 +08:00
|
|
|
return S;
|
2009-03-29 01:33:57 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return 0;
|
2008-04-18 07:44:37 +08:00
|
|
|
}
|
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
static inline const Stmt*
|
2011-08-13 07:37:29 +08:00
|
|
|
GetCurrentOrPreviousStmt(const ExplodedNode *N) {
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const Stmt *S = GetStmt(N->getLocation()))
|
2009-02-24 06:44:26 +08:00
|
|
|
return S;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return GetPreviousStmt(N);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-07-23 06:35:28 +08:00
|
|
|
static inline const Stmt*
|
2011-08-13 07:37:29 +08:00
|
|
|
GetCurrentOrNextStmt(const ExplodedNode *N) {
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const Stmt *S = GetStmt(N->getLocation()))
|
2009-02-24 06:44:26 +08:00
|
|
|
return S;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
return GetNextStmt(N);
|
2009-01-24 08:55:43 +08:00
|
|
|
}
|
|
|
|
|
2012-02-29 07:06:21 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Diagnostic cleanup.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2012-10-26 06:07:10 +08:00
|
|
|
static PathDiagnosticEventPiece *
|
|
|
|
eventsDescribeSameCondition(PathDiagnosticEventPiece *X,
|
|
|
|
PathDiagnosticEventPiece *Y) {
|
|
|
|
// Prefer diagnostics that come from ConditionBRVisitor over
|
|
|
|
// those that came from TrackConstraintBRVisitor.
|
|
|
|
const void *tagPreferred = ConditionBRVisitor::getTag();
|
|
|
|
const void *tagLesser = TrackConstraintBRVisitor::getTag();
|
|
|
|
|
|
|
|
if (X->getLocation() != Y->getLocation())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (X->getTag() == tagPreferred && Y->getTag() == tagLesser)
|
|
|
|
return X;
|
|
|
|
|
|
|
|
if (Y->getTag() == tagPreferred && X->getTag() == tagLesser)
|
|
|
|
return Y;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-27 00:02:36 +08:00
|
|
|
/// An optimization pass over PathPieces that removes redundant diagnostics
|
|
|
|
/// generated by both ConditionBRVisitor and TrackConstraintBRVisitor. Both
|
|
|
|
/// BugReporterVisitors use different methods to generate diagnostics, with
|
|
|
|
/// one capable of emitting diagnostics in some cases but not in others. This
|
|
|
|
/// can lead to redundant diagnostic pieces at the same point in a path.
|
|
|
|
static void removeRedundantMsgs(PathPieces &path) {
|
2012-10-26 06:07:10 +08:00
|
|
|
unsigned N = path.size();
|
|
|
|
if (N < 2)
|
|
|
|
return;
|
2012-10-27 00:02:36 +08:00
|
|
|
// NOTE: this loop intentionally is not using an iterator. Instead, we
|
|
|
|
// are streaming the path and modifying it in place. This is done by
|
|
|
|
// grabbing the front, processing it, and if we decide to keep it append
|
|
|
|
// it to the end of the path. The entire path is processed in this way.
|
2012-10-26 06:07:10 +08:00
|
|
|
for (unsigned i = 0; i < N; ++i) {
|
|
|
|
IntrusiveRefCntPtr<PathDiagnosticPiece> piece(path.front());
|
|
|
|
path.pop_front();
|
|
|
|
|
|
|
|
switch (piece->getKind()) {
|
|
|
|
case clang::ento::PathDiagnosticPiece::Call:
|
2012-10-27 00:02:36 +08:00
|
|
|
removeRedundantMsgs(cast<PathDiagnosticCallPiece>(piece)->path);
|
2012-10-26 06:07:10 +08:00
|
|
|
break;
|
|
|
|
case clang::ento::PathDiagnosticPiece::Macro:
|
2012-10-27 00:02:36 +08:00
|
|
|
removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(piece)->subPieces);
|
2012-10-26 06:07:10 +08:00
|
|
|
break;
|
|
|
|
case clang::ento::PathDiagnosticPiece::ControlFlow:
|
|
|
|
break;
|
|
|
|
case clang::ento::PathDiagnosticPiece::Event: {
|
|
|
|
if (i == N-1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (PathDiagnosticEventPiece *nextEvent =
|
|
|
|
dyn_cast<PathDiagnosticEventPiece>(path.front().getPtr())) {
|
|
|
|
PathDiagnosticEventPiece *event =
|
|
|
|
cast<PathDiagnosticEventPiece>(piece);
|
|
|
|
// Check to see if we should keep one of the two pieces. If we
|
|
|
|
// come up with a preference, record which piece to keep, and consume
|
|
|
|
// another piece from the path.
|
|
|
|
if (PathDiagnosticEventPiece *pieceToKeep =
|
|
|
|
eventsDescribeSameCondition(event, nextEvent)) {
|
|
|
|
piece = pieceToKeep;
|
|
|
|
path.pop_front();
|
|
|
|
++i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
path.push_back(piece);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-02-29 07:06:21 +08:00
|
|
|
/// Recursively scan through a path and prune out calls and macros pieces
|
|
|
|
/// that aren't needed. Return true if afterwards the path contains
|
2012-12-08 03:56:29 +08:00
|
|
|
/// "interesting stuff" which means it shouldn't be pruned from the parent path.
|
|
|
|
bool BugReporter::RemoveUnneededCalls(PathPieces &pieces, BugReport *R) {
|
2012-02-29 07:06:21 +08:00
|
|
|
bool containsSomethingInteresting = false;
|
|
|
|
const unsigned N = pieces.size();
|
|
|
|
|
|
|
|
for (unsigned i = 0 ; i < N ; ++i) {
|
|
|
|
// Remove the front piece from the path. If it is still something we
|
|
|
|
// want to keep once we are done, we will push it back on the end.
|
|
|
|
IntrusiveRefCntPtr<PathDiagnosticPiece> piece(pieces.front());
|
|
|
|
pieces.pop_front();
|
|
|
|
|
2012-12-08 03:56:29 +08:00
|
|
|
// Throw away pieces with invalid locations. Note that we can't throw away
|
|
|
|
// calls just yet because they might have something interesting inside them.
|
|
|
|
// If so, their locations will be adjusted as necessary later.
|
2012-09-21 08:09:11 +08:00
|
|
|
if (piece->getKind() != PathDiagnosticPiece::Call &&
|
|
|
|
piece->getLocation().asLocation().isInvalid())
|
|
|
|
continue;
|
|
|
|
|
2012-03-01 08:05:06 +08:00
|
|
|
switch (piece->getKind()) {
|
|
|
|
case PathDiagnosticPiece::Call: {
|
|
|
|
PathDiagnosticCallPiece *call = cast<PathDiagnosticCallPiece>(piece);
|
2012-08-30 05:22:37 +08:00
|
|
|
// Check if the location context is interesting.
|
|
|
|
assert(LocationContextMap.count(call));
|
|
|
|
if (R->isInteresting(LocationContextMap[call])) {
|
|
|
|
containsSomethingInteresting = true;
|
|
|
|
break;
|
|
|
|
}
|
2012-11-15 10:07:23 +08:00
|
|
|
|
2012-12-08 03:56:29 +08:00
|
|
|
if (!RemoveUnneededCalls(call->path, R))
|
2012-11-15 10:07:23 +08:00
|
|
|
continue;
|
2012-09-21 08:09:11 +08:00
|
|
|
|
2012-03-01 08:05:06 +08:00
|
|
|
containsSomethingInteresting = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PathDiagnosticPiece::Macro: {
|
|
|
|
PathDiagnosticMacroPiece *macro = cast<PathDiagnosticMacroPiece>(piece);
|
2012-12-08 03:56:29 +08:00
|
|
|
if (!RemoveUnneededCalls(macro->subPieces, R))
|
2012-03-01 08:05:06 +08:00
|
|
|
continue;
|
2012-02-29 07:06:21 +08:00
|
|
|
containsSomethingInteresting = true;
|
2012-03-01 08:05:06 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PathDiagnosticPiece::Event: {
|
|
|
|
PathDiagnosticEventPiece *event = cast<PathDiagnosticEventPiece>(piece);
|
2012-09-21 08:09:11 +08:00
|
|
|
|
2012-03-01 08:05:06 +08:00
|
|
|
// We never throw away an event, but we do throw it away wholesale
|
|
|
|
// as part of a path if we throw the entire path away.
|
2012-09-08 15:18:18 +08:00
|
|
|
containsSomethingInteresting |= !event->isPrunable();
|
2012-03-01 08:05:06 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case PathDiagnosticPiece::ControlFlow:
|
|
|
|
break;
|
2012-02-29 07:06:21 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pieces.push_back(piece);
|
|
|
|
}
|
|
|
|
|
|
|
|
return containsSomethingInteresting;
|
|
|
|
}
|
|
|
|
|
2012-12-08 03:56:29 +08:00
|
|
|
/// Recursively scan through a path and make sure that all call pieces have
|
|
|
|
/// valid locations. Note that all other pieces with invalid locations should
|
|
|
|
/// have already been pruned out.
|
|
|
|
static void adjustCallLocations(PathPieces &Pieces,
|
|
|
|
PathDiagnosticLocation *LastCallLocation = 0) {
|
|
|
|
for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E; ++I) {
|
|
|
|
PathDiagnosticCallPiece *Call = dyn_cast<PathDiagnosticCallPiece>(*I);
|
|
|
|
|
|
|
|
if (!Call) {
|
|
|
|
assert((*I)->getLocation().asLocation().isValid());
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (LastCallLocation) {
|
2013-01-22 02:28:30 +08:00
|
|
|
if (!Call->callEnter.asLocation().isValid() ||
|
|
|
|
Call->getCaller()->isImplicit())
|
2012-12-08 03:56:29 +08:00
|
|
|
Call->callEnter = *LastCallLocation;
|
2013-01-22 02:28:30 +08:00
|
|
|
if (!Call->callReturn.asLocation().isValid() ||
|
|
|
|
Call->getCaller()->isImplicit())
|
2012-12-08 03:56:29 +08:00
|
|
|
Call->callReturn = *LastCallLocation;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Recursively clean out the subclass. Keep this call around if
|
|
|
|
// it contains any informative diagnostics.
|
|
|
|
PathDiagnosticLocation *ThisCallLocation;
|
2013-01-22 02:28:30 +08:00
|
|
|
if (Call->callEnterWithin.asLocation().isValid() &&
|
|
|
|
!Call->getCallee()->isImplicit())
|
2012-12-08 03:56:29 +08:00
|
|
|
ThisCallLocation = &Call->callEnterWithin;
|
|
|
|
else
|
|
|
|
ThisCallLocation = &Call->callEnter;
|
|
|
|
|
|
|
|
assert(ThisCallLocation && "Outermost call has an invalid location");
|
|
|
|
adjustCallLocations(Call->path, ThisCallLocation);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-24 06:44:26 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// PathDiagnosticBuilder and its associated routines and helper objects.
|
2009-02-24 06:44:26 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-02-24 07:13:51 +08:00
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class NodeMapClosure : public BugReport::NodeResolver {
|
2013-03-16 09:07:53 +08:00
|
|
|
InterExplodedGraphMap &M;
|
2009-04-01 04:22:36 +08:00
|
|
|
public:
|
2013-03-16 09:07:53 +08:00
|
|
|
NodeMapClosure(InterExplodedGraphMap &m) : M(m) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *getOriginalNode(const ExplodedNode *N) {
|
2013-03-16 09:07:53 +08:00
|
|
|
return M.lookup(N);
|
2009-04-01 04:22:36 +08:00
|
|
|
}
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class PathDiagnosticBuilder : public BugReporterContext {
|
2009-04-01 04:22:36 +08:00
|
|
|
BugReport *R;
|
2011-09-26 08:51:36 +08:00
|
|
|
PathDiagnosticConsumer *PDC;
|
2009-04-01 04:22:36 +08:00
|
|
|
NodeMapClosure NMC;
|
2009-09-09 23:08:12 +08:00
|
|
|
public:
|
2012-02-24 15:12:52 +08:00
|
|
|
const LocationContext *LC;
|
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
PathDiagnosticBuilder(GRBugReporter &br,
|
2013-03-16 09:07:53 +08:00
|
|
|
BugReport *r, InterExplodedGraphMap &Backmap,
|
2011-09-26 08:51:36 +08:00
|
|
|
PathDiagnosticConsumer *pdc)
|
2009-05-07 05:39:49 +08:00
|
|
|
: BugReporterContext(br),
|
2012-02-24 15:12:52 +08:00
|
|
|
R(r), PDC(pdc), NMC(Backmap), LC(r->getErrorNode()->getLocationContext())
|
|
|
|
{}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os,
|
|
|
|
const ExplodedNode *N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-19 06:37:56 +08:00
|
|
|
BugReport *getBugReport() { return R; }
|
|
|
|
|
2010-09-16 11:50:38 +08:00
|
|
|
Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
|
2012-02-24 15:12:52 +08:00
|
|
|
|
|
|
|
ParentMap& getParentMap() { return LC->getParentMap(); }
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-02 01:18:21 +08:00
|
|
|
const Stmt *getParent(const Stmt *S) {
|
|
|
|
return getParentMap().getParent(S);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
virtual NodeMapClosure& getNodeResolver() { return NMC; }
|
2009-04-18 08:02:19 +08:00
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-09-26 08:51:36 +08:00
|
|
|
PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const {
|
|
|
|
return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Extensive;
|
2009-04-01 04:22:36 +08:00
|
|
|
}
|
|
|
|
|
2009-03-27 13:06:10 +08:00
|
|
|
bool supportsLogicalOpControlFlow() const {
|
|
|
|
return PDC ? PDC->supportsLogicalOpControlFlow() : true;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-03-27 13:06:10 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation
|
2011-08-13 07:37:29 +08:00
|
|
|
PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
|
2009-07-23 06:35:28 +08:00
|
|
|
if (const Stmt *S = GetNextStmt(N))
|
2012-02-24 15:12:52 +08:00
|
|
|
return PathDiagnosticLocation(S, getSourceManager(), LC);
|
2009-03-28 04:55:39 +08:00
|
|
|
|
2011-09-17 03:18:30 +08:00
|
|
|
return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(),
|
|
|
|
getSourceManager());
|
2009-03-13 02:41:53 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation
|
2011-08-13 07:37:29 +08:00
|
|
|
PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os,
|
|
|
|
const ExplodedNode *N) {
|
2009-03-27 13:06:10 +08:00
|
|
|
|
2008-05-07 02:11:09 +08:00
|
|
|
// Slow, but probably doesn't matter.
|
2009-02-24 06:44:26 +08:00
|
|
|
if (os.str().empty())
|
|
|
|
os << ' ';
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
const PathDiagnosticLocation &Loc = ExecutionContinues(N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
if (Loc.asStmt())
|
2009-02-24 06:44:26 +08:00
|
|
|
os << "Execution continues on line "
|
2011-07-26 05:09:52 +08:00
|
|
|
<< getSourceManager().getExpansionLineNumber(Loc.asLocation())
|
2009-05-07 05:39:49 +08:00
|
|
|
<< '.';
|
2009-12-05 04:34:31 +08:00
|
|
|
else {
|
|
|
|
os << "Execution jumps to the end of the ";
|
|
|
|
const Decl *D = N->getLocationContext()->getDecl();
|
|
|
|
if (isa<ObjCMethodDecl>(D))
|
|
|
|
os << "method";
|
|
|
|
else if (isa<FunctionDecl>(D))
|
|
|
|
os << "function";
|
|
|
|
else {
|
|
|
|
assert(isa<BlockDecl>(D));
|
|
|
|
os << "anonymous block";
|
|
|
|
}
|
|
|
|
os << '.';
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-13 02:41:53 +08:00
|
|
|
return Loc;
|
2008-05-07 02:11:09 +08:00
|
|
|
}
|
|
|
|
|
2009-05-15 09:50:15 +08:00
|
|
|
static bool IsNested(const Stmt *S, ParentMap &PM) {
|
|
|
|
if (isa<Expr>(S) && PM.isConsumedExpr(cast<Expr>(S)))
|
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-15 09:50:15 +08:00
|
|
|
const Stmt *Parent = PM.getParentIgnoreParens(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-15 09:50:15 +08:00
|
|
|
if (Parent)
|
|
|
|
switch (Parent->getStmtClass()) {
|
|
|
|
case Stmt::ForStmtClass:
|
|
|
|
case Stmt::DoStmtClass:
|
|
|
|
case Stmt::WhileStmtClass:
|
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
return false;
|
2009-05-15 09:50:15 +08:00
|
|
|
}
|
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
PathDiagnosticLocation
|
|
|
|
PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
|
2011-08-13 07:37:29 +08:00
|
|
|
assert(S && "Null Stmt *passed to getEnclosingStmtLocation");
|
2009-09-09 23:08:12 +08:00
|
|
|
ParentMap &P = getParentMap();
|
2009-05-07 05:39:49 +08:00
|
|
|
SourceManager &SMgr = getSourceManager();
|
2009-05-12 06:19:32 +08:00
|
|
|
|
2009-05-15 09:50:15 +08:00
|
|
|
while (IsNested(S, P)) {
|
2009-05-12 03:50:47 +08:00
|
|
|
const Stmt *Parent = P.getParentIgnoreParens(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 11:37:59 +08:00
|
|
|
if (!Parent)
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 11:37:59 +08:00
|
|
|
switch (Parent->getStmtClass()) {
|
2009-04-01 14:13:56 +08:00
|
|
|
case Stmt::BinaryOperatorClass: {
|
|
|
|
const BinaryOperator *B = cast<BinaryOperator>(Parent);
|
|
|
|
if (B->isLogicalOp())
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-04-01 14:13:56 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::CompoundStmtClass:
|
|
|
|
case Stmt::StmtExprClass:
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-03-28 12:08:14 +08:00
|
|
|
case Stmt::ChooseExprClass:
|
|
|
|
// Similar to '?' if we are referring to condition, just have the edge
|
|
|
|
// point to the entire choose expression.
|
|
|
|
if (cast<ChooseExpr>(Parent)->getCond() == S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(Parent, SMgr, LC);
|
2009-03-28 12:08:14 +08:00
|
|
|
else
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2011-02-17 18:25:35 +08:00
|
|
|
case Stmt::BinaryConditionalOperatorClass:
|
2009-03-28 12:08:14 +08:00
|
|
|
case Stmt::ConditionalOperatorClass:
|
|
|
|
// For '?', if we are referring to condition, just have the edge point
|
|
|
|
// to the entire '?' expression.
|
2011-02-17 18:25:35 +08:00
|
|
|
if (cast<AbstractConditionalOperator>(Parent)->getCond() == S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(Parent, SMgr, LC);
|
2009-03-28 12:08:14 +08:00
|
|
|
else
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::DoStmtClass:
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::ForStmtClass:
|
|
|
|
if (cast<ForStmt>(Parent)->getBody() == S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-09-09 23:08:12 +08:00
|
|
|
break;
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::IfStmtClass:
|
|
|
|
if (cast<IfStmt>(Parent)->getCond() != S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-04-28 12:23:15 +08:00
|
|
|
break;
|
2009-03-28 11:37:59 +08:00
|
|
|
case Stmt::ObjCForCollectionStmtClass:
|
|
|
|
if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-03-28 11:37:59 +08:00
|
|
|
break;
|
|
|
|
case Stmt::WhileStmtClass:
|
|
|
|
if (cast<WhileStmt>(Parent)->getCond() != S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-03-28 11:37:59 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
S = Parent;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
assert(S && "Cannot have null Stmt for PathDiagnosticLocation");
|
2009-05-12 06:19:32 +08:00
|
|
|
|
|
|
|
// Special case: DeclStmts can appear in for statement declarations, in which
|
|
|
|
// case the ForStmt is the context.
|
|
|
|
if (isa<DeclStmt>(S)) {
|
|
|
|
if (const Stmt *Parent = P.getParent(S)) {
|
|
|
|
switch (Parent->getStmtClass()) {
|
|
|
|
case Stmt::ForStmtClass:
|
|
|
|
case Stmt::ObjCForCollectionStmtClass:
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(Parent, SMgr, LC);
|
2009-05-12 06:19:32 +08:00
|
|
|
default:
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
|
|
|
}
|
2009-05-12 06:19:32 +08:00
|
|
|
}
|
|
|
|
else if (isa<BinaryOperator>(S)) {
|
|
|
|
// Special case: the binary operator represents the initialization
|
|
|
|
// code in a for statement (this can happen when the variable being
|
|
|
|
// initialized is an old variable.
|
|
|
|
if (const ForStmt *FS =
|
|
|
|
dyn_cast_or_null<ForStmt>(P.getParentIgnoreParens(S))) {
|
|
|
|
if (FS->getInit() == S)
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(FS, SMgr, LC);
|
2009-05-12 06:19:32 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-09-15 09:08:34 +08:00
|
|
|
return PathDiagnosticLocation(S, SMgr, LC);
|
2009-03-28 05:16:25 +08:00
|
|
|
}
|
|
|
|
|
2012-09-22 09:24:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// "Visitors only" path diagnostic generation algorithm.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
static bool GenerateVisitorsOnlyPathDiagnostic(PathDiagnostic &PD,
|
|
|
|
PathDiagnosticBuilder &PDB,
|
|
|
|
const ExplodedNode *N,
|
|
|
|
ArrayRef<BugReporterVisitor *> visitors) {
|
|
|
|
// All path generation skips the very first node (the error node).
|
|
|
|
// This is because there is special handling for the end-of-path note.
|
|
|
|
N = N->getFirstPred();
|
|
|
|
if (!N)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
BugReport *R = PDB.getBugReport();
|
|
|
|
while (const ExplodedNode *Pred = N->getFirstPred()) {
|
|
|
|
for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(),
|
|
|
|
E = visitors.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
// Visit all the node pairs, but throw the path pieces away.
|
|
|
|
PathDiagnosticPiece *Piece = (*I)->VisitNode(N, Pred, PDB, *R);
|
|
|
|
delete Piece;
|
|
|
|
}
|
|
|
|
|
|
|
|
N = Pred;
|
|
|
|
}
|
|
|
|
|
|
|
|
return R->isValid();
|
|
|
|
}
|
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2009-04-01 07:00:32 +08:00
|
|
|
// "Minimal" path diagnostic generation algorithm.
|
2009-02-05 07:49:09 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2012-03-17 07:24:20 +08:00
|
|
|
typedef std::pair<PathDiagnosticCallPiece*, const ExplodedNode*> StackDiagPair;
|
|
|
|
typedef SmallVector<StackDiagPair, 6> StackDiagVector;
|
|
|
|
|
2012-03-16 05:13:02 +08:00
|
|
|
static void updateStackPiecesWithMessage(PathDiagnosticPiece *P,
|
2012-03-17 07:24:20 +08:00
|
|
|
StackDiagVector &CallStack) {
|
2012-03-16 05:13:02 +08:00
|
|
|
// If the piece contains a special message, add it to all the call
|
|
|
|
// pieces on the active stack.
|
|
|
|
if (PathDiagnosticEventPiece *ep =
|
|
|
|
dyn_cast<PathDiagnosticEventPiece>(P)) {
|
|
|
|
|
2012-03-17 07:24:20 +08:00
|
|
|
if (ep->hasCallStackHint())
|
|
|
|
for (StackDiagVector::iterator I = CallStack.begin(),
|
|
|
|
E = CallStack.end(); I != E; ++I) {
|
|
|
|
PathDiagnosticCallPiece *CP = I->first;
|
|
|
|
const ExplodedNode *N = I->second;
|
2012-03-17 21:06:05 +08:00
|
|
|
std::string stackMsg = ep->getCallStackMessage(N);
|
2012-03-17 07:24:20 +08:00
|
|
|
|
2012-03-16 05:13:02 +08:00
|
|
|
// The last message on the path to final bug is the most important
|
|
|
|
// one. Since we traverse the path backwards, do not add the message
|
|
|
|
// if one has been previously added.
|
2012-03-17 07:24:20 +08:00
|
|
|
if (!CP->hasCallStackMessage())
|
|
|
|
CP->setCallStackMessage(stackMsg);
|
|
|
|
}
|
2012-03-16 05:13:02 +08:00
|
|
|
}
|
|
|
|
}
|
2009-02-05 07:49:09 +08:00
|
|
|
|
2012-03-02 09:27:31 +08:00
|
|
|
static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM);
|
2009-04-07 07:06:54 +08:00
|
|
|
|
2012-09-22 09:24:53 +08:00
|
|
|
static bool GenerateMinimalPathDiagnostic(PathDiagnostic& PD,
|
2009-04-01 07:00:32 +08:00
|
|
|
PathDiagnosticBuilder &PDB,
|
2012-03-24 11:03:29 +08:00
|
|
|
const ExplodedNode *N,
|
|
|
|
ArrayRef<BugReporterVisitor *> visitors) {
|
2009-05-07 05:39:49 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
SourceManager& SMgr = PDB.getSourceManager();
|
2012-02-24 15:12:52 +08:00
|
|
|
const LocationContext *LC = PDB.LC;
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *NextNode = N->pred_empty()
|
2009-04-01 07:00:32 +08:00
|
|
|
? NULL : *(N->pred_begin());
|
2012-03-16 05:13:02 +08:00
|
|
|
|
2012-03-17 07:24:20 +08:00
|
|
|
StackDiagVector CallStack;
|
2012-03-16 05:13:02 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
while (NextNode) {
|
2009-09-09 23:08:12 +08:00
|
|
|
N = NextNode;
|
2012-02-24 15:12:52 +08:00
|
|
|
PDB.LC = N->getLocationContext();
|
2009-04-01 07:00:32 +08:00
|
|
|
NextNode = GetPredecessorNode(N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
ProgramPoint P = N->getLocation();
|
2012-08-30 05:22:37 +08:00
|
|
|
|
|
|
|
do {
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
|
2012-08-30 05:22:37 +08:00
|
|
|
PathDiagnosticCallPiece *C =
|
|
|
|
PathDiagnosticCallPiece::construct(N, *CE, SMgr);
|
|
|
|
GRBugReporter& BR = PDB.getBugReporter();
|
|
|
|
BR.addCallPieceLocationContextPair(C, CE->getCalleeContext());
|
|
|
|
PD.getActivePath().push_front(C);
|
|
|
|
PD.pushActivePath(&C->path);
|
|
|
|
CallStack.push_back(StackDiagPair(C, N));
|
|
|
|
break;
|
2012-03-15 02:58:28 +08:00
|
|
|
}
|
2012-07-27 04:04:05 +08:00
|
|
|
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
|
2012-08-30 05:22:37 +08:00
|
|
|
// Flush all locations, and pop the active path.
|
|
|
|
bool VisitedEntireCall = PD.isWithinCall();
|
|
|
|
PD.popActivePath();
|
|
|
|
|
|
|
|
// Either we just added a bunch of stuff to the top-level path, or
|
|
|
|
// we have a previous CallExitEnd. If the former, it means that the
|
|
|
|
// path terminated within a function call. We must then take the
|
|
|
|
// current contents of the active path and place it within
|
|
|
|
// a new PathDiagnosticCallPiece.
|
|
|
|
PathDiagnosticCallPiece *C;
|
|
|
|
if (VisitedEntireCall) {
|
|
|
|
C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
|
|
|
|
} else {
|
|
|
|
const Decl *Caller = CE->getLocationContext()->getDecl();
|
|
|
|
C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
|
|
|
|
GRBugReporter& BR = PDB.getBugReporter();
|
|
|
|
BR.addCallPieceLocationContextPair(C, CE->getCalleeContext());
|
|
|
|
}
|
|
|
|
|
|
|
|
C->setCallee(*CE, SMgr);
|
|
|
|
if (!CallStack.empty()) {
|
|
|
|
assert(CallStack.back().first == C);
|
|
|
|
CallStack.pop_back();
|
|
|
|
}
|
|
|
|
break;
|
2012-03-16 05:13:02 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
|
2012-08-30 05:22:37 +08:00
|
|
|
const CFGBlock *Src = BE->getSrc();
|
|
|
|
const CFGBlock *Dst = BE->getDst();
|
|
|
|
const Stmt *T = Src->getTerminator();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
if (!T)
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
PathDiagnosticLocation Start =
|
|
|
|
PathDiagnosticLocation::createBegin(T, SMgr,
|
|
|
|
N->getLocationContext());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
switch (T->getStmtClass()) {
|
2008-04-03 12:42:52 +08:00
|
|
|
default:
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
case Stmt::GotoStmtClass:
|
2009-09-09 23:08:12 +08:00
|
|
|
case Stmt::IndirectGotoStmtClass: {
|
2011-08-13 07:37:29 +08:00
|
|
|
const Stmt *S = GetNextStmt(N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
if (!S)
|
2012-08-30 05:22:37 +08:00
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
2009-09-09 23:08:12 +08:00
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-03-28 05:16:25 +08:00
|
|
|
const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
os << "Control jumps to line "
|
2012-08-30 05:22:37 +08:00
|
|
|
<< End.asLocation().getExpansionLineNumber();
|
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2008-04-03 12:42:52 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
case Stmt::SwitchStmtClass: {
|
2008-04-03 12:42:52 +08:00
|
|
|
// Figure out what case arm we took.
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
if (const Stmt *S = Dst->getLabel()) {
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation End(S, SMgr, LC);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-24 07:35:07 +08:00
|
|
|
switch (S->getStmtClass()) {
|
2012-08-30 05:22:37 +08:00
|
|
|
default:
|
|
|
|
os << "No cases match in the switch statement. "
|
|
|
|
"Control jumps to line "
|
|
|
|
<< End.asLocation().getExpansionLineNumber();
|
|
|
|
break;
|
|
|
|
case Stmt::DefaultStmtClass:
|
|
|
|
os << "Control jumps to the 'default' case at line "
|
|
|
|
<< End.asLocation().getExpansionLineNumber();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Stmt::CaseStmtClass: {
|
|
|
|
os << "Control jumps to 'case ";
|
|
|
|
const CaseStmt *Case = cast<CaseStmt>(S);
|
|
|
|
const Expr *LHS = Case->getLHS()->IgnoreParenCasts();
|
|
|
|
|
|
|
|
// Determine if it is an enum.
|
|
|
|
bool GetRawInt = true;
|
|
|
|
|
|
|
|
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(LHS)) {
|
|
|
|
// FIXME: Maybe this should be an assertion. Are there cases
|
|
|
|
// were it is not an EnumConstantDecl?
|
|
|
|
const EnumConstantDecl *D =
|
2010-07-20 14:22:24 +08:00
|
|
|
dyn_cast<EnumConstantDecl>(DR->getDecl());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
if (D) {
|
|
|
|
GetRawInt = false;
|
|
|
|
os << *D;
|
2008-04-24 07:35:07 +08:00
|
|
|
}
|
2012-08-30 05:22:37 +08:00
|
|
|
}
|
2009-04-27 03:04:51 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
if (GetRawInt)
|
|
|
|
os << LHS->EvaluateKnownConstInt(PDB.getASTContext());
|
2009-04-27 03:04:51 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
os << ":' at line "
|
|
|
|
<< End.asLocation().getExpansionLineNumber();
|
|
|
|
break;
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2012-08-30 05:22:37 +08:00
|
|
|
}
|
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2008-04-25 09:29:56 +08:00
|
|
|
else {
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "'Default' branch taken. ";
|
2009-09-09 23:08:12 +08:00
|
|
|
const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N);
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2008-04-25 09:29:56 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-03 12:42:52 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-26 03:01:27 +08:00
|
|
|
case Stmt::BreakStmtClass:
|
|
|
|
case Stmt::ContinueStmtClass: {
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2008-04-26 03:01:27 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
// Determine control-flow for ternary '?'.
|
2011-02-17 18:25:35 +08:00
|
|
|
case Stmt::BinaryConditionalOperatorClass:
|
2008-04-08 07:35:17 +08:00
|
|
|
case Stmt::ConditionalOperatorClass: {
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-03-28 12:08:14 +08:00
|
|
|
os << "'?' condition is ";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-08 07:35:17 +08:00
|
|
|
if (*(Src->succ_begin()+1) == Dst)
|
2009-03-13 02:41:53 +08:00
|
|
|
os << "false";
|
2008-04-08 07:35:17 +08:00
|
|
|
else
|
2009-03-13 02:41:53 +08:00
|
|
|
os << "true";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 12:08:14 +08:00
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-03-27 13:06:10 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
// Determine control-flow for short-circuited '&&' and '||'.
|
2009-03-27 13:06:10 +08:00
|
|
|
case Stmt::BinaryOperatorClass: {
|
|
|
|
if (!PDB.supportsLogicalOpControlFlow())
|
|
|
|
break;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-07-20 14:22:24 +08:00
|
|
|
const BinaryOperator *B = cast<BinaryOperator>(T);
|
2009-03-27 13:06:10 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
|
|
|
os << "Left side of '";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2010-08-25 19:45:40 +08:00
|
|
|
if (B->getOpcode() == BO_LAnd) {
|
2009-03-29 01:33:57 +08:00
|
|
|
os << "&&" << "' is ";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-29 01:33:57 +08:00
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
os << "false";
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
|
2011-09-17 03:18:30 +08:00
|
|
|
PathDiagnosticLocation Start =
|
2012-08-30 05:22:37 +08:00
|
|
|
PathDiagnosticLocation::createOperatorLoc(B, SMgr);
|
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-03-29 01:33:57 +08:00
|
|
|
else {
|
|
|
|
os << "true";
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
|
2009-03-29 01:33:57 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-03-27 13:06:10 +08:00
|
|
|
}
|
|
|
|
else {
|
2010-08-25 19:45:40 +08:00
|
|
|
assert(B->getOpcode() == BO_LOr);
|
2009-03-29 01:33:57 +08:00
|
|
|
os << "||" << "' is ";
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-29 01:33:57 +08:00
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
os << "false";
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation Start(B->getLHS(), SMgr, LC);
|
2009-03-29 01:33:57 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-03-29 01:33:57 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
os << "true";
|
2011-09-15 09:08:34 +08:00
|
|
|
PathDiagnosticLocation End(B->getLHS(), SMgr, LC);
|
2011-09-17 03:18:30 +08:00
|
|
|
PathDiagnosticLocation Start =
|
2012-08-30 05:22:37 +08:00
|
|
|
PathDiagnosticLocation::createOperatorLoc(B, SMgr);
|
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-03-29 01:33:57 +08:00
|
|
|
}
|
2009-03-27 13:06:10 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-04-08 07:35:17 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
case Stmt::DoStmtClass: {
|
2008-04-08 07:35:17 +08:00
|
|
|
if (*(Src->succ_begin()) == Dst) {
|
2009-02-11 07:56:07 +08:00
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2008-09-13 02:17:46 +08:00
|
|
|
os << "Loop condition is true. ";
|
2009-03-28 05:16:25 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-03-13 02:41:53 +08:00
|
|
|
}
|
|
|
|
else {
|
2009-03-28 04:55:39 +08:00
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-03-28 05:16:25 +08:00
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, "Loop condition is false. Exiting loop"));
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
case Stmt::WhileStmtClass:
|
2009-09-09 23:08:12 +08:00
|
|
|
case Stmt::ForStmtClass: {
|
2009-04-01 07:00:32 +08:00
|
|
|
if (*(Src->succ_begin()+1) == Dst) {
|
|
|
|
std::string sbuf;
|
|
|
|
llvm::raw_string_ostream os(sbuf);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
os << "Loop condition is false. ";
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
|
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, os.str()));
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, "Loop condition is true. Entering loop body"));
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
case Stmt::IfStmtClass: {
|
|
|
|
PathDiagnosticLocation End = PDB.ExecutionContinues(N);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (const Stmt *S = End.asStmt())
|
|
|
|
End = PDB.getEnclosingStmtLocation(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (*(Src->succ_begin()+1) == Dst)
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, "Taking false branch"));
|
2009-09-09 23:08:12 +08:00
|
|
|
else
|
2012-08-30 05:22:37 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(
|
|
|
|
Start, End, "Taking true branch"));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-08-30 05:22:37 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2012-08-30 05:22:37 +08:00
|
|
|
} while(0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
if (NextNode) {
|
2011-08-19 06:37:56 +08:00
|
|
|
// Add diagnostic pieces from custom visitors.
|
|
|
|
BugReport *R = PDB.getBugReport();
|
2012-03-24 11:03:29 +08:00
|
|
|
for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(),
|
|
|
|
E = visitors.end();
|
|
|
|
I != E; ++I) {
|
2012-03-16 05:13:02 +08:00
|
|
|
if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) {
|
2012-02-24 14:00:00 +08:00
|
|
|
PD.getActivePath().push_front(p);
|
2012-03-16 05:13:02 +08:00
|
|
|
updateStackPiecesWithMessage(p, CallStack);
|
|
|
|
}
|
2009-05-07 08:45:33 +08:00
|
|
|
}
|
2009-05-07 05:39:49 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-09-22 09:24:53 +08:00
|
|
|
if (!PDB.getBugReport()->isValid())
|
|
|
|
return false;
|
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
// After constructing the full PathDiagnostic, do a pass over it to compact
|
|
|
|
// PathDiagnosticPieces that occur within a macro.
|
2012-03-02 09:27:31 +08:00
|
|
|
CompactPathDiagnostic(PD.getMutablePieces(), PDB.getSourceManager());
|
2012-09-22 09:24:53 +08:00
|
|
|
return true;
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
2009-04-01 14:13:56 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// "Extensive" PathDiagnostic generation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
static bool IsControlFlowExpr(const Stmt *S) {
|
|
|
|
const Expr *E = dyn_cast<Expr>(S);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 14:13:56 +08:00
|
|
|
if (!E)
|
|
|
|
return false;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
E = E->IgnoreParenCasts();
|
|
|
|
|
2011-02-17 18:25:35 +08:00
|
|
|
if (isa<AbstractConditionalOperator>(E))
|
2009-04-01 14:13:56 +08:00
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 14:13:56 +08:00
|
|
|
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(E))
|
|
|
|
if (B->isLogicalOp())
|
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
return false;
|
2009-04-01 14:13:56 +08:00
|
|
|
}
|
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
class ContextLocation : public PathDiagnosticLocation {
|
2009-05-02 00:08:09 +08:00
|
|
|
bool IsDead;
|
|
|
|
public:
|
|
|
|
ContextLocation(const PathDiagnosticLocation &L, bool isdead = false)
|
|
|
|
: PathDiagnosticLocation(L), IsDead(isdead) {}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
void markDead() { IsDead = true; }
|
2009-05-02 00:08:09 +08:00
|
|
|
bool isDead() const { return IsDead; }
|
|
|
|
};
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-11-28 14:07:30 +08:00
|
|
|
class EdgeBuilder {
|
2009-05-02 00:08:09 +08:00
|
|
|
std::vector<ContextLocation> CLocs;
|
|
|
|
typedef std::vector<ContextLocation>::iterator iterator;
|
2009-04-07 07:06:54 +08:00
|
|
|
PathDiagnostic &PD;
|
|
|
|
PathDiagnosticBuilder &PDB;
|
|
|
|
PathDiagnosticLocation PrevLoc;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-02 00:08:09 +08:00
|
|
|
bool IsConsumedExpr(const PathDiagnosticLocation &L);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
bool containsLocation(const PathDiagnosticLocation &Container,
|
|
|
|
const PathDiagnosticLocation &Containee);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 05:42:34 +08:00
|
|
|
PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L,
|
|
|
|
bool firstCharOnly = false) {
|
2009-05-12 03:50:47 +08:00
|
|
|
if (const Stmt *S = L.asStmt()) {
|
2009-05-12 05:42:34 +08:00
|
|
|
const Stmt *Original = S;
|
2009-05-12 03:50:47 +08:00
|
|
|
while (1) {
|
|
|
|
// Adjust the location for some expressions that are best referenced
|
|
|
|
// by one of their subexpressions.
|
2009-05-12 05:42:34 +08:00
|
|
|
switch (S->getStmtClass()) {
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
case Stmt::ParenExprClass:
|
2011-04-15 08:35:48 +08:00
|
|
|
case Stmt::GenericSelectionExprClass:
|
|
|
|
S = cast<Expr>(S)->IgnoreParens();
|
2009-05-12 05:42:34 +08:00
|
|
|
firstCharOnly = true;
|
|
|
|
continue;
|
2011-02-17 18:25:35 +08:00
|
|
|
case Stmt::BinaryConditionalOperatorClass:
|
2009-05-12 05:42:34 +08:00
|
|
|
case Stmt::ConditionalOperatorClass:
|
2011-02-17 18:25:35 +08:00
|
|
|
S = cast<AbstractConditionalOperator>(S)->getCond();
|
2009-05-12 05:42:34 +08:00
|
|
|
firstCharOnly = true;
|
|
|
|
continue;
|
|
|
|
case Stmt::ChooseExprClass:
|
|
|
|
S = cast<ChooseExpr>(S)->getCond();
|
|
|
|
firstCharOnly = true;
|
|
|
|
continue;
|
|
|
|
case Stmt::BinaryOperatorClass:
|
|
|
|
S = cast<BinaryOperator>(S)->getLHS();
|
|
|
|
firstCharOnly = true;
|
|
|
|
continue;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 05:42:34 +08:00
|
|
|
break;
|
2009-05-12 03:50:47 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 05:42:34 +08:00
|
|
|
if (S != Original)
|
2012-02-24 15:12:52 +08:00
|
|
|
L = PathDiagnosticLocation(S, L.getManager(), PDB.LC);
|
2009-05-12 03:50:47 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 05:42:34 +08:00
|
|
|
if (firstCharOnly)
|
2011-09-20 09:38:47 +08:00
|
|
|
L = PathDiagnosticLocation::createSingleLocation(L);
|
2009-05-12 05:42:34 +08:00
|
|
|
|
2009-05-12 03:50:47 +08:00
|
|
|
return L;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
void popLocation() {
|
2009-05-02 00:08:09 +08:00
|
|
|
if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) {
|
2009-04-23 04:36:26 +08:00
|
|
|
// For contexts, we only one the first character as the range.
|
2009-05-15 10:46:13 +08:00
|
|
|
rawAddEdge(cleanUpLocation(CLocs.back(), true));
|
2009-04-23 04:36:26 +08:00
|
|
|
}
|
2009-04-07 07:06:54 +08:00
|
|
|
CLocs.pop_back();
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
public:
|
|
|
|
EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb)
|
|
|
|
: PD(pd), PDB(pdb) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-23 02:16:20 +08:00
|
|
|
// If the PathDiagnostic already has pieces, add the enclosing statement
|
|
|
|
// of the first piece as a context as well.
|
2012-02-08 12:32:34 +08:00
|
|
|
if (!PD.path.empty()) {
|
|
|
|
PrevLoc = (*PD.path.begin())->getLocation();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
if (const Stmt *S = PrevLoc.asStmt())
|
2009-05-06 07:13:38 +08:00
|
|
|
addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt());
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~EdgeBuilder() {
|
|
|
|
while (!CLocs.empty()) popLocation();
|
2011-09-17 03:18:30 +08:00
|
|
|
|
2009-04-23 02:16:20 +08:00
|
|
|
// Finally, add an initial edge from the start location of the first
|
|
|
|
// statement (if it doesn't already exist).
|
2011-09-17 03:18:30 +08:00
|
|
|
PathDiagnosticLocation L = PathDiagnosticLocation::createDeclBegin(
|
2012-02-24 15:12:52 +08:00
|
|
|
PDB.LC,
|
2011-09-17 03:18:30 +08:00
|
|
|
PDB.getSourceManager());
|
|
|
|
if (L.isValid())
|
|
|
|
rawAddEdge(L);
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
2012-02-07 10:26:17 +08:00
|
|
|
void flushLocations() {
|
|
|
|
while (!CLocs.empty())
|
|
|
|
popLocation();
|
|
|
|
PrevLoc = PathDiagnosticLocation();
|
|
|
|
}
|
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-28 12:23:15 +08:00
|
|
|
void rawAddEdge(PathDiagnosticLocation NewLoc);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
void addContext(const Stmt *S);
|
2012-07-27 04:04:05 +08:00
|
|
|
void addContext(const PathDiagnosticLocation &L);
|
2009-05-06 07:13:38 +08:00
|
|
|
void addExtendedContext(const Stmt *S);
|
2009-09-09 23:08:12 +08:00
|
|
|
};
|
2009-04-07 07:06:54 +08:00
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
|
|
|
|
PathDiagnosticLocation
|
|
|
|
EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) {
|
|
|
|
if (const Stmt *S = L.asStmt()) {
|
|
|
|
if (IsControlFlowExpr(S))
|
|
|
|
return L;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
return PDB.getEnclosingStmtLocation(S);
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
return L;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container,
|
|
|
|
const PathDiagnosticLocation &Containee) {
|
|
|
|
|
|
|
|
if (Container == Containee)
|
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
if (Container.asDecl())
|
|
|
|
return true;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
if (const Stmt *S = Containee.asStmt())
|
|
|
|
if (const Stmt *ContainerS = Container.asStmt()) {
|
|
|
|
while (S) {
|
|
|
|
if (S == ContainerS)
|
|
|
|
return true;
|
|
|
|
S = PDB.getParent(S);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Less accurate: compare using source ranges.
|
|
|
|
SourceRange ContainerR = Container.asRange();
|
|
|
|
SourceRange ContaineeR = Containee.asRange();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
SourceManager &SM = PDB.getSourceManager();
|
2011-07-26 00:49:02 +08:00
|
|
|
SourceLocation ContainerRBeg = SM.getExpansionLoc(ContainerR.getBegin());
|
|
|
|
SourceLocation ContainerREnd = SM.getExpansionLoc(ContainerR.getEnd());
|
|
|
|
SourceLocation ContaineeRBeg = SM.getExpansionLoc(ContaineeR.getBegin());
|
|
|
|
SourceLocation ContaineeREnd = SM.getExpansionLoc(ContaineeR.getEnd());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-07-26 05:09:52 +08:00
|
|
|
unsigned ContainerBegLine = SM.getExpansionLineNumber(ContainerRBeg);
|
|
|
|
unsigned ContainerEndLine = SM.getExpansionLineNumber(ContainerREnd);
|
|
|
|
unsigned ContaineeBegLine = SM.getExpansionLineNumber(ContaineeRBeg);
|
|
|
|
unsigned ContaineeEndLine = SM.getExpansionLineNumber(ContaineeREnd);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
assert(ContainerBegLine <= ContainerEndLine);
|
2009-09-09 23:08:12 +08:00
|
|
|
assert(ContaineeBegLine <= ContaineeEndLine);
|
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
return (ContainerBegLine <= ContaineeBegLine &&
|
|
|
|
ContainerEndLine >= ContaineeEndLine &&
|
|
|
|
(ContainerBegLine != ContaineeBegLine ||
|
2011-07-26 04:57:57 +08:00
|
|
|
SM.getExpansionColumnNumber(ContainerRBeg) <=
|
|
|
|
SM.getExpansionColumnNumber(ContaineeRBeg)) &&
|
2009-04-07 07:06:54 +08:00
|
|
|
(ContainerEndLine != ContaineeEndLine ||
|
2011-07-26 04:57:57 +08:00
|
|
|
SM.getExpansionColumnNumber(ContainerREnd) >=
|
2012-03-28 13:24:50 +08:00
|
|
|
SM.getExpansionColumnNumber(ContaineeREnd)));
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) {
|
|
|
|
if (!PrevLoc.isValid()) {
|
|
|
|
PrevLoc = NewLoc;
|
|
|
|
return;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-12 03:50:47 +08:00
|
|
|
const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc);
|
|
|
|
const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-09-21 08:09:11 +08:00
|
|
|
if (PrevLocClean.asLocation().isInvalid()) {
|
|
|
|
PrevLoc = NewLoc;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-05-12 03:50:47 +08:00
|
|
|
if (NewLocClean.asLocation() == PrevLocClean.asLocation())
|
2009-04-07 07:06:54 +08:00
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
// FIXME: Ignore intra-macro edges for now.
|
2011-07-26 00:49:02 +08:00
|
|
|
if (NewLocClean.asLocation().getExpansionLoc() ==
|
|
|
|
PrevLocClean.asLocation().getExpansionLoc())
|
2009-04-07 07:06:54 +08:00
|
|
|
return;
|
|
|
|
|
2012-02-24 14:00:00 +08:00
|
|
|
PD.getActivePath().push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean));
|
2009-05-12 03:50:47 +08:00
|
|
|
PrevLoc = NewLoc;
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-23 02:16:20 +08:00
|
|
|
if (!alwaysAdd && NewLoc.asLocation().isMacroID())
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc);
|
|
|
|
|
|
|
|
while (!CLocs.empty()) {
|
2009-05-02 00:08:09 +08:00
|
|
|
ContextLocation &TopContextLoc = CLocs.back();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
// Is the top location context the same as the one for the new location?
|
|
|
|
if (TopContextLoc == CLoc) {
|
2009-05-02 00:08:09 +08:00
|
|
|
if (alwaysAdd) {
|
2009-05-05 02:15:17 +08:00
|
|
|
if (IsConsumedExpr(TopContextLoc) &&
|
|
|
|
!IsControlFlowExpr(TopContextLoc.asStmt()))
|
2009-05-02 00:08:09 +08:00
|
|
|
TopContextLoc.markDead();
|
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
rawAddEdge(NewLoc);
|
2009-05-02 00:08:09 +08:00
|
|
|
}
|
2009-04-07 07:06:54 +08:00
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (containsLocation(TopContextLoc, CLoc)) {
|
2009-05-02 00:08:09 +08:00
|
|
|
if (alwaysAdd) {
|
2009-04-07 07:06:54 +08:00
|
|
|
rawAddEdge(NewLoc);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-05 02:15:17 +08:00
|
|
|
if (IsConsumedExpr(CLoc) && !IsControlFlowExpr(CLoc.asStmt())) {
|
2009-05-02 00:08:09 +08:00
|
|
|
CLocs.push_back(ContextLocation(CLoc, true));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
CLocs.push_back(CLoc);
|
2009-09-09 23:08:12 +08:00
|
|
|
return;
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Context does not contain the location. Flush it.
|
|
|
|
popLocation();
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-23 04:36:26 +08:00
|
|
|
// If we reach here, there is no enclosing context. Just add the edge.
|
|
|
|
rawAddEdge(NewLoc);
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
2009-05-02 00:08:09 +08:00
|
|
|
bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) {
|
|
|
|
if (const Expr *X = dyn_cast_or_null<Expr>(L.asStmt()))
|
|
|
|
return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-02 00:08:09 +08:00
|
|
|
return false;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-06 07:13:38 +08:00
|
|
|
void EdgeBuilder::addExtendedContext(const Stmt *S) {
|
|
|
|
if (!S)
|
|
|
|
return;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
const Stmt *Parent = PDB.getParent(S);
|
2009-05-06 07:13:38 +08:00
|
|
|
while (Parent) {
|
|
|
|
if (isa<CompoundStmt>(Parent))
|
|
|
|
Parent = PDB.getParent(Parent);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Parent) {
|
|
|
|
switch (Parent->getStmtClass()) {
|
|
|
|
case Stmt::DoStmtClass:
|
|
|
|
case Stmt::ObjCAtSynchronizedStmtClass:
|
|
|
|
addContext(Parent);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-06 07:13:38 +08:00
|
|
|
addContext(S);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-07 07:06:54 +08:00
|
|
|
void EdgeBuilder::addContext(const Stmt *S) {
|
|
|
|
if (!S)
|
|
|
|
return;
|
|
|
|
|
2012-02-24 15:12:52 +08:00
|
|
|
PathDiagnosticLocation L(S, PDB.getSourceManager(), PDB.LC);
|
2012-07-27 04:04:05 +08:00
|
|
|
addContext(L);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-07-27 04:04:05 +08:00
|
|
|
void EdgeBuilder::addContext(const PathDiagnosticLocation &L) {
|
2009-04-07 07:06:54 +08:00
|
|
|
while (!CLocs.empty()) {
|
|
|
|
const PathDiagnosticLocation &TopContextLoc = CLocs.back();
|
|
|
|
|
|
|
|
// Is the top location context the same as the one for the new location?
|
|
|
|
if (TopContextLoc == L)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (containsLocation(TopContextLoc, L)) {
|
|
|
|
CLocs.push_back(L);
|
2009-09-09 23:08:12 +08:00
|
|
|
return;
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// Context does not contain the location. Flush it.
|
|
|
|
popLocation();
|
|
|
|
}
|
|
|
|
|
|
|
|
CLocs.push_back(L);
|
|
|
|
}
|
|
|
|
|
2012-05-02 08:31:29 +08:00
|
|
|
// Cone-of-influence: support the reverse propagation of "interesting" symbols
|
|
|
|
// and values by tracing interesting calculations backwards through evaluated
|
|
|
|
// expressions along a path. This is probably overly complicated, but the idea
|
|
|
|
// is that if an expression computed an "interesting" value, the child
|
|
|
|
// expressions are are also likely to be "interesting" as well (which then
|
|
|
|
// propagates to the values they in turn compute). This reverse propagation
|
|
|
|
// is needed to track interesting correlations across function call boundaries,
|
|
|
|
// where formal arguments bind to actual arguments, etc. This is also needed
|
|
|
|
// because the constraint solver sometimes simplifies certain symbolic values
|
|
|
|
// into constants when appropriate, and this complicates reasoning about
|
|
|
|
// interesting values.
|
|
|
|
typedef llvm::DenseSet<const Expr *> InterestingExprs;
|
|
|
|
|
|
|
|
static void reversePropagateIntererstingSymbols(BugReport &R,
|
|
|
|
InterestingExprs &IE,
|
|
|
|
const ProgramState *State,
|
|
|
|
const Expr *Ex,
|
|
|
|
const LocationContext *LCtx) {
|
|
|
|
SVal V = State->getSVal(Ex, LCtx);
|
|
|
|
if (!(R.isInteresting(V) || IE.count(Ex)))
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (Ex->getStmtClass()) {
|
|
|
|
default:
|
|
|
|
if (!isa<CastExpr>(Ex))
|
|
|
|
break;
|
|
|
|
// Fall through.
|
|
|
|
case Stmt::BinaryOperatorClass:
|
|
|
|
case Stmt::UnaryOperatorClass: {
|
|
|
|
for (Stmt::const_child_iterator CI = Ex->child_begin(),
|
|
|
|
CE = Ex->child_end();
|
|
|
|
CI != CE; ++CI) {
|
|
|
|
if (const Expr *child = dyn_cast_or_null<Expr>(*CI)) {
|
|
|
|
IE.insert(child);
|
|
|
|
SVal ChildV = State->getSVal(child, LCtx);
|
|
|
|
R.markInteresting(ChildV);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
R.markInteresting(V);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void reversePropagateInterestingSymbols(BugReport &R,
|
|
|
|
InterestingExprs &IE,
|
|
|
|
const ProgramState *State,
|
|
|
|
const LocationContext *CalleeCtx,
|
|
|
|
const LocationContext *CallerCtx)
|
|
|
|
{
|
2012-07-11 06:07:52 +08:00
|
|
|
// FIXME: Handle non-CallExpr-based CallEvents.
|
2012-05-02 08:31:29 +08:00
|
|
|
const StackFrameContext *Callee = CalleeCtx->getCurrentStackFrame();
|
|
|
|
const Stmt *CallSite = Callee->getCallSite();
|
2012-07-11 06:07:52 +08:00
|
|
|
if (const CallExpr *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
|
2012-05-02 08:31:29 +08:00
|
|
|
if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
|
|
|
|
FunctionDecl::param_const_iterator PI = FD->param_begin(),
|
|
|
|
PE = FD->param_end();
|
|
|
|
CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
|
|
|
|
for (; AI != AE && PI != PE; ++AI, ++PI) {
|
|
|
|
if (const Expr *ArgE = *AI) {
|
|
|
|
if (const ParmVarDecl *PD = *PI) {
|
|
|
|
Loc LV = State->getLValue(PD, CalleeCtx);
|
|
|
|
if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV)))
|
|
|
|
IE.insert(ArgE);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2013-02-09 03:51:43 +08:00
|
|
|
|
2013-02-22 13:45:33 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Functions for determining if a loop was executed 0 times.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-02-09 03:51:43 +08:00
|
|
|
/// Return true if the terminator is a loop and the destination is the
|
|
|
|
/// false branch.
|
|
|
|
static bool isLoopJumpPastBody(const Stmt *Term, const BlockEdge *BE) {
|
|
|
|
switch (Term->getStmtClass()) {
|
|
|
|
case Stmt::ForStmtClass:
|
|
|
|
case Stmt::WhileStmtClass:
|
2013-03-14 04:03:31 +08:00
|
|
|
case Stmt::ObjCForCollectionStmtClass:
|
2013-02-09 03:51:43 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
// Note that we intentionally do not include do..while here.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Did we take the false branch?
|
|
|
|
const CFGBlock *Src = BE->getSrc();
|
|
|
|
assert(Src->succ_size() == 2);
|
|
|
|
return (*(Src->succ_begin()+1) == BE->getDst());
|
|
|
|
}
|
|
|
|
|
2013-02-22 13:45:33 +08:00
|
|
|
static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) {
|
|
|
|
while (SubS) {
|
|
|
|
if (SubS == S)
|
|
|
|
return true;
|
|
|
|
SubS = PM.getParent(SubS);
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term,
|
|
|
|
const ExplodedNode *N) {
|
|
|
|
while (N) {
|
|
|
|
Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
|
|
|
|
if (SP) {
|
|
|
|
const Stmt *S = SP->getStmt();
|
|
|
|
if (!isContainedByStmt(PM, Term, S))
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
N = GetPredecessorNode(N);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) {
|
|
|
|
const Stmt *LoopBody = 0;
|
|
|
|
switch (Term->getStmtClass()) {
|
|
|
|
case Stmt::ForStmtClass: {
|
|
|
|
const ForStmt *FS = cast<ForStmt>(Term);
|
|
|
|
if (isContainedByStmt(PM, FS->getInc(), S))
|
|
|
|
return true;
|
|
|
|
LoopBody = FS->getBody();
|
|
|
|
break;
|
|
|
|
}
|
2013-03-14 04:03:31 +08:00
|
|
|
case Stmt::ObjCForCollectionStmtClass: {
|
|
|
|
const ObjCForCollectionStmt *FC = cast<ObjCForCollectionStmt>(Term);
|
|
|
|
LoopBody = FC->getBody();
|
|
|
|
break;
|
|
|
|
}
|
2013-02-22 13:45:33 +08:00
|
|
|
case Stmt::WhileStmtClass:
|
|
|
|
LoopBody = cast<WhileStmt>(Term)->getBody();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return isContainedByStmt(PM, LoopBody, S);
|
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Top-level logic for generating extensive path diagnostics.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2012-09-22 09:24:53 +08:00
|
|
|
static bool GenerateExtensivePathDiagnostic(PathDiagnostic& PD,
|
2009-04-07 07:06:54 +08:00
|
|
|
PathDiagnosticBuilder &PDB,
|
2012-03-24 11:03:29 +08:00
|
|
|
const ExplodedNode *N,
|
|
|
|
ArrayRef<BugReporterVisitor *> visitors) {
|
2009-04-07 07:06:54 +08:00
|
|
|
EdgeBuilder EB(PD, PDB);
|
2011-09-17 03:18:30 +08:00
|
|
|
const SourceManager& SM = PDB.getSourceManager();
|
2012-03-17 07:24:20 +08:00
|
|
|
StackDiagVector CallStack;
|
2012-05-02 08:31:29 +08:00
|
|
|
InterestingExprs IE;
|
2009-04-07 07:06:54 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
|
2009-04-07 07:06:54 +08:00
|
|
|
while (NextNode) {
|
|
|
|
N = NextNode;
|
|
|
|
NextNode = GetPredecessorNode(N);
|
|
|
|
ProgramPoint P = N->getLocation();
|
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
do {
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<PostStmt> PS = P.getAs<PostStmt>()) {
|
2012-05-02 08:31:29 +08:00
|
|
|
if (const Expr *Ex = PS->getStmtAs<Expr>())
|
|
|
|
reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
|
|
|
|
N->getState().getPtr(), Ex,
|
|
|
|
N->getLocationContext());
|
|
|
|
}
|
|
|
|
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
|
2012-07-27 04:04:05 +08:00
|
|
|
const Stmt *S = CE->getCalleeContext()->getCallSite();
|
|
|
|
if (const Expr *Ex = dyn_cast_or_null<Expr>(S)) {
|
2012-07-11 06:07:52 +08:00
|
|
|
reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
|
|
|
|
N->getState().getPtr(), Ex,
|
|
|
|
N->getLocationContext());
|
|
|
|
}
|
2012-07-27 04:04:05 +08:00
|
|
|
|
|
|
|
PathDiagnosticCallPiece *C =
|
|
|
|
PathDiagnosticCallPiece::construct(N, *CE, SM);
|
2012-08-30 05:22:37 +08:00
|
|
|
GRBugReporter& BR = PDB.getBugReporter();
|
|
|
|
BR.addCallPieceLocationContextPair(C, CE->getCalleeContext());
|
2012-07-27 04:04:05 +08:00
|
|
|
|
|
|
|
EB.addEdge(C->callReturn, true);
|
|
|
|
EB.flushLocations();
|
|
|
|
|
|
|
|
PD.getActivePath().push_front(C);
|
|
|
|
PD.pushActivePath(&C->path);
|
|
|
|
CallStack.push_back(StackDiagPair(C, N));
|
2012-02-07 10:26:17 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-03-03 05:16:22 +08:00
|
|
|
|
2012-02-24 14:00:00 +08:00
|
|
|
// Pop the call hierarchy if we are done walking the contents
|
|
|
|
// of a function call.
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
|
2012-03-06 09:25:01 +08:00
|
|
|
// Add an edge to the start of the function.
|
|
|
|
const Decl *D = CE->getCalleeContext()->getDecl();
|
|
|
|
PathDiagnosticLocation pos =
|
|
|
|
PathDiagnosticLocation::createBegin(D, SM);
|
|
|
|
EB.addEdge(pos);
|
|
|
|
|
|
|
|
// Flush all locations, and pop the active path.
|
2012-07-27 04:04:05 +08:00
|
|
|
bool VisitedEntireCall = PD.isWithinCall();
|
2012-03-03 05:16:22 +08:00
|
|
|
EB.flushLocations();
|
2012-02-24 14:00:00 +08:00
|
|
|
PD.popActivePath();
|
2012-03-03 05:16:22 +08:00
|
|
|
PDB.LC = N->getLocationContext();
|
2012-03-06 09:25:01 +08:00
|
|
|
|
2012-07-27 04:04:05 +08:00
|
|
|
// Either we just added a bunch of stuff to the top-level path, or
|
|
|
|
// we have a previous CallExitEnd. If the former, it means that the
|
2012-02-24 14:00:00 +08:00
|
|
|
// path terminated within a function call. We must then take the
|
|
|
|
// current contents of the active path and place it within
|
|
|
|
// a new PathDiagnosticCallPiece.
|
2012-07-27 04:04:05 +08:00
|
|
|
PathDiagnosticCallPiece *C;
|
|
|
|
if (VisitedEntireCall) {
|
|
|
|
C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front());
|
|
|
|
} else {
|
|
|
|
const Decl *Caller = CE->getLocationContext()->getDecl();
|
2012-03-15 02:58:28 +08:00
|
|
|
C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
|
2012-08-30 05:22:37 +08:00
|
|
|
GRBugReporter& BR = PDB.getBugReporter();
|
|
|
|
BR.addCallPieceLocationContextPair(C, CE->getCalleeContext());
|
2012-03-15 02:58:28 +08:00
|
|
|
}
|
2012-07-11 06:07:52 +08:00
|
|
|
|
2012-07-27 04:04:05 +08:00
|
|
|
C->setCallee(*CE, SM);
|
|
|
|
EB.addContext(C->getLocation());
|
2012-03-16 05:13:02 +08:00
|
|
|
|
|
|
|
if (!CallStack.empty()) {
|
2012-03-17 07:24:20 +08:00
|
|
|
assert(CallStack.back().first == C);
|
2012-03-16 05:13:02 +08:00
|
|
|
CallStack.pop_back();
|
|
|
|
}
|
2012-02-24 14:00:00 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-03-03 05:16:22 +08:00
|
|
|
|
|
|
|
// Note that is important that we update the LocationContext
|
|
|
|
// after looking at CallExits. CallExit basically adds an
|
|
|
|
// edge in the *caller*, so we don't want to update the LocationContext
|
|
|
|
// too soon.
|
|
|
|
PDB.LC = N->getLocationContext();
|
2012-02-07 10:26:17 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
// Block edges.
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
|
2012-05-02 08:31:29 +08:00
|
|
|
// Does this represent entering a call? If so, look at propagating
|
|
|
|
// interesting symbols across call boundaries.
|
|
|
|
if (NextNode) {
|
|
|
|
const LocationContext *CallerCtx = NextNode->getLocationContext();
|
|
|
|
const LocationContext *CalleeCtx = PDB.LC;
|
|
|
|
if (CallerCtx != CalleeCtx) {
|
|
|
|
reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
|
|
|
|
N->getState().getPtr(),
|
|
|
|
CalleeCtx, CallerCtx);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
// Are we jumping to the head of a loop? Add a special diagnostic.
|
2012-09-12 14:22:18 +08:00
|
|
|
if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
|
2012-02-24 15:12:52 +08:00
|
|
|
PathDiagnosticLocation L(Loop, SM, PDB.LC);
|
2009-05-15 09:50:15 +08:00
|
|
|
const CompoundStmt *CS = NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-09-12 14:22:18 +08:00
|
|
|
if (const ForStmt *FS = dyn_cast<ForStmt>(Loop))
|
|
|
|
CS = dyn_cast<CompoundStmt>(FS->getBody());
|
|
|
|
else if (const WhileStmt *WS = dyn_cast<WhileStmt>(Loop))
|
|
|
|
CS = dyn_cast<CompoundStmt>(WS->getBody());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
PathDiagnosticEventPiece *p =
|
|
|
|
new PathDiagnosticEventPiece(L,
|
2009-05-15 10:46:13 +08:00
|
|
|
"Looping back to the head of the loop");
|
2012-03-06 09:00:36 +08:00
|
|
|
p->setPrunable(true);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
EB.addEdge(p->getLocation(), true);
|
2012-02-24 14:00:00 +08:00
|
|
|
PD.getActivePath().push_front(p);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-15 09:50:15 +08:00
|
|
|
if (CS) {
|
2011-09-17 03:18:30 +08:00
|
|
|
PathDiagnosticLocation BL =
|
|
|
|
PathDiagnosticLocation::createEndBrace(CS, SM);
|
2009-05-15 10:46:13 +08:00
|
|
|
EB.addEdge(BL);
|
2009-05-07 08:45:33 +08:00
|
|
|
}
|
2009-04-28 12:23:15 +08:00
|
|
|
}
|
2013-02-09 03:51:43 +08:00
|
|
|
|
2013-02-22 13:45:33 +08:00
|
|
|
const CFGBlock *BSrc = BE->getSrc();
|
|
|
|
ParentMap &PM = PDB.getParentMap();
|
|
|
|
|
|
|
|
if (const Stmt *Term = BSrc->getTerminator()) {
|
2013-02-09 03:51:43 +08:00
|
|
|
// Are we jumping past the loop body without ever executing the
|
|
|
|
// loop (because the condition was false)?
|
2013-02-22 06:23:56 +08:00
|
|
|
if (isLoopJumpPastBody(Term, &*BE) &&
|
2013-02-22 13:45:33 +08:00
|
|
|
!isInLoopBody(PM,
|
|
|
|
getStmtBeforeCond(PM,
|
|
|
|
BSrc->getTerminatorCondition(),
|
|
|
|
N),
|
|
|
|
Term)) {
|
2013-02-09 03:51:43 +08:00
|
|
|
PathDiagnosticLocation L(Term, SM, PDB.LC);
|
|
|
|
PathDiagnosticEventPiece *PE =
|
2013-02-22 13:45:33 +08:00
|
|
|
new PathDiagnosticEventPiece(L, "Loop body executed 0 times");
|
2013-02-09 03:51:43 +08:00
|
|
|
PE->setPrunable(true);
|
|
|
|
|
|
|
|
EB.addEdge(PE->getLocation(), true);
|
|
|
|
PD.getActivePath().push_front(PE);
|
|
|
|
}
|
|
|
|
|
2013-02-22 13:45:33 +08:00
|
|
|
// In any case, add the terminator as the current statement
|
|
|
|
// context for control edges.
|
2009-05-15 09:50:15 +08:00
|
|
|
EB.addContext(Term);
|
2013-02-09 03:51:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
break;
|
2009-04-28 12:23:15 +08:00
|
|
|
}
|
2009-04-07 07:06:54 +08:00
|
|
|
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
|
2013-02-23 08:29:34 +08:00
|
|
|
Optional<CFGElement> First = BE->getFirstElement();
|
|
|
|
if (Optional<CFGStmt> S = First ? First->getAs<CFGStmt>() : None) {
|
|
|
|
const Stmt *stmt = S->getStmt();
|
2011-03-01 11:15:10 +08:00
|
|
|
if (IsControlFlowExpr(stmt)) {
|
2010-09-16 09:25:47 +08:00
|
|
|
// Add the proper context for '&&', '||', and '?'.
|
2011-03-01 11:15:10 +08:00
|
|
|
EB.addContext(stmt);
|
2010-09-16 09:25:47 +08:00
|
|
|
}
|
|
|
|
else
|
2011-03-01 11:15:10 +08:00
|
|
|
EB.addExtendedContext(PDB.getEnclosingStmtLocation(stmt).asStmt());
|
2009-05-07 08:45:33 +08:00
|
|
|
}
|
2010-09-16 09:25:47 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-02-07 10:26:17 +08:00
|
|
|
|
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
} while (0);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-07 08:45:33 +08:00
|
|
|
if (!NextNode)
|
2009-04-07 07:06:54 +08:00
|
|
|
continue;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-08-19 06:37:56 +08:00
|
|
|
// Add pieces from custom visitors.
|
|
|
|
BugReport *R = PDB.getBugReport();
|
2012-03-24 11:03:29 +08:00
|
|
|
for (ArrayRef<BugReporterVisitor *>::iterator I = visitors.begin(),
|
|
|
|
E = visitors.end();
|
|
|
|
I != E; ++I) {
|
2011-08-19 06:37:56 +08:00
|
|
|
if (PathDiagnosticPiece *p = (*I)->VisitNode(N, NextNode, PDB, *R)) {
|
2009-05-07 05:39:49 +08:00
|
|
|
const PathDiagnosticLocation &Loc = p->getLocation();
|
|
|
|
EB.addEdge(Loc, true);
|
2012-02-24 14:00:00 +08:00
|
|
|
PD.getActivePath().push_front(p);
|
2012-03-16 05:13:02 +08:00
|
|
|
updateStackPiecesWithMessage(p, CallStack);
|
|
|
|
|
2009-05-07 05:39:49 +08:00
|
|
|
if (const Stmt *S = Loc.asStmt())
|
2009-09-09 23:08:12 +08:00
|
|
|
EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt());
|
2009-05-07 05:39:49 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
}
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
2012-09-22 09:24:53 +08:00
|
|
|
|
|
|
|
return PDB.getBugReport()->isValid();
|
2009-04-07 07:06:54 +08:00
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Methods for BugType and subclasses.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-02-23 08:16:01 +08:00
|
|
|
BugType::~BugType() { }
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
void BugType::FlushReports(BugReporter &BR) {}
|
|
|
|
|
2011-12-20 10:48:34 +08:00
|
|
|
void BuiltinBug::anchor() {}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Methods for BugReport and subclasses.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2011-08-18 07:00:25 +08:00
|
|
|
|
2011-12-20 10:48:34 +08:00
|
|
|
void BugReport::NodeResolver::anchor() {}
|
|
|
|
|
2011-08-19 06:37:56 +08:00
|
|
|
void BugReport::addVisitor(BugReporterVisitor* visitor) {
|
|
|
|
if (!visitor)
|
|
|
|
return;
|
|
|
|
|
|
|
|
llvm::FoldingSetNodeID ID;
|
|
|
|
visitor->Profile(ID);
|
|
|
|
void *InsertPos;
|
|
|
|
|
|
|
|
if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
|
|
|
|
delete visitor;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CallbacksSet.InsertNode(visitor, InsertPos);
|
2012-03-24 11:03:29 +08:00
|
|
|
Callbacks.push_back(visitor);
|
|
|
|
++ConfigurationChangeToken;
|
2011-08-19 06:37:56 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
BugReport::~BugReport() {
|
|
|
|
for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) {
|
2011-08-20 07:21:56 +08:00
|
|
|
delete *I;
|
2011-08-19 06:37:56 +08:00
|
|
|
}
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
while (!interestingSymbols.empty()) {
|
|
|
|
popInterestingSymbolsAndRegions();
|
|
|
|
}
|
2011-08-19 06:37:56 +08:00
|
|
|
}
|
2011-08-18 07:00:25 +08:00
|
|
|
|
2012-04-05 02:11:35 +08:00
|
|
|
const Decl *BugReport::getDeclWithIssue() const {
|
|
|
|
if (DeclWithIssue)
|
|
|
|
return DeclWithIssue;
|
|
|
|
|
|
|
|
const ExplodedNode *N = getErrorNode();
|
|
|
|
if (!N)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
const LocationContext *LC = N->getLocationContext();
|
|
|
|
return LC->getCurrentStackFrame()->getDecl();
|
|
|
|
}
|
|
|
|
|
2011-08-18 07:00:25 +08:00
|
|
|
void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
|
|
|
|
hash.AddPointer(&BT);
|
|
|
|
hash.AddString(Description);
|
2013-01-08 08:25:29 +08:00
|
|
|
PathDiagnosticLocation UL = getUniqueingLocation();
|
|
|
|
if (UL.isValid()) {
|
|
|
|
UL.Profile(hash);
|
2012-02-24 05:38:21 +08:00
|
|
|
} else if (Location.isValid()) {
|
2011-09-21 05:38:35 +08:00
|
|
|
Location.Profile(hash);
|
|
|
|
} else {
|
|
|
|
assert(ErrorNode);
|
|
|
|
hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode));
|
|
|
|
}
|
2011-08-18 07:00:25 +08:00
|
|
|
|
|
|
|
for (SmallVectorImpl<SourceRange>::const_iterator I =
|
|
|
|
Ranges.begin(), E = Ranges.end(); I != E; ++I) {
|
|
|
|
const SourceRange range = *I;
|
|
|
|
if (!range.isValid())
|
|
|
|
continue;
|
|
|
|
hash.AddInteger(range.getBegin().getRawEncoding());
|
|
|
|
hash.AddInteger(range.getEnd().getRawEncoding());
|
|
|
|
}
|
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2012-03-09 09:13:14 +08:00
|
|
|
void BugReport::markInteresting(SymbolRef sym) {
|
|
|
|
if (!sym)
|
|
|
|
return;
|
2012-03-24 11:03:29 +08:00
|
|
|
|
|
|
|
// If the symbol wasn't already in our set, note a configuration change.
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
if (getInterestingSymbols().insert(sym).second)
|
2012-03-24 11:03:29 +08:00
|
|
|
++ConfigurationChangeToken;
|
2012-03-16 06:45:29 +08:00
|
|
|
|
|
|
|
if (const SymbolMetadata *meta = dyn_cast<SymbolMetadata>(sym))
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
getInterestingRegions().insert(meta->getRegion());
|
2012-03-09 09:13:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void BugReport::markInteresting(const MemRegion *R) {
|
|
|
|
if (!R)
|
|
|
|
return;
|
2012-03-24 11:03:29 +08:00
|
|
|
|
|
|
|
// If the base region wasn't already in our set, note a configuration change.
|
2012-03-09 09:13:14 +08:00
|
|
|
R = R->getBaseRegion();
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
if (getInterestingRegions().insert(R).second)
|
2012-03-24 11:03:29 +08:00
|
|
|
++ConfigurationChangeToken;
|
2012-03-16 06:45:29 +08:00
|
|
|
|
2012-03-09 09:13:14 +08:00
|
|
|
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
getInterestingSymbols().insert(SR->getSymbol());
|
2012-03-09 09:13:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void BugReport::markInteresting(SVal V) {
|
|
|
|
markInteresting(V.getAsRegion());
|
|
|
|
markInteresting(V.getAsSymbol());
|
|
|
|
}
|
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
void BugReport::markInteresting(const LocationContext *LC) {
|
|
|
|
if (!LC)
|
|
|
|
return;
|
|
|
|
InterestingLocationContexts.insert(LC);
|
|
|
|
}
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
bool BugReport::isInteresting(SVal V) {
|
2012-03-09 09:13:14 +08:00
|
|
|
return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol());
|
|
|
|
}
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
bool BugReport::isInteresting(SymbolRef sym) {
|
2012-03-09 09:13:14 +08:00
|
|
|
if (!sym)
|
|
|
|
return false;
|
2012-03-16 06:45:29 +08:00
|
|
|
// We don't currently consider metadata symbols to be interesting
|
|
|
|
// even if we know their region is interesting. Is that correct behavior?
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
return getInterestingSymbols().count(sym);
|
2012-03-09 09:13:14 +08:00
|
|
|
}
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
bool BugReport::isInteresting(const MemRegion *R) {
|
2012-03-09 09:13:14 +08:00
|
|
|
if (!R)
|
|
|
|
return false;
|
|
|
|
R = R->getBaseRegion();
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
bool b = getInterestingRegions().count(R);
|
2012-03-09 09:13:14 +08:00
|
|
|
if (b)
|
|
|
|
return true;
|
|
|
|
if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
return getInterestingSymbols().count(SR->getSymbol());
|
2012-03-09 09:13:14 +08:00
|
|
|
return false;
|
|
|
|
}
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
|
2012-08-30 05:22:37 +08:00
|
|
|
bool BugReport::isInteresting(const LocationContext *LC) {
|
|
|
|
if (!LC)
|
|
|
|
return false;
|
|
|
|
return InterestingLocationContexts.count(LC);
|
|
|
|
}
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
void BugReport::lazyInitializeInterestingSets() {
|
|
|
|
if (interestingSymbols.empty()) {
|
|
|
|
interestingSymbols.push_back(new Symbols());
|
|
|
|
interestingRegions.push_back(new Regions());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BugReport::Symbols &BugReport::getInterestingSymbols() {
|
|
|
|
lazyInitializeInterestingSets();
|
|
|
|
return *interestingSymbols.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
BugReport::Regions &BugReport::getInterestingRegions() {
|
|
|
|
lazyInitializeInterestingSets();
|
|
|
|
return *interestingRegions.back();
|
|
|
|
}
|
|
|
|
|
|
|
|
void BugReport::pushInterestingSymbolsAndRegions() {
|
|
|
|
interestingSymbols.push_back(new Symbols(getInterestingSymbols()));
|
|
|
|
interestingRegions.push_back(new Regions(getInterestingRegions()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void BugReport::popInterestingSymbolsAndRegions() {
|
|
|
|
delete interestingSymbols.back();
|
|
|
|
interestingSymbols.pop_back();
|
|
|
|
delete interestingRegions.back();
|
|
|
|
interestingRegions.pop_back();
|
|
|
|
}
|
2012-03-09 09:13:14 +08:00
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
const Stmt *BugReport::getStmt() const {
|
2011-08-18 07:00:25 +08:00
|
|
|
if (!ErrorNode)
|
|
|
|
return 0;
|
|
|
|
|
2010-09-16 11:50:38 +08:00
|
|
|
ProgramPoint ProgP = ErrorNode->getLocation();
|
2009-07-23 06:35:28 +08:00
|
|
|
const Stmt *S = NULL;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-02-22 06:23:56 +08:00
|
|
|
if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
|
2009-08-20 09:23:34 +08:00
|
|
|
CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
|
2009-08-18 16:46:04 +08:00
|
|
|
if (BE->getBlock() == &Exit)
|
2010-09-16 11:50:38 +08:00
|
|
|
S = GetPreviousStmt(ErrorNode);
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2009-07-23 06:35:28 +08:00
|
|
|
if (!S)
|
2009-09-09 23:08:12 +08:00
|
|
|
S = GetStmt(ProgP);
|
|
|
|
|
|
|
|
return S;
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
2010-12-04 09:12:15 +08:00
|
|
|
std::pair<BugReport::ranges_iterator, BugReport::ranges_iterator>
|
2011-08-18 07:00:25 +08:00
|
|
|
BugReport::getRanges() {
|
|
|
|
// If no custom ranges, add the range of the statement corresponding to
|
|
|
|
// the error node.
|
|
|
|
if (Ranges.empty()) {
|
|
|
|
if (const Expr *E = dyn_cast_or_null<Expr>(getStmt()))
|
|
|
|
addRange(E->getSourceRange());
|
|
|
|
else
|
|
|
|
return std::make_pair(ranges_iterator(), ranges_iterator());
|
|
|
|
}
|
|
|
|
|
2011-08-25 04:31:06 +08:00
|
|
|
// User-specified absence of range info.
|
|
|
|
if (Ranges.size() == 1 && !Ranges.begin()->isValid())
|
|
|
|
return std::make_pair(ranges_iterator(), ranges_iterator());
|
|
|
|
|
2011-08-18 07:00:25 +08:00
|
|
|
return std::make_pair(Ranges.begin(), Ranges.end());
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
2011-09-21 05:38:35 +08:00
|
|
|
PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
|
2011-08-18 07:21:23 +08:00
|
|
|
if (ErrorNode) {
|
2011-09-21 05:38:35 +08:00
|
|
|
assert(!Location.isValid() &&
|
2011-08-18 07:21:23 +08:00
|
|
|
"Either Location or ErrorNode should be specified but not both.");
|
|
|
|
|
2011-08-13 07:37:29 +08:00
|
|
|
if (const Stmt *S = GetCurrentOrPreviousStmt(ErrorNode)) {
|
2011-09-21 05:38:35 +08:00
|
|
|
const LocationContext *LC = ErrorNode->getLocationContext();
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// For member expressions, return the location of the '.' or '->'.
|
2009-09-12 06:07:28 +08:00
|
|
|
if (const MemberExpr *ME = dyn_cast<MemberExpr>(S))
|
2011-09-21 05:38:35 +08:00
|
|
|
return PathDiagnosticLocation::createMemberLoc(ME, SM);
|
2009-09-12 06:07:28 +08:00
|
|
|
// For binary operators, return the location of the operator.
|
|
|
|
if (const BinaryOperator *B = dyn_cast<BinaryOperator>(S))
|
2011-09-21 05:38:35 +08:00
|
|
|
return PathDiagnosticLocation::createOperatorLoc(B, SM);
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2013-02-22 06:23:56 +08:00
|
|
|
if (ErrorNode->getLocation().getAs<PostStmtPurgeDeadSymbols>())
|
2012-11-16 03:11:43 +08:00
|
|
|
return PathDiagnosticLocation::createEnd(S, SM, LC);
|
|
|
|
|
2011-09-21 05:38:35 +08:00
|
|
|
return PathDiagnosticLocation::createBegin(S, SM, LC);
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2011-08-18 07:21:23 +08:00
|
|
|
} else {
|
|
|
|
assert(Location.isValid());
|
|
|
|
return Location;
|
|
|
|
}
|
|
|
|
|
2011-09-21 05:38:35 +08:00
|
|
|
return PathDiagnosticLocation();
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Methods for BugReporter and subclasses.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2012-04-02 03:30:51 +08:00
|
|
|
BugReportEquivClass::~BugReportEquivClass() { }
|
2009-09-05 14:06:49 +08:00
|
|
|
GRBugReporter::~GRBugReporter() { }
|
2009-04-01 07:00:32 +08:00
|
|
|
BugReporterData::~BugReporterData() {}
|
|
|
|
|
2009-08-06 14:28:40 +08:00
|
|
|
ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); }
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2011-08-16 06:09:50 +08:00
|
|
|
ProgramStateManager&
|
2009-04-01 07:00:32 +08:00
|
|
|
GRBugReporter::getStateManager() { return Eng.getStateManager(); }
|
|
|
|
|
2011-08-19 09:57:09 +08:00
|
|
|
BugReporter::~BugReporter() {
|
|
|
|
FlushReports();
|
|
|
|
|
|
|
|
// Free the bug reports we are tracking.
|
|
|
|
typedef std::vector<BugReportEquivClass *> ContTy;
|
|
|
|
for (ContTy::iterator I = EQClassesVector.begin(), E = EQClassesVector.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
delete *I;
|
|
|
|
}
|
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
void BugReporter::FlushReports() {
|
|
|
|
if (BugTypes.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// First flush the warnings for each BugType. This may end up creating new
|
2011-02-23 08:16:01 +08:00
|
|
|
// warnings and new BugTypes.
|
|
|
|
// FIXME: Only NSErrorChecker needs BugType's FlushReports.
|
|
|
|
// Turn NSErrorChecker into a proper checker and remove this.
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVector<const BugType*, 16> bugTypes;
|
2009-04-01 07:00:32 +08:00
|
|
|
for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I)
|
2011-02-23 08:16:01 +08:00
|
|
|
bugTypes.push_back(*I);
|
2011-07-23 18:55:15 +08:00
|
|
|
for (SmallVector<const BugType*, 16>::iterator
|
2011-02-23 08:16:01 +08:00
|
|
|
I = bugTypes.begin(), E = bugTypes.end(); I != E; ++I)
|
2009-04-01 07:00:32 +08:00
|
|
|
const_cast<BugType*>(*I)->FlushReports(*this);
|
|
|
|
|
2012-08-03 07:41:05 +08:00
|
|
|
// We need to flush reports in deterministic order to ensure the order
|
|
|
|
// of the reports is consistent between runs.
|
2012-08-02 08:41:43 +08:00
|
|
|
typedef std::vector<BugReportEquivClass *> ContVecTy;
|
|
|
|
for (ContVecTy::iterator EI=EQClassesVector.begin(), EE=EQClassesVector.end();
|
|
|
|
EI != EE; ++EI){
|
|
|
|
BugReportEquivClass& EQ = **EI;
|
2011-02-23 08:16:01 +08:00
|
|
|
FlushReport(EQ);
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
2011-02-23 08:16:01 +08:00
|
|
|
// BugReporter owns and deletes only BugTypes created implicitly through
|
|
|
|
// EmitBasicReport.
|
|
|
|
// FIXME: There are leaks from checkers that assume that the BugTypes they
|
|
|
|
// create will be destroyed by the BugReporter.
|
|
|
|
for (llvm::StringMap<BugType*>::iterator
|
|
|
|
I = StrBugTypes.begin(), E = StrBugTypes.end(); I != E; ++I)
|
|
|
|
delete I->second;
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Remove all references to the BugType objects.
|
2010-11-24 08:54:37 +08:00
|
|
|
BugTypes = F.getEmptySet();
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// PathDiagnostics generation.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2013-03-16 09:07:58 +08:00
|
|
|
namespace {
|
|
|
|
/// A wrapper around a report graph, which contains only a single path, and its
|
|
|
|
/// node maps.
|
|
|
|
class ReportGraph {
|
|
|
|
public:
|
|
|
|
OwningPtr<ExplodedGraph> Graph;
|
|
|
|
InterExplodedGraphMap BackMap;
|
|
|
|
ExplodedNode *ErrorNode;
|
|
|
|
size_t Index;
|
|
|
|
};
|
|
|
|
|
|
|
|
/// A wrapper around a trimmed graph and its node maps.
|
|
|
|
class TrimmedGraph {
|
|
|
|
InterExplodedGraphMap ForwardMap;
|
|
|
|
InterExplodedGraphMap InverseMap;
|
2013-03-19 07:34:37 +08:00
|
|
|
|
|
|
|
typedef llvm::DenseMap<const ExplodedNode *, unsigned> PriorityMapTy;
|
|
|
|
PriorityMapTy PriorityMap;
|
|
|
|
|
2013-03-16 09:07:58 +08:00
|
|
|
OwningPtr<ExplodedGraph> G;
|
2013-03-19 07:34:37 +08:00
|
|
|
const ExplodedNode *Root;
|
2013-03-16 09:07:58 +08:00
|
|
|
public:
|
|
|
|
TrimmedGraph(const ExplodedGraph *OriginalGraph,
|
2013-03-19 07:34:37 +08:00
|
|
|
ArrayRef<const ExplodedNode *> Nodes);
|
2013-03-16 09:07:58 +08:00
|
|
|
|
|
|
|
void createBestReportGraph(ArrayRef<const ExplodedNode *> Nodes,
|
|
|
|
ReportGraph &GraphWrapper) const;
|
2013-03-19 07:34:37 +08:00
|
|
|
|
|
|
|
void removeErrorNode(const ExplodedNode *Node);
|
2013-03-16 09:07:58 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
|
|
|
|
ArrayRef<const ExplodedNode *> Nodes) {
|
|
|
|
// The trimmed graph is created in the body of the constructor to ensure
|
|
|
|
// that the DenseMaps have been initialized already.
|
|
|
|
G.reset(OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap));
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Find the (first) error node in the trimmed graph. We just need to consult
|
2013-03-16 09:07:58 +08:00
|
|
|
// the node map which maps from nodes in the original graph to nodes
|
2009-04-01 07:00:32 +08:00
|
|
|
// in the new graph.
|
2013-03-19 07:34:37 +08:00
|
|
|
std::queue<std::pair<const ExplodedNode *, unsigned> > WS;
|
2013-03-16 09:07:58 +08:00
|
|
|
typedef llvm::SmallDenseMap<const ExplodedNode *, size_t, 32> IndexMapTy;
|
2013-03-19 07:34:37 +08:00
|
|
|
IndexMapTy IndexMap(llvm::NextPowerOf2(Nodes.size() + 1));
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2013-03-16 09:07:58 +08:00
|
|
|
for (unsigned i = 0, count = Nodes.size(); i < count; ++i) {
|
|
|
|
const ExplodedNode *OriginalNode = Nodes[i];
|
|
|
|
if (const ExplodedNode *N = ForwardMap.lookup(OriginalNode)) {
|
2013-03-19 07:34:37 +08:00
|
|
|
WS.push(std::make_pair(N, 0));
|
2013-03-16 09:07:58 +08:00
|
|
|
IndexMap[OriginalNode] = i;
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2010-12-03 14:52:30 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-05-16 09:11:58 +08:00
|
|
|
assert(!WS.empty() && "No error node found in the trimmed graph.");
|
2009-04-01 07:00:32 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
// Perform a reverse BFS to find all the shortest paths.
|
|
|
|
Root = 0;
|
2009-04-01 07:00:32 +08:00
|
|
|
while (!WS.empty()) {
|
2013-03-19 07:34:37 +08:00
|
|
|
const ExplodedNode *Node;
|
|
|
|
unsigned Priority;
|
|
|
|
llvm::tie(Node, Priority) = WS.front();
|
2009-04-01 07:00:32 +08:00
|
|
|
WS.pop();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
PriorityMapTy::iterator I = PriorityMap.find(Node);
|
|
|
|
if (I != PriorityMap.end()) {
|
|
|
|
assert(I->second <= Priority);
|
2009-04-01 07:00:32 +08:00
|
|
|
continue;
|
2013-03-19 07:34:37 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
PriorityMap[Node] = Priority;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
if (Node->pred_empty())
|
2009-04-01 07:00:32 +08:00
|
|
|
Root = Node;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
for (ExplodedNode::const_pred_iterator I = Node->pred_begin(),
|
|
|
|
E = Node->pred_end();
|
|
|
|
I != E; ++I)
|
|
|
|
WS.push(std::make_pair(*I, Priority + 1));
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2013-03-19 07:34:37 +08:00
|
|
|
|
2009-05-16 09:11:58 +08:00
|
|
|
assert(Root);
|
2013-03-19 07:34:37 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void TrimmedGraph::createBestReportGraph(ArrayRef<const ExplodedNode *> Nodes,
|
|
|
|
ReportGraph &GraphWrapper) const {
|
|
|
|
assert(!GraphWrapper.Graph && "ReportGraph is already in use");
|
|
|
|
assert(GraphWrapper.BackMap.empty() && "ReportGraph is already in use");
|
|
|
|
|
|
|
|
// Find the (first) error node in the trimmed graph. We just need to consult
|
|
|
|
// the node map which maps from nodes in the original graph to nodes
|
|
|
|
// in the new graph.
|
|
|
|
std::queue<std::pair<const ExplodedNode *, unsigned> > WS;
|
|
|
|
typedef llvm::SmallDenseMap<const ExplodedNode *, size_t, 32> IndexMapTy;
|
|
|
|
IndexMapTy IndexMap(llvm::NextPowerOf2(Nodes.size() + 1));
|
|
|
|
|
|
|
|
for (unsigned i = 0, count = Nodes.size(); i < count; ++i) {
|
|
|
|
const ExplodedNode *OriginalNode = Nodes[i];
|
|
|
|
if (const ExplodedNode *N = ForwardMap.lookup(OriginalNode)) {
|
|
|
|
WS.push(std::make_pair(N, 0));
|
|
|
|
IndexMap[OriginalNode] = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(!WS.empty() && "No error node found in the trimmed graph.");
|
|
|
|
|
|
|
|
// Create a new graph with a single path. This is the graph
|
|
|
|
// that will be returned to the caller.
|
|
|
|
ExplodedGraph *GNew = new ExplodedGraph();
|
|
|
|
GraphWrapper.Graph.reset(GNew);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Now walk from the root down the BFS path, always taking the successor
|
|
|
|
// with the lowest number.
|
2013-03-16 09:07:58 +08:00
|
|
|
ExplodedNode *Last = 0;
|
2009-08-06 09:32:16 +08:00
|
|
|
for ( const ExplodedNode *N = Root ;;) {
|
2009-04-01 07:00:32 +08:00
|
|
|
// Lookup the number associated with the current node.
|
2013-03-19 07:34:37 +08:00
|
|
|
PriorityMapTy::const_iterator I = PriorityMap.find(N);
|
|
|
|
assert(I != PriorityMap.end());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Create the equivalent node in the new graph with the same state
|
|
|
|
// and location.
|
2011-08-13 07:37:29 +08:00
|
|
|
ExplodedNode *NewN = GNew->getNode(N->getLocation(), N->getState());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Store the mapping to the original node.
|
2013-03-16 09:07:58 +08:00
|
|
|
InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(N);
|
2009-04-01 07:00:32 +08:00
|
|
|
assert(IMitr != InverseMap.end() && "No mapping to original node.");
|
2013-03-16 09:07:58 +08:00
|
|
|
GraphWrapper.BackMap[NewN] = IMitr->second;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Link up the new node with the previous node.
|
|
|
|
if (Last)
|
2009-10-07 08:42:52 +08:00
|
|
|
NewN->addPredecessor(Last, *GNew);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
Last = NewN;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Are we at the final node?
|
2013-03-16 09:07:58 +08:00
|
|
|
IndexMapTy::iterator IMI = IndexMap.find(IMitr->second);
|
2009-05-16 09:11:58 +08:00
|
|
|
if (IMI != IndexMap.end()) {
|
2013-03-16 09:07:58 +08:00
|
|
|
GraphWrapper.ErrorNode = NewN;
|
|
|
|
GraphWrapper.Index = IMI->second;
|
2009-04-01 07:00:32 +08:00
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Find the next successor node. We choose the node that is marked
|
|
|
|
// with the lowest DFS number.
|
2013-03-16 09:07:58 +08:00
|
|
|
unsigned MinVal = -1U;
|
|
|
|
for (ExplodedNode::const_succ_iterator SI = N->succ_begin(),
|
|
|
|
SE = N->succ_end();
|
|
|
|
SI != SE; ++SI) {
|
2013-03-19 07:34:37 +08:00
|
|
|
I = PriorityMap.find(*SI);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
if (I == PriorityMap.end())
|
2009-04-01 07:00:32 +08:00
|
|
|
continue;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-16 09:07:58 +08:00
|
|
|
if (I->second < MinVal) {
|
2009-04-01 07:00:32 +08:00
|
|
|
N = *SI;
|
|
|
|
MinVal = I->second;
|
|
|
|
}
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-16 09:07:58 +08:00
|
|
|
assert(MinVal != -1U);
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
|
|
|
}
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
void TrimmedGraph::removeErrorNode(const ExplodedNode *ErrorNode) {
|
|
|
|
ErrorNode = ForwardMap[ErrorNode];
|
|
|
|
assert(ErrorNode && "not an error node");
|
|
|
|
|
|
|
|
PriorityMapTy::iterator PriorityEntry = PriorityMap.find(ErrorNode);
|
|
|
|
assert(PriorityEntry != PriorityMap.end() && "error node already removed");
|
|
|
|
PriorityMap.erase(PriorityEntry);
|
|
|
|
|
|
|
|
std::queue<const ExplodedNode *> WS;
|
|
|
|
for (ExplodedNode::const_pred_iterator PI = ErrorNode->pred_begin(),
|
|
|
|
PE = ErrorNode->pred_end();
|
|
|
|
PI != PE; ++PI) {
|
|
|
|
assert(PriorityMap.find(*PI) != PriorityMap.end());
|
|
|
|
WS.push(*PI);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Update all nodes possibly affected by this change.
|
|
|
|
while (!WS.empty()) {
|
|
|
|
const ExplodedNode *N = WS.front();
|
|
|
|
WS.pop();
|
|
|
|
|
|
|
|
unsigned MinPriority = -1U;
|
|
|
|
for (ExplodedNode::const_succ_iterator SI = N->succ_begin(),
|
|
|
|
SE = N->succ_end();
|
|
|
|
SI != SE; ++SI) {
|
|
|
|
PriorityMapTy::iterator SuccEntry = PriorityMap.find(*SI);
|
|
|
|
if (SuccEntry == PriorityMap.end())
|
|
|
|
continue;
|
|
|
|
MinPriority = std::min(SuccEntry->second, MinPriority);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (MinPriority == -1U)
|
|
|
|
PriorityMap.erase(N);
|
|
|
|
else if (PriorityMap[N] == MinPriority + 1)
|
|
|
|
continue;
|
|
|
|
else
|
|
|
|
PriorityMap[N] = MinPriority + 1;
|
|
|
|
|
|
|
|
for (ExplodedNode::const_pred_iterator PI = N->pred_begin(),
|
|
|
|
PE = N->pred_end();
|
|
|
|
PI != PE; ++PI) {
|
|
|
|
assert(PriorityMap.find(*PI) != PriorityMap.end());
|
|
|
|
WS.push(*PI);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object
|
|
|
|
/// and collapses PathDiagosticPieces that are expanded by macros.
|
2012-03-02 09:27:31 +08:00
|
|
|
static void CompactPathDiagnostic(PathPieces &path, const SourceManager& SM) {
|
2012-02-24 14:00:00 +08:00
|
|
|
typedef std::vector<std::pair<IntrusiveRefCntPtr<PathDiagnosticMacroPiece>,
|
|
|
|
SourceLocation> > MacroStackTy;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-02-20 22:00:23 +08:00
|
|
|
typedef std::vector<IntrusiveRefCntPtr<PathDiagnosticPiece> >
|
2009-04-01 07:00:32 +08:00
|
|
|
PiecesTy;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
MacroStackTy MacroStack;
|
|
|
|
PiecesTy Pieces;
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-02 09:27:31 +08:00
|
|
|
for (PathPieces::const_iterator I = path.begin(), E = path.end();
|
2012-02-24 14:00:00 +08:00
|
|
|
I!=E; ++I) {
|
2012-03-02 09:27:31 +08:00
|
|
|
|
|
|
|
PathDiagnosticPiece *piece = I->getPtr();
|
|
|
|
|
|
|
|
// Recursively compact calls.
|
|
|
|
if (PathDiagnosticCallPiece *call=dyn_cast<PathDiagnosticCallPiece>(piece)){
|
|
|
|
CompactPathDiagnostic(call->path, SM);
|
|
|
|
}
|
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Get the location of the PathDiagnosticPiece.
|
2012-03-02 09:27:31 +08:00
|
|
|
const FullSourceLoc Loc = piece->getLocation().asLocation();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Determine the instantiation location, which is the location we group
|
|
|
|
// related PathDiagnosticPieces.
|
2009-09-09 23:08:12 +08:00
|
|
|
SourceLocation InstantiationLoc = Loc.isMacroID() ?
|
2011-07-26 00:49:02 +08:00
|
|
|
SM.getExpansionLoc(Loc) :
|
2009-04-01 07:00:32 +08:00
|
|
|
SourceLocation();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (Loc.isFileID()) {
|
|
|
|
MacroStack.clear();
|
2012-03-02 09:27:31 +08:00
|
|
|
Pieces.push_back(piece);
|
2009-04-01 07:00:32 +08:00
|
|
|
continue;
|
|
|
|
}
|
2008-04-08 07:35:17 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
assert(Loc.isMacroID());
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Is the PathDiagnosticPiece within the same macro group?
|
|
|
|
if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
|
2012-03-02 09:27:31 +08:00
|
|
|
MacroStack.back().first->subPieces.push_back(piece);
|
2009-04-01 07:00:32 +08:00
|
|
|
continue;
|
|
|
|
}
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// We aren't in the same group. Are we descending into a new macro
|
|
|
|
// or are part of an old one?
|
2012-02-20 22:00:23 +08:00
|
|
|
IntrusiveRefCntPtr<PathDiagnosticMacroPiece> MacroGroup;
|
2009-03-28 05:16:25 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
|
2011-07-26 00:49:02 +08:00
|
|
|
SM.getExpansionLoc(Loc) :
|
2009-04-01 07:00:32 +08:00
|
|
|
SourceLocation();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Walk the entire macro stack.
|
|
|
|
while (!MacroStack.empty()) {
|
|
|
|
if (InstantiationLoc == MacroStack.back().second) {
|
|
|
|
MacroGroup = MacroStack.back().first;
|
|
|
|
break;
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (ParentInstantiationLoc == MacroStack.back().second) {
|
|
|
|
MacroGroup = MacroStack.back().first;
|
|
|
|
break;
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
MacroStack.pop_back();
|
2008-04-09 08:20:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
|
|
|
|
// Create a new macro group and add it to the stack.
|
2011-09-21 05:38:35 +08:00
|
|
|
PathDiagnosticMacroPiece *NewGroup =
|
|
|
|
new PathDiagnosticMacroPiece(
|
2012-03-02 09:27:31 +08:00
|
|
|
PathDiagnosticLocation::createSingleLocation(piece->getLocation()));
|
2008-04-24 07:35:07 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
if (MacroGroup)
|
2012-02-08 12:32:34 +08:00
|
|
|
MacroGroup->subPieces.push_back(NewGroup);
|
2009-04-01 07:00:32 +08:00
|
|
|
else {
|
|
|
|
assert(InstantiationLoc.isFileID());
|
|
|
|
Pieces.push_back(NewGroup);
|
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
MacroGroup = NewGroup;
|
|
|
|
MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
|
2009-04-01 04:22:36 +08:00
|
|
|
}
|
2009-04-01 07:00:32 +08:00
|
|
|
|
|
|
|
// Finally, add the PathDiagnosticPiece to the group.
|
2012-03-02 09:27:31 +08:00
|
|
|
MacroGroup->subPieces.push_back(piece);
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-04-01 07:00:32 +08:00
|
|
|
// Now take the pieces and construct a new PathDiagnostic.
|
2012-03-02 09:27:31 +08:00
|
|
|
path.clear();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-03-02 09:27:31 +08:00
|
|
|
for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I)
|
|
|
|
path.push_back(*I);
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
|
2012-09-22 09:24:53 +08:00
|
|
|
bool GRBugReporter::generatePathDiagnostic(PathDiagnostic& PD,
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
PathDiagnosticConsumer &PC,
|
|
|
|
ArrayRef<BugReport *> &bugReports) {
|
2010-12-03 14:52:30 +08:00
|
|
|
assert(!bugReports.empty());
|
2012-09-22 09:24:53 +08:00
|
|
|
|
2013-03-16 09:07:47 +08:00
|
|
|
bool HasValid = false;
|
2013-03-16 05:41:55 +08:00
|
|
|
SmallVector<const ExplodedNode *, 32> errorNodes;
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
for (ArrayRef<BugReport*>::iterator I = bugReports.begin(),
|
2013-03-16 09:07:47 +08:00
|
|
|
E = bugReports.end(); I != E; ++I) {
|
|
|
|
if ((*I)->isValid()) {
|
|
|
|
HasValid = true;
|
|
|
|
errorNodes.push_back((*I)->getErrorNode());
|
|
|
|
} else {
|
|
|
|
errorNodes.push_back(0);
|
|
|
|
}
|
2010-12-03 14:52:30 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2013-03-16 09:07:47 +08:00
|
|
|
// If all the reports have been marked invalid by a previous path generation,
|
|
|
|
// we're done.
|
|
|
|
if (!HasValid)
|
|
|
|
return false;
|
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
typedef PathDiagnosticConsumer::PathGenerationScheme PathGenerationScheme;
|
|
|
|
PathGenerationScheme ActiveScheme = PC.getGenerationScheme();
|
|
|
|
|
2013-03-16 09:07:58 +08:00
|
|
|
TrimmedGraph TrimG(&getGraph(), errorNodes);
|
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
for (size_t Remaining = bugReports.size(); Remaining > 0; --Remaining) {
|
|
|
|
// Construct a new graph that contains only a single path from the error
|
|
|
|
// node to a root.
|
2013-03-16 09:07:58 +08:00
|
|
|
ReportGraph ErrorGraph;
|
|
|
|
TrimG.createBestReportGraph(errorNodes, ErrorGraph);
|
2013-03-16 05:41:55 +08:00
|
|
|
|
|
|
|
// Find the BugReport with the original location.
|
2013-03-16 09:07:58 +08:00
|
|
|
assert(ErrorGraph.Index < bugReports.size());
|
|
|
|
BugReport *R = bugReports[ErrorGraph.Index];
|
2013-03-16 05:41:55 +08:00
|
|
|
assert(R && "No original report found for sliced graph.");
|
|
|
|
assert(R->isValid() && "Report selected by trimmed graph marked invalid.");
|
|
|
|
|
|
|
|
// Don't try to reuse this report if it ends up being suppressed.
|
2013-03-16 09:07:58 +08:00
|
|
|
errorNodes[ErrorGraph.Index] = 0;
|
2013-03-16 05:41:55 +08:00
|
|
|
|
|
|
|
// Start building the path diagnostic...
|
2013-03-16 09:07:58 +08:00
|
|
|
PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, &PC);
|
|
|
|
const ExplodedNode *N = ErrorGraph.ErrorNode;
|
2013-03-16 05:41:55 +08:00
|
|
|
|
|
|
|
// Register additional node visitors.
|
|
|
|
R->addVisitor(new NilReceiverBRVisitor());
|
|
|
|
R->addVisitor(new ConditionBRVisitor());
|
|
|
|
R->addVisitor(new LikelyFalsePositiveSuppressionBRVisitor());
|
|
|
|
|
|
|
|
BugReport::VisitorList visitors;
|
|
|
|
unsigned origReportConfigToken, finalReportConfigToken;
|
|
|
|
|
|
|
|
// While generating diagnostics, it's possible the visitors will decide
|
|
|
|
// new symbols and regions are interesting, or add other visitors based on
|
|
|
|
// the information they find. If they do, we need to regenerate the path
|
|
|
|
// based on our new report configuration.
|
|
|
|
do {
|
|
|
|
// Get a clean copy of all the visitors.
|
|
|
|
for (BugReport::visitor_iterator I = R->visitor_begin(),
|
|
|
|
E = R->visitor_end(); I != E; ++I)
|
|
|
|
visitors.push_back((*I)->clone());
|
|
|
|
|
|
|
|
// Clear out the active path from any previous work.
|
|
|
|
PD.resetPath();
|
|
|
|
origReportConfigToken = R->getConfigurationChangeToken();
|
|
|
|
|
|
|
|
// Generate the very last diagnostic piece - the piece is visible before
|
|
|
|
// the trace is expanded.
|
|
|
|
PathDiagnosticPiece *LastPiece = 0;
|
|
|
|
for (BugReport::visitor_iterator I = visitors.begin(), E = visitors.end();
|
|
|
|
I != E; ++I) {
|
|
|
|
if (PathDiagnosticPiece *Piece = (*I)->getEndPath(PDB, N, *R)) {
|
|
|
|
assert (!LastPiece &&
|
|
|
|
"There can only be one final piece in a diagnostic.");
|
|
|
|
LastPiece = Piece;
|
|
|
|
}
|
2012-03-24 11:03:29 +08:00
|
|
|
}
|
2013-01-31 03:12:34 +08:00
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
if (ActiveScheme != PathDiagnosticConsumer::None) {
|
|
|
|
if (!LastPiece)
|
|
|
|
LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, N, *R);
|
|
|
|
assert(LastPiece);
|
2012-09-22 09:24:56 +08:00
|
|
|
PD.setEndOfPath(LastPiece);
|
2012-09-22 09:24:53 +08:00
|
|
|
}
|
2013-03-16 05:41:55 +08:00
|
|
|
|
|
|
|
switch (ActiveScheme) {
|
|
|
|
case PathDiagnosticConsumer::Extensive:
|
|
|
|
GenerateExtensivePathDiagnostic(PD, PDB, N, visitors);
|
|
|
|
break;
|
|
|
|
case PathDiagnosticConsumer::Minimal:
|
|
|
|
GenerateMinimalPathDiagnostic(PD, PDB, N, visitors);
|
|
|
|
break;
|
|
|
|
case PathDiagnosticConsumer::None:
|
|
|
|
GenerateVisitorsOnlyPathDiagnostic(PD, PDB, N, visitors);
|
|
|
|
break;
|
2012-09-22 09:24:56 +08:00
|
|
|
}
|
2012-03-24 11:03:29 +08:00
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
// Clean up the visitors we used.
|
|
|
|
llvm::DeleteContainerPointers(visitors);
|
2012-03-24 11:03:29 +08:00
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
// Did anything change while generating this path?
|
|
|
|
finalReportConfigToken = R->getConfigurationChangeToken();
|
|
|
|
} while (finalReportConfigToken != origReportConfigToken);
|
2012-03-24 11:03:29 +08:00
|
|
|
|
2013-03-19 07:34:37 +08:00
|
|
|
if (!R->isValid()) {
|
|
|
|
TrimG.removeErrorNode(R->getErrorNode());
|
2013-03-16 05:41:55 +08:00
|
|
|
continue;
|
2013-03-19 07:34:37 +08:00
|
|
|
}
|
2012-10-26 06:07:10 +08:00
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
// Finally, prune the diagnostic path of uninteresting stuff.
|
|
|
|
if (!PD.path.empty()) {
|
|
|
|
// Remove messages that are basically the same.
|
|
|
|
removeRedundantMsgs(PD.getMutablePieces());
|
|
|
|
|
|
|
|
if (R->shouldPrunePath() &&
|
|
|
|
getEngine().getAnalysisManager().options.shouldPrunePaths()) {
|
|
|
|
bool stillHasNotes = RemoveUnneededCalls(PD.getMutablePieces(), R);
|
|
|
|
assert(stillHasNotes);
|
|
|
|
(void)stillHasNotes;
|
|
|
|
}
|
|
|
|
|
|
|
|
adjustCallLocations(PD.getMutablePieces());
|
2012-10-26 06:07:10 +08:00
|
|
|
}
|
2012-12-08 03:56:29 +08:00
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
// We found a report and didn't suppress it.
|
|
|
|
return true;
|
2012-05-31 14:03:17 +08:00
|
|
|
}
|
2012-09-22 09:24:53 +08:00
|
|
|
|
2013-03-16 05:41:55 +08:00
|
|
|
// We suppressed all the reports in this equivalence class.
|
|
|
|
return false;
|
2009-04-01 07:00:32 +08:00
|
|
|
}
|
2008-05-23 07:45:19 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
void BugReporter::Register(BugType *BT) {
|
2010-11-24 08:54:37 +08:00
|
|
|
BugTypes = F.add(BugTypes, BT);
|
2008-05-17 02:33:14 +08:00
|
|
|
}
|
|
|
|
|
2012-11-02 09:53:40 +08:00
|
|
|
void BugReporter::emitReport(BugReport* R) {
|
2009-02-05 07:49:09 +08:00
|
|
|
// Compute the bug report's hash to determine its equivalence class.
|
|
|
|
llvm::FoldingSetNodeID ID;
|
|
|
|
R->Profile(ID);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
|
|
|
// Lookup the equivance class. If there isn't one, create it.
|
2009-02-05 07:49:09 +08:00
|
|
|
BugType& BT = R->getBugType();
|
|
|
|
Register(&BT);
|
|
|
|
void *InsertPos;
|
2011-02-23 08:16:01 +08:00
|
|
|
BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos);
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
if (!EQ) {
|
|
|
|
EQ = new BugReportEquivClass(R);
|
2011-02-23 08:16:01 +08:00
|
|
|
EQClasses.InsertNode(EQ, InsertPos);
|
2011-08-19 09:57:09 +08:00
|
|
|
EQClassesVector.push_back(EQ);
|
2009-02-05 07:49:09 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
EQ->AddReport(R);
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
|
|
|
|
2009-09-15 06:01:32 +08:00
|
|
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Emitting reports in equivalence classes.
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
2009-11-28 14:07:30 +08:00
|
|
|
struct FRIEC_WLItem {
|
2009-09-15 06:01:32 +08:00
|
|
|
const ExplodedNode *N;
|
|
|
|
ExplodedNode::const_succ_iterator I, E;
|
|
|
|
|
|
|
|
FRIEC_WLItem(const ExplodedNode *n)
|
|
|
|
: N(n), I(N->succ_begin()), E(N->succ_end()) {}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2010-09-10 03:05:34 +08:00
|
|
|
static BugReport *
|
|
|
|
FindReportInEquivalenceClass(BugReportEquivClass& EQ,
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVectorImpl<BugReport*> &bugReports) {
|
2010-09-10 03:05:34 +08:00
|
|
|
|
2009-09-15 06:01:32 +08:00
|
|
|
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
|
|
|
|
assert(I != E);
|
2012-04-02 03:30:51 +08:00
|
|
|
BugType& BT = I->getBugType();
|
2010-09-10 03:05:34 +08:00
|
|
|
|
2010-12-03 14:52:30 +08:00
|
|
|
// If we don't need to suppress any of the nodes because they are
|
|
|
|
// post-dominated by a sink, simply add all the nodes in the equivalence class
|
|
|
|
// to 'Nodes'. Any of the reports will serve as a "representative" report.
|
2010-09-10 03:05:34 +08:00
|
|
|
if (!BT.isSuppressOnSink()) {
|
2012-04-02 03:30:51 +08:00
|
|
|
BugReport *R = I;
|
2010-09-10 03:05:34 +08:00
|
|
|
for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) {
|
2011-08-13 07:37:29 +08:00
|
|
|
const ExplodedNode *N = I->getErrorNode();
|
2010-09-10 03:05:34 +08:00
|
|
|
if (N) {
|
2012-04-02 03:30:51 +08:00
|
|
|
R = I;
|
2010-12-03 14:52:30 +08:00
|
|
|
bugReports.push_back(R);
|
2010-09-10 03:05:34 +08:00
|
|
|
}
|
|
|
|
}
|
2009-09-15 06:01:32 +08:00
|
|
|
return R;
|
2010-09-10 03:05:34 +08:00
|
|
|
}
|
|
|
|
|
2009-09-15 06:01:32 +08:00
|
|
|
// For bug reports that should be suppressed when all paths are post-dominated
|
|
|
|
// by a sink node, iterate through the reports in the equivalence class
|
|
|
|
// until we find one that isn't post-dominated (if one exists). We use a
|
|
|
|
// DFS traversal of the ExplodedGraph to find a non-sink node. We could write
|
|
|
|
// this as a recursive function, but we don't want to risk blowing out the
|
|
|
|
// stack for very long paths.
|
2010-12-03 14:52:30 +08:00
|
|
|
BugReport *exampleReport = 0;
|
2010-09-10 03:05:34 +08:00
|
|
|
|
2009-09-15 06:01:32 +08:00
|
|
|
for (; I != E; ++I) {
|
2012-04-02 03:30:51 +08:00
|
|
|
const ExplodedNode *errorNode = I->getErrorNode();
|
2009-09-15 06:01:32 +08:00
|
|
|
|
2010-12-03 14:52:30 +08:00
|
|
|
if (!errorNode)
|
2009-09-15 06:01:32 +08:00
|
|
|
continue;
|
2010-12-03 14:52:30 +08:00
|
|
|
if (errorNode->isSink()) {
|
2011-09-23 13:06:16 +08:00
|
|
|
llvm_unreachable(
|
2009-09-15 06:01:32 +08:00
|
|
|
"BugType::isSuppressSink() should not be 'true' for sink end nodes");
|
|
|
|
}
|
2010-09-10 03:05:34 +08:00
|
|
|
// No successors? By definition this nodes isn't post-dominated by a sink.
|
2010-12-03 14:52:30 +08:00
|
|
|
if (errorNode->succ_empty()) {
|
2012-04-02 03:30:51 +08:00
|
|
|
bugReports.push_back(I);
|
2010-12-03 14:52:30 +08:00
|
|
|
if (!exampleReport)
|
2012-04-02 03:30:51 +08:00
|
|
|
exampleReport = I;
|
2010-09-10 03:05:34 +08:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-09-15 06:01:32 +08:00
|
|
|
// At this point we know that 'N' is not a sink and it has at least one
|
|
|
|
// successor. Use a DFS worklist to find a non-sink end-of-path node.
|
|
|
|
typedef FRIEC_WLItem WLItem;
|
2011-07-23 18:55:15 +08:00
|
|
|
typedef SmallVector<WLItem, 10> DFSWorkList;
|
2009-09-15 06:01:32 +08:00
|
|
|
llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
|
|
|
|
|
|
|
|
DFSWorkList WL;
|
2010-12-03 14:52:30 +08:00
|
|
|
WL.push_back(errorNode);
|
|
|
|
Visited[errorNode] = 1;
|
2009-09-15 06:01:32 +08:00
|
|
|
|
|
|
|
while (!WL.empty()) {
|
|
|
|
WLItem &WI = WL.back();
|
|
|
|
assert(!WI.N->succ_empty());
|
|
|
|
|
|
|
|
for (; WI.I != WI.E; ++WI.I) {
|
|
|
|
const ExplodedNode *Succ = *WI.I;
|
|
|
|
// End-of-path node?
|
|
|
|
if (Succ->succ_empty()) {
|
2010-09-10 03:05:34 +08:00
|
|
|
// If we found an end-of-path node that is not a sink.
|
|
|
|
if (!Succ->isSink()) {
|
2012-04-02 03:30:51 +08:00
|
|
|
bugReports.push_back(I);
|
2010-12-03 14:52:30 +08:00
|
|
|
if (!exampleReport)
|
2012-04-02 03:30:51 +08:00
|
|
|
exampleReport = I;
|
2010-09-10 03:05:34 +08:00
|
|
|
WL.clear();
|
|
|
|
break;
|
|
|
|
}
|
2009-09-15 06:01:32 +08:00
|
|
|
// Found a sink? Continue on to the next successor.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Mark the successor as visited. If it hasn't been explored,
|
|
|
|
// enqueue it to the DFS worklist.
|
|
|
|
unsigned &mark = Visited[Succ];
|
|
|
|
if (!mark) {
|
|
|
|
mark = 1;
|
|
|
|
WL.push_back(Succ);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-09-10 03:05:34 +08:00
|
|
|
|
|
|
|
// The worklist may have been cleared at this point. First
|
|
|
|
// check if it is empty before checking the last item.
|
|
|
|
if (!WL.empty() && &WL.back() == &WI)
|
2009-09-15 06:01:32 +08:00
|
|
|
WL.pop_back();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-10 03:05:34 +08:00
|
|
|
// ExampleReport will be NULL if all the nodes in the equivalence class
|
|
|
|
// were post-dominated by sinks.
|
2010-12-03 14:52:30 +08:00
|
|
|
return exampleReport;
|
2010-09-10 03:05:34 +08:00
|
|
|
}
|
2009-09-19 06:37:37 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
void BugReporter::FlushReport(BugReportEquivClass& EQ) {
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVector<BugReport*, 10> bugReports;
|
2010-12-03 14:52:30 +08:00
|
|
|
BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports);
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
if (exampleReport) {
|
|
|
|
const PathDiagnosticConsumers &C = getPathDiagnosticConsumers();
|
|
|
|
for (PathDiagnosticConsumers::const_iterator I=C.begin(),
|
|
|
|
E=C.end(); I != E; ++I) {
|
|
|
|
FlushReport(exampleReport, **I, bugReports);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BugReporter::FlushReport(BugReport *exampleReport,
|
|
|
|
PathDiagnosticConsumer &PD,
|
|
|
|
ArrayRef<BugReport*> bugReports) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2009-02-05 07:49:09 +08:00
|
|
|
// FIXME: Make sure we use the 'R' for the path that was actually used.
|
2009-09-09 23:08:12 +08:00
|
|
|
// Probably doesn't make a difference in practice.
|
2010-12-03 14:52:30 +08:00
|
|
|
BugType& BT = exampleReport->getBugType();
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2012-02-05 10:12:40 +08:00
|
|
|
OwningPtr<PathDiagnostic>
|
2012-04-05 02:11:35 +08:00
|
|
|
D(new PathDiagnostic(exampleReport->getDeclWithIssue(),
|
|
|
|
exampleReport->getBugType().getName(),
|
2012-08-31 08:36:26 +08:00
|
|
|
exampleReport->getDescription(),
|
|
|
|
exampleReport->getShortDescription(/*Fallback=*/false),
|
2013-01-08 08:25:29 +08:00
|
|
|
BT.getCategory(),
|
|
|
|
exampleReport->getUniqueingLocation(),
|
|
|
|
exampleReport->getUniqueingDecl()));
|
2009-04-30 05:58:13 +08:00
|
|
|
|
2013-03-16 05:41:53 +08:00
|
|
|
MaxBugClassSize = std::max(bugReports.size(),
|
|
|
|
static_cast<size_t>(MaxBugClassSize));
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
// Generate the full path diagnostic, using the generation scheme
|
2012-09-22 09:24:56 +08:00
|
|
|
// specified by the PathDiagnosticConsumer. Note that we have to generate
|
|
|
|
// path diagnostics even for consumers which do not support paths, because
|
|
|
|
// the BugReporterVisitors may mark this bug as a false positive.
|
|
|
|
if (!bugReports.empty())
|
|
|
|
if (!generatePathDiagnostic(*D.get(), PD, bugReports))
|
|
|
|
return;
|
2009-01-24 08:55:43 +08:00
|
|
|
|
2013-03-16 05:41:53 +08:00
|
|
|
MaxValidBugClassSize = std::max(bugReports.size(),
|
|
|
|
static_cast<size_t>(MaxValidBugClassSize));
|
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
// If the path is empty, generate a single step path with the location
|
|
|
|
// of the issue.
|
2012-02-08 12:32:34 +08:00
|
|
|
if (D->path.empty()) {
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
PathDiagnosticLocation L = exampleReport->getLocation(getSourceManager());
|
|
|
|
PathDiagnosticPiece *piece =
|
|
|
|
new PathDiagnosticEventPiece(L, exampleReport->getDescription());
|
|
|
|
BugReport::ranges_iterator Beg, End;
|
|
|
|
llvm::tie(Beg, End) = exampleReport->getRanges();
|
2012-04-05 02:11:35 +08:00
|
|
|
for ( ; Beg != End; ++Beg)
|
|
|
|
piece->addRange(*Beg);
|
2012-08-31 08:36:26 +08:00
|
|
|
D->setEndOfPath(piece);
|
2009-01-24 08:55:43 +08:00
|
|
|
}
|
2009-09-09 23:08:12 +08:00
|
|
|
|
Allow multiple PathDiagnosticConsumers to be used with a BugReporter at the same time.
This fixes several issues:
- removes egregious hack where PlistDiagnosticConsumer would forward to HTMLDiagnosticConsumer,
but diagnostics wouldn't be generated consistently in the same way if PlistDiagnosticConsumer
was used by itself.
- emitting diagnostics to the terminal (using clang's diagnostic machinery) is no longer a special
case, just another PathDiagnosticConsumer. This also magically resolved some duplicate warnings,
as we now use PathDiagnosticConsumer's diagnostic pruning, which has scope for the entire translation
unit, not just the scope of a BugReporter (which is limited to a particular ExprEngine).
As an interesting side-effect, diagnostics emitted to the terminal also have their trailing "." stripped,
just like with diagnostics emitted to plists and HTML. This required some tests to be updated, but now
the tests have higher fidelity with what users will see.
There are some inefficiencies in this patch. We currently generate the report graph (from the ExplodedGraph)
once per PathDiagnosticConsumer, which is a bit wasteful, but that could be pulled up higher in the
logic stack. There is some intended duplication, however, as we now generate different PathDiagnostics (for the same issue)
for different PathDiagnosticConsumers. This is necessary to produce the diagnostics that a particular
consumer expects.
llvm-svn: 162028
2012-08-17 01:45:23 +08:00
|
|
|
// Get the meta data.
|
|
|
|
const BugReport::ExtraTextList &Meta = exampleReport->getExtraText();
|
|
|
|
for (BugReport::ExtraTextList::const_iterator i = Meta.begin(),
|
|
|
|
e = Meta.end(); i != e; ++i) {
|
|
|
|
D->addMeta(*i);
|
|
|
|
}
|
|
|
|
|
|
|
|
PD.HandlePathDiagnostic(D.take());
|
2008-04-03 12:42:52 +08:00
|
|
|
}
|
2008-07-15 01:40:50 +08:00
|
|
|
|
2012-04-05 02:11:35 +08:00
|
|
|
void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
|
|
|
|
StringRef name,
|
2011-07-23 18:55:15 +08:00
|
|
|
StringRef category,
|
2011-09-21 05:38:35 +08:00
|
|
|
StringRef str, PathDiagnosticLocation Loc,
|
2008-09-20 12:23:38 +08:00
|
|
|
SourceRange* RBeg, unsigned NumRanges) {
|
2009-09-09 23:08:12 +08:00
|
|
|
|
2011-02-23 08:16:01 +08:00
|
|
|
// 'BT' is owned by BugReporter.
|
|
|
|
BugType *BT = getBugTypeForName(name, category);
|
2011-09-21 05:38:35 +08:00
|
|
|
BugReport *R = new BugReport(*BT, str, Loc);
|
2012-04-05 02:11:35 +08:00
|
|
|
R->setDeclWithIssue(DeclWithIssue);
|
2009-02-05 07:49:09 +08:00
|
|
|
for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg);
|
2012-11-02 09:53:40 +08:00
|
|
|
emitReport(R);
|
2009-01-24 04:28:53 +08:00
|
|
|
}
|
2011-02-23 08:16:01 +08:00
|
|
|
|
2011-07-23 18:55:15 +08:00
|
|
|
BugType *BugReporter::getBugTypeForName(StringRef name,
|
|
|
|
StringRef category) {
|
2012-02-05 10:13:05 +08:00
|
|
|
SmallString<136> fullDesc;
|
2011-02-23 08:16:01 +08:00
|
|
|
llvm::raw_svector_ostream(fullDesc) << name << ":" << category;
|
|
|
|
llvm::StringMapEntry<BugType *> &
|
|
|
|
entry = StrBugTypes.GetOrCreateValue(fullDesc);
|
|
|
|
BugType *BT = entry.getValue();
|
|
|
|
if (!BT) {
|
|
|
|
BT = new BugType(name, category);
|
|
|
|
entry.setValue(BT);
|
|
|
|
}
|
|
|
|
return BT;
|
|
|
|
}
|