blob: 2ae096020602959bbe290372ae8827281d743c3f [file] [log] [blame]
Ted Kremenek61f3e052008-04-03 04:42:52 +00001// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines BugReporter, a utility class for generating
11// PathDiagnostics for analyses based on GRSimpleVals.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/Analysis/PathSensitive/BugReporter.h"
16#include "clang/Basic/SourceManager.h"
17#include "clang/Basic/SourceLocation.h"
18#include "clang/AST/ASTContext.h"
19#include "clang/AST/CFG.h"
20#include "clang/AST/Expr.h"
21#include "clang/Analysis/ProgramPoint.h"
22#include "clang/Analysis/PathDiagnostic.h"
23#include <sstream>
24
25using namespace clang;
26
27BugReporter::~BugReporter() {}
28
29static inline Stmt* GetStmt(const ProgramPoint& P) {
30 if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
31 return PS->getStmt();
32 }
33 else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
34 return BE->getSrc()->getTerminator();
35 }
36 else if (const BlockEntrance* BE = dyn_cast<BlockEntrance>(&P)) {
37 return BE->getFirstStmt();
38 }
39
40 assert (false && "Unsupported ProgramPoint.");
41 return NULL;
42}
43
44
45PathDiagnosticPiece*
46BugDescription::getEndPath(ASTContext& Ctx,
47 ExplodedNode<ValueState> *N) const {
48
49 Stmt* S = GetStmt(N->getLocation());
50
51 if (!S)
52 return NULL;
53
54 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
55 PathDiagnosticPiece* P = new PathDiagnosticPiece(L, getDescription());
56
57 if (Expr* E = dyn_cast<Expr>(S))
58 P->addRange(E->getSourceRange());
59
60 return P;
61}
62
63void BugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, ASTContext& Ctx,
64 const BugDescription& B,
65 ExplodedGraph<GRExprEngine>& G,
66 ExplodedNode<ValueState>* N) {
67
68 PD.push_back(B.getEndPath(Ctx, N));
69
70 SourceManager& SMgr = Ctx.getSourceManager();
71
72
73 llvm::OwningPtr<ExplodedGraph<GRExprEngine> > GTrim(G.Trim(&N, &N+1));
74
75 while (!N->pred_empty()) {
76
77 ExplodedNode<ValueState>* LastNode = N;
78 N = *(N->pred_begin());
79
80 ProgramPoint P = N->getLocation();
81
82 if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
83
84 CFGBlock* Src = BE->getSrc();
85 CFGBlock* Dst = BE->getDst();
86
87 Stmt* T = Src->getTerminator();
88
89 if (!T)
90 continue;
91
92 FullSourceLoc L(T->getLocStart(), SMgr);
93
94 switch (T->getStmtClass()) {
95 default:
96 break;
97
98 case Stmt::GotoStmtClass:
99 case Stmt::IndirectGotoStmtClass: {
100
101 Stmt* S = GetStmt(LastNode->getLocation());
102
103 if (!S)
104 continue;
105
106 std::ostringstream os;
107
108 os << "Control jumps to line "
109 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
110
111 PD.push_front(new PathDiagnosticPiece(L, os.str()));
112 break;
113 }
114
115 case Stmt::SwitchStmtClass: {
116
117 // Figure out what case arm we took.
118
119 Stmt* S = Dst->getLabel();
120
121 if (!S)
122 continue;
123
124 std::ostringstream os;
125
126 switch (S->getStmtClass()) {
127 default:
128 continue;
129
130 case Stmt::DefaultStmtClass: {
131
132 os << "Control jumps to the 'default' case at line "
133 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
134
135 break;
136 }
137
138 case Stmt::CaseStmtClass: {
139
140 os << "Control jumps to 'case ";
141
142 Expr* CondE = cast<SwitchStmt>(T)->getCond();
143 unsigned bits = Ctx.getTypeSize(CondE->getType());
144
145 llvm::APSInt V1(bits, false);
146
147 CaseStmt* Case = cast<CaseStmt>(S);
148
149 if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
150 assert (false &&
151 "Case condition must evaluate to an integer constant.");
152 continue;
153 }
154
155 os << V1.toString();
156
157 // Get the RHS of the case, if it exists.
158
159 if (Expr* E = Case->getRHS()) {
160
161 llvm::APSInt V2(bits, false);
162
163 if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
164 assert (false &&
165 "Case condition (RHS) must evaluate to an integer constant.");
166 continue;
167 }
168
169 os << " .. " << V2.toString();
170 }
171
172 os << ":' at line "
173 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
174
175 break;
176
177 }
178 }
179
180 PD.push_front(new PathDiagnosticPiece(L, os.str()));
181 break;
182 }
183
184
185 case Stmt::DoStmtClass:
186 case Stmt::WhileStmtClass:
187 case Stmt::ForStmtClass:
188 case Stmt::IfStmtClass: {
189
190 if (*(Src->succ_begin()+1) == Dst)
191 PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
192 else
193 PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
194
195 break;
196 }
197 }
198 }
199 }
200}
201
202bool BugReporter::IsCached(ExplodedNode<ValueState>* N) {
203
204 // HACK: Cache the location of the error. Don't emit the same
205 // warning for the same error type that occurs at the same program
206 // location but along a different path.
207
208 void* p = N->getLocation().getRawData();
209
210 if (CachedErrors.count(p))
211 return true;
212
213 CachedErrors.insert(p);
214
215 return false;
216}
217
218void BugReporter::EmitPathWarning(Diagnostic& Diag,
219 PathDiagnosticClient* PDC,
220 ASTContext& Ctx,
221 const BugDescription& B,
222 ExplodedGraph<GRExprEngine>& G,
223 ExplodedNode<ValueState>* N) {
224
225 if (!PDC) {
226 EmitWarning(Diag, Ctx, B, N);
227 return;
228 }
229
230 if (IsCached(N))
231 return;
232
233 PathDiagnostic D;
234 GeneratePathDiagnostic(D, Ctx, B, G, N);
235 PDC->HandlePathDiagnostic(D);
236}
237
238
239void BugReporter::EmitWarning(Diagnostic& Diag, ASTContext& Ctx,
240 const BugDescription& B,
241 ExplodedNode<ValueState>* N) {
242 if (IsCached(N))
243 return;
244
245 std::ostringstream os;
246 os << "[CHECKER] " << B.getName();
247
248 unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
249 os.str().c_str());
250
251 // FIXME: Add support for multiple ranges.
252
253 Stmt* S = GetStmt(N->getLocation());
254
255 if (!S)
256 return;
257
258 SourceRange R = S->getSourceRange();
259
260 Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
261 ErrorDiag, NULL, 0, &R, 1);
262}