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