blob: 14f98fa1f4af90cc3521f0461af712a9eb3b0a96 [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
Ted Kremenek706e3cf2008-04-07 23:35:17 +000044static inline Stmt* GetStmt(const CFGBlock* B) {
45 assert (!B->empty());
46 return (*B)[0];
47}
48
Ted Kremenek61f3e052008-04-03 04:42:52 +000049
50PathDiagnosticPiece*
Ted Kremenekf1ae7052008-04-03 17:57:38 +000051BugDescription::getEndPath(ASTContext& Ctx, ExplodedNode<ValueState> *N) const {
Ted Kremenek61f3e052008-04-03 04:42:52 +000052
53 Stmt* S = GetStmt(N->getLocation());
54
55 if (!S)
56 return NULL;
57
58 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
59 PathDiagnosticPiece* P = new PathDiagnosticPiece(L, getDescription());
60
Ted Kremenekde7161f2008-04-03 18:00:37 +000061 const SourceRange *Beg, *End;
62 getRanges(Beg, End);
63
64 if (Beg == End) {
65 if (Expr* E = dyn_cast<Expr>(S))
66 P->addRange(E->getSourceRange());
67 }
68 else {
69 assert (Beg < End);
70 for (; Beg != End; ++Beg)
71 P->addRange(*Beg);
72 }
Ted Kremenek61f3e052008-04-03 04:42:52 +000073
74 return P;
75}
76
Ted Kremenekf1ae7052008-04-03 17:57:38 +000077void BugDescription::getRanges(const SourceRange*& beg,
78 const SourceRange*& end) const {
79 beg = NULL;
80 end = NULL;
81}
82
Ted Kremenek61f3e052008-04-03 04:42:52 +000083void BugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, ASTContext& Ctx,
84 const BugDescription& B,
85 ExplodedGraph<GRExprEngine>& G,
86 ExplodedNode<ValueState>* N) {
Ted Kremenek70d17222008-04-03 07:33:55 +000087
88 if (PathDiagnosticPiece* Piece = B.getEndPath(Ctx,N))
89 PD.push_back(Piece);
90 else
91 return;
Ted Kremenek61f3e052008-04-03 04:42:52 +000092
93 SourceManager& SMgr = Ctx.getSourceManager();
94
Ted Kremenek61f3e052008-04-03 04:42:52 +000095 llvm::OwningPtr<ExplodedGraph<GRExprEngine> > GTrim(G.Trim(&N, &N+1));
Ted Kremenek94826a72008-04-03 04:59:14 +000096
97 // Find the sink in the trimmed graph.
98 // FIXME: Should we eventually have a sink iterator?
99
100 ExplodedNode<ValueState>* NewN = 0;
101
102 for (ExplodedGraph<GRExprEngine>::node_iterator
103 I = GTrim->nodes_begin(), E = GTrim->nodes_end(); I != E; ++I) {
104
105 if (I->isSink()) {
106 NewN = &*I;
107 break;
108 }
109 }
110
111 assert (NewN);
112 assert (NewN->getLocation() == N->getLocation());
113
114 N = NewN;
Ted Kremenek61f3e052008-04-03 04:42:52 +0000115
116 while (!N->pred_empty()) {
117
118 ExplodedNode<ValueState>* LastNode = N;
119 N = *(N->pred_begin());
120
121 ProgramPoint P = N->getLocation();
122
123 if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
124
125 CFGBlock* Src = BE->getSrc();
126 CFGBlock* Dst = BE->getDst();
127
128 Stmt* T = Src->getTerminator();
129
130 if (!T)
131 continue;
132
133 FullSourceLoc L(T->getLocStart(), SMgr);
134
135 switch (T->getStmtClass()) {
136 default:
137 break;
138
139 case Stmt::GotoStmtClass:
140 case Stmt::IndirectGotoStmtClass: {
141
142 Stmt* S = GetStmt(LastNode->getLocation());
143
144 if (!S)
145 continue;
146
147 std::ostringstream os;
148
149 os << "Control jumps to line "
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000150 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000151
152 PD.push_front(new PathDiagnosticPiece(L, os.str()));
153 break;
154 }
155
156 case Stmt::SwitchStmtClass: {
157
158 // Figure out what case arm we took.
159
160 Stmt* S = Dst->getLabel();
161
162 if (!S)
163 continue;
164
165 std::ostringstream os;
166
167 switch (S->getStmtClass()) {
168 default:
169 continue;
170
171 case Stmt::DefaultStmtClass: {
172
173 os << "Control jumps to the 'default' case at line "
174 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
175
176 break;
177 }
178
179 case Stmt::CaseStmtClass: {
180
181 os << "Control jumps to 'case ";
182
183 Expr* CondE = cast<SwitchStmt>(T)->getCond();
184 unsigned bits = Ctx.getTypeSize(CondE->getType());
185
186 llvm::APSInt V1(bits, false);
187
188 CaseStmt* Case = cast<CaseStmt>(S);
189
190 if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
191 assert (false &&
192 "Case condition must evaluate to an integer constant.");
193 continue;
194 }
195
196 os << V1.toString();
197
198 // Get the RHS of the case, if it exists.
199
200 if (Expr* E = Case->getRHS()) {
201
202 llvm::APSInt V2(bits, false);
203
204 if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
205 assert (false &&
206 "Case condition (RHS) must evaluate to an integer constant.");
207 continue;
208 }
209
210 os << " .. " << V2.toString();
211 }
212
213 os << ":' at line "
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000214 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000215
216 break;
217
218 }
219 }
220
221 PD.push_front(new PathDiagnosticPiece(L, os.str()));
222 break;
223 }
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000224
225 case Stmt::ConditionalOperatorClass: {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000226
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000227 std::ostringstream os;
228 os << "'?' condition evaluates to ";
229
230 if (*(Src->succ_begin()+1) == Dst)
231 os << "false.";
232 else
233 os << "true.";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000234
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000235 PD.push_front(new PathDiagnosticPiece(L, os.str()));
236
237 break;
238 }
239
240 case Stmt::DoStmtClass: {
241
242 if (*(Src->succ_begin()) == Dst) {
243
244 std::ostringstream os;
245
246 os << "Loop condition is true. Execution continues on line "
247 << SMgr.getLogicalLineNumber(GetStmt(Dst)->getLocStart()) << '.';
248
249 PD.push_front(new PathDiagnosticPiece(L, os.str()));
250 }
251 else
252 PD.push_front(new PathDiagnosticPiece(L,
253 "Loop condition is false. Exiting loop."));
254
255 break;
256 }
257
Ted Kremenek61f3e052008-04-03 04:42:52 +0000258 case Stmt::WhileStmtClass:
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000259 case Stmt::ForStmtClass: {
260
261 if (*(Src->succ_begin()+1) == Dst) {
262
263 std::ostringstream os;
264
265 os << "Loop condition is false. Execution continues on line "
266 << SMgr.getLogicalLineNumber(GetStmt(Dst)->getLocStart()) << '.';
267
268 PD.push_front(new PathDiagnosticPiece(L, os.str()));
269 }
270 else
271 PD.push_front(new PathDiagnosticPiece(L,
272 "Loop condition is true. Entering loop body."));
273
274 break;
275 }
276
Ted Kremenek61f3e052008-04-03 04:42:52 +0000277 case Stmt::IfStmtClass: {
278
279 if (*(Src->succ_begin()+1) == Dst)
280 PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
281 else
282 PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
283
284 break;
285 }
286 }
287 }
288 }
289}
290
291bool BugReporter::IsCached(ExplodedNode<ValueState>* N) {
292
293 // HACK: Cache the location of the error. Don't emit the same
294 // warning for the same error type that occurs at the same program
295 // location but along a different path.
296
297 void* p = N->getLocation().getRawData();
298
299 if (CachedErrors.count(p))
300 return true;
301
302 CachedErrors.insert(p);
303
304 return false;
305}
306
307void BugReporter::EmitPathWarning(Diagnostic& Diag,
308 PathDiagnosticClient* PDC,
309 ASTContext& Ctx,
310 const BugDescription& B,
311 ExplodedGraph<GRExprEngine>& G,
312 ExplodedNode<ValueState>* N) {
313
314 if (!PDC) {
315 EmitWarning(Diag, Ctx, B, N);
316 return;
317 }
318
319 if (IsCached(N))
320 return;
321
Ted Kremenek1c192452008-04-03 05:23:19 +0000322 PathDiagnostic D(B.getName());
Ted Kremenek61f3e052008-04-03 04:42:52 +0000323 GeneratePathDiagnostic(D, Ctx, B, G, N);
Ted Kremenek70d17222008-04-03 07:33:55 +0000324
325 if (!D.empty())
326 PDC->HandlePathDiagnostic(D);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000327}
328
329
330void BugReporter::EmitWarning(Diagnostic& Diag, ASTContext& Ctx,
331 const BugDescription& B,
332 ExplodedNode<ValueState>* N) {
333 if (IsCached(N))
334 return;
335
336 std::ostringstream os;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000337 os << "[CHECKER] " << B.getDescription();
Ted Kremenek61f3e052008-04-03 04:42:52 +0000338
339 unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
340 os.str().c_str());
341
342 // FIXME: Add support for multiple ranges.
343
344 Stmt* S = GetStmt(N->getLocation());
345
346 if (!S)
347 return;
348
349 SourceRange R = S->getSourceRange();
350
351 Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
352 ErrorDiag, NULL, 0, &R, 1);
353}