blob: 148d2582dc97a468e2eb49e584b73d4aa2883df8 [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"
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000016#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenek61f3e052008-04-03 04:42:52 +000017#include "clang/Basic/SourceManager.h"
18#include "clang/Basic/SourceLocation.h"
19#include "clang/AST/ASTContext.h"
20#include "clang/AST/CFG.h"
21#include "clang/AST/Expr.h"
22#include "clang/Analysis/ProgramPoint.h"
23#include "clang/Analysis/PathDiagnostic.h"
24#include <sstream>
25
26using namespace clang;
27
28BugReporter::~BugReporter() {}
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000029BugType::~BugType() {}
30BugReport::~BugReport() {}
Ted Kremenek5e55cda2008-04-11 18:40:29 +000031RangedBugReport::~RangedBugReport() {}
32
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000033ExplodedGraph<ValueState>& BugReporter::getGraph() { return Eng.getGraph(); }
Ted Kremenek61f3e052008-04-03 04:42:52 +000034
35static inline Stmt* GetStmt(const ProgramPoint& P) {
36 if (const PostStmt* PS = dyn_cast<PostStmt>(&P)) {
37 return PS->getStmt();
38 }
39 else if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
40 return BE->getSrc()->getTerminator();
41 }
42 else if (const BlockEntrance* BE = dyn_cast<BlockEntrance>(&P)) {
43 return BE->getFirstStmt();
44 }
45
46 assert (false && "Unsupported ProgramPoint.");
47 return NULL;
48}
49
Ted Kremenek706e3cf2008-04-07 23:35:17 +000050static inline Stmt* GetStmt(const CFGBlock* B) {
51 assert (!B->empty());
52 return (*B)[0];
53}
54
Ted Kremenek61f3e052008-04-03 04:42:52 +000055
56PathDiagnosticPiece*
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000057BugReport::getEndPath(ASTContext& Ctx, ExplodedNode<ValueState> *N) const {
Ted Kremenek61f3e052008-04-03 04:42:52 +000058
59 Stmt* S = GetStmt(N->getLocation());
60
61 if (!S)
62 return NULL;
63
64 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000065
66 PathDiagnosticPiece* P =
67 new PathDiagnosticPiece(L, getDescription());
Ted Kremenek61f3e052008-04-03 04:42:52 +000068
Ted Kremenekde7161f2008-04-03 18:00:37 +000069 const SourceRange *Beg, *End;
70 getRanges(Beg, End);
71
72 if (Beg == End) {
73 if (Expr* E = dyn_cast<Expr>(S))
74 P->addRange(E->getSourceRange());
75 }
76 else {
77 assert (Beg < End);
78 for (; Beg != End; ++Beg)
79 P->addRange(*Beg);
80 }
Ted Kremenek61f3e052008-04-03 04:42:52 +000081
82 return P;
83}
84
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000085void BugReport::getRanges(const SourceRange*& beg,
86 const SourceRange*& end) const {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000087 beg = NULL;
88 end = NULL;
89}
90
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000091PathDiagnosticPiece* BugReport::VisitNode(ExplodedNode<ValueState>* N,
92 ExplodedNode<ValueState>* PrevN,
93 ExplodedGraph<ValueState>& G,
94 ASTContext& Ctx) {
95 return NULL;
96}
97
98void BugReporter::GeneratePathDiagnostic(PathDiagnostic& PD,
99 BugReport& R,
100 ExplodedNode<ValueState>* N) {
101
102 if (PathDiagnosticPiece* Piece = R.getEndPath(Ctx,N))
Ted Kremenek70d17222008-04-03 07:33:55 +0000103 PD.push_back(Piece);
104 else
105 return;
Ted Kremenek61f3e052008-04-03 04:42:52 +0000106
107 SourceManager& SMgr = Ctx.getSourceManager();
108
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000109 llvm::OwningPtr<ExplodedGraph<ValueState> > GTrim(getGraph().Trim(&N, &N+1));
Ted Kremenek94826a72008-04-03 04:59:14 +0000110
111 // Find the sink in the trimmed graph.
112 // FIXME: Should we eventually have a sink iterator?
113
114 ExplodedNode<ValueState>* NewN = 0;
115
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000116 for (ExplodedGraph<ValueState>::node_iterator
Ted Kremenek94826a72008-04-03 04:59:14 +0000117 I = GTrim->nodes_begin(), E = GTrim->nodes_end(); I != E; ++I) {
118
119 if (I->isSink()) {
120 NewN = &*I;
121 break;
122 }
123 }
124
125 assert (NewN);
126 assert (NewN->getLocation() == N->getLocation());
127
128 N = NewN;
Ted Kremenek6837faa2008-04-09 00:20:43 +0000129
130 ExplodedNode<ValueState>* NextNode = N->pred_empty()
131 ? NULL : *(N->pred_begin());
132
133 while (NextNode) {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000134
135 ExplodedNode<ValueState>* LastNode = N;
Ted Kremenek6837faa2008-04-09 00:20:43 +0000136 N = NextNode;
137 NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
Ted Kremenek61f3e052008-04-03 04:42:52 +0000138
139 ProgramPoint P = N->getLocation();
140
141 if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
142
143 CFGBlock* Src = BE->getSrc();
144 CFGBlock* Dst = BE->getDst();
145
146 Stmt* T = Src->getTerminator();
147
148 if (!T)
149 continue;
150
151 FullSourceLoc L(T->getLocStart(), SMgr);
152
153 switch (T->getStmtClass()) {
154 default:
155 break;
156
157 case Stmt::GotoStmtClass:
158 case Stmt::IndirectGotoStmtClass: {
159
160 Stmt* S = GetStmt(LastNode->getLocation());
161
162 if (!S)
163 continue;
164
165 std::ostringstream os;
166
167 os << "Control jumps to line "
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000168 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000169
170 PD.push_front(new PathDiagnosticPiece(L, os.str()));
171 break;
172 }
173
174 case Stmt::SwitchStmtClass: {
175
176 // Figure out what case arm we took.
177
178 Stmt* S = Dst->getLabel();
179
180 if (!S)
181 continue;
182
183 std::ostringstream os;
184
185 switch (S->getStmtClass()) {
186 default:
187 continue;
188
189 case Stmt::DefaultStmtClass: {
190
191 os << "Control jumps to the 'default' case at line "
Ted Kremenek6837faa2008-04-09 00:20:43 +0000192 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000193
194 break;
195 }
196
197 case Stmt::CaseStmtClass: {
198
199 os << "Control jumps to 'case ";
200
201 Expr* CondE = cast<SwitchStmt>(T)->getCond();
202 unsigned bits = Ctx.getTypeSize(CondE->getType());
203
204 llvm::APSInt V1(bits, false);
205
206 CaseStmt* Case = cast<CaseStmt>(S);
207
208 if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
209 assert (false &&
210 "Case condition must evaluate to an integer constant.");
211 continue;
212 }
213
214 os << V1.toString();
215
216 // Get the RHS of the case, if it exists.
217
218 if (Expr* E = Case->getRHS()) {
219
220 llvm::APSInt V2(bits, false);
221
222 if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
223 assert (false &&
224 "Case condition (RHS) must evaluate to an integer constant.");
225 continue;
226 }
227
228 os << " .. " << V2.toString();
229 }
230
231 os << ":' at line "
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000232 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000233
234 break;
235
236 }
237 }
238
239 PD.push_front(new PathDiagnosticPiece(L, os.str()));
240 break;
241 }
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000242
243 case Stmt::ConditionalOperatorClass: {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000244
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000245 std::ostringstream os;
246 os << "'?' condition evaluates to ";
247
248 if (*(Src->succ_begin()+1) == Dst)
249 os << "false.";
250 else
251 os << "true.";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000252
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000253 PD.push_front(new PathDiagnosticPiece(L, os.str()));
254
255 break;
256 }
257
258 case Stmt::DoStmtClass: {
259
260 if (*(Src->succ_begin()) == Dst) {
261
262 std::ostringstream os;
263
264 os << "Loop condition is true. Execution continues on line "
265 << SMgr.getLogicalLineNumber(GetStmt(Dst)->getLocStart()) << '.';
266
267 PD.push_front(new PathDiagnosticPiece(L, os.str()));
268 }
269 else
270 PD.push_front(new PathDiagnosticPiece(L,
271 "Loop condition is false. Exiting loop."));
272
273 break;
274 }
275
Ted Kremenek61f3e052008-04-03 04:42:52 +0000276 case Stmt::WhileStmtClass:
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000277 case Stmt::ForStmtClass: {
278
279 if (*(Src->succ_begin()+1) == Dst) {
280
281 std::ostringstream os;
282
283 os << "Loop condition is false. Execution continues on line "
284 << SMgr.getLogicalLineNumber(GetStmt(Dst)->getLocStart()) << '.';
285
286 PD.push_front(new PathDiagnosticPiece(L, os.str()));
287 }
288 else
289 PD.push_front(new PathDiagnosticPiece(L,
290 "Loop condition is true. Entering loop body."));
291
292 break;
293 }
294
Ted Kremenek61f3e052008-04-03 04:42:52 +0000295 case Stmt::IfStmtClass: {
296
297 if (*(Src->succ_begin()+1) == Dst)
298 PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
299 else
300 PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
301
302 break;
303 }
304 }
Ted Kremenek6837faa2008-04-09 00:20:43 +0000305 }
306 else
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000307 if (PathDiagnosticPiece* piece = R.VisitNode(N, NextNode, *GTrim, Ctx))
308 PD.push_front(piece);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000309 }
310}
311
312bool BugReporter::IsCached(ExplodedNode<ValueState>* N) {
313
314 // HACK: Cache the location of the error. Don't emit the same
315 // warning for the same error type that occurs at the same program
316 // location but along a different path.
317
318 void* p = N->getLocation().getRawData();
319
320 if (CachedErrors.count(p))
321 return true;
322
323 CachedErrors.insert(p);
324
325 return false;
326}
327
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000328void BugReporter::EmitPathWarning(BugReport& R, ExplodedNode<ValueState>* N) {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000329
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000330 if (!PD) {
331 EmitWarning(R, N);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000332 return;
333 }
334
335 if (IsCached(N))
336 return;
337
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000338 PathDiagnostic D(R.getName());
339 GeneratePathDiagnostic(D, R, N);
Ted Kremenek70d17222008-04-03 07:33:55 +0000340
341 if (!D.empty())
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000342 PD->HandlePathDiagnostic(D);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000343}
344
345
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000346void BugReporter::EmitWarning(BugReport& R, ExplodedNode<ValueState>* N) {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000347 if (IsCached(N))
348 return;
349
350 std::ostringstream os;
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000351 os << "[CHECKER] " << R.getDescription();
Ted Kremenek61f3e052008-04-03 04:42:52 +0000352
353 unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
354 os.str().c_str());
355
356 // FIXME: Add support for multiple ranges.
357
358 Stmt* S = GetStmt(N->getLocation());
359
360 if (!S)
361 return;
362
Ted Kremenek4bb6ac22008-04-10 16:12:38 +0000363 const SourceRange *Beg, *End;
364 R.getRanges(Beg, End);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000365
Ted Kremenek4bb6ac22008-04-10 16:12:38 +0000366 if (Beg == End) {
367 SourceRange Range = S->getSourceRange();
368
369 Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
370 ErrorDiag, NULL, 0, &Range, 1);
371
372 }
373 else
374 Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
375 ErrorDiag, NULL, 0, Beg, End - Beg);
376
Ted Kremenek61f3e052008-04-03 04:42:52 +0000377}