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