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