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 
     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 }
    663