]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / BasicObjCFoundationChecks.cpp
1 //== 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
16 #include "ClangSACheckers.h"
17 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
27 #include "clang/AST/DeclObjC.h"
28 #include "clang/AST/Expr.h"
29 #include "clang/AST/ExprObjC.h"
30 #include "clang/AST/ASTContext.h"
31
32 using namespace clang;
33 using namespace ento;
34
35 namespace {
36 class APIMisuse : public BugType {
37 public:
38   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
39 };
40 } // end anonymous namespace
41
42 //===----------------------------------------------------------------------===//
43 // Utility functions.
44 //===----------------------------------------------------------------------===//
45
46 static const char* GetReceiverNameType(const ObjCMessage &msg) {
47   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
48     return ID->getIdentifier()->getNameStart();
49   return 0;
50 }
51
52 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
53                                         StringRef ClassName) {
54   if (ID->getIdentifier()->getName() == ClassName)
55     return true;
56
57   if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
58     return isReceiverClassOrSuperclass(Super, ClassName);
59
60   return false;
61 }
62
63 static inline bool isNil(SVal X) {
64   return isa<loc::ConcreteInt>(X);
65 }
66
67 //===----------------------------------------------------------------------===//
68 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
69 //===----------------------------------------------------------------------===//
70
71 namespace {
72   class NilArgChecker : public Checker<check::PreObjCMessage> {
73     mutable llvm::OwningPtr<APIMisuse> BT;
74
75     void WarnNilArg(CheckerContext &C,
76                     const ObjCMessage &msg, unsigned Arg) const;
77
78   public:
79     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
80   };
81 }
82
83 void NilArgChecker::WarnNilArg(CheckerContext &C,
84                                const ObjCMessage &msg,
85                                unsigned int Arg) const
86 {
87   if (!BT)
88     BT.reset(new APIMisuse("nil argument"));
89   
90   if (ExplodedNode *N = C.generateSink()) {
91     llvm::SmallString<128> sbuf;
92     llvm::raw_svector_ostream os(sbuf);
93     os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
94        << msg.getSelector().getAsString() << "' cannot be nil";
95
96     BugReport *R = new BugReport(*BT, os.str(), N);
97     R->addRange(msg.getArgSourceRange(Arg));
98     C.EmitReport(R);
99   }
100 }
101
102 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
103                                         CheckerContext &C) const {
104   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
105   if (!ID)
106     return;
107   
108   if (isReceiverClassOrSuperclass(ID, "NSString")) {
109     Selector S = msg.getSelector();
110     
111     if (S.isUnarySelector())
112       return;
113     
114     // FIXME: This is going to be really slow doing these checks with
115     //  lexical comparisons.
116     
117     std::string NameStr = S.getAsString();
118     StringRef Name(NameStr);
119     assert(!Name.empty());
120     
121     // FIXME: Checking for initWithFormat: will not work in most cases
122     //  yet because [NSString alloc] returns id, not NSString*.  We will
123     //  need support for tracking expected-type information in the analyzer
124     //  to find these errors.
125     if (Name == "caseInsensitiveCompare:" ||
126         Name == "compare:" ||
127         Name == "compare:options:" ||
128         Name == "compare:options:range:" ||
129         Name == "compare:options:range:locale:" ||
130         Name == "componentsSeparatedByCharactersInSet:" ||
131         Name == "initWithFormat:") {
132       if (isNil(msg.getArgSVal(0, C.getState())))
133         WarnNilArg(C, msg, 0);
134     }
135   }
136 }
137
138 //===----------------------------------------------------------------------===//
139 // Error reporting.
140 //===----------------------------------------------------------------------===//
141
142 namespace {
143 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
144   mutable llvm::OwningPtr<APIMisuse> BT;
145   mutable IdentifierInfo* II;
146 public:
147   CFNumberCreateChecker() : II(0) {}
148
149   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
150
151 private:
152   void EmitError(const TypedRegion* R, const Expr *Ex,
153                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
154 };
155 } // end anonymous namespace
156
157 enum CFNumberType {
158   kCFNumberSInt8Type = 1,
159   kCFNumberSInt16Type = 2,
160   kCFNumberSInt32Type = 3,
161   kCFNumberSInt64Type = 4,
162   kCFNumberFloat32Type = 5,
163   kCFNumberFloat64Type = 6,
164   kCFNumberCharType = 7,
165   kCFNumberShortType = 8,
166   kCFNumberIntType = 9,
167   kCFNumberLongType = 10,
168   kCFNumberLongLongType = 11,
169   kCFNumberFloatType = 12,
170   kCFNumberDoubleType = 13,
171   kCFNumberCFIndexType = 14,
172   kCFNumberNSIntegerType = 15,
173   kCFNumberCGFloatType = 16
174 };
175
176 namespace {
177   template<typename T>
178   class Optional {
179     bool IsKnown;
180     T Val;
181   public:
182     Optional() : IsKnown(false), Val(0) {}
183     Optional(const T& val) : IsKnown(true), Val(val) {}
184
185     bool isKnown() const { return IsKnown; }
186
187     const T& getValue() const {
188       assert (isKnown());
189       return Val;
190     }
191
192     operator const T&() const {
193       return getValue();
194     }
195   };
196 }
197
198 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
199   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
200
201   if (i < kCFNumberCharType)
202     return FixedSize[i-1];
203
204   QualType T;
205
206   switch (i) {
207     case kCFNumberCharType:     T = Ctx.CharTy;     break;
208     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
209     case kCFNumberIntType:      T = Ctx.IntTy;      break;
210     case kCFNumberLongType:     T = Ctx.LongTy;     break;
211     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
212     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
213     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
214     case kCFNumberCFIndexType:
215     case kCFNumberNSIntegerType:
216     case kCFNumberCGFloatType:
217       // FIXME: We need a way to map from names to Type*.
218     default:
219       return Optional<uint64_t>();
220   }
221
222   return Ctx.getTypeSize(T);
223 }
224
225 #if 0
226 static const char* GetCFNumberTypeStr(uint64_t i) {
227   static const char* Names[] = {
228     "kCFNumberSInt8Type",
229     "kCFNumberSInt16Type",
230     "kCFNumberSInt32Type",
231     "kCFNumberSInt64Type",
232     "kCFNumberFloat32Type",
233     "kCFNumberFloat64Type",
234     "kCFNumberCharType",
235     "kCFNumberShortType",
236     "kCFNumberIntType",
237     "kCFNumberLongType",
238     "kCFNumberLongLongType",
239     "kCFNumberFloatType",
240     "kCFNumberDoubleType",
241     "kCFNumberCFIndexType",
242     "kCFNumberNSIntegerType",
243     "kCFNumberCGFloatType"
244   };
245
246   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
247 }
248 #endif
249
250 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
251                                          CheckerContext &C) const {
252   const Expr *Callee = CE->getCallee();
253   const ProgramState *state = C.getState();
254   SVal CallV = state->getSVal(Callee);
255   const FunctionDecl *FD = CallV.getAsFunctionDecl();
256
257   if (!FD)
258     return;
259   
260   ASTContext &Ctx = C.getASTContext();
261   if (!II)
262     II = &Ctx.Idents.get("CFNumberCreate");
263
264   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
265     return;
266
267   // Get the value of the "theType" argument.
268   SVal TheTypeVal = state->getSVal(CE->getArg(1));
269
270   // FIXME: We really should allow ranges of valid theType values, and
271   //   bifurcate the state appropriately.
272   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
273   if (!V)
274     return;
275
276   uint64_t NumberKind = V->getValue().getLimitedValue();
277   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
278
279   // FIXME: In some cases we can emit an error.
280   if (!TargetSize.isKnown())
281     return;
282
283   // Look at the value of the integer being passed by reference.  Essentially
284   // we want to catch cases where the value passed in is not equal to the
285   // size of the type being created.
286   SVal TheValueExpr = state->getSVal(CE->getArg(2));
287
288   // FIXME: Eventually we should handle arbitrary locations.  We can do this
289   //  by having an enhanced memory model that does low-level typing.
290   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
291   if (!LV)
292     return;
293
294   const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
295   if (!R)
296     return;
297
298   QualType T = Ctx.getCanonicalType(R->getValueType());
299
300   // FIXME: If the pointee isn't an integer type, should we flag a warning?
301   //  People can do weird stuff with pointers.
302
303   if (!T->isIntegerType())
304     return;
305
306   uint64_t SourceSize = Ctx.getTypeSize(T);
307
308   // CHECK: is SourceSize == TargetSize
309   if (SourceSize == TargetSize)
310     return;
311
312   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
313   // otherwise generate a regular node.
314   //
315   // FIXME: We can actually create an abstract "CFNumber" object that has
316   //  the bits initialized to the provided values.
317   //
318   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 
319                                                 : C.generateNode()) {
320     llvm::SmallString<128> sbuf;
321     llvm::raw_svector_ostream os(sbuf);
322     
323     os << (SourceSize == 8 ? "An " : "A ")
324        << SourceSize << " bit integer is used to initialize a CFNumber "
325                         "object that represents "
326        << (TargetSize == 8 ? "an " : "a ")
327        << TargetSize << " bit integer. ";
328     
329     if (SourceSize < TargetSize)
330       os << (TargetSize - SourceSize)
331       << " bits of the CFNumber value will be garbage." ;
332     else
333       os << (SourceSize - TargetSize)
334       << " bits of the input integer will be lost.";
335
336     if (!BT)
337       BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
338     
339     BugReport *report = new BugReport(*BT, os.str(), N);
340     report->addRange(CE->getArg(2)->getSourceRange());
341     C.EmitReport(report);
342   }
343 }
344
345 //===----------------------------------------------------------------------===//
346 // CFRetain/CFRelease checking for null arguments.
347 //===----------------------------------------------------------------------===//
348
349 namespace {
350 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
351   mutable llvm::OwningPtr<APIMisuse> BT;
352   mutable IdentifierInfo *Retain, *Release;
353 public:
354   CFRetainReleaseChecker(): Retain(0), Release(0) {}
355   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
356 };
357 } // end anonymous namespace
358
359
360 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
361                                           CheckerContext &C) const {
362   // If the CallExpr doesn't have exactly 1 argument just give up checking.
363   if (CE->getNumArgs() != 1)
364     return;
365
366   // Get the function declaration of the callee.
367   const ProgramState *state = C.getState();
368   SVal X = state->getSVal(CE->getCallee());
369   const FunctionDecl *FD = X.getAsFunctionDecl();
370
371   if (!FD)
372     return;
373   
374   if (!BT) {
375     ASTContext &Ctx = C.getASTContext();
376     Retain = &Ctx.Idents.get("CFRetain");
377     Release = &Ctx.Idents.get("CFRelease");
378     BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
379   }
380
381   // Check if we called CFRetain/CFRelease.
382   const IdentifierInfo *FuncII = FD->getIdentifier();
383   if (!(FuncII == Retain || FuncII == Release))
384     return;
385
386   // FIXME: The rest of this just checks that the argument is non-null.
387   // It should probably be refactored and combined with AttrNonNullChecker.
388
389   // Get the argument's value.
390   const Expr *Arg = CE->getArg(0);
391   SVal ArgVal = state->getSVal(Arg);
392   DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
393   if (!DefArgVal)
394     return;
395
396   // Get a NULL value.
397   SValBuilder &svalBuilder = C.getSValBuilder();
398   DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
399
400   // Make an expression asserting that they're equal.
401   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
402
403   // Are they equal?
404   const ProgramState *stateTrue, *stateFalse;
405   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
406
407   if (stateTrue && !stateFalse) {
408     ExplodedNode *N = C.generateSink(stateTrue);
409     if (!N)
410       return;
411
412     const char *description = (FuncII == Retain)
413                             ? "Null pointer argument in call to CFRetain"
414                             : "Null pointer argument in call to CFRelease";
415
416     BugReport *report = new BugReport(*BT, description, N);
417     report->addRange(Arg->getSourceRange());
418     report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
419     C.EmitReport(report);
420     return;
421   }
422
423   // From here on, we know the argument is non-null.
424   C.addTransition(stateFalse);
425 }
426
427 //===----------------------------------------------------------------------===//
428 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
429 //===----------------------------------------------------------------------===//
430
431 namespace {
432 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
433   mutable Selector releaseS;
434   mutable Selector retainS;
435   mutable Selector autoreleaseS;
436   mutable Selector drainS;
437   mutable llvm::OwningPtr<BugType> BT;
438
439 public:
440   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
441 };
442 }
443
444 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
445                                               CheckerContext &C) const {
446   
447   if (!BT) {
448     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
449                            "instance"));
450   
451     ASTContext &Ctx = C.getASTContext();
452     releaseS = GetNullarySelector("release", Ctx);
453     retainS = GetNullarySelector("retain", Ctx);
454     autoreleaseS = GetNullarySelector("autorelease", Ctx);
455     drainS = GetNullarySelector("drain", Ctx);
456   }
457   
458   if (msg.isInstanceMessage())
459     return;
460   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
461   assert(Class);
462
463   Selector S = msg.getSelector();
464   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
465     return;
466   
467   if (ExplodedNode *N = C.generateNode()) {
468     llvm::SmallString<200> buf;
469     llvm::raw_svector_ostream os(buf);
470
471     os << "The '" << S.getAsString() << "' message should be sent to instances "
472           "of class '" << Class->getName()
473        << "' and not the class directly";
474   
475     BugReport *report = new BugReport(*BT, os.str(), N);
476     report->addRange(msg.getSourceRange());
477     C.EmitReport(report);
478   }
479 }
480
481 //===----------------------------------------------------------------------===//
482 // Check for passing non-Objective-C types to variadic methods that expect
483 // only Objective-C types.
484 //===----------------------------------------------------------------------===//
485
486 namespace {
487 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
488   mutable Selector arrayWithObjectsS;
489   mutable Selector dictionaryWithObjectsAndKeysS;
490   mutable Selector setWithObjectsS;
491   mutable Selector initWithObjectsS;
492   mutable Selector initWithObjectsAndKeysS;
493   mutable llvm::OwningPtr<BugType> BT;
494
495   bool isVariadicMessage(const ObjCMessage &msg) const;
496
497 public:
498   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
499 };
500 }
501
502 /// isVariadicMessage - Returns whether the given message is a variadic message,
503 /// where all arguments must be Objective-C types.
504 bool
505 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
506   const ObjCMethodDecl *MD = msg.getMethodDecl();
507   
508   if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
509     return false;
510   
511   Selector S = msg.getSelector();
512   
513   if (msg.isInstanceMessage()) {
514     // FIXME: Ideally we'd look at the receiver interface here, but that's not
515     // useful for init, because alloc returns 'id'. In theory, this could lead
516     // to false positives, for example if there existed a class that had an
517     // initWithObjects: implementation that does accept non-Objective-C pointer
518     // types, but the chance of that happening is pretty small compared to the
519     // gains that this analysis gives.
520     const ObjCInterfaceDecl *Class = MD->getClassInterface();
521
522     // -[NSArray initWithObjects:]
523     if (isReceiverClassOrSuperclass(Class, "NSArray") &&
524         S == initWithObjectsS)
525       return true;
526
527     // -[NSDictionary initWithObjectsAndKeys:]
528     if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
529         S == initWithObjectsAndKeysS)
530       return true;
531
532     // -[NSSet initWithObjects:]
533     if (isReceiverClassOrSuperclass(Class, "NSSet") &&
534         S == initWithObjectsS)
535       return true;
536   } else {
537     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
538
539     // -[NSArray arrayWithObjects:]
540     if (isReceiverClassOrSuperclass(Class, "NSArray") &&
541         S == arrayWithObjectsS)
542       return true;
543
544     // -[NSDictionary dictionaryWithObjectsAndKeys:]
545     if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
546         S == dictionaryWithObjectsAndKeysS)
547       return true;
548
549     // -[NSSet setWithObjects:]
550     if (isReceiverClassOrSuperclass(Class, "NSSet") &&
551         S == setWithObjectsS)
552       return true;
553   }
554
555   return false;
556 }
557
558 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
559                                                     CheckerContext &C) const {
560   if (!BT) {
561     BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
562                            "Objective-C pointer types"));
563
564     ASTContext &Ctx = C.getASTContext();
565     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
566     dictionaryWithObjectsAndKeysS = 
567       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
568     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
569
570     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
571     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
572   }
573
574   if (!isVariadicMessage(msg))
575       return;
576
577   // We are not interested in the selector arguments since they have
578   // well-defined types, so the compiler will issue a warning for them.
579   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
580
581   // We're not interested in the last argument since it has to be nil or the
582   // compiler would have issued a warning for it elsewhere.
583   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
584
585   if (variadicArgsEnd <= variadicArgsBegin)
586     return;
587
588   // Verify that all arguments have Objective-C types.
589   llvm::Optional<ExplodedNode*> errorNode;
590   const ProgramState *state = C.getState();
591   
592   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
593     QualType ArgTy = msg.getArgType(I);
594     if (ArgTy->isObjCObjectPointerType())
595       continue;
596
597     // Block pointers are treaded as Objective-C pointers.
598     if (ArgTy->isBlockPointerType())
599       continue;
600
601     // Ignore pointer constants.
602     if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
603       continue;
604     
605     // Ignore pointer types annotated with 'NSObject' attribute.
606     if (C.getASTContext().isObjCNSObjectType(ArgTy))
607       continue;
608     
609     // Ignore CF references, which can be toll-free bridged.
610     if (coreFoundation::isCFObjectRef(ArgTy))
611       continue;
612
613     // Generate only one error node to use for all bug reports.
614     if (!errorNode.hasValue()) {
615       errorNode = C.generateNode();
616     }
617
618     if (!errorNode.getValue())
619       continue;
620
621     llvm::SmallString<128> sbuf;
622     llvm::raw_svector_ostream os(sbuf);
623
624     if (const char *TypeName = GetReceiverNameType(msg))
625       os << "Argument to '" << TypeName << "' method '";
626     else
627       os << "Argument to method '";
628
629     os << msg.getSelector().getAsString() 
630       << "' should be an Objective-C pointer type, not '" 
631       << ArgTy.getAsString() << "'";
632
633     BugReport *R = new BugReport(*BT, os.str(),
634                                              errorNode.getValue());
635     R->addRange(msg.getArgSourceRange(I));
636     C.EmitReport(R);
637   }
638 }
639
640 //===----------------------------------------------------------------------===//
641 // Check registration.
642 //===----------------------------------------------------------------------===//
643
644 void ento::registerNilArgChecker(CheckerManager &mgr) {
645   mgr.registerChecker<NilArgChecker>();
646 }
647
648 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
649   mgr.registerChecker<CFNumberCreateChecker>();
650 }
651
652 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
653   mgr.registerChecker<CFRetainReleaseChecker>();
654 }
655
656 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
657   mgr.registerChecker<ClassReleaseChecker>();
658 }
659
660 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
661   mgr.registerChecker<VariadicMethodTypeChecker>();
662 }