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