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