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