blob: 66b1b04c9d0fa639e481ea4d103a3a3fdd4e8318 [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,
Ted Kremenek6837faa2008-04-09 00:20:43 +000086 ExplodedNode<ValueState>* N,
87 BugReporterHelper** BegHelpers,
88 BugReporterHelper** EndHelpers) {
Ted Kremenek70d17222008-04-03 07:33:55 +000089
90 if (PathDiagnosticPiece* Piece = B.getEndPath(Ctx,N))
91 PD.push_back(Piece);
92 else
93 return;
Ted Kremenek61f3e052008-04-03 04:42:52 +000094
95 SourceManager& SMgr = Ctx.getSourceManager();
96
Ted Kremenek61f3e052008-04-03 04:42:52 +000097 llvm::OwningPtr<ExplodedGraph<GRExprEngine> > GTrim(G.Trim(&N, &N+1));
Ted Kremenek94826a72008-04-03 04:59:14 +000098
99 // Find the sink in the trimmed graph.
100 // FIXME: Should we eventually have a sink iterator?
101
102 ExplodedNode<ValueState>* NewN = 0;
103
104 for (ExplodedGraph<GRExprEngine>::node_iterator
105 I = GTrim->nodes_begin(), E = GTrim->nodes_end(); I != E; ++I) {
106
107 if (I->isSink()) {
108 NewN = &*I;
109 break;
110 }
111 }
112
113 assert (NewN);
114 assert (NewN->getLocation() == N->getLocation());
115
116 N = NewN;
Ted Kremenek6837faa2008-04-09 00:20:43 +0000117
118 ExplodedNode<ValueState>* NextNode = N->pred_empty()
119 ? NULL : *(N->pred_begin());
120
121 while (NextNode) {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000122
123 ExplodedNode<ValueState>* LastNode = N;
Ted Kremenek6837faa2008-04-09 00:20:43 +0000124 N = NextNode;
125 NextNode = N->pred_empty() ? NULL : *(N->pred_begin());
Ted Kremenek61f3e052008-04-03 04:42:52 +0000126
127 ProgramPoint P = N->getLocation();
128
129 if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
130
131 CFGBlock* Src = BE->getSrc();
132 CFGBlock* Dst = BE->getDst();
133
134 Stmt* T = Src->getTerminator();
135
136 if (!T)
137 continue;
138
139 FullSourceLoc L(T->getLocStart(), SMgr);
140
141 switch (T->getStmtClass()) {
142 default:
143 break;
144
145 case Stmt::GotoStmtClass:
146 case Stmt::IndirectGotoStmtClass: {
147
148 Stmt* S = GetStmt(LastNode->getLocation());
149
150 if (!S)
151 continue;
152
153 std::ostringstream os;
154
155 os << "Control jumps to line "
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000156 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000157
158 PD.push_front(new PathDiagnosticPiece(L, os.str()));
159 break;
160 }
161
162 case Stmt::SwitchStmtClass: {
163
164 // Figure out what case arm we took.
165
166 Stmt* S = Dst->getLabel();
167
168 if (!S)
169 continue;
170
171 std::ostringstream os;
172
173 switch (S->getStmtClass()) {
174 default:
175 continue;
176
177 case Stmt::DefaultStmtClass: {
178
179 os << "Control jumps to the 'default' case at line "
Ted Kremenek6837faa2008-04-09 00:20:43 +0000180 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000181
182 break;
183 }
184
185 case Stmt::CaseStmtClass: {
186
187 os << "Control jumps to 'case ";
188
189 Expr* CondE = cast<SwitchStmt>(T)->getCond();
190 unsigned bits = Ctx.getTypeSize(CondE->getType());
191
192 llvm::APSInt V1(bits, false);
193
194 CaseStmt* Case = cast<CaseStmt>(S);
195
196 if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
197 assert (false &&
198 "Case condition must evaluate to an integer constant.");
199 continue;
200 }
201
202 os << V1.toString();
203
204 // Get the RHS of the case, if it exists.
205
206 if (Expr* E = Case->getRHS()) {
207
208 llvm::APSInt V2(bits, false);
209
210 if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
211 assert (false &&
212 "Case condition (RHS) must evaluate to an integer constant.");
213 continue;
214 }
215
216 os << " .. " << V2.toString();
217 }
218
219 os << ":' at line "
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000220 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000221
222 break;
223
224 }
225 }
226
227 PD.push_front(new PathDiagnosticPiece(L, os.str()));
228 break;
229 }
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000230
231 case Stmt::ConditionalOperatorClass: {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000232
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000233 std::ostringstream os;
234 os << "'?' condition evaluates to ";
235
236 if (*(Src->succ_begin()+1) == Dst)
237 os << "false.";
238 else
239 os << "true.";
Ted Kremenek61f3e052008-04-03 04:42:52 +0000240
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000241 PD.push_front(new PathDiagnosticPiece(L, os.str()));
242
243 break;
244 }
245
246 case Stmt::DoStmtClass: {
247
248 if (*(Src->succ_begin()) == Dst) {
249
250 std::ostringstream os;
251
252 os << "Loop condition is true. Execution continues on line "
253 << SMgr.getLogicalLineNumber(GetStmt(Dst)->getLocStart()) << '.';
254
255 PD.push_front(new PathDiagnosticPiece(L, os.str()));
256 }
257 else
258 PD.push_front(new PathDiagnosticPiece(L,
259 "Loop condition is false. Exiting loop."));
260
261 break;
262 }
263
Ted Kremenek61f3e052008-04-03 04:42:52 +0000264 case Stmt::WhileStmtClass:
Ted Kremenek706e3cf2008-04-07 23:35:17 +0000265 case Stmt::ForStmtClass: {
266
267 if (*(Src->succ_begin()+1) == Dst) {
268
269 std::ostringstream os;
270
271 os << "Loop condition is false. Execution continues on line "
272 << SMgr.getLogicalLineNumber(GetStmt(Dst)->getLocStart()) << '.';
273
274 PD.push_front(new PathDiagnosticPiece(L, os.str()));
275 }
276 else
277 PD.push_front(new PathDiagnosticPiece(L,
278 "Loop condition is true. Entering loop body."));
279
280 break;
281 }
282
Ted Kremenek61f3e052008-04-03 04:42:52 +0000283 case Stmt::IfStmtClass: {
284
285 if (*(Src->succ_begin()+1) == Dst)
286 PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
287 else
288 PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
289
290 break;
291 }
292 }
Ted Kremenek6837faa2008-04-09 00:20:43 +0000293 }
294 else
295 for (BugReporterHelper** I = BegHelpers; I != EndHelpers; ++I) {
296
297 PathDiagnosticPiece* piece = (*I)->VisitNode(N, NextNode, G, Ctx);
298
299 if (piece)
300 PD.push_front(piece);
301 }
Ted Kremenek61f3e052008-04-03 04:42:52 +0000302 }
303}
304
305bool BugReporter::IsCached(ExplodedNode<ValueState>* N) {
306
307 // HACK: Cache the location of the error. Don't emit the same
308 // warning for the same error type that occurs at the same program
309 // location but along a different path.
310
311 void* p = N->getLocation().getRawData();
312
313 if (CachedErrors.count(p))
314 return true;
315
316 CachedErrors.insert(p);
317
318 return false;
319}
320
321void BugReporter::EmitPathWarning(Diagnostic& Diag,
322 PathDiagnosticClient* PDC,
323 ASTContext& Ctx,
324 const BugDescription& B,
325 ExplodedGraph<GRExprEngine>& G,
Ted Kremenek6837faa2008-04-09 00:20:43 +0000326 ExplodedNode<ValueState>* N,
327 BugReporterHelper** BegHelpers,
328 BugReporterHelper** EndHelpers) {
Ted Kremenek61f3e052008-04-03 04:42:52 +0000329
330 if (!PDC) {
331 EmitWarning(Diag, Ctx, B, N);
332 return;
333 }
334
335 if (IsCached(N))
336 return;
337
Ted Kremenek1c192452008-04-03 05:23:19 +0000338 PathDiagnostic D(B.getName());
Ted Kremenek6837faa2008-04-09 00:20:43 +0000339 GeneratePathDiagnostic(D, Ctx, B, G, N, BegHelpers, EndHelpers);
Ted Kremenek70d17222008-04-03 07:33:55 +0000340
341 if (!D.empty())
342 PDC->HandlePathDiagnostic(D);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000343}
344
345
346void BugReporter::EmitWarning(Diagnostic& Diag, ASTContext& Ctx,
347 const BugDescription& B,
348 ExplodedNode<ValueState>* N) {
349 if (IsCached(N))
350 return;
351
352 std::ostringstream os;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000353 os << "[CHECKER] " << B.getDescription();
Ted Kremenek61f3e052008-04-03 04:42:52 +0000354
355 unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
356 os.str().c_str());
357
358 // FIXME: Add support for multiple ranges.
359
360 Stmt* S = GetStmt(N->getLocation());
361
362 if (!S)
363 return;
364
365 SourceRange R = S->getSourceRange();
366
367 Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
368 ErrorDiag, NULL, 0, &R, 1);
369}