blob: cddc86ee9dca1b2eb8fa36cbefd7f6d61e43a376 [file] [log] [blame]
Ted Kremenek53500662009-07-22 17:55:28 +00001// BugReporterVisitors.cpp - Helpers for reporting 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 a set of BugReporter "visitors" which can be used to
11// enhance the diagnostics reported for a bug.
12//
13//===----------------------------------------------------------------------===//
14
15#include "clang/AST/Expr.h"
16#include "clang/AST/ExprObjC.h"
Ted Kremenek6b676302010-01-25 17:10:22 +000017#include "clang/Checker/BugReporter/BugReporter.h"
18#include "clang/Checker/BugReporter/PathDiagnostic.h"
Benjamin Kramer5e2d2c22010-03-27 21:19:47 +000019#include "clang/Checker/PathSensitive/ExplodedGraph.h"
Ted Kremenek1309f9a2010-01-25 04:41:41 +000020#include "clang/Checker/PathSensitive/GRState.h"
Ted Kremenek53500662009-07-22 17:55:28 +000021
22using namespace clang;
23
24//===----------------------------------------------------------------------===//
25// Utility functions.
26//===----------------------------------------------------------------------===//
27
Zhongxing Xuc5619d92009-08-06 01:32:16 +000028const Stmt *clang::bugreporter::GetDerefExpr(const ExplodedNode *N) {
Ted Kremenek53500662009-07-22 17:55:28 +000029 // Pattern match for a few useful cases (do something smarter later):
30 // a[0], p->f, *p
31 const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
Mike Stump1eb44332009-09-09 15:08:12 +000032
Ted Kremenek53500662009-07-22 17:55:28 +000033 if (const UnaryOperator *U = dyn_cast<UnaryOperator>(S)) {
John McCall2de56d12010-08-25 11:45:40 +000034 if (U->getOpcode() == UO_Deref)
Ted Kremenek53500662009-07-22 17:55:28 +000035 return U->getSubExpr()->IgnoreParenCasts();
36 }
37 else if (const MemberExpr *ME = dyn_cast<MemberExpr>(S)) {
38 return ME->getBase()->IgnoreParenCasts();
39 }
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.
43 return AE->getBase();
44 }
Mike Stump1eb44332009-09-09 15:08:12 +000045
46 return NULL;
Ted Kremenek53500662009-07-22 17:55:28 +000047}
48
49const Stmt*
Zhongxing Xuc5619d92009-08-06 01:32:16 +000050clang::bugreporter::GetDenomExpr(const ExplodedNode *N) {
Zhongxing Xu6403b572009-09-02 13:26:26 +000051 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
Ted Kremenek53500662009-07-22 17:55:28 +000052 if (const BinaryOperator *BE = dyn_cast<BinaryOperator>(S))
53 return BE->getRHS();
54 return NULL;
55}
56
57const Stmt*
Zhongxing Xuc5619d92009-08-06 01:32:16 +000058clang::bugreporter::GetCalleeExpr(const ExplodedNode *N) {
Zhongxing Xud99f3612009-09-02 08:10:35 +000059 // Callee is checked as a PreVisit to the CallExpr.
60 const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
Ted Kremenek53500662009-07-22 17:55:28 +000061 if (const CallExpr *CE = dyn_cast<CallExpr>(S))
62 return CE->getCallee();
63 return NULL;
64}
65
66const Stmt*
Zhongxing Xuc5619d92009-08-06 01:32:16 +000067clang::bugreporter::GetRetValExpr(const ExplodedNode *N) {
Ted Kremenek53500662009-07-22 17:55:28 +000068 const Stmt *S = N->getLocationAs<PostStmt>()->getStmt();
69 if (const ReturnStmt *RS = dyn_cast<ReturnStmt>(S))
70 return RS->getRetValue();
71 return NULL;
72}
73
74//===----------------------------------------------------------------------===//
75// Definitions for bug reporter visitors.
76//===----------------------------------------------------------------------===//
77
78namespace {
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +000079class FindLastStoreBRVisitor : public BugReporterVisitor {
Ted Kremenek53500662009-07-22 17:55:28 +000080 const MemRegion *R;
81 SVal V;
82 bool satisfied;
Zhongxing Xuc5619d92009-08-06 01:32:16 +000083 const ExplodedNode *StoreSite;
Ted Kremenek53500662009-07-22 17:55:28 +000084public:
85 FindLastStoreBRVisitor(SVal v, const MemRegion *r)
86 : R(r), V(v), satisfied(false), StoreSite(0) {}
Mike Stump1eb44332009-09-09 15:08:12 +000087
Ted Kremenek1b431022010-03-20 18:01:57 +000088 virtual void Profile(llvm::FoldingSetNodeID &ID) const {
89 static int tag = 0;
90 ID.AddPointer(&tag);
91 ID.AddPointer(R);
92 ID.Add(V);
93 }
94
Zhongxing Xuc5619d92009-08-06 01:32:16 +000095 PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
96 const ExplodedNode *PrevN,
Ted Kremenek53500662009-07-22 17:55:28 +000097 BugReporterContext& BRC) {
Mike Stump1eb44332009-09-09 15:08:12 +000098
Ted Kremenek53500662009-07-22 17:55:28 +000099 if (satisfied)
100 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000101
102 if (!StoreSite) {
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000103 const ExplodedNode *Node = N, *Last = NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000104
Ted Kremenek53500662009-07-22 17:55:28 +0000105 for ( ; Node ; Last = Node, Node = Node->getFirstPred()) {
Daniel Dunbar0f2c9072010-03-20 04:28:39 +0000106
Ted Kremenek53500662009-07-22 17:55:28 +0000107 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()) {
111 Last = Node;
112 break;
113 }
114 }
Mike Stump1eb44332009-09-09 15:08:12 +0000115
Ted Kremenek13976632010-02-08 16:18:51 +0000116 if (Node->getState()->getSVal(R) != V)
Ted Kremenek53500662009-07-22 17:55:28 +0000117 break;
118 }
Mike Stump1eb44332009-09-09 15:08:12 +0000119
Ted Kremenek53500662009-07-22 17:55:28 +0000120 if (!Node || !Last) {
121 satisfied = true;
122 return NULL;
123 }
Mike Stump1eb44332009-09-09 15:08:12 +0000124
Ted Kremenek53500662009-07-22 17:55:28 +0000125 StoreSite = Last;
126 }
Mike Stump1eb44332009-09-09 15:08:12 +0000127
Ted Kremenek53500662009-07-22 17:55:28 +0000128 if (StoreSite != N)
129 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000130
Ted Kremenek53500662009-07-22 17:55:28 +0000131 satisfied = true;
Ted Kremenek1b431022010-03-20 18:01:57 +0000132 llvm::SmallString<256> sbuf;
133 llvm::raw_svector_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000134
Ted Kremenek53500662009-07-22 17:55:28 +0000135 if (const PostStmt *PS = N->getLocationAs<PostStmt>()) {
136 if (const DeclStmt *DS = PS->getStmtAs<DeclStmt>()) {
Mike Stump1eb44332009-09-09 15:08:12 +0000137
Ted Kremenek53500662009-07-22 17:55:28 +0000138 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
Benjamin Kramer900fc632010-04-17 09:33:03 +0000139 os << "Variable '" << VR->getDecl() << "' ";
Ted Kremenek53500662009-07-22 17:55:28 +0000140 }
141 else
142 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000143
Ted Kremenek53500662009-07-22 17:55:28 +0000144 if (isa<loc::ConcreteInt>(V)) {
145 bool b = false;
Ted Kremenek53500662009-07-22 17:55:28 +0000146 if (R->isBoundable()) {
147 if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
Zhongxing Xu018220c2010-08-11 06:10:55 +0000148 if (TR->getValueType()->isObjCObjectPointerType()) {
Ted Kremenek53500662009-07-22 17:55:28 +0000149 os << "initialized to nil";
150 b = true;
151 }
152 }
153 }
Mike Stump1eb44332009-09-09 15:08:12 +0000154
Ted Kremenek53500662009-07-22 17:55:28 +0000155 if (!b)
156 os << "initialized to a null pointer value";
157 }
158 else if (isa<nonloc::ConcreteInt>(V)) {
159 os << "initialized to " << cast<nonloc::ConcreteInt>(V).getValue();
160 }
161 else if (V.isUndef()) {
162 if (isa<VarRegion>(R)) {
163 const VarDecl *VD = cast<VarDecl>(DS->getSingleDecl());
164 if (VD->getInit())
165 os << "initialized to a garbage value";
166 else
Mike Stump1eb44332009-09-09 15:08:12 +0000167 os << "declared without an initial value";
168 }
Ted Kremenek53500662009-07-22 17:55:28 +0000169 }
170 }
171 }
Mike Stump1eb44332009-09-09 15:08:12 +0000172
173 if (os.str().empty()) {
Ted Kremenek53500662009-07-22 17:55:28 +0000174 if (isa<loc::ConcreteInt>(V)) {
175 bool b = false;
Ted Kremenek53500662009-07-22 17:55:28 +0000176 if (R->isBoundable()) {
177 if (const TypedRegion *TR = dyn_cast<TypedRegion>(R)) {
Zhongxing Xu018220c2010-08-11 06:10:55 +0000178 if (TR->getValueType()->isObjCObjectPointerType()) {
Ted Kremenek53500662009-07-22 17:55:28 +0000179 os << "nil object reference stored to ";
180 b = true;
181 }
182 }
183 }
Mike Stump1eb44332009-09-09 15:08:12 +0000184
Ted Kremenek53500662009-07-22 17:55:28 +0000185 if (!b)
186 os << "Null pointer value stored to ";
187 }
188 else if (V.isUndef()) {
189 os << "Uninitialized value stored to ";
190 }
Ted Kremenek592362b2009-08-18 01:05:30 +0000191 else if (isa<nonloc::ConcreteInt>(V)) {
192 os << "The value " << cast<nonloc::ConcreteInt>(V).getValue()
193 << " is assigned to ";
194 }
Ted Kremenek53500662009-07-22 17:55:28 +0000195 else
196 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000197
Ted Kremenek53500662009-07-22 17:55:28 +0000198 if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
Benjamin Kramer900fc632010-04-17 09:33:03 +0000199 os << '\'' << VR->getDecl() << '\'';
Ted Kremenek53500662009-07-22 17:55:28 +0000200 }
201 else
202 return NULL;
203 }
Mike Stump1eb44332009-09-09 15:08:12 +0000204
Ted Kremenek53500662009-07-22 17:55:28 +0000205 // FIXME: Refactor this into BugReporterContext.
Mike Stump1eb44332009-09-09 15:08:12 +0000206 const Stmt *S = 0;
Ted Kremenek53500662009-07-22 17:55:28 +0000207 ProgramPoint P = N->getLocation();
Mike Stump1eb44332009-09-09 15:08:12 +0000208
Ted Kremenek53500662009-07-22 17:55:28 +0000209 if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
Zhongxing Xu03509ae2010-07-20 06:22:24 +0000210 const CFGBlock *BSrc = BE->getSrc();
Ted Kremenek53500662009-07-22 17:55:28 +0000211 S = BSrc->getTerminatorCondition();
212 }
213 else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
214 S = PS->getStmt();
215 }
Mike Stump1eb44332009-09-09 15:08:12 +0000216
Ted Kremenek53500662009-07-22 17:55:28 +0000217 if (!S)
218 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000219
Ted Kremenek53500662009-07-22 17:55:28 +0000220 // Construct a new PathDiagnosticPiece.
221 PathDiagnosticLocation L(S, BRC.getSourceManager());
222 return new PathDiagnosticEventPiece(L, os.str());
223 }
224};
225
226
227static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R,
228 SVal V) {
229 BRC.addVisitor(new FindLastStoreBRVisitor(V, R));
230}
231
Kovarththanan Rajaratnamba5fb5a2009-11-28 06:07:30 +0000232class TrackConstraintBRVisitor : public BugReporterVisitor {
Ted Kremenek5b9bd212009-09-11 22:07:28 +0000233 DefinedSVal Constraint;
Ted Kremenek53500662009-07-22 17:55:28 +0000234 const bool Assumption;
235 bool isSatisfied;
236public:
Ted Kremenek5b9bd212009-09-11 22:07:28 +0000237 TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption)
Ted Kremenek53500662009-07-22 17:55:28 +0000238 : Constraint(constraint), Assumption(assumption), isSatisfied(false) {}
Mike Stump1eb44332009-09-09 15:08:12 +0000239
Ted Kremenek1b431022010-03-20 18:01:57 +0000240 void Profile(llvm::FoldingSetNodeID &ID) const {
241 static int tag = 0;
242 ID.AddPointer(&tag);
243 ID.AddBoolean(Assumption);
244 ID.Add(Constraint);
245 }
246
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000247 PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
248 const ExplodedNode *PrevN,
Ted Kremenek53500662009-07-22 17:55:28 +0000249 BugReporterContext& BRC) {
250 if (isSatisfied)
251 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000252
Ted Kremenek53500662009-07-22 17:55:28 +0000253 // Check if in the previous state it was feasible for this constraint
254 // to *not* be true.
Ted Kremenek5b9bd212009-09-11 22:07:28 +0000255 if (PrevN->getState()->Assume(Constraint, !Assumption)) {
Daniel Dunbar0f2c9072010-03-20 04:28:39 +0000256
Ted Kremenek53500662009-07-22 17:55:28 +0000257 isSatisfied = true;
Mike Stump1eb44332009-09-09 15:08:12 +0000258
Ted Kremenek53500662009-07-22 17:55:28 +0000259 // 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.
Ted Kremenek5b9bd212009-09-11 22:07:28 +0000262 if (N->getState()->Assume(Constraint, !Assumption))
Ted Kremenek53500662009-07-22 17:55:28 +0000263 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000264
Ted Kremenek53500662009-07-22 17:55:28 +0000265 // We found the transition point for the constraint. We now need to
Mike Stump1eb44332009-09-09 15:08:12 +0000266 // pretty-print the constraint. (work-in-progress)
Daniel Dunbar0f2c9072010-03-20 04:28:39 +0000267 std::string sbuf;
268 llvm::raw_string_ostream os(sbuf);
Mike Stump1eb44332009-09-09 15:08:12 +0000269
Ted Kremenek53500662009-07-22 17:55:28 +0000270 if (isa<Loc>(Constraint)) {
271 os << "Assuming pointer value is ";
272 os << (Assumption ? "non-null" : "null");
273 }
Mike Stump1eb44332009-09-09 15:08:12 +0000274
Ted Kremenek53500662009-07-22 17:55:28 +0000275 if (os.str().empty())
276 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000277
Ted Kremenek53500662009-07-22 17:55:28 +0000278 // FIXME: Refactor this into BugReporterContext.
Mike Stump1eb44332009-09-09 15:08:12 +0000279 const Stmt *S = 0;
Ted Kremenek53500662009-07-22 17:55:28 +0000280 ProgramPoint P = N->getLocation();
Mike Stump1eb44332009-09-09 15:08:12 +0000281
Ted Kremenek53500662009-07-22 17:55:28 +0000282 if (BlockEdge *BE = dyn_cast<BlockEdge>(&P)) {
Zhongxing Xu03509ae2010-07-20 06:22:24 +0000283 const CFGBlock *BSrc = BE->getSrc();
Ted Kremenek53500662009-07-22 17:55:28 +0000284 S = BSrc->getTerminatorCondition();
285 }
286 else if (PostStmt *PS = dyn_cast<PostStmt>(&P)) {
287 S = PS->getStmt();
288 }
Mike Stump1eb44332009-09-09 15:08:12 +0000289
Ted Kremenek53500662009-07-22 17:55:28 +0000290 if (!S)
291 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000292
Ted Kremenek53500662009-07-22 17:55:28 +0000293 // Construct a new PathDiagnosticPiece.
294 PathDiagnosticLocation L(S, BRC.getSourceManager());
295 return new PathDiagnosticEventPiece(L, os.str());
296 }
Mike Stump1eb44332009-09-09 15:08:12 +0000297
Ted Kremenek53500662009-07-22 17:55:28 +0000298 return NULL;
Mike Stump1eb44332009-09-09 15:08:12 +0000299 }
Ted Kremenek53500662009-07-22 17:55:28 +0000300};
301} // end anonymous namespace
302
Ted Kremenek5b9bd212009-09-11 22:07:28 +0000303static void registerTrackConstraint(BugReporterContext& BRC,
304 DefinedSVal Constraint,
Ted Kremenek53500662009-07-22 17:55:28 +0000305 bool Assumption) {
Mike Stump1eb44332009-09-09 15:08:12 +0000306 BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption));
Ted Kremenek53500662009-07-22 17:55:28 +0000307}
308
309void clang::bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC,
Ted Kremenek592362b2009-08-18 01:05:30 +0000310 const void *data,
Zhongxing Xuc5619d92009-08-06 01:32:16 +0000311 const ExplodedNode* N) {
Mike Stump1eb44332009-09-09 15:08:12 +0000312
Ted Kremenek592362b2009-08-18 01:05:30 +0000313 const Stmt *S = static_cast<const Stmt*>(data);
Mike Stump1eb44332009-09-09 15:08:12 +0000314
Ted Kremenek53500662009-07-22 17:55:28 +0000315 if (!S)
316 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000317
Ted Kremenek53500662009-07-22 17:55:28 +0000318 GRStateManager &StateMgr = BRC.getStateManager();
Mike Stump1eb44332009-09-09 15:08:12 +0000319 const GRState *state = N->getState();
320
321 if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
322 if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
Ted Kremenek53500662009-07-22 17:55:28 +0000323 const VarRegion *R =
Ted Kremenekd17da2b2009-08-21 22:28:32 +0000324 StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
Mike Stump1eb44332009-09-09 15:08:12 +0000325
Ted Kremenek53500662009-07-22 17:55:28 +0000326 // What did we load?
Ted Kremenek13976632010-02-08 16:18:51 +0000327 SVal V = state->getSVal(S);
Mike Stump1eb44332009-09-09 15:08:12 +0000328
329 if (isa<loc::ConcreteInt>(V) || isa<nonloc::ConcreteInt>(V)
Ted Kremenek53500662009-07-22 17:55:28 +0000330 || V.isUndef()) {
Ted Kremenek94fd0b82010-02-16 08:33:59 +0000331 ::registerFindLastStore(BRC, R, V);
Ted Kremenek53500662009-07-22 17:55:28 +0000332 }
333 }
334 }
Mike Stump1eb44332009-09-09 15:08:12 +0000335
Ted Kremenek13976632010-02-08 16:18:51 +0000336 SVal V = state->getSValAsScalarOrLoc(S);
Mike Stump1eb44332009-09-09 15:08:12 +0000337
Ted Kremenek53500662009-07-22 17:55:28 +0000338 // Uncomment this to find cases where we aren't properly getting the
339 // base value that was dereferenced.
340 // assert(!V.isUnknownOrUndef());
Mike Stump1eb44332009-09-09 15:08:12 +0000341
Ted Kremenek53500662009-07-22 17:55:28 +0000342 // Is it a symbolic value?
343 if (loc::MemRegionVal *L = dyn_cast<loc::MemRegionVal>(&V)) {
344 const SubRegion *R = cast<SubRegion>(L->getRegion());
345 while (R && !isa<SymbolicRegion>(R)) {
346 R = dyn_cast<SubRegion>(R->getSuperRegion());
347 }
Mike Stump1eb44332009-09-09 15:08:12 +0000348
Ted Kremenek53500662009-07-22 17:55:28 +0000349 if (R) {
350 assert(isa<SymbolicRegion>(R));
351 registerTrackConstraint(BRC, loc::MemRegionVal(R), false);
352 }
353 }
354}
Ted Kremenek94fd0b82010-02-16 08:33:59 +0000355
356void clang::bugreporter::registerFindLastStore(BugReporterContext& BRC,
357 const void *data,
358 const ExplodedNode* N) {
359
360 const MemRegion *R = static_cast<const MemRegion*>(data);
361
362 if (!R)
363 return;
364
365 const GRState *state = N->getState();
366 SVal V = state->getSVal(R);
367
368 if (V.isUnknown())
369 return;
370
371 BRC.addVisitor(new FindLastStoreBRVisitor(V, R));
372}
Ted Kremenekff7f7362010-03-20 18:02:01 +0000373
374
375namespace {
376class NilReceiverVisitor : public BugReporterVisitor {
377public:
378 NilReceiverVisitor() {}
379
380 void Profile(llvm::FoldingSetNodeID &ID) const {
381 static int x = 0;
382 ID.AddPointer(&x);
383 }
384
385 PathDiagnosticPiece* VisitNode(const ExplodedNode *N,
386 const ExplodedNode *PrevN,
387 BugReporterContext& BRC) {
388
389 const PostStmt *P = N->getLocationAs<PostStmt>();
390 if (!P)
391 return 0;
392 const ObjCMessageExpr *ME = P->getStmtAs<ObjCMessageExpr>();
393 if (!ME)
394 return 0;
Douglas Gregor04badcf2010-04-21 00:45:42 +0000395 const Expr *Receiver = ME->getInstanceReceiver();
Ted Kremenekff7f7362010-03-20 18:02:01 +0000396 if (!Receiver)
397 return 0;
398 const GRState *state = N->getState();
399 const SVal &V = state->getSVal(Receiver);
400 const DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&V);
401 if (!DV)
402 return 0;
403 state = state->Assume(*DV, true);
404 if (state)
405 return 0;
406
407 // The receiver was nil, and hence the method was skipped.
408 // Register a BugReporterVisitor to issue a message telling us how
409 // the receiver was null.
410 bugreporter::registerTrackNullOrUndefValue(BRC, Receiver, N);
411 // Issue a message saying that the method was skipped.
412 PathDiagnosticLocation L(Receiver, BRC.getSourceManager());
413 return new PathDiagnosticEventPiece(L, "No method actually called "
414 "because the receiver is nil");
415 }
416};
417} // end anonymous namespace
418
419void clang::bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) {
420 BRC.addVisitor(new NilReceiverVisitor());
421}