clang API Documentation
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 }