BasicObjCFoundationChecks.cpp revision 226633
114895Swollman//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- 214895Swollman// 314895Swollman// The LLVM Compiler Infrastructure 414895Swollman// 514895Swollman// This file is distributed under the University of Illinois Open Source 614895Swollman// License. See LICENSE.TXT for details. 714895Swollman// 814895Swollman//===----------------------------------------------------------------------===// 914895Swollman// 1014895Swollman// This file defines BasicObjCFoundationChecks, a class that encapsulates 1114895Swollman// a set of simple checks to run on Objective-C code using Apple's Foundation 1214895Swollman// classes. 1314895Swollman// 1414895Swollman//===----------------------------------------------------------------------===// 1514895Swollman 1614895Swollman#include "ClangSACheckers.h" 1714895Swollman#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 1814895Swollman#include "clang/StaticAnalyzer/Core/Checker.h" 1914895Swollman#include "clang/StaticAnalyzer/Core/CheckerManager.h" 2014895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 2114895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 2214895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 2314895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h" 2414895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 2514895Swollman#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 2614895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 2714895Swollman#include "clang/AST/DeclObjC.h" 2814895Swollman#include "clang/AST/Expr.h" 2950476Speter#include "clang/AST/ExprObjC.h" 3014895Swollman#include "clang/AST/ASTContext.h" 3114895Swollman 3214895Swollmanusing namespace clang; 3314895Swollmanusing namespace ento; 3414895Swollman 35253750Savgnamespace { 3614895Swollmanclass APIMisuse : public BugType { 3714895Swollmanpublic: 3814895Swollman APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 3914895Swollman}; 4014895Swollman} // end anonymous namespace 4114895Swollman 4214895Swollman//===----------------------------------------------------------------------===// 4314895Swollman// Utility functions. 4414895Swollman//===----------------------------------------------------------------------===// 4514895Swollman 4614895Swollmanstatic const char* GetReceiverNameType(const ObjCMessage &msg) { 4714895Swollman if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 4827085Sbde return ID->getIdentifier()->getNameStart(); 4914895Swollman return 0; 5014895Swollman} 5114895Swollman 5214895Swollmanstatic bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID, 5314895Swollman StringRef ClassName) { 5414895Swollman if (ID->getIdentifier()->getName() == ClassName) 5527085Sbde return true; 5614895Swollman 5714895Swollman if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 5814895Swollman return isReceiverClassOrSuperclass(Super, ClassName); 5914895Swollman 6014895Swollman return false; 6114895Swollman} 6214895Swollman 6314895Swollmanstatic inline bool isNil(SVal X) { 6414895Swollman return isa<loc::ConcreteInt>(X); 6514895Swollman} 6614895Swollman 6727085Sbde//===----------------------------------------------------------------------===// 6814895Swollman// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 6914895Swollman//===----------------------------------------------------------------------===// 7014895Swollman 7127085Sbdenamespace { 7214895Swollman class NilArgChecker : public Checker<check::PreObjCMessage> { 7314895Swollman mutable llvm::OwningPtr<APIMisuse> BT; 7414895Swollman 7514895Swollman void WarnNilArg(CheckerContext &C, 7614895Swollman const ObjCMessage &msg, unsigned Arg) const; 7714895Swollman 7814895Swollman public: 7914895Swollman void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 8014895Swollman }; 8114895Swollman} 8214895Swollman 8314895Swollmanvoid NilArgChecker::WarnNilArg(CheckerContext &C, 8414895Swollman const ObjCMessage &msg, 8514895Swollman unsigned int Arg) const 8614895Swollman{ 8714895Swollman if (!BT) 8814895Swollman BT.reset(new APIMisuse("nil argument")); 8914895Swollman 9014895Swollman if (ExplodedNode *N = C.generateSink()) { 9114895Swollman llvm::SmallString<128> sbuf; 9214895Swollman llvm::raw_svector_ostream os(sbuf); 9314895Swollman os << "Argument to '" << GetReceiverNameType(msg) << "' method '" 9414895Swollman << msg.getSelector().getAsString() << "' cannot be nil"; 9514895Swollman 9614895Swollman BugReport *R = new BugReport(*BT, os.str(), N); 9727085Sbde R->addRange(msg.getArgSourceRange(Arg)); 9827085Sbde C.EmitReport(R); 9927085Sbde } 10014895Swollman} 10114895Swollman 10214895Swollmanvoid NilArgChecker::checkPreObjCMessage(ObjCMessage msg, 10314895Swollman CheckerContext &C) const { 10414895Swollman const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 10514895Swollman if (!ID) 10614895Swollman return; 10714895Swollman 10814895Swollman if (isReceiverClassOrSuperclass(ID, "NSString")) { 10914895Swollman Selector S = msg.getSelector(); 11014895Swollman 11114895Swollman if (S.isUnarySelector()) 11214895Swollman return; 11314895Swollman 11414895Swollman // FIXME: This is going to be really slow doing these checks with 11514895Swollman // lexical comparisons. 11614895Swollman 11714895Swollman std::string NameStr = S.getAsString(); 11814895Swollman StringRef Name(NameStr); 11914895Swollman assert(!Name.empty()); 12014895Swollman 12114895Swollman // FIXME: Checking for initWithFormat: will not work in most cases 12214895Swollman // yet because [NSString alloc] returns id, not NSString*. We will 12314895Swollman // need support for tracking expected-type information in the analyzer 12414895Swollman // to find these errors. 12514895Swollman if (Name == "caseInsensitiveCompare:" || 12614895Swollman Name == "compare:" || 12714895Swollman Name == "compare:options:" || 12814895Swollman Name == "compare:options:range:" || 12914895Swollman Name == "compare:options:range:locale:" || 13014895Swollman Name == "componentsSeparatedByCharactersInSet:" || 13114895Swollman Name == "initWithFormat:") { 13214895Swollman if (isNil(msg.getArgSVal(0, C.getState()))) 13314895Swollman WarnNilArg(C, msg, 0); 13414895Swollman } 13514895Swollman } 13614895Swollman} 13714895Swollman 13814895Swollman//===----------------------------------------------------------------------===// 13927085Sbde// Error reporting. 14027085Sbde//===----------------------------------------------------------------------===// 14127085Sbde 14227085Sbdenamespace { 14314895Swollmanclass CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 14414895Swollman mutable llvm::OwningPtr<APIMisuse> BT; 14514895Swollman mutable IdentifierInfo* II; 14614895Swollmanpublic: 14714895Swollman CFNumberCreateChecker() : II(0) {} 14814895Swollman 14914895Swollman void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 15014895Swollman 15114895Swollmanprivate: 15214895Swollman void EmitError(const TypedRegion* R, const Expr *Ex, 15314895Swollman uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 15414895Swollman}; 15514895Swollman} // end anonymous namespace 15614895Swollman 15714895Swollmanenum CFNumberType { 15814895Swollman kCFNumberSInt8Type = 1, 15914895Swollman kCFNumberSInt16Type = 2, 16014895Swollman kCFNumberSInt32Type = 3, 16114895Swollman kCFNumberSInt64Type = 4, 16214895Swollman kCFNumberFloat32Type = 5, 16314895Swollman kCFNumberFloat64Type = 6, 16414895Swollman kCFNumberCharType = 7, 16514895Swollman kCFNumberShortType = 8, 16614895Swollman kCFNumberIntType = 9, 16714895Swollman kCFNumberLongType = 10, 16814895Swollman kCFNumberLongLongType = 11, 16914895Swollman kCFNumberFloatType = 12, 17014895Swollman kCFNumberDoubleType = 13, 17114895Swollman kCFNumberCFIndexType = 14, 17214895Swollman kCFNumberNSIntegerType = 15, 17314895Swollman kCFNumberCGFloatType = 16 17414895Swollman}; 17514895Swollman 176209455Skevlonamespace { 17714895Swollman template<typename T> 17814895Swollman class Optional { 17914895Swollman bool IsKnown; 180209455Skevlo T Val; 181209455Skevlo public: 182209455Skevlo Optional() : IsKnown(false), Val(0) {} 18314895Swollman Optional(const T& val) : IsKnown(true), Val(val) {} 18414895Swollman 18514895Swollman bool isKnown() const { return IsKnown; } 18614895Swollman 18714895Swollman const T& getValue() const { 18814895Swollman assert (isKnown()); 18927085Sbde return Val; 19027085Sbde } 19127085Sbde 19214895Swollman operator const T&() const { 19314895Swollman return getValue(); 194 } 195 }; 196} 197 198static 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 226static 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 250void 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 349namespace { 350class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 351 mutable llvm::OwningPtr<APIMisuse> BT; 352 mutable IdentifierInfo *Retain, *Release; 353public: 354 CFRetainReleaseChecker(): Retain(0), Release(0) {} 355 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 356}; 357} // end anonymous namespace 358 359 360void 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 431namespace { 432class 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 439public: 440 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const; 441}; 442} 443 444void 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 486namespace { 487class 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 497public: 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. 504bool 505VariadicMethodTypeChecker::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 558void 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 644void ento::registerNilArgChecker(CheckerManager &mgr) { 645 mgr.registerChecker<NilArgChecker>(); 646} 647 648void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 649 mgr.registerChecker<CFNumberCreateChecker>(); 650} 651 652void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 653 mgr.registerChecker<CFRetainReleaseChecker>(); 654} 655 656void ento::registerClassReleaseChecker(CheckerManager &mgr) { 657 mgr.registerChecker<ClassReleaseChecker>(); 658} 659 660void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 661 mgr.registerChecker<VariadicMethodTypeChecker>(); 662} 663