aboutsummaryrefslogtreecommitdiff
path: root/lib/Analysis/BugReporter.cpp
diff options
context:
space:
mode:
authorTed Kremenek <kremenek@apple.com>2008-04-03 04:42:52 +0000
committerTed Kremenek <kremenek@apple.com>2008-04-03 04:42:52 +0000
commit61f3e058056ab519d249aa67e3d52b0ead57c63e (patch)
tree1102f7b290b5f9bdf8b78ca1040ce0c5bd2adfb3 /lib/Analysis/BugReporter.cpp
parentd065d6080f0620bb80b933f3f5d52d37bb2ea770 (diff)
Created new path-sensitive bug-reporting scheme based on the classes
"BugReporter" and "BugDescription". BugDescription is used to describe a bug and provide pieces of the PathDiagnostic, and BugReporter creates the actual PathDiagnostic by crawling through the ExplodedGraph. Migrated checks done by GRSimpleVals to be reported using the new BugReporter mechanism. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@49127 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'lib/Analysis/BugReporter.cpp')
-rw-r--r--lib/Analysis/BugReporter.cpp262
1 files changed, 262 insertions, 0 deletions
diff --git a/lib/Analysis/BugReporter.cpp b/lib/Analysis/BugReporter.cpp
new file mode 100644
index 0000000000..2ae0960206
--- /dev/null
+++ b/lib/Analysis/BugReporter.cpp
@@ -0,0 +1,262 @@
+// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines BugReporter, a utility class for generating
+// PathDiagnostics for analyses based on GRSimpleVals.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/PathSensitive/BugReporter.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CFG.h"
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Analysis/PathDiagnostic.h"
+#include <sstream>
+
+using namespace clang;
+
+BugReporter::~BugReporter() {}
+
+static inline Stmt* GetStmt(const ProgramPoint& P) {
+ if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
+ return PS->getStmt();
+ }
+ else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
+ return BE->getSrc()->getTerminator();
+ }
+ else if (const BlockEntrance* BE = dyn_cast<BlockEntrance>(&P)) {
+ return BE->getFirstStmt();
+ }
+
+ assert (false && "Unsupported ProgramPoint.");
+ return NULL;
+}
+
+
+PathDiagnosticPiece*
+BugDescription::getEndPath(ASTContext& Ctx,
+ ExplodedNode<ValueState> *N) const {
+
+ Stmt* S = GetStmt(N->getLocation());
+
+ if (!S)
+ return NULL;
+
+ FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
+ PathDiagnosticPiece* P = new PathDiagnosticPiece(L, getDescription());
+
+ if (Expr* E = dyn_cast<Expr>(S))
+ P->addRange(E->getSourceRange());
+
+ return P;
+}
+
+void BugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, ASTContext& Ctx,
+ const BugDescription& B,
+ ExplodedGraph<GRExprEngine>& G,
+ ExplodedNode<ValueState>* N) {
+
+ PD.push_back(B.getEndPath(Ctx, N));
+
+ SourceManager& SMgr = Ctx.getSourceManager();
+
+
+ llvm::OwningPtr<ExplodedGraph<GRExprEngine> > GTrim(G.Trim(&N, &N+1));
+
+ while (!N->pred_empty()) {
+
+ ExplodedNode<ValueState>* LastNode = N;
+ N = *(N->pred_begin());
+
+ ProgramPoint P = N->getLocation();
+
+ if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
+
+ CFGBlock* Src = BE->getSrc();
+ CFGBlock* Dst = BE->getDst();
+
+ Stmt* T = Src->getTerminator();
+
+ if (!T)
+ continue;
+
+ FullSourceLoc L(T->getLocStart(), SMgr);
+
+ switch (T->getStmtClass()) {
+ default:
+ break;
+
+ case Stmt::GotoStmtClass:
+ case Stmt::IndirectGotoStmtClass: {
+
+ Stmt* S = GetStmt(LastNode->getLocation());
+
+ if (!S)
+ continue;
+
+ std::ostringstream os;
+
+ os << "Control jumps to line "
+ << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
+
+ PD.push_front(new PathDiagnosticPiece(L, os.str()));
+ break;
+ }
+
+ case Stmt::SwitchStmtClass: {
+
+ // Figure out what case arm we took.
+
+ Stmt* S = Dst->getLabel();
+
+ if (!S)
+ continue;
+
+ std::ostringstream os;
+
+ switch (S->getStmtClass()) {
+ default:
+ continue;
+
+ case Stmt::DefaultStmtClass: {
+
+ os << "Control jumps to the 'default' case at line "
+ << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
+
+ break;
+ }
+
+ case Stmt::CaseStmtClass: {
+
+ os << "Control jumps to 'case ";
+
+ Expr* CondE = cast<SwitchStmt>(T)->getCond();
+ unsigned bits = Ctx.getTypeSize(CondE->getType());
+
+ llvm::APSInt V1(bits, false);
+
+ CaseStmt* Case = cast<CaseStmt>(S);
+
+ if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
+ assert (false &&
+ "Case condition must evaluate to an integer constant.");
+ continue;
+ }
+
+ os << V1.toString();
+
+ // Get the RHS of the case, if it exists.
+
+ if (Expr* E = Case->getRHS()) {
+
+ llvm::APSInt V2(bits, false);
+
+ if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
+ assert (false &&
+ "Case condition (RHS) must evaluate to an integer constant.");
+ continue;
+ }
+
+ os << " .. " << V2.toString();
+ }
+
+ os << ":' at line "
+ << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
+
+ break;
+
+ }
+ }
+
+ PD.push_front(new PathDiagnosticPiece(L, os.str()));
+ break;
+ }
+
+
+ case Stmt::DoStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::IfStmtClass: {
+
+ if (*(Src->succ_begin()+1) == Dst)
+ PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
+ else
+ PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
+
+ break;
+ }
+ }
+ }
+ }
+}
+
+bool BugReporter::IsCached(ExplodedNode<ValueState>* N) {
+
+ // HACK: Cache the location of the error. Don't emit the same
+ // warning for the same error type that occurs at the same program
+ // location but along a different path.
+
+ void* p = N->getLocation().getRawData();
+
+ if (CachedErrors.count(p))
+ return true;
+
+ CachedErrors.insert(p);
+
+ return false;
+}
+
+void BugReporter::EmitPathWarning(Diagnostic& Diag,
+ PathDiagnosticClient* PDC,
+ ASTContext& Ctx,
+ const BugDescription& B,
+ ExplodedGraph<GRExprEngine>& G,
+ ExplodedNode<ValueState>* N) {
+
+ if (!PDC) {
+ EmitWarning(Diag, Ctx, B, N);
+ return;
+ }
+
+ if (IsCached(N))
+ return;
+
+ PathDiagnostic D;
+ GeneratePathDiagnostic(D, Ctx, B, G, N);
+ PDC->HandlePathDiagnostic(D);
+}
+
+
+void BugReporter::EmitWarning(Diagnostic& Diag, ASTContext& Ctx,
+ const BugDescription& B,
+ ExplodedNode<ValueState>* N) {
+ if (IsCached(N))
+ return;
+
+ std::ostringstream os;
+ os << "[CHECKER] " << B.getName();
+
+ unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
+ os.str().c_str());
+
+ // FIXME: Add support for multiple ranges.
+
+ Stmt* S = GetStmt(N->getLocation());
+
+ if (!S)
+ return;
+
+ SourceRange R = S->getSourceRange();
+
+ Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
+ ErrorDiag, NULL, 0, &R, 1);
+}