blob: 678e190ea5fd5a88095d4b62df40502a0a225f0d [file] [log] [blame]
Ted Kremenekc0414922008-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 Kremeneka4d60b62008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekc0414922008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000020#include "clang/Analysis/PathSensitive/GRExprEngine.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000021#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000022#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000023#include "clang/Analysis/PathDiagnostic.h"
Ted Kremenek1f352db2008-07-22 16:21:24 +000024#include "clang/Analysis/LocalCheckers.h"
25
Ted Kremenekc0414922008-03-27 07:25:52 +000026#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000027#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000028#include "clang/AST/ASTContext.h"
29#include "llvm/Support/Compiler.h"
30
Ted Kremenek276278e2008-03-27 22:05:32 +000031#include <sstream>
Ted Kremenekc0414922008-03-27 07:25:52 +000032
33using namespace clang;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000034
Ted Kremenek276278e2008-03-27 22:05:32 +000035static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
36 Expr* Receiver = ME->getReceiver();
37
38 if (!Receiver)
39 return NULL;
40
Ted Kremenekd1a2efa2008-04-03 21:44:24 +000041 QualType X = Receiver->getType();
Ted Kremenek575f24e2008-04-19 19:12:50 +000042
Ted Kremenekf20e2282008-04-30 22:48:21 +000043 if (X->isPointerType()) {
44 Type* TP = X.getTypePtr();
45 const PointerType* T = TP->getAsPointerType();
46 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
47 }
48
49 // FIXME: Support ObjCQualifiedIdType?
50 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000051}
52
53static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
54 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
55 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
56 : NULL;
57}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000058
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000059namespace {
60
Ted Kremenek31484b22008-04-18 20:54:29 +000061class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000062public:
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000063 virtual ~NilArg() {}
64
65 virtual const char* getName() const {
66 return "nil argument";
67 }
68
Ted Kremenek7acc3a32008-04-09 21:41:14 +000069 class Report : public BugReport {
70 std::string Msg;
71 const char* s;
72 SourceRange R;
73 public:
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +000074
Ted Kremenek7e151302008-04-14 17:39:48 +000075 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
76 ObjCMessageExpr* ME, unsigned Arg)
77 : BugReport(Desc, N) {
Ted Kremenek7acc3a32008-04-09 21:41:14 +000078
79 Expr* E = ME->getArg(Arg);
80 R = E->getSourceRange();
81
82 std::ostringstream os;
83
84 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
85 << ME->getSelector().getName() << "' cannot be nil.";
86
87 Msg = os.str();
88 s = Msg.c_str();
89 }
90
91 virtual ~Report() {}
92
Ted Kremenek83744dd2008-04-10 16:05:13 +000093 virtual const char* getDescription() const { return s; }
94
Ted Kremenekfabfb462008-05-01 22:50:36 +000095 virtual void getRanges(BugReporter& BR,
96 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek83744dd2008-04-10 16:05:13 +000097 B = &R;
98 E = B+1;
99 }
Ted Kremenek7acc3a32008-04-09 21:41:14 +0000100 };
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000101};
Ted Kremenek7acc3a32008-04-09 21:41:14 +0000102
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000103
104class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek7acc3a32008-04-09 21:41:14 +0000105 NilArg Desc;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000106 ASTContext &Ctx;
107 ValueStateManager* VMgr;
108
Ted Kremenek7e151302008-04-14 17:39:48 +0000109 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000110 ErrorsTy Errors;
111
Ted Kremeneka7b8ffb2008-07-10 22:03:41 +0000112 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000113
114 bool isNSString(ObjCInterfaceType* T, const char* suffix);
115 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
116
117 void Warn(NodeTy* N, Expr* E, const std::string& s);
118 void WarnNilArg(NodeTy* N, Expr* E);
119
120 bool CheckNilArg(NodeTy* N, unsigned Arg);
121
122public:
123 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
124 : Ctx(ctx), VMgr(vmgr) {}
125
126 virtual ~BasicObjCFoundationChecks() {
127 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremenek7e151302008-04-14 17:39:48 +0000128 delete *I;
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000129 }
130
Ted Kremenek98f6e582008-07-22 00:46:16 +0000131 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000132
Ted Kremenek7acc3a32008-04-09 21:41:14 +0000133 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000134
135private:
136
Ted Kremenek7e151302008-04-14 17:39:48 +0000137 void AddError(BugReport* R) {
138 Errors.push_back(R);
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000139 }
140
141 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenek7e151302008-04-14 17:39:48 +0000142 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000143 }
144};
145
146} // end anonymous namespace
147
148
149GRSimpleAPICheck*
150clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
151 ValueStateManager* VMgr) {
152
153 return new BasicObjCFoundationChecks(Ctx, VMgr);
154}
155
156
157
Ted Kremenek98f6e582008-07-22 00:46:16 +0000158bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N,
159 ValueStateManager&) {
Ted Kremenekc0414922008-03-27 07:25:52 +0000160
161 ObjCMessageExpr* ME =
162 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekc0414922008-03-27 07:25:52 +0000163
Ted Kremenek276278e2008-03-27 22:05:32 +0000164 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenekc0414922008-03-27 07:25:52 +0000165
166 if (!ReceiverType)
Nuno Lopes652eaab2008-05-20 17:33:56 +0000167 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000168
Ted Kremenek276278e2008-03-27 22:05:32 +0000169 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
170
171 if (!name)
172 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000173
174 if (name[0] != 'N' || name[1] != 'S')
175 return false;
176
177 name += 2;
178
179 // FIXME: Make all of this faster.
180
181 if (isNSString(ReceiverType, name))
182 return AuditNSString(N, ME);
183
Nuno Lopes652eaab2008-05-20 17:33:56 +0000184 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +0000185}
186
Ted Kremenek27156c82008-03-27 21:15:17 +0000187static inline bool isNil(RVal X) {
188 return isa<lval::ConcreteInt>(X);
189}
190
Ted Kremenekc0414922008-03-27 07:25:52 +0000191//===----------------------------------------------------------------------===//
192// Error reporting.
193//===----------------------------------------------------------------------===//
194
195
Ted Kremenek7acc3a32008-04-09 21:41:14 +0000196void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
197
Ted Kremenek7e151302008-04-14 17:39:48 +0000198 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenekcffe6352008-04-18 01:56:37 +0000199 BR.EmitWarning(**I);
Ted Kremenek276278e2008-03-27 22:05:32 +0000200}
201
202bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
203 ObjCMessageExpr* ME =
204 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
205
206 Expr * E = ME->getArg(Arg);
207
208 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekcb2dc8e2008-04-03 17:57:38 +0000209 WarnNilArg(N, ME, Arg);
Ted Kremenek276278e2008-03-27 22:05:32 +0000210 return true;
211 }
212
213 return false;
214}
215
Ted Kremenekc0414922008-03-27 07:25:52 +0000216//===----------------------------------------------------------------------===//
217// NSString checking.
218//===----------------------------------------------------------------------===//
219
220bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
221 const char* suffix) {
222
223 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
224}
225
226bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
227 ObjCMessageExpr* ME) {
228
229 Selector S = ME->getSelector();
230
231 if (S.isUnarySelector())
232 return false;
233
234 // FIXME: This is going to be really slow doing these checks with
235 // lexical comparisons.
236
237 std::string name = S.getName();
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000238 assert (!name.empty());
239 const char* cstr = &name[0];
240 unsigned len = name.size();
Ted Kremenek276278e2008-03-27 22:05:32 +0000241
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000242 switch (len) {
243 default:
244 break;
Ted Kremenekc7194242008-03-28 16:09:38 +0000245 case 8:
Ted Kremenek276278e2008-03-27 22:05:32 +0000246 if (!strcmp(cstr, "compare:"))
247 return CheckNilArg(N, 0);
248
249 break;
Ted Kremenekc7194242008-03-28 16:09:38 +0000250
251 case 15:
252 // FIXME: Checking for initWithFormat: will not work in most cases
253 // yet because [NSString alloc] returns id, not NSString*. We will
254 // need support for tracking expected-type information in the analyzer
255 // to find these errors.
256 if (!strcmp(cstr, "initWithFormat:"))
257 return CheckNilArg(N, 0);
258
259 break;
Ted Kremenekc0414922008-03-27 07:25:52 +0000260
Ted Kremenek276278e2008-03-27 22:05:32 +0000261 case 16:
262 if (!strcmp(cstr, "compare:options:"))
263 return CheckNilArg(N, 0);
Ted Kremenek2e4e7cc2008-03-27 21:23:57 +0000264
265 break;
Ted Kremenek276278e2008-03-27 22:05:32 +0000266
267 case 22:
268 if (!strcmp(cstr, "compare:options:range:"))
269 return CheckNilArg(N, 0);
270
271 break;
272
273 case 23:
274
275 if (!strcmp(cstr, "caseInsensitiveCompare:"))
276 return CheckNilArg(N, 0);
277
278 break;
Ted Kremenekc7194242008-03-28 16:09:38 +0000279
Ted Kremenek276278e2008-03-27 22:05:32 +0000280 case 29:
281 if (!strcmp(cstr, "compare:options:range:locale:"))
282 return CheckNilArg(N, 0);
283
284 break;
285
286 case 37:
287 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
288 return CheckNilArg(N, 0);
289
290 break;
Ted Kremenekc0414922008-03-27 07:25:52 +0000291 }
292
293 return false;
294}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000295
296//===----------------------------------------------------------------------===//
297// Error reporting.
298//===----------------------------------------------------------------------===//
299
300namespace {
301
302class VISIBILITY_HIDDEN BadCFNumberCreate : public BugTypeCacheLocation {
303public:
304 typedef std::vector<BugReport*> AllErrorsTy;
305 AllErrorsTy AllErrors;
306
307 virtual const char* getName() const {
308 return "Bad use of CFNumberCreate";
309 }
310
311 virtual void EmitWarnings(BugReporter& BR) {
312 // FIXME: Refactor this method.
313 for (AllErrorsTy::iterator I=AllErrors.begin(), E=AllErrors.end(); I!=E;++I)
314 BR.EmitWarning(**I);
315 }
316};
317
318 // FIXME: This entire class should be refactored into the common
319 // BugReporter classes.
320class VISIBILITY_HIDDEN StrBugReport : public RangedBugReport {
321 std::string str;
322 const char* cstr;
323public:
324 StrBugReport(BugType& D, ExplodedNode<ValueState>* N, std::string s)
325 : RangedBugReport(D, N), str(s) {
326 cstr = str.c_str();
327 }
328
329 virtual const char* getDescription() const { return cstr; }
330};
331
332
333class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
334 // FIXME: Who should own this?
335 BadCFNumberCreate Desc;
336
337 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
338 // it should always be passed with a call to Audit. The latter
339 // approach makes this class more stateless.
340 ASTContext& Ctx;
341 IdentifierInfo* II;
342 ValueStateManager* VMgr;
343
Ted Kremeneka7b8ffb2008-07-10 22:03:41 +0000344 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
345 RVal GetRVal(const ValueState* St, LVal LV) { return VMgr->GetRVal(St, LV); }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000346
347public:
348
349 AuditCFNumberCreate(ASTContext& ctx, ValueStateManager* vmgr)
350 : Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), VMgr(vmgr) {}
351
352 virtual ~AuditCFNumberCreate() {}
353
Ted Kremenek98f6e582008-07-22 00:46:16 +0000354 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000355
356 virtual void EmitWarnings(BugReporter& BR) {
357 Desc.EmitWarnings(BR);
358 }
359
360private:
361
362 void AddError(VarDecl* V, Expr* Ex, ExplodedNode<ValueState> *N,
363 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
364};
365} // end anonymous namespace
366
367enum CFNumberType {
368 kCFNumberSInt8Type = 1,
369 kCFNumberSInt16Type = 2,
370 kCFNumberSInt32Type = 3,
371 kCFNumberSInt64Type = 4,
372 kCFNumberFloat32Type = 5,
373 kCFNumberFloat64Type = 6,
374 kCFNumberCharType = 7,
375 kCFNumberShortType = 8,
376 kCFNumberIntType = 9,
377 kCFNumberLongType = 10,
378 kCFNumberLongLongType = 11,
379 kCFNumberFloatType = 12,
380 kCFNumberDoubleType = 13,
381 kCFNumberCFIndexType = 14,
382 kCFNumberNSIntegerType = 15,
383 kCFNumberCGFloatType = 16
384};
385
386namespace {
387 template<typename T>
388 class Optional {
389 bool IsKnown;
390 T Val;
391 public:
392 Optional() : IsKnown(false), Val(0) {}
393 Optional(const T& val) : IsKnown(true), Val(val) {}
394
395 bool isKnown() const { return IsKnown; }
396
397 const T& getValue() const {
398 assert (isKnown());
399 return Val;
400 }
401
402 operator const T&() const {
403 return getValue();
404 }
405 };
406}
407
408static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
409 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
410
411 if (i < kCFNumberCharType)
412 return FixedSize[i-1];
413
414 QualType T;
415
416 switch (i) {
417 case kCFNumberCharType: T = Ctx.CharTy; break;
418 case kCFNumberShortType: T = Ctx.ShortTy; break;
419 case kCFNumberIntType: T = Ctx.IntTy; break;
420 case kCFNumberLongType: T = Ctx.LongTy; break;
421 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
422 case kCFNumberFloatType: T = Ctx.FloatTy; break;
423 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
424 case kCFNumberCFIndexType:
425 case kCFNumberNSIntegerType:
426 case kCFNumberCGFloatType:
427 // FIXME: We need a way to map from names to Type*.
428 default:
429 return Optional<uint64_t>();
430 }
431
432 return Ctx.getTypeSize(T);
433}
434
435#if 0
436static const char* GetCFNumberTypeStr(uint64_t i) {
437 static const char* Names[] = {
438 "kCFNumberSInt8Type",
439 "kCFNumberSInt16Type",
440 "kCFNumberSInt32Type",
441 "kCFNumberSInt64Type",
442 "kCFNumberFloat32Type",
443 "kCFNumberFloat64Type",
444 "kCFNumberCharType",
445 "kCFNumberShortType",
446 "kCFNumberIntType",
447 "kCFNumberLongType",
448 "kCFNumberLongLongType",
449 "kCFNumberFloatType",
450 "kCFNumberDoubleType",
451 "kCFNumberCFIndexType",
452 "kCFNumberNSIntegerType",
453 "kCFNumberCGFloatType"
454 };
455
456 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
457}
458#endif
459
Ted Kremenek98f6e582008-07-22 00:46:16 +0000460bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N,ValueStateManager&){
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000461 CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
462 Expr* Callee = CE->getCallee();
463 RVal CallV = GetRVal(N->getState(), Callee);
464 lval::FuncVal* FuncV = dyn_cast<lval::FuncVal>(&CallV);
465
466 if (!FuncV || FuncV->getDecl()->getIdentifier() != II || CE->getNumArgs()!=3)
467 return false;
468
469 // Get the value of the "theType" argument.
470 RVal TheTypeVal = GetRVal(N->getState(), CE->getArg(1));
471
472 // FIXME: We really should allow ranges of valid theType values, and
473 // bifurcate the state appropriately.
474 nonlval::ConcreteInt* V = dyn_cast<nonlval::ConcreteInt>(&TheTypeVal);
475
476 if (!V)
477 return false;
478
479 uint64_t NumberKind = V->getValue().getLimitedValue();
480 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
481
482 // FIXME: In some cases we can emit an error.
483 if (!TargetSize.isKnown())
484 return false;
485
486 // Look at the value of the integer being passed by reference. Essentially
487 // we want to catch cases where the value passed in is not equal to the
488 // size of the type being created.
489 RVal TheValueExpr = GetRVal(N->getState(), CE->getArg(2));
490
491 // FIXME: Eventually we should handle arbitrary locations. We can do this
492 // by having an enhanced memory model that does low-level typing.
493 lval::DeclVal* LV = dyn_cast<lval::DeclVal>(&TheValueExpr);
494
495 if (!LV)
496 return false;
497
Chris Lattner574dee62008-07-26 22:17:49 +0000498 QualType T = Ctx.getCanonicalType(LV->getDecl()->getType());
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000499
500 // FIXME: If the pointee isn't an integer type, should we flag a warning?
501 // People can do weird stuff with pointers.
502
503 if (!T->isIntegerType())
504 return false;
505
506 uint64_t SourceSize = Ctx.getTypeSize(T);
507
508 // CHECK: is SourceSize == TargetSize
509
510 if (SourceSize == TargetSize)
511 return false;
512
513 AddError(LV->getDecl(), CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
514
515 // FIXME: We can actually create an abstract "CFNumber" object that has
516 // the bits initialized to the provided values.
517 return SourceSize < TargetSize;
518}
519
520void AuditCFNumberCreate::AddError(VarDecl* V, Expr* Ex,
521 ExplodedNode<ValueState> *N,
522 uint64_t SourceSize, uint64_t TargetSize,
523 uint64_t NumberKind) {
524
525 std::ostringstream os;
526
527 os << (SourceSize == 8 ? "An " : "A ")
528 << SourceSize << " bit integer is used to initialize a CFNumber "
529 "object that represents "
530 << (TargetSize == 8 ? "an " : "a ")
531 << TargetSize << " bit integer. ";
532
533 if (SourceSize < TargetSize)
534 os << (TargetSize - SourceSize)
535 << " bits of the CFNumber value will be garbage." ;
536 else
537 os << (SourceSize - TargetSize)
538 << " bits of the input integer will be lost.";
539
540 StrBugReport* B = new StrBugReport(Desc, N, os.str());
541 B->addRange(Ex->getSourceRange());
542 Desc.AllErrors.push_back(B);
543}
544
545GRSimpleAPICheck*
546clang::CreateAuditCFNumberCreate(ASTContext& Ctx,
547 ValueStateManager* VMgr) {
548
549 return new AuditCFNumberCreate(Ctx, VMgr);
550}
551
Ted Kremenek1f352db2008-07-22 16:21:24 +0000552//===----------------------------------------------------------------------===//
553// Check registration.
554
555void clang::RegisterAppleChecks(GRExprEngine& Eng) {
556 ASTContext& Ctx = Eng.getContext();
557 ValueStateManager* VMgr = &Eng.getStateManager();
558
559 Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, VMgr),
560 Stmt::ObjCMessageExprClass);
561
562 Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, VMgr), Stmt::CallExprClass);
563}