clang API Documentation

BasicObjCFoundationChecks.cpp
Go to the documentation of this file.
00001 //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This file defines BasicObjCFoundationChecks, a class that encapsulates
00011 //  a set of simple checks to run on Objective-C code using Apple's Foundation
00012 //  classes.
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #include "ClangSACheckers.h"
00017 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
00018 #include "clang/StaticAnalyzer/Core/Checker.h"
00019 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
00020 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
00021 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
00022 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
00023 #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
00024 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
00025 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
00026 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
00027 #include "clang/AST/DeclObjC.h"
00028 #include "clang/AST/Expr.h"
00029 #include "clang/AST/ExprObjC.h"
00030 #include "clang/AST/ASTContext.h"
00031 #include "llvm/ADT/SmallString.h"
00032 
00033 using namespace clang;
00034 using namespace ento;
00035 
00036 namespace {
00037 class APIMisuse : public BugType {
00038 public:
00039   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
00040 };
00041 } // end anonymous namespace
00042 
00043 //===----------------------------------------------------------------------===//
00044 // Utility functions.
00045 //===----------------------------------------------------------------------===//
00046 
00047 static const char* GetReceiverNameType(const ObjCMessage &msg) {
00048   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
00049     return ID->getIdentifier()->getNameStart();
00050   return 0;
00051 }
00052 
00053 static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
00054                                         StringRef ClassName) {
00055   if (ID->getIdentifier()->getName() == ClassName)
00056     return true;
00057 
00058   if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
00059     return isReceiverClassOrSuperclass(Super, ClassName);
00060 
00061   return false;
00062 }
00063 
00064 static inline bool isNil(SVal X) {
00065   return isa<loc::ConcreteInt>(X);
00066 }
00067 
00068 //===----------------------------------------------------------------------===//
00069 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
00070 //===----------------------------------------------------------------------===//
00071 
00072 namespace {
00073   class NilArgChecker : public Checker<check::PreObjCMessage> {
00074     mutable OwningPtr<APIMisuse> BT;
00075 
00076     void WarnNilArg(CheckerContext &C,
00077                     const ObjCMessage &msg, unsigned Arg) const;
00078 
00079   public:
00080     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
00081   };
00082 }
00083 
00084 void NilArgChecker::WarnNilArg(CheckerContext &C,
00085                                const ObjCMessage &msg,
00086                                unsigned int Arg) const
00087 {
00088   if (!BT)
00089     BT.reset(new APIMisuse("nil argument"));
00090   
00091   if (ExplodedNode *N = C.generateSink()) {
00092     SmallString<128> sbuf;
00093     llvm::raw_svector_ostream os(sbuf);
00094     os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
00095        << msg.getSelector().getAsString() << "' cannot be nil";
00096 
00097     BugReport *R = new BugReport(*BT, os.str(), N);
00098     R->addRange(msg.getArgSourceRange(Arg));
00099     C.EmitReport(R);
00100   }
00101 }
00102 
00103 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
00104                                         CheckerContext &C) const {
00105   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
00106   if (!ID)
00107     return;
00108   
00109   if (isReceiverClassOrSuperclass(ID, "NSString")) {
00110     Selector S = msg.getSelector();
00111     
00112     if (S.isUnarySelector())
00113       return;
00114     
00115     // FIXME: This is going to be really slow doing these checks with
00116     //  lexical comparisons.
00117     
00118     std::string NameStr = S.getAsString();
00119     StringRef Name(NameStr);
00120     assert(!Name.empty());
00121     
00122     // FIXME: Checking for initWithFormat: will not work in most cases
00123     //  yet because [NSString alloc] returns id, not NSString*.  We will
00124     //  need support for tracking expected-type information in the analyzer
00125     //  to find these errors.
00126     if (Name == "caseInsensitiveCompare:" ||
00127         Name == "compare:" ||
00128         Name == "compare:options:" ||
00129         Name == "compare:options:range:" ||
00130         Name == "compare:options:range:locale:" ||
00131         Name == "componentsSeparatedByCharactersInSet:" ||
00132         Name == "initWithFormat:") {
00133       if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
00134         WarnNilArg(C, msg, 0);
00135     }
00136   }
00137 }
00138 
00139 //===----------------------------------------------------------------------===//
00140 // Error reporting.
00141 //===----------------------------------------------------------------------===//
00142 
00143 namespace {
00144 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
00145   mutable OwningPtr<APIMisuse> BT;
00146   mutable IdentifierInfo* II;
00147 public:
00148   CFNumberCreateChecker() : II(0) {}
00149 
00150   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
00151 
00152 private:
00153   void EmitError(const TypedRegion* R, const Expr *Ex,
00154                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
00155 };
00156 } // end anonymous namespace
00157 
00158 enum CFNumberType {
00159   kCFNumberSInt8Type = 1,
00160   kCFNumberSInt16Type = 2,
00161   kCFNumberSInt32Type = 3,
00162   kCFNumberSInt64Type = 4,
00163   kCFNumberFloat32Type = 5,
00164   kCFNumberFloat64Type = 6,
00165   kCFNumberCharType = 7,
00166   kCFNumberShortType = 8,
00167   kCFNumberIntType = 9,
00168   kCFNumberLongType = 10,
00169   kCFNumberLongLongType = 11,
00170   kCFNumberFloatType = 12,
00171   kCFNumberDoubleType = 13,
00172   kCFNumberCFIndexType = 14,
00173   kCFNumberNSIntegerType = 15,
00174   kCFNumberCGFloatType = 16
00175 };
00176 
00177 namespace {
00178   template<typename T>
00179   class Optional {
00180     bool IsKnown;
00181     T Val;
00182   public:
00183     Optional() : IsKnown(false), Val(0) {}
00184     Optional(const T& val) : IsKnown(true), Val(val) {}
00185 
00186     bool isKnown() const { return IsKnown; }
00187 
00188     const T& getValue() const {
00189       assert (isKnown());
00190       return Val;
00191     }
00192 
00193     operator const T&() const {
00194       return getValue();
00195     }
00196   };
00197 }
00198 
00199 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
00200   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
00201 
00202   if (i < kCFNumberCharType)
00203     return FixedSize[i-1];
00204 
00205   QualType T;
00206 
00207   switch (i) {
00208     case kCFNumberCharType:     T = Ctx.CharTy;     break;
00209     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
00210     case kCFNumberIntType:      T = Ctx.IntTy;      break;
00211     case kCFNumberLongType:     T = Ctx.LongTy;     break;
00212     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
00213     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
00214     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
00215     case kCFNumberCFIndexType:
00216     case kCFNumberNSIntegerType:
00217     case kCFNumberCGFloatType:
00218       // FIXME: We need a way to map from names to Type*.
00219     default:
00220       return Optional<uint64_t>();
00221   }
00222 
00223   return Ctx.getTypeSize(T);
00224 }
00225 
00226 #if 0
00227 static const char* GetCFNumberTypeStr(uint64_t i) {
00228   static const char* Names[] = {
00229     "kCFNumberSInt8Type",
00230     "kCFNumberSInt16Type",
00231     "kCFNumberSInt32Type",
00232     "kCFNumberSInt64Type",
00233     "kCFNumberFloat32Type",
00234     "kCFNumberFloat64Type",
00235     "kCFNumberCharType",
00236     "kCFNumberShortType",
00237     "kCFNumberIntType",
00238     "kCFNumberLongType",
00239     "kCFNumberLongLongType",
00240     "kCFNumberFloatType",
00241     "kCFNumberDoubleType",
00242     "kCFNumberCFIndexType",
00243     "kCFNumberNSIntegerType",
00244     "kCFNumberCGFloatType"
00245   };
00246 
00247   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
00248 }
00249 #endif
00250 
00251 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
00252                                          CheckerContext &C) const {
00253   ProgramStateRef state = C.getState();
00254   const FunctionDecl *FD = C.getCalleeDecl(CE);
00255   if (!FD)
00256     return;
00257   
00258   ASTContext &Ctx = C.getASTContext();
00259   if (!II)
00260     II = &Ctx.Idents.get("CFNumberCreate");
00261 
00262   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
00263     return;
00264 
00265   // Get the value of the "theType" argument.
00266   const LocationContext *LCtx = C.getLocationContext();
00267   SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
00268 
00269   // FIXME: We really should allow ranges of valid theType values, and
00270   //   bifurcate the state appropriately.
00271   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
00272   if (!V)
00273     return;
00274 
00275   uint64_t NumberKind = V->getValue().getLimitedValue();
00276   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
00277 
00278   // FIXME: In some cases we can emit an error.
00279   if (!TargetSize.isKnown())
00280     return;
00281 
00282   // Look at the value of the integer being passed by reference.  Essentially
00283   // we want to catch cases where the value passed in is not equal to the
00284   // size of the type being created.
00285   SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
00286 
00287   // FIXME: Eventually we should handle arbitrary locations.  We can do this
00288   //  by having an enhanced memory model that does low-level typing.
00289   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
00290   if (!LV)
00291     return;
00292 
00293   const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
00294   if (!R)
00295     return;
00296 
00297   QualType T = Ctx.getCanonicalType(R->getValueType());
00298 
00299   // FIXME: If the pointee isn't an integer type, should we flag a warning?
00300   //  People can do weird stuff with pointers.
00301 
00302   if (!T->isIntegerType())
00303     return;
00304 
00305   uint64_t SourceSize = Ctx.getTypeSize(T);
00306 
00307   // CHECK: is SourceSize == TargetSize
00308   if (SourceSize == TargetSize)
00309     return;
00310 
00311   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
00312   // otherwise generate a regular node.
00313   //
00314   // FIXME: We can actually create an abstract "CFNumber" object that has
00315   //  the bits initialized to the provided values.
00316   //
00317   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 
00318                                                 : C.addTransition()) {
00319     SmallString<128> sbuf;
00320     llvm::raw_svector_ostream os(sbuf);
00321     
00322     os << (SourceSize == 8 ? "An " : "A ")
00323        << SourceSize << " bit integer is used to initialize a CFNumber "
00324                         "object that represents "
00325        << (TargetSize == 8 ? "an " : "a ")
00326        << TargetSize << " bit integer. ";
00327     
00328     if (SourceSize < TargetSize)
00329       os << (TargetSize - SourceSize)
00330       << " bits of the CFNumber value will be garbage." ;
00331     else
00332       os << (SourceSize - TargetSize)
00333       << " bits of the input integer will be lost.";
00334 
00335     if (!BT)
00336       BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
00337     
00338     BugReport *report = new BugReport(*BT, os.str(), N);
00339     report->addRange(CE->getArg(2)->getSourceRange());
00340     C.EmitReport(report);
00341   }
00342 }
00343 
00344 //===----------------------------------------------------------------------===//
00345 // CFRetain/CFRelease checking for null arguments.
00346 //===----------------------------------------------------------------------===//
00347 
00348 namespace {
00349 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
00350   mutable OwningPtr<APIMisuse> BT;
00351   mutable IdentifierInfo *Retain, *Release;
00352 public:
00353   CFRetainReleaseChecker(): Retain(0), Release(0) {}
00354   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
00355 };
00356 } // end anonymous namespace
00357 
00358 
00359 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
00360                                           CheckerContext &C) const {
00361   // If the CallExpr doesn't have exactly 1 argument just give up checking.
00362   if (CE->getNumArgs() != 1)
00363     return;
00364 
00365   ProgramStateRef state = C.getState();
00366   const FunctionDecl *FD = C.getCalleeDecl(CE);
00367   if (!FD)
00368     return;
00369   
00370   if (!BT) {
00371     ASTContext &Ctx = C.getASTContext();
00372     Retain = &Ctx.Idents.get("CFRetain");
00373     Release = &Ctx.Idents.get("CFRelease");
00374     BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
00375   }
00376 
00377   // Check if we called CFRetain/CFRelease.
00378   const IdentifierInfo *FuncII = FD->getIdentifier();
00379   if (!(FuncII == Retain || FuncII == Release))
00380     return;
00381 
00382   // FIXME: The rest of this just checks that the argument is non-null.
00383   // It should probably be refactored and combined with AttrNonNullChecker.
00384 
00385   // Get the argument's value.
00386   const Expr *Arg = CE->getArg(0);
00387   SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
00388   DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
00389   if (!DefArgVal)
00390     return;
00391 
00392   // Get a NULL value.
00393   SValBuilder &svalBuilder = C.getSValBuilder();
00394   DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
00395 
00396   // Make an expression asserting that they're equal.
00397   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
00398 
00399   // Are they equal?
00400   ProgramStateRef stateTrue, stateFalse;
00401   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
00402 
00403   if (stateTrue && !stateFalse) {
00404     ExplodedNode *N = C.generateSink(stateTrue);
00405     if (!N)
00406       return;
00407 
00408     const char *description = (FuncII == Retain)
00409                             ? "Null pointer argument in call to CFRetain"
00410                             : "Null pointer argument in call to CFRelease";
00411 
00412     BugReport *report = new BugReport(*BT, description, N);
00413     report->addRange(Arg->getSourceRange());
00414     report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg,
00415                                                                     report));
00416     C.EmitReport(report);
00417     return;
00418   }
00419 
00420   // From here on, we know the argument is non-null.
00421   C.addTransition(stateFalse);
00422 }
00423 
00424 //===----------------------------------------------------------------------===//
00425 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
00426 //===----------------------------------------------------------------------===//
00427 
00428 namespace {
00429 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
00430   mutable Selector releaseS;
00431   mutable Selector retainS;
00432   mutable Selector autoreleaseS;
00433   mutable Selector drainS;
00434   mutable OwningPtr<BugType> BT;
00435 
00436 public:
00437   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
00438 };
00439 }
00440 
00441 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
00442                                               CheckerContext &C) const {
00443   
00444   if (!BT) {
00445     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
00446                            "instance"));
00447   
00448     ASTContext &Ctx = C.getASTContext();
00449     releaseS = GetNullarySelector("release", Ctx);
00450     retainS = GetNullarySelector("retain", Ctx);
00451     autoreleaseS = GetNullarySelector("autorelease", Ctx);
00452     drainS = GetNullarySelector("drain", Ctx);
00453   }
00454   
00455   if (msg.isInstanceMessage())
00456     return;
00457   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
00458   assert(Class);
00459 
00460   Selector S = msg.getSelector();
00461   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
00462     return;
00463   
00464   if (ExplodedNode *N = C.addTransition()) {
00465     SmallString<200> buf;
00466     llvm::raw_svector_ostream os(buf);
00467 
00468     os << "The '" << S.getAsString() << "' message should be sent to instances "
00469           "of class '" << Class->getName()
00470        << "' and not the class directly";
00471   
00472     BugReport *report = new BugReport(*BT, os.str(), N);
00473     report->addRange(msg.getSourceRange());
00474     C.EmitReport(report);
00475   }
00476 }
00477 
00478 //===----------------------------------------------------------------------===//
00479 // Check for passing non-Objective-C types to variadic methods that expect
00480 // only Objective-C types.
00481 //===----------------------------------------------------------------------===//
00482 
00483 namespace {
00484 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
00485   mutable Selector arrayWithObjectsS;
00486   mutable Selector dictionaryWithObjectsAndKeysS;
00487   mutable Selector setWithObjectsS;
00488   mutable Selector orderedSetWithObjectsS;
00489   mutable Selector initWithObjectsS;
00490   mutable Selector initWithObjectsAndKeysS;
00491   mutable OwningPtr<BugType> BT;
00492 
00493   bool isVariadicMessage(const ObjCMessage &msg) const;
00494 
00495 public:
00496   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
00497 };
00498 }
00499 
00500 /// isVariadicMessage - Returns whether the given message is a variadic message,
00501 /// where all arguments must be Objective-C types.
00502 bool
00503 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
00504   const ObjCMethodDecl *MD = msg.getMethodDecl();
00505   
00506   if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
00507     return false;
00508   
00509   Selector S = msg.getSelector();
00510   
00511   if (msg.isInstanceMessage()) {
00512     // FIXME: Ideally we'd look at the receiver interface here, but that's not
00513     // useful for init, because alloc returns 'id'. In theory, this could lead
00514     // to false positives, for example if there existed a class that had an
00515     // initWithObjects: implementation that does accept non-Objective-C pointer
00516     // types, but the chance of that happening is pretty small compared to the
00517     // gains that this analysis gives.
00518     const ObjCInterfaceDecl *Class = MD->getClassInterface();
00519 
00520     // -[NSArray initWithObjects:]
00521     if (isReceiverClassOrSuperclass(Class, "NSArray") &&
00522         S == initWithObjectsS)
00523       return true;
00524 
00525     // -[NSDictionary initWithObjectsAndKeys:]
00526     if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
00527         S == initWithObjectsAndKeysS)
00528       return true;
00529 
00530     // -[NSSet initWithObjects:]
00531     if (isReceiverClassOrSuperclass(Class, "NSSet") &&
00532         S == initWithObjectsS)
00533       return true;
00534 
00535     // -[NSOrderedSet initWithObjects:]
00536     if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
00537         S == initWithObjectsS)
00538       return true;
00539   } else {
00540     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
00541 
00542     // -[NSArray arrayWithObjects:]
00543     if (isReceiverClassOrSuperclass(Class, "NSArray") &&
00544         S == arrayWithObjectsS)
00545       return true;
00546 
00547     // -[NSDictionary dictionaryWithObjectsAndKeys:]
00548     if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
00549         S == dictionaryWithObjectsAndKeysS)
00550       return true;
00551 
00552     // -[NSSet setWithObjects:]
00553     if (isReceiverClassOrSuperclass(Class, "NSSet") &&
00554         S == setWithObjectsS)
00555       return true;
00556 
00557     // -[NSOrderedSet orderedSetWithObjects:]
00558     if (isReceiverClassOrSuperclass(Class, "NSOrderedSet") &&
00559         S == orderedSetWithObjectsS)
00560       return true;
00561   }
00562 
00563   return false;
00564 }
00565 
00566 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
00567                                                     CheckerContext &C) const {
00568   if (!BT) {
00569     BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
00570                            "Objective-C pointer types"));
00571 
00572     ASTContext &Ctx = C.getASTContext();
00573     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
00574     dictionaryWithObjectsAndKeysS = 
00575       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
00576     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
00577     orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
00578 
00579     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
00580     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
00581   }
00582 
00583   if (!isVariadicMessage(msg))
00584       return;
00585 
00586   // We are not interested in the selector arguments since they have
00587   // well-defined types, so the compiler will issue a warning for them.
00588   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
00589 
00590   // We're not interested in the last argument since it has to be nil or the
00591   // compiler would have issued a warning for it elsewhere.
00592   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
00593 
00594   if (variadicArgsEnd <= variadicArgsBegin)
00595     return;
00596 
00597   // Verify that all arguments have Objective-C types.
00598   llvm::Optional<ExplodedNode*> errorNode;
00599   ProgramStateRef state = C.getState();
00600   
00601   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
00602     QualType ArgTy = msg.getArgType(I);
00603     if (ArgTy->isObjCObjectPointerType())
00604       continue;
00605 
00606     // Block pointers are treaded as Objective-C pointers.
00607     if (ArgTy->isBlockPointerType())
00608       continue;
00609 
00610     // Ignore pointer constants.
00611     if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
00612                                              state)))
00613       continue;
00614     
00615     // Ignore pointer types annotated with 'NSObject' attribute.
00616     if (C.getASTContext().isObjCNSObjectType(ArgTy))
00617       continue;
00618     
00619     // Ignore CF references, which can be toll-free bridged.
00620     if (coreFoundation::isCFObjectRef(ArgTy))
00621       continue;
00622 
00623     // Generate only one error node to use for all bug reports.
00624     if (!errorNode.hasValue()) {
00625       errorNode = C.addTransition();
00626     }
00627 
00628     if (!errorNode.getValue())
00629       continue;
00630 
00631     SmallString<128> sbuf;
00632     llvm::raw_svector_ostream os(sbuf);
00633 
00634     if (const char *TypeName = GetReceiverNameType(msg))
00635       os << "Argument to '" << TypeName << "' method '";
00636     else
00637       os << "Argument to method '";
00638 
00639     os << msg.getSelector().getAsString() 
00640       << "' should be an Objective-C pointer type, not '" 
00641       << ArgTy.getAsString() << "'";
00642 
00643     BugReport *R = new BugReport(*BT, os.str(),
00644                                              errorNode.getValue());
00645     R->addRange(msg.getArgSourceRange(I));
00646     C.EmitReport(R);
00647   }
00648 }
00649 
00650 //===----------------------------------------------------------------------===//
00651 // Check registration.
00652 //===----------------------------------------------------------------------===//
00653 
00654 void ento::registerNilArgChecker(CheckerManager &mgr) {
00655   mgr.registerChecker<NilArgChecker>();
00656 }
00657 
00658 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
00659   mgr.registerChecker<CFNumberCreateChecker>();
00660 }
00661 
00662 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
00663   mgr.registerChecker<CFRetainReleaseChecker>();
00664 }
00665 
00666 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
00667   mgr.registerChecker<ClassReleaseChecker>();
00668 }
00669 
00670 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
00671   mgr.registerChecker<VariadicMethodTypeChecker>();
00672 }