blob: 29c0b92432ce282aeda5dd84e42b69c2678407e6 [file] [log] [blame]
Ted Kremenekb0a2e472008-03-27 07:25:52 +00001//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- 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 BasicObjCFoundationChecks, a class that encapsulates
11// a set of simple checks to run on Objective-C code using Apple's Foundation
12// classes.
13//
14//===----------------------------------------------------------------------===//
15
Ted Kremenek7a681942008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekb0a2e472008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
Ted Kremenek9f20c7c2008-07-22 16:21:24 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000021#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenekf00daf02008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000023#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek9f20c7c2008-07-22 16:21:24 +000024#include "clang/Analysis/LocalCheckers.h"
25
Daniel Dunbar64789f82008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Naroff9ed3e772008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
30#include "llvm/Support/Compiler.h"
31
Ted Kremenek639247a2008-03-27 22:05:32 +000032#include <sstream>
Ted Kremenekb0a2e472008-03-27 07:25:52 +000033
34using namespace clang;
Ted Kremenek7a681942008-03-27 17:17:22 +000035
Ted Kremenek639247a2008-03-27 22:05:32 +000036static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
37 Expr* Receiver = ME->getReceiver();
38
39 if (!Receiver)
40 return NULL;
41
Ted Kremenek92c78b62008-04-03 21:44:24 +000042 QualType X = Receiver->getType();
Ted Kremenekbfc6cce2008-04-19 19:12:50 +000043
Ted Kremenekd810bf82008-04-30 22:48:21 +000044 if (X->isPointerType()) {
45 Type* TP = X.getTypePtr();
46 const PointerType* T = TP->getAsPointerType();
47 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
48 }
49
50 // FIXME: Support ObjCQualifiedIdType?
51 return NULL;
Ted Kremenek639247a2008-03-27 22:05:32 +000052}
53
54static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
55 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
56 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
57 : NULL;
58}
Ted Kremenek7a681942008-03-27 17:17:22 +000059
Ted Kremenekf00daf02008-04-03 17:57:38 +000060namespace {
61
Ted Kremeneke3769852008-04-18 20:54:29 +000062class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf00daf02008-04-03 17:57:38 +000063public:
Ted Kremenekf00daf02008-04-03 17:57:38 +000064 virtual ~NilArg() {}
65
66 virtual const char* getName() const {
67 return "nil argument";
68 }
69
Ted Kremenek0e80dea2008-04-09 21:41:14 +000070 class Report : public BugReport {
71 std::string Msg;
72 const char* s;
73 SourceRange R;
74 public:
Ted Kremenekf00daf02008-04-03 17:57:38 +000075
Ted Kremeneke3ef1c72008-04-14 17:39:48 +000076 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
77 ObjCMessageExpr* ME, unsigned Arg)
78 : BugReport(Desc, N) {
Ted Kremenek0e80dea2008-04-09 21:41:14 +000079
80 Expr* E = ME->getArg(Arg);
81 R = E->getSourceRange();
82
83 std::ostringstream os;
84
85 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
86 << ME->getSelector().getName() << "' cannot be nil.";
87
88 Msg = os.str();
89 s = Msg.c_str();
90 }
91
92 virtual ~Report() {}
93
Ted Kremenek128d65f2008-04-10 16:05:13 +000094 virtual const char* getDescription() const { return s; }
95
Ted Kremenek5c3407a2008-05-01 22:50:36 +000096 virtual void getRanges(BugReporter& BR,
97 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek128d65f2008-04-10 16:05:13 +000098 B = &R;
99 E = B+1;
100 }
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000101 };
Ted Kremenekf00daf02008-04-03 17:57:38 +0000102};
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000103
Ted Kremenekf00daf02008-04-03 17:57:38 +0000104
105class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000106 NilArg Desc;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000107 ASTContext &Ctx;
108 ValueStateManager* VMgr;
109
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000110 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000111 ErrorsTy Errors;
112
Ted Kremenekf22f8682008-07-10 22:03:41 +0000113 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenekf00daf02008-04-03 17:57:38 +0000114
115 bool isNSString(ObjCInterfaceType* T, const char* suffix);
116 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
117
118 void Warn(NodeTy* N, Expr* E, const std::string& s);
119 void WarnNilArg(NodeTy* N, Expr* E);
120
121 bool CheckNilArg(NodeTy* N, unsigned Arg);
122
123public:
124 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
125 : Ctx(ctx), VMgr(vmgr) {}
126
127 virtual ~BasicObjCFoundationChecks() {
128 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000129 delete *I;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000130 }
131
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000132 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000133
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000134 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000135
136private:
137
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000138 void AddError(BugReport* R) {
139 Errors.push_back(R);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000140 }
141
142 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000143 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf00daf02008-04-03 17:57:38 +0000144 }
145};
146
147} // end anonymous namespace
148
149
150GRSimpleAPICheck*
151clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
152 ValueStateManager* VMgr) {
153
154 return new BasicObjCFoundationChecks(Ctx, VMgr);
155}
156
157
158
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000159bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N,
160 ValueStateManager&) {
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000161
162 ObjCMessageExpr* ME =
163 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000164
Ted Kremenek639247a2008-03-27 22:05:32 +0000165 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000166
167 if (!ReceiverType)
Nuno Lopesf44f0932008-05-20 17:33:56 +0000168 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000169
Ted Kremenek639247a2008-03-27 22:05:32 +0000170 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
171
172 if (!name)
173 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000174
175 if (name[0] != 'N' || name[1] != 'S')
176 return false;
177
178 name += 2;
179
180 // FIXME: Make all of this faster.
181
182 if (isNSString(ReceiverType, name))
183 return AuditNSString(N, ME);
184
Nuno Lopesf44f0932008-05-20 17:33:56 +0000185 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000186}
187
Ted Kremenek583c4382008-03-27 21:15:17 +0000188static inline bool isNil(RVal X) {
189 return isa<lval::ConcreteInt>(X);
190}
191
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000192//===----------------------------------------------------------------------===//
193// Error reporting.
194//===----------------------------------------------------------------------===//
195
196
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000197void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
198
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000199 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek270ab7d2008-04-18 01:56:37 +0000200 BR.EmitWarning(**I);
Ted Kremenek639247a2008-03-27 22:05:32 +0000201}
202
203bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
204 ObjCMessageExpr* ME =
205 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
206
207 Expr * E = ME->getArg(Arg);
208
209 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf00daf02008-04-03 17:57:38 +0000210 WarnNilArg(N, ME, Arg);
Ted Kremenek639247a2008-03-27 22:05:32 +0000211 return true;
212 }
213
214 return false;
215}
216
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000217//===----------------------------------------------------------------------===//
218// NSString checking.
219//===----------------------------------------------------------------------===//
220
221bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
222 const char* suffix) {
223
224 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
225}
226
227bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
228 ObjCMessageExpr* ME) {
229
230 Selector S = ME->getSelector();
231
232 if (S.isUnarySelector())
233 return false;
234
235 // FIXME: This is going to be really slow doing these checks with
236 // lexical comparisons.
237
238 std::string name = S.getName();
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000239 assert (!name.empty());
240 const char* cstr = &name[0];
241 unsigned len = name.size();
Ted Kremenek639247a2008-03-27 22:05:32 +0000242
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000243 switch (len) {
244 default:
245 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000246 case 8:
Ted Kremenek639247a2008-03-27 22:05:32 +0000247 if (!strcmp(cstr, "compare:"))
248 return CheckNilArg(N, 0);
249
250 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000251
252 case 15:
253 // FIXME: Checking for initWithFormat: will not work in most cases
254 // yet because [NSString alloc] returns id, not NSString*. We will
255 // need support for tracking expected-type information in the analyzer
256 // to find these errors.
257 if (!strcmp(cstr, "initWithFormat:"))
258 return CheckNilArg(N, 0);
259
260 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000261
Ted Kremenek639247a2008-03-27 22:05:32 +0000262 case 16:
263 if (!strcmp(cstr, "compare:options:"))
264 return CheckNilArg(N, 0);
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000265
266 break;
Ted Kremenek639247a2008-03-27 22:05:32 +0000267
268 case 22:
269 if (!strcmp(cstr, "compare:options:range:"))
270 return CheckNilArg(N, 0);
271
272 break;
273
274 case 23:
275
276 if (!strcmp(cstr, "caseInsensitiveCompare:"))
277 return CheckNilArg(N, 0);
278
279 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000280
Ted Kremenek639247a2008-03-27 22:05:32 +0000281 case 29:
282 if (!strcmp(cstr, "compare:options:range:locale:"))
283 return CheckNilArg(N, 0);
284
285 break;
286
287 case 37:
288 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
289 return CheckNilArg(N, 0);
290
291 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000292 }
293
294 return false;
295}
Ted Kremenekc1290552008-06-26 23:59:48 +0000296
297//===----------------------------------------------------------------------===//
298// Error reporting.
299//===----------------------------------------------------------------------===//
300
301namespace {
302
303class VISIBILITY_HIDDEN BadCFNumberCreate : public BugTypeCacheLocation {
304public:
305 typedef std::vector<BugReport*> AllErrorsTy;
306 AllErrorsTy AllErrors;
307
308 virtual const char* getName() const {
309 return "Bad use of CFNumberCreate";
310 }
311
312 virtual void EmitWarnings(BugReporter& BR) {
313 // FIXME: Refactor this method.
314 for (AllErrorsTy::iterator I=AllErrors.begin(), E=AllErrors.end(); I!=E;++I)
315 BR.EmitWarning(**I);
316 }
317};
318
319 // FIXME: This entire class should be refactored into the common
320 // BugReporter classes.
321class VISIBILITY_HIDDEN StrBugReport : public RangedBugReport {
322 std::string str;
323 const char* cstr;
324public:
325 StrBugReport(BugType& D, ExplodedNode<ValueState>* N, std::string s)
326 : RangedBugReport(D, N), str(s) {
327 cstr = str.c_str();
328 }
329
330 virtual const char* getDescription() const { return cstr; }
331};
332
333
334class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
335 // FIXME: Who should own this?
336 BadCFNumberCreate Desc;
337
338 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
339 // it should always be passed with a call to Audit. The latter
340 // approach makes this class more stateless.
341 ASTContext& Ctx;
342 IdentifierInfo* II;
343 ValueStateManager* VMgr;
344
Ted Kremenekf22f8682008-07-10 22:03:41 +0000345 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
346 RVal GetRVal(const ValueState* St, LVal LV) { return VMgr->GetRVal(St, LV); }
Ted Kremenekc1290552008-06-26 23:59:48 +0000347
348public:
349
350 AuditCFNumberCreate(ASTContext& ctx, ValueStateManager* vmgr)
351 : Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), VMgr(vmgr) {}
352
353 virtual ~AuditCFNumberCreate() {}
354
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000355 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekc1290552008-06-26 23:59:48 +0000356
357 virtual void EmitWarnings(BugReporter& BR) {
358 Desc.EmitWarnings(BR);
359 }
360
361private:
362
363 void AddError(VarDecl* V, Expr* Ex, ExplodedNode<ValueState> *N,
364 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
365};
366} // end anonymous namespace
367
368enum CFNumberType {
369 kCFNumberSInt8Type = 1,
370 kCFNumberSInt16Type = 2,
371 kCFNumberSInt32Type = 3,
372 kCFNumberSInt64Type = 4,
373 kCFNumberFloat32Type = 5,
374 kCFNumberFloat64Type = 6,
375 kCFNumberCharType = 7,
376 kCFNumberShortType = 8,
377 kCFNumberIntType = 9,
378 kCFNumberLongType = 10,
379 kCFNumberLongLongType = 11,
380 kCFNumberFloatType = 12,
381 kCFNumberDoubleType = 13,
382 kCFNumberCFIndexType = 14,
383 kCFNumberNSIntegerType = 15,
384 kCFNumberCGFloatType = 16
385};
386
387namespace {
388 template<typename T>
389 class Optional {
390 bool IsKnown;
391 T Val;
392 public:
393 Optional() : IsKnown(false), Val(0) {}
394 Optional(const T& val) : IsKnown(true), Val(val) {}
395
396 bool isKnown() const { return IsKnown; }
397
398 const T& getValue() const {
399 assert (isKnown());
400 return Val;
401 }
402
403 operator const T&() const {
404 return getValue();
405 }
406 };
407}
408
409static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
410 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
411
412 if (i < kCFNumberCharType)
413 return FixedSize[i-1];
414
415 QualType T;
416
417 switch (i) {
418 case kCFNumberCharType: T = Ctx.CharTy; break;
419 case kCFNumberShortType: T = Ctx.ShortTy; break;
420 case kCFNumberIntType: T = Ctx.IntTy; break;
421 case kCFNumberLongType: T = Ctx.LongTy; break;
422 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
423 case kCFNumberFloatType: T = Ctx.FloatTy; break;
424 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
425 case kCFNumberCFIndexType:
426 case kCFNumberNSIntegerType:
427 case kCFNumberCGFloatType:
428 // FIXME: We need a way to map from names to Type*.
429 default:
430 return Optional<uint64_t>();
431 }
432
433 return Ctx.getTypeSize(T);
434}
435
436#if 0
437static const char* GetCFNumberTypeStr(uint64_t i) {
438 static const char* Names[] = {
439 "kCFNumberSInt8Type",
440 "kCFNumberSInt16Type",
441 "kCFNumberSInt32Type",
442 "kCFNumberSInt64Type",
443 "kCFNumberFloat32Type",
444 "kCFNumberFloat64Type",
445 "kCFNumberCharType",
446 "kCFNumberShortType",
447 "kCFNumberIntType",
448 "kCFNumberLongType",
449 "kCFNumberLongLongType",
450 "kCFNumberFloatType",
451 "kCFNumberDoubleType",
452 "kCFNumberCFIndexType",
453 "kCFNumberNSIntegerType",
454 "kCFNumberCGFloatType"
455 };
456
457 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
458}
459#endif
460
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000461bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N,ValueStateManager&){
Ted Kremenekc1290552008-06-26 23:59:48 +0000462 CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
463 Expr* Callee = CE->getCallee();
464 RVal CallV = GetRVal(N->getState(), Callee);
465 lval::FuncVal* FuncV = dyn_cast<lval::FuncVal>(&CallV);
466
467 if (!FuncV || FuncV->getDecl()->getIdentifier() != II || CE->getNumArgs()!=3)
468 return false;
469
470 // Get the value of the "theType" argument.
471 RVal TheTypeVal = GetRVal(N->getState(), CE->getArg(1));
472
473 // FIXME: We really should allow ranges of valid theType values, and
474 // bifurcate the state appropriately.
475 nonlval::ConcreteInt* V = dyn_cast<nonlval::ConcreteInt>(&TheTypeVal);
476
477 if (!V)
478 return false;
479
480 uint64_t NumberKind = V->getValue().getLimitedValue();
481 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
482
483 // FIXME: In some cases we can emit an error.
484 if (!TargetSize.isKnown())
485 return false;
486
487 // Look at the value of the integer being passed by reference. Essentially
488 // we want to catch cases where the value passed in is not equal to the
489 // size of the type being created.
490 RVal TheValueExpr = GetRVal(N->getState(), CE->getArg(2));
491
492 // FIXME: Eventually we should handle arbitrary locations. We can do this
493 // by having an enhanced memory model that does low-level typing.
494 lval::DeclVal* LV = dyn_cast<lval::DeclVal>(&TheValueExpr);
495
496 if (!LV)
497 return false;
498
Chris Lattnerd5a56aa2008-07-26 22:17:49 +0000499 QualType T = Ctx.getCanonicalType(LV->getDecl()->getType());
Ted Kremenekc1290552008-06-26 23:59:48 +0000500
501 // FIXME: If the pointee isn't an integer type, should we flag a warning?
502 // People can do weird stuff with pointers.
503
504 if (!T->isIntegerType())
505 return false;
506
507 uint64_t SourceSize = Ctx.getTypeSize(T);
508
509 // CHECK: is SourceSize == TargetSize
510
511 if (SourceSize == TargetSize)
512 return false;
513
514 AddError(LV->getDecl(), CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
515
516 // FIXME: We can actually create an abstract "CFNumber" object that has
517 // the bits initialized to the provided values.
518 return SourceSize < TargetSize;
519}
520
521void AuditCFNumberCreate::AddError(VarDecl* V, Expr* Ex,
522 ExplodedNode<ValueState> *N,
523 uint64_t SourceSize, uint64_t TargetSize,
524 uint64_t NumberKind) {
525
526 std::ostringstream os;
527
528 os << (SourceSize == 8 ? "An " : "A ")
529 << SourceSize << " bit integer is used to initialize a CFNumber "
530 "object that represents "
531 << (TargetSize == 8 ? "an " : "a ")
532 << TargetSize << " bit integer. ";
533
534 if (SourceSize < TargetSize)
535 os << (TargetSize - SourceSize)
536 << " bits of the CFNumber value will be garbage." ;
537 else
538 os << (SourceSize - TargetSize)
539 << " bits of the input integer will be lost.";
540
541 StrBugReport* B = new StrBugReport(Desc, N, os.str());
542 B->addRange(Ex->getSourceRange());
543 Desc.AllErrors.push_back(B);
544}
545
546GRSimpleAPICheck*
547clang::CreateAuditCFNumberCreate(ASTContext& Ctx,
548 ValueStateManager* VMgr) {
549
550 return new AuditCFNumberCreate(Ctx, VMgr);
551}
552
Ted Kremenek9f20c7c2008-07-22 16:21:24 +0000553//===----------------------------------------------------------------------===//
554// Check registration.
555
556void clang::RegisterAppleChecks(GRExprEngine& Eng) {
557 ASTContext& Ctx = Eng.getContext();
558 ValueStateManager* VMgr = &Eng.getStateManager();
559
560 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, VMgr),
561 Stmt::ObjCMessageExprClass);
562
563 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, VMgr), Stmt::CallExprClass);
564}