1 // BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- C++ -*--//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines a set of BugReporter "visitors" which can be used to
11 // enhance the diagnostics reported for a bug.
13 //===----------------------------------------------------------------------===//
15 #include "clang/AST/Expr.h"
16 #include "clang/AST/ExprObjC.h"
17 #include "clang/GR/BugReporter/BugReporter.h"
18 #include "clang/GR/BugReporter/PathDiagnostic.h"
19 #include "clang/GR/PathSensitive/ExplodedGraph.h"
20 #include "clang/GR/PathSensitive/GRState.h"
22 using namespace clang
;
24 //===----------------------------------------------------------------------===//
26 //===----------------------------------------------------------------------===//
28 const Stmt
*clang::bugreporter::GetDerefExpr(const ExplodedNode
*N
) {
29 // Pattern match for a few useful cases (do something smarter later):
31 const Stmt
*S
= N
->getLocationAs
<PostStmt
>()->getStmt();
33 if (const UnaryOperator
*U
= dyn_cast
<UnaryOperator
>(S
)) {
34 if (U
->getOpcode() == UO_Deref
)
35 return U
->getSubExpr()->IgnoreParenCasts();
37 else if (const MemberExpr
*ME
= dyn_cast
<MemberExpr
>(S
)) {
38 return ME
->getBase()->IgnoreParenCasts();
40 else if (const ArraySubscriptExpr
*AE
= dyn_cast
<ArraySubscriptExpr
>(S
)) {
41 // Retrieve the base for arrays since BasicStoreManager doesn't know how
42 // to reason about them.
50 clang::bugreporter::GetDenomExpr(const ExplodedNode
*N
) {
51 const Stmt
*S
= N
->getLocationAs
<PreStmt
>()->getStmt();
52 if (const BinaryOperator
*BE
= dyn_cast
<BinaryOperator
>(S
))
58 clang::bugreporter::GetCalleeExpr(const ExplodedNode
*N
) {
59 // Callee is checked as a PreVisit to the CallExpr.
60 const Stmt
*S
= N
->getLocationAs
<PreStmt
>()->getStmt();
61 if (const CallExpr
*CE
= dyn_cast
<CallExpr
>(S
))
62 return CE
->getCallee();
67 clang::bugreporter::GetRetValExpr(const ExplodedNode
*N
) {
68 const Stmt
*S
= N
->getLocationAs
<PostStmt
>()->getStmt();
69 if (const ReturnStmt
*RS
= dyn_cast
<ReturnStmt
>(S
))
70 return RS
->getRetValue();
74 //===----------------------------------------------------------------------===//
75 // Definitions for bug reporter visitors.
76 //===----------------------------------------------------------------------===//
79 class FindLastStoreBRVisitor
: public BugReporterVisitor
{
83 const ExplodedNode
*StoreSite
;
85 FindLastStoreBRVisitor(SVal v
, const MemRegion
*r
)
86 : R(r
), V(v
), satisfied(false), StoreSite(0) {}
88 virtual void Profile(llvm::FoldingSetNodeID
&ID
) const {
95 PathDiagnosticPiece
* VisitNode(const ExplodedNode
*N
,
96 const ExplodedNode
*PrevN
,
97 BugReporterContext
& BRC
) {
103 const ExplodedNode
*Node
= N
, *Last
= NULL
;
105 for ( ; Node
; Last
= Node
, Node
= Node
->getFirstPred()) {
107 if (const VarRegion
*VR
= dyn_cast
<VarRegion
>(R
)) {
108 if (const PostStmt
*P
= Node
->getLocationAs
<PostStmt
>())
109 if (const DeclStmt
*DS
= P
->getStmtAs
<DeclStmt
>())
110 if (DS
->getSingleDecl() == VR
->getDecl()) {
116 if (Node
->getState()->getSVal(R
) != V
)
120 if (!Node
|| !Last
) {
132 llvm::SmallString
<256> sbuf
;
133 llvm::raw_svector_ostream
os(sbuf
);
135 if (const PostStmt
*PS
= N
->getLocationAs
<PostStmt
>()) {
136 if (const DeclStmt
*DS
= PS
->getStmtAs
<DeclStmt
>()) {
138 if (const VarRegion
*VR
= dyn_cast
<VarRegion
>(R
)) {
139 os
<< "Variable '" << VR
->getDecl() << "' ";
144 if (isa
<loc::ConcreteInt
>(V
)) {
146 if (R
->isBoundable()) {
147 if (const TypedRegion
*TR
= dyn_cast
<TypedRegion
>(R
)) {
148 if (TR
->getValueType()->isObjCObjectPointerType()) {
149 os
<< "initialized to nil";
156 os
<< "initialized to a null pointer value";
158 else if (isa
<nonloc::ConcreteInt
>(V
)) {
159 os
<< "initialized to " << cast
<nonloc::ConcreteInt
>(V
).getValue();
161 else if (V
.isUndef()) {
162 if (isa
<VarRegion
>(R
)) {
163 const VarDecl
*VD
= cast
<VarDecl
>(DS
->getSingleDecl());
165 os
<< "initialized to a garbage value";
167 os
<< "declared without an initial value";
173 if (os
.str().empty()) {
174 if (isa
<loc::ConcreteInt
>(V
)) {
176 if (R
->isBoundable()) {
177 if (const TypedRegion
*TR
= dyn_cast
<TypedRegion
>(R
)) {
178 if (TR
->getValueType()->isObjCObjectPointerType()) {
179 os
<< "nil object reference stored to ";
186 os
<< "Null pointer value stored to ";
188 else if (V
.isUndef()) {
189 os
<< "Uninitialized value stored to ";
191 else if (isa
<nonloc::ConcreteInt
>(V
)) {
192 os
<< "The value " << cast
<nonloc::ConcreteInt
>(V
).getValue()
193 << " is assigned to ";
198 if (const VarRegion
*VR
= dyn_cast
<VarRegion
>(R
)) {
199 os
<< '\'' << VR
->getDecl() << '\'';
205 // FIXME: Refactor this into BugReporterContext.
207 ProgramPoint P
= N
->getLocation();
209 if (BlockEdge
*BE
= dyn_cast
<BlockEdge
>(&P
)) {
210 const CFGBlock
*BSrc
= BE
->getSrc();
211 S
= BSrc
->getTerminatorCondition();
213 else if (PostStmt
*PS
= dyn_cast
<PostStmt
>(&P
)) {
220 // Construct a new PathDiagnosticPiece.
221 PathDiagnosticLocation
L(S
, BRC
.getSourceManager());
222 return new PathDiagnosticEventPiece(L
, os
.str());
227 static void registerFindLastStore(BugReporterContext
& BRC
, const MemRegion
*R
,
229 BRC
.addVisitor(new FindLastStoreBRVisitor(V
, R
));
232 class TrackConstraintBRVisitor
: public BugReporterVisitor
{
233 DefinedSVal Constraint
;
234 const bool Assumption
;
237 TrackConstraintBRVisitor(DefinedSVal constraint
, bool assumption
)
238 : Constraint(constraint
), Assumption(assumption
), isSatisfied(false) {}
240 void Profile(llvm::FoldingSetNodeID
&ID
) const {
243 ID
.AddBoolean(Assumption
);
247 PathDiagnosticPiece
* VisitNode(const ExplodedNode
*N
,
248 const ExplodedNode
*PrevN
,
249 BugReporterContext
& BRC
) {
253 // Check if in the previous state it was feasible for this constraint
255 if (PrevN
->getState()->assume(Constraint
, !Assumption
)) {
259 // As a sanity check, make sure that the negation of the constraint
260 // was infeasible in the current state. If it is feasible, we somehow
261 // missed the transition point.
262 if (N
->getState()->assume(Constraint
, !Assumption
))
265 // We found the transition point for the constraint. We now need to
266 // pretty-print the constraint. (work-in-progress)
268 llvm::raw_string_ostream
os(sbuf
);
270 if (isa
<Loc
>(Constraint
)) {
271 os
<< "Assuming pointer value is ";
272 os
<< (Assumption
? "non-null" : "null");
275 if (os
.str().empty())
278 // FIXME: Refactor this into BugReporterContext.
280 ProgramPoint P
= N
->getLocation();
282 if (BlockEdge
*BE
= dyn_cast
<BlockEdge
>(&P
)) {
283 const CFGBlock
*BSrc
= BE
->getSrc();
284 S
= BSrc
->getTerminatorCondition();
286 else if (PostStmt
*PS
= dyn_cast
<PostStmt
>(&P
)) {
293 // Construct a new PathDiagnosticPiece.
294 PathDiagnosticLocation
L(S
, BRC
.getSourceManager());
295 return new PathDiagnosticEventPiece(L
, os
.str());
301 } // end anonymous namespace
303 static void registerTrackConstraint(BugReporterContext
& BRC
,
304 DefinedSVal Constraint
,
306 BRC
.addVisitor(new TrackConstraintBRVisitor(Constraint
, Assumption
));
309 void clang::bugreporter::registerTrackNullOrUndefValue(BugReporterContext
& BRC
,
311 const ExplodedNode
* N
) {
313 const Stmt
*S
= static_cast<const Stmt
*>(data
);
318 GRStateManager
&StateMgr
= BRC
.getStateManager();
319 const GRState
*state
= N
->getState();
321 // Walk through lvalue-to-rvalue conversions.
322 if (const DeclRefExpr
*DR
= dyn_cast
<DeclRefExpr
>(S
)) {
323 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl())) {
325 StateMgr
.getRegionManager().getVarRegion(VD
, N
->getLocationContext());
328 SVal V
= state
->getSVal(loc::MemRegionVal(R
));
330 if (isa
<loc::ConcreteInt
>(V
) || isa
<nonloc::ConcreteInt
>(V
)
332 ::registerFindLastStore(BRC
, R
, V
);
337 SVal V
= state
->getSValAsScalarOrLoc(S
);
339 // Uncomment this to find cases where we aren't properly getting the
340 // base value that was dereferenced.
341 // assert(!V.isUnknownOrUndef());
343 // Is it a symbolic value?
344 if (loc::MemRegionVal
*L
= dyn_cast
<loc::MemRegionVal
>(&V
)) {
345 const SubRegion
*R
= cast
<SubRegion
>(L
->getRegion());
346 while (R
&& !isa
<SymbolicRegion
>(R
)) {
347 R
= dyn_cast
<SubRegion
>(R
->getSuperRegion());
351 assert(isa
<SymbolicRegion
>(R
));
352 registerTrackConstraint(BRC
, loc::MemRegionVal(R
), false);
357 void clang::bugreporter::registerFindLastStore(BugReporterContext
& BRC
,
359 const ExplodedNode
* N
) {
361 const MemRegion
*R
= static_cast<const MemRegion
*>(data
);
366 const GRState
*state
= N
->getState();
367 SVal V
= state
->getSVal(R
);
372 BRC
.addVisitor(new FindLastStoreBRVisitor(V
, R
));
377 class NilReceiverVisitor
: public BugReporterVisitor
{
379 NilReceiverVisitor() {}
381 void Profile(llvm::FoldingSetNodeID
&ID
) const {
386 PathDiagnosticPiece
* VisitNode(const ExplodedNode
*N
,
387 const ExplodedNode
*PrevN
,
388 BugReporterContext
& BRC
) {
390 const PostStmt
*P
= N
->getLocationAs
<PostStmt
>();
393 const ObjCMessageExpr
*ME
= P
->getStmtAs
<ObjCMessageExpr
>();
396 const Expr
*Receiver
= ME
->getInstanceReceiver();
399 const GRState
*state
= N
->getState();
400 const SVal
&V
= state
->getSVal(Receiver
);
401 const DefinedOrUnknownSVal
*DV
= dyn_cast
<DefinedOrUnknownSVal
>(&V
);
404 state
= state
->assume(*DV
, true);
408 // The receiver was nil, and hence the method was skipped.
409 // Register a BugReporterVisitor to issue a message telling us how
410 // the receiver was null.
411 bugreporter::registerTrackNullOrUndefValue(BRC
, Receiver
, N
);
412 // Issue a message saying that the method was skipped.
413 PathDiagnosticLocation
L(Receiver
, BRC
.getSourceManager());
414 return new PathDiagnosticEventPiece(L
, "No method actually called "
415 "because the receiver is nil");
418 } // end anonymous namespace
420 void clang::bugreporter::registerNilReceiverVisitor(BugReporterContext
&BRC
) {
421 BRC
.addVisitor(new NilReceiverVisitor());
424 // Registers every VarDecl inside a Stmt with a last store vistor.
425 void clang::bugreporter::registerVarDeclsLastStore(BugReporterContext
&BRC
,
427 const ExplodedNode
*N
) {
428 const Stmt
*S
= static_cast<const Stmt
*>(stmt
);
430 std::deque
<const Stmt
*> WorkList
;
432 WorkList
.push_back(S
);
434 while (!WorkList
.empty()) {
435 const Stmt
*Head
= WorkList
.front();
436 WorkList
.pop_front();
438 GRStateManager
&StateMgr
= BRC
.getStateManager();
439 const GRState
*state
= N
->getState();
441 if (const DeclRefExpr
*DR
= dyn_cast
<DeclRefExpr
>(Head
)) {
442 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DR
->getDecl())) {
444 StateMgr
.getRegionManager().getVarRegion(VD
, N
->getLocationContext());
447 SVal V
= state
->getSVal(S
);
449 if (isa
<loc::ConcreteInt
>(V
) || isa
<nonloc::ConcreteInt
>(V
)) {
450 ::registerFindLastStore(BRC
, R
, V
);
455 for (Stmt::const_child_iterator I
= Head
->child_begin();
456 I
!= Head
->child_end(); ++I
)
457 WorkList
.push_back(*I
);