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