blob: 8eb200017f2b25cec03379caffd6ccfe0003d99d [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*
Ted Kremenekf1ae7052008-04-03 17:57:38 +000046BugDescription::getEndPath(ASTContext& Ctx, ExplodedNode<ValueState> *N) const {
Ted Kremenek61f3e052008-04-03 04:42:52 +000047
48 Stmt* S = GetStmt(N->getLocation());
49
50 if (!S)
51 return NULL;
52
53 FullSourceLoc L(S->getLocStart(), Ctx.getSourceManager());
54 PathDiagnosticPiece* P = new PathDiagnosticPiece(L, getDescription());
55
Ted Kremenekde7161f2008-04-03 18:00:37 +000056 const SourceRange *Beg, *End;
57 getRanges(Beg, End);
58
59 if (Beg == End) {
60 if (Expr* E = dyn_cast<Expr>(S))
61 P->addRange(E->getSourceRange());
62 }
63 else {
64 assert (Beg < End);
65 for (; Beg != End; ++Beg)
66 P->addRange(*Beg);
67 }
Ted Kremenek61f3e052008-04-03 04:42:52 +000068
69 return P;
70}
71
Ted Kremenekf1ae7052008-04-03 17:57:38 +000072void BugDescription::getRanges(const SourceRange*& beg,
73 const SourceRange*& end) const {
74 beg = NULL;
75 end = NULL;
76}
77
Ted Kremenek61f3e052008-04-03 04:42:52 +000078void BugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, ASTContext& Ctx,
79 const BugDescription& B,
80 ExplodedGraph<GRExprEngine>& G,
81 ExplodedNode<ValueState>* N) {
Ted Kremenek70d17222008-04-03 07:33:55 +000082
83 if (PathDiagnosticPiece* Piece = B.getEndPath(Ctx,N))
84 PD.push_back(Piece);
85 else
86 return;
Ted Kremenek61f3e052008-04-03 04:42:52 +000087
88 SourceManager& SMgr = Ctx.getSourceManager();
89
Ted Kremenek61f3e052008-04-03 04:42:52 +000090 llvm::OwningPtr<ExplodedGraph<GRExprEngine> > GTrim(G.Trim(&N, &N+1));
Ted Kremenek94826a72008-04-03 04:59:14 +000091
92 // Find the sink in the trimmed graph.
93 // FIXME: Should we eventually have a sink iterator?
94
95 ExplodedNode<ValueState>* NewN = 0;
96
97 for (ExplodedGraph<GRExprEngine>::node_iterator
98 I = GTrim->nodes_begin(), E = GTrim->nodes_end(); I != E; ++I) {
99
100 if (I->isSink()) {
101 NewN = &*I;
102 break;
103 }
104 }
105
106 assert (NewN);
107 assert (NewN->getLocation() == N->getLocation());
108
109 N = NewN;
Ted Kremenek61f3e052008-04-03 04:42:52 +0000110
111 while (!N->pred_empty()) {
112
113 ExplodedNode<ValueState>* LastNode = N;
114 N = *(N->pred_begin());
115
116 ProgramPoint P = N->getLocation();
117
118 if (const BlockEdge* BE = dyn_cast<BlockEdge>(&P)) {
119
120 CFGBlock* Src = BE->getSrc();
121 CFGBlock* Dst = BE->getDst();
122
123 Stmt* T = Src->getTerminator();
124
125 if (!T)
126 continue;
127
128 FullSourceLoc L(T->getLocStart(), SMgr);
129
130 switch (T->getStmtClass()) {
131 default:
132 break;
133
134 case Stmt::GotoStmtClass:
135 case Stmt::IndirectGotoStmtClass: {
136
137 Stmt* S = GetStmt(LastNode->getLocation());
138
139 if (!S)
140 continue;
141
142 std::ostringstream os;
143
144 os << "Control jumps to line "
145 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
146
147 PD.push_front(new PathDiagnosticPiece(L, os.str()));
148 break;
149 }
150
151 case Stmt::SwitchStmtClass: {
152
153 // Figure out what case arm we took.
154
155 Stmt* S = Dst->getLabel();
156
157 if (!S)
158 continue;
159
160 std::ostringstream os;
161
162 switch (S->getStmtClass()) {
163 default:
164 continue;
165
166 case Stmt::DefaultStmtClass: {
167
168 os << "Control jumps to the 'default' case at line "
169 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
170
171 break;
172 }
173
174 case Stmt::CaseStmtClass: {
175
176 os << "Control jumps to 'case ";
177
178 Expr* CondE = cast<SwitchStmt>(T)->getCond();
179 unsigned bits = Ctx.getTypeSize(CondE->getType());
180
181 llvm::APSInt V1(bits, false);
182
183 CaseStmt* Case = cast<CaseStmt>(S);
184
185 if (!Case->getLHS()->isIntegerConstantExpr(V1, Ctx, 0, true)) {
186 assert (false &&
187 "Case condition must evaluate to an integer constant.");
188 continue;
189 }
190
191 os << V1.toString();
192
193 // Get the RHS of the case, if it exists.
194
195 if (Expr* E = Case->getRHS()) {
196
197 llvm::APSInt V2(bits, false);
198
199 if (!E->isIntegerConstantExpr(V2, Ctx, 0, true)) {
200 assert (false &&
201 "Case condition (RHS) must evaluate to an integer constant.");
202 continue;
203 }
204
205 os << " .. " << V2.toString();
206 }
207
208 os << ":' at line "
209 << SMgr.getLogicalLineNumber(S->getLocStart()) << ".\n";
210
211 break;
212
213 }
214 }
215
216 PD.push_front(new PathDiagnosticPiece(L, os.str()));
217 break;
218 }
219
220
221 case Stmt::DoStmtClass:
222 case Stmt::WhileStmtClass:
223 case Stmt::ForStmtClass:
224 case Stmt::IfStmtClass: {
225
226 if (*(Src->succ_begin()+1) == Dst)
227 PD.push_front(new PathDiagnosticPiece(L, "Taking false branch."));
228 else
229 PD.push_front(new PathDiagnosticPiece(L, "Taking true branch."));
230
231 break;
232 }
233 }
234 }
235 }
236}
237
238bool BugReporter::IsCached(ExplodedNode<ValueState>* N) {
239
240 // HACK: Cache the location of the error. Don't emit the same
241 // warning for the same error type that occurs at the same program
242 // location but along a different path.
243
244 void* p = N->getLocation().getRawData();
245
246 if (CachedErrors.count(p))
247 return true;
248
249 CachedErrors.insert(p);
250
251 return false;
252}
253
254void BugReporter::EmitPathWarning(Diagnostic& Diag,
255 PathDiagnosticClient* PDC,
256 ASTContext& Ctx,
257 const BugDescription& B,
258 ExplodedGraph<GRExprEngine>& G,
259 ExplodedNode<ValueState>* N) {
260
261 if (!PDC) {
262 EmitWarning(Diag, Ctx, B, N);
263 return;
264 }
265
266 if (IsCached(N))
267 return;
268
Ted Kremenek1c192452008-04-03 05:23:19 +0000269 PathDiagnostic D(B.getName());
Ted Kremenek61f3e052008-04-03 04:42:52 +0000270 GeneratePathDiagnostic(D, Ctx, B, G, N);
Ted Kremenek70d17222008-04-03 07:33:55 +0000271
272 if (!D.empty())
273 PDC->HandlePathDiagnostic(D);
Ted Kremenek61f3e052008-04-03 04:42:52 +0000274}
275
276
277void BugReporter::EmitWarning(Diagnostic& Diag, ASTContext& Ctx,
278 const BugDescription& B,
279 ExplodedNode<ValueState>* N) {
280 if (IsCached(N))
281 return;
282
283 std::ostringstream os;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000284 os << "[CHECKER] " << B.getDescription();
Ted Kremenek61f3e052008-04-03 04:42:52 +0000285
286 unsigned ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning,
287 os.str().c_str());
288
289 // FIXME: Add support for multiple ranges.
290
291 Stmt* S = GetStmt(N->getLocation());
292
293 if (!S)
294 return;
295
296 SourceRange R = S->getSourceRange();
297
298 Diag.Report(FullSourceLoc(S->getLocStart(), Ctx.getSourceManager()),
299 ErrorDiag, NULL, 0, &R, 1);
300}