blob: 678e190ea5fd5a88095d4b62df40502a0a225f0d [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
Ted Kremenekb0a2e472008-03-27 07:25:52 +000026#include "clang/AST/Expr.h"
Steve Naroff9ed3e772008-05-29 21:12:08 +000027#include "clang/AST/ExprObjC.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000028#include "clang/AST/ASTContext.h"
29#include "llvm/Support/Compiler.h"
30
Ted Kremenek639247a2008-03-27 22:05:32 +000031#include <sstream>
Ted Kremenekb0a2e472008-03-27 07:25:52 +000032
33using namespace clang;
Ted Kremenek7a681942008-03-27 17:17:22 +000034
Ted Kremenek639247a2008-03-27 22:05:32 +000035static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
36 Expr* Receiver = ME->getReceiver();
37
38 if (!Receiver)
39 return NULL;
40
Ted Kremenek92c78b62008-04-03 21:44:24 +000041 QualType X = Receiver->getType();
Ted Kremenekbfc6cce2008-04-19 19:12:50 +000042
Ted Kremenekd810bf82008-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 Kremenek639247a2008-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 Kremenek7a681942008-03-27 17:17:22 +000058
Ted Kremenekf00daf02008-04-03 17:57:38 +000059namespace {
60
Ted Kremeneke3769852008-04-18 20:54:29 +000061class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf00daf02008-04-03 17:57:38 +000062public:
Ted Kremenekf00daf02008-04-03 17:57:38 +000063 virtual ~NilArg() {}
64
65 virtual const char* getName() const {
66 return "nil argument";
67 }
68
Ted Kremenek0e80dea2008-04-09 21:41:14 +000069 class Report : public BugReport {
70 std::string Msg;
71 const char* s;
72 SourceRange R;
73 public:
Ted Kremenekf00daf02008-04-03 17:57:38 +000074
Ted Kremeneke3ef1c72008-04-14 17:39:48 +000075 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
76 ObjCMessageExpr* ME, unsigned Arg)
77 : BugReport(Desc, N) {
Ted Kremenek0e80dea2008-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 Kremenek128d65f2008-04-10 16:05:13 +000093 virtual const char* getDescription() const { return s; }
94
Ted Kremenek5c3407a2008-05-01 22:50:36 +000095 virtual void getRanges(BugReporter& BR,
96 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek128d65f2008-04-10 16:05:13 +000097 B = &R;
98 E = B+1;
99 }
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000100 };
Ted Kremenekf00daf02008-04-03 17:57:38 +0000101};
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000102
Ted Kremenekf00daf02008-04-03 17:57:38 +0000103
104class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000105 NilArg Desc;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000106 ASTContext &Ctx;
107 ValueStateManager* VMgr;
108
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000109 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000110 ErrorsTy Errors;
111
Ted Kremenekf22f8682008-07-10 22:03:41 +0000112 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenekf00daf02008-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 Kremeneke3ef1c72008-04-14 17:39:48 +0000128 delete *I;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000129 }
130
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000131 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000132
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000133 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000134
135private:
136
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000137 void AddError(BugReport* R) {
138 Errors.push_back(R);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000139 }
140
141 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000142 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf00daf02008-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 Kremenekbbafa5b2008-07-22 00:46:16 +0000158bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N,
159 ValueStateManager&) {
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000160
161 ObjCMessageExpr* ME =
162 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000163
Ted Kremenek639247a2008-03-27 22:05:32 +0000164 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000165
166 if (!ReceiverType)
Nuno Lopesf44f0932008-05-20 17:33:56 +0000167 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000168
Ted Kremenek639247a2008-03-27 22:05:32 +0000169 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
170
171 if (!name)
172 return false;
Ted Kremenekb0a2e472008-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 Lopesf44f0932008-05-20 17:33:56 +0000184 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000185}
186
Ted Kremenek583c4382008-03-27 21:15:17 +0000187static inline bool isNil(RVal X) {
188 return isa<lval::ConcreteInt>(X);
189}
190
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000191//===----------------------------------------------------------------------===//
192// Error reporting.
193//===----------------------------------------------------------------------===//
194
195
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000196void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
197
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000198 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek270ab7d2008-04-18 01:56:37 +0000199 BR.EmitWarning(**I);
Ted Kremenek639247a2008-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 Kremenekf00daf02008-04-03 17:57:38 +0000209 WarnNilArg(N, ME, Arg);
Ted Kremenek639247a2008-03-27 22:05:32 +0000210 return true;
211 }
212
213 return false;
214}
215
Ted Kremenekb0a2e472008-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 Kremenekb20dccc2008-03-27 21:23:57 +0000238 assert (!name.empty());
239 const char* cstr = &name[0];
240 unsigned len = name.size();
Ted Kremenek639247a2008-03-27 22:05:32 +0000241
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000242 switch (len) {
243 default:
244 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000245 case 8:
Ted Kremenek639247a2008-03-27 22:05:32 +0000246 if (!strcmp(cstr, "compare:"))
247 return CheckNilArg(N, 0);
248
249 break;
Ted Kremenek44e74042008-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 Kremenekb0a2e472008-03-27 07:25:52 +0000260
Ted Kremenek639247a2008-03-27 22:05:32 +0000261 case 16:
262 if (!strcmp(cstr, "compare:options:"))
263 return CheckNilArg(N, 0);
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000264
265 break;
Ted Kremenek639247a2008-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 Kremenek44e74042008-03-28 16:09:38 +0000279
Ted Kremenek639247a2008-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 Kremenekb0a2e472008-03-27 07:25:52 +0000291 }
292
293 return false;
294}
Ted Kremenekc1290552008-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 Kremenekf22f8682008-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 Kremenekc1290552008-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 Kremenekbbafa5b2008-07-22 00:46:16 +0000354 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekc1290552008-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 Kremenekbbafa5b2008-07-22 00:46:16 +0000460bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N,ValueStateManager&){
Ted Kremenekc1290552008-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 Lattnerd5a56aa2008-07-26 22:17:49 +0000498 QualType T = Ctx.getCanonicalType(LV->getDecl()->getType());
Ted Kremenekc1290552008-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 Kremenek9f20c7c2008-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}