BasicObjCFoundationChecks.cpp revision 55fc873017f10f6f566b182b70f6fc22aefa3464
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/AST/ASTContext.h" 18#include "clang/AST/DeclObjC.h" 19#include "clang/AST/Expr.h" 20#include "clang/AST/ExprObjC.h" 21#include "clang/AST/StmtObjC.h" 22#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 23#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 24#include "clang/StaticAnalyzer/Core/Checker.h" 25#include "clang/StaticAnalyzer/Core/CheckerManager.h" 26#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 27#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 28#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 29#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 30#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 31#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 32#include "llvm/ADT/SmallString.h" 33#include "llvm/ADT/StringMap.h" 34#include "llvm/Support/raw_ostream.h" 35 36using namespace clang; 37using namespace ento; 38 39namespace { 40class APIMisuse : public BugType { 41public: 42 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} 43}; 44} // end anonymous namespace 45 46//===----------------------------------------------------------------------===// 47// Utility functions. 48//===----------------------------------------------------------------------===// 49 50static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { 51 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 52 return ID->getIdentifier()->getName(); 53 return StringRef(); 54} 55 56enum FoundationClass { 57 FC_None, 58 FC_NSArray, 59 FC_NSDictionary, 60 FC_NSEnumerator, 61 FC_NSOrderedSet, 62 FC_NSSet, 63 FC_NSString 64}; 65 66static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) { 67 static llvm::StringMap<FoundationClass> Classes; 68 if (Classes.empty()) { 69 Classes["NSArray"] = FC_NSArray; 70 Classes["NSDictionary"] = FC_NSDictionary; 71 Classes["NSEnumerator"] = FC_NSEnumerator; 72 Classes["NSOrderedSet"] = FC_NSOrderedSet; 73 Classes["NSSet"] = FC_NSSet; 74 Classes["NSString"] = FC_NSString; 75 } 76 77 // FIXME: Should we cache this at all? 78 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); 79 if (result == FC_None) 80 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 81 return findKnownClass(Super); 82 83 return result; 84} 85 86static inline bool isNil(SVal X) { 87 return isa<loc::ConcreteInt>(X); 88} 89 90//===----------------------------------------------------------------------===// 91// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 92//===----------------------------------------------------------------------===// 93 94namespace { 95 class NilArgChecker : public Checker<check::PreObjCMessage> { 96 mutable OwningPtr<APIMisuse> BT; 97 98 void WarnNilArg(CheckerContext &C, 99 const ObjCMethodCall &msg, unsigned Arg) const; 100 101 public: 102 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 103 }; 104} 105 106void NilArgChecker::WarnNilArg(CheckerContext &C, 107 const ObjCMethodCall &msg, 108 unsigned int Arg) const 109{ 110 if (!BT) 111 BT.reset(new APIMisuse("nil argument")); 112 113 if (ExplodedNode *N = C.generateSink()) { 114 SmallString<128> sbuf; 115 llvm::raw_svector_ostream os(sbuf); 116 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '" 117 << msg.getSelector().getAsString() << "' cannot be nil"; 118 119 BugReport *R = new BugReport(*BT, os.str(), N); 120 R->addRange(msg.getArgSourceRange(Arg)); 121 C.emitReport(R); 122 } 123} 124 125void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 126 CheckerContext &C) const { 127 const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 128 if (!ID) 129 return; 130 131 if (findKnownClass(ID) == FC_NSString) { 132 Selector S = msg.getSelector(); 133 134 if (S.isUnarySelector()) 135 return; 136 137 // FIXME: This is going to be really slow doing these checks with 138 // lexical comparisons. 139 140 std::string NameStr = S.getAsString(); 141 StringRef Name(NameStr); 142 assert(!Name.empty()); 143 144 // FIXME: Checking for initWithFormat: will not work in most cases 145 // yet because [NSString alloc] returns id, not NSString*. We will 146 // need support for tracking expected-type information in the analyzer 147 // to find these errors. 148 if (Name == "caseInsensitiveCompare:" || 149 Name == "compare:" || 150 Name == "compare:options:" || 151 Name == "compare:options:range:" || 152 Name == "compare:options:range:locale:" || 153 Name == "componentsSeparatedByCharactersInSet:" || 154 Name == "initWithFormat:") { 155 if (isNil(msg.getArgSVal(0))) 156 WarnNilArg(C, msg, 0); 157 } 158 } 159} 160 161//===----------------------------------------------------------------------===// 162// Error reporting. 163//===----------------------------------------------------------------------===// 164 165namespace { 166class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 167 mutable OwningPtr<APIMisuse> BT; 168 mutable IdentifierInfo* II; 169public: 170 CFNumberCreateChecker() : II(0) {} 171 172 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 173 174private: 175 void EmitError(const TypedRegion* R, const Expr *Ex, 176 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 177}; 178} // end anonymous namespace 179 180enum CFNumberType { 181 kCFNumberSInt8Type = 1, 182 kCFNumberSInt16Type = 2, 183 kCFNumberSInt32Type = 3, 184 kCFNumberSInt64Type = 4, 185 kCFNumberFloat32Type = 5, 186 kCFNumberFloat64Type = 6, 187 kCFNumberCharType = 7, 188 kCFNumberShortType = 8, 189 kCFNumberIntType = 9, 190 kCFNumberLongType = 10, 191 kCFNumberLongLongType = 11, 192 kCFNumberFloatType = 12, 193 kCFNumberDoubleType = 13, 194 kCFNumberCFIndexType = 14, 195 kCFNumberNSIntegerType = 15, 196 kCFNumberCGFloatType = 16 197}; 198 199namespace { 200 template<typename T> 201 class Optional { 202 bool IsKnown; 203 T Val; 204 public: 205 Optional() : IsKnown(false), Val(0) {} 206 Optional(const T& val) : IsKnown(true), Val(val) {} 207 208 bool isKnown() const { return IsKnown; } 209 210 const T& getValue() const { 211 assert (isKnown()); 212 return Val; 213 } 214 215 operator const T&() const { 216 return getValue(); 217 } 218 }; 219} 220 221static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 222 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 223 224 if (i < kCFNumberCharType) 225 return FixedSize[i-1]; 226 227 QualType T; 228 229 switch (i) { 230 case kCFNumberCharType: T = Ctx.CharTy; break; 231 case kCFNumberShortType: T = Ctx.ShortTy; break; 232 case kCFNumberIntType: T = Ctx.IntTy; break; 233 case kCFNumberLongType: T = Ctx.LongTy; break; 234 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 235 case kCFNumberFloatType: T = Ctx.FloatTy; break; 236 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 237 case kCFNumberCFIndexType: 238 case kCFNumberNSIntegerType: 239 case kCFNumberCGFloatType: 240 // FIXME: We need a way to map from names to Type*. 241 default: 242 return Optional<uint64_t>(); 243 } 244 245 return Ctx.getTypeSize(T); 246} 247 248#if 0 249static const char* GetCFNumberTypeStr(uint64_t i) { 250 static const char* Names[] = { 251 "kCFNumberSInt8Type", 252 "kCFNumberSInt16Type", 253 "kCFNumberSInt32Type", 254 "kCFNumberSInt64Type", 255 "kCFNumberFloat32Type", 256 "kCFNumberFloat64Type", 257 "kCFNumberCharType", 258 "kCFNumberShortType", 259 "kCFNumberIntType", 260 "kCFNumberLongType", 261 "kCFNumberLongLongType", 262 "kCFNumberFloatType", 263 "kCFNumberDoubleType", 264 "kCFNumberCFIndexType", 265 "kCFNumberNSIntegerType", 266 "kCFNumberCGFloatType" 267 }; 268 269 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 270} 271#endif 272 273void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 274 CheckerContext &C) const { 275 ProgramStateRef state = C.getState(); 276 const FunctionDecl *FD = C.getCalleeDecl(CE); 277 if (!FD) 278 return; 279 280 ASTContext &Ctx = C.getASTContext(); 281 if (!II) 282 II = &Ctx.Idents.get("CFNumberCreate"); 283 284 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 285 return; 286 287 // Get the value of the "theType" argument. 288 const LocationContext *LCtx = C.getLocationContext(); 289 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); 290 291 // FIXME: We really should allow ranges of valid theType values, and 292 // bifurcate the state appropriately. 293 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal); 294 if (!V) 295 return; 296 297 uint64_t NumberKind = V->getValue().getLimitedValue(); 298 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind); 299 300 // FIXME: In some cases we can emit an error. 301 if (!TargetSize.isKnown()) 302 return; 303 304 // Look at the value of the integer being passed by reference. Essentially 305 // we want to catch cases where the value passed in is not equal to the 306 // size of the type being created. 307 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); 308 309 // FIXME: Eventually we should handle arbitrary locations. We can do this 310 // by having an enhanced memory model that does low-level typing. 311 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr); 312 if (!LV) 313 return; 314 315 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 316 if (!R) 317 return; 318 319 QualType T = Ctx.getCanonicalType(R->getValueType()); 320 321 // FIXME: If the pointee isn't an integer type, should we flag a warning? 322 // People can do weird stuff with pointers. 323 324 if (!T->isIntegerType()) 325 return; 326 327 uint64_t SourceSize = Ctx.getTypeSize(T); 328 329 // CHECK: is SourceSize == TargetSize 330 if (SourceSize == TargetSize) 331 return; 332 333 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 334 // otherwise generate a regular node. 335 // 336 // FIXME: We can actually create an abstract "CFNumber" object that has 337 // the bits initialized to the provided values. 338 // 339 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 340 : C.addTransition()) { 341 SmallString<128> sbuf; 342 llvm::raw_svector_ostream os(sbuf); 343 344 os << (SourceSize == 8 ? "An " : "A ") 345 << SourceSize << " bit integer is used to initialize a CFNumber " 346 "object that represents " 347 << (TargetSize == 8 ? "an " : "a ") 348 << TargetSize << " bit integer. "; 349 350 if (SourceSize < TargetSize) 351 os << (TargetSize - SourceSize) 352 << " bits of the CFNumber value will be garbage." ; 353 else 354 os << (SourceSize - TargetSize) 355 << " bits of the input integer will be lost."; 356 357 if (!BT) 358 BT.reset(new APIMisuse("Bad use of CFNumberCreate")); 359 360 BugReport *report = new BugReport(*BT, os.str(), N); 361 report->addRange(CE->getArg(2)->getSourceRange()); 362 C.emitReport(report); 363 } 364} 365 366//===----------------------------------------------------------------------===// 367// CFRetain/CFRelease/CFMakeCollectable checking for null arguments. 368//===----------------------------------------------------------------------===// 369 370namespace { 371class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 372 mutable OwningPtr<APIMisuse> BT; 373 mutable IdentifierInfo *Retain, *Release, *MakeCollectable; 374public: 375 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {} 376 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 377}; 378} // end anonymous namespace 379 380 381void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, 382 CheckerContext &C) const { 383 // If the CallExpr doesn't have exactly 1 argument just give up checking. 384 if (CE->getNumArgs() != 1) 385 return; 386 387 ProgramStateRef state = C.getState(); 388 const FunctionDecl *FD = C.getCalleeDecl(CE); 389 if (!FD) 390 return; 391 392 if (!BT) { 393 ASTContext &Ctx = C.getASTContext(); 394 Retain = &Ctx.Idents.get("CFRetain"); 395 Release = &Ctx.Idents.get("CFRelease"); 396 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); 397 BT.reset( 398 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable")); 399 } 400 401 // Check if we called CFRetain/CFRelease/CFMakeCollectable. 402 const IdentifierInfo *FuncII = FD->getIdentifier(); 403 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable)) 404 return; 405 406 // FIXME: The rest of this just checks that the argument is non-null. 407 // It should probably be refactored and combined with AttrNonNullChecker. 408 409 // Get the argument's value. 410 const Expr *Arg = CE->getArg(0); 411 SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); 412 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal); 413 if (!DefArgVal) 414 return; 415 416 // Get a NULL value. 417 SValBuilder &svalBuilder = C.getSValBuilder(); 418 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType())); 419 420 // Make an expression asserting that they're equal. 421 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 422 423 // Are they equal? 424 ProgramStateRef stateTrue, stateFalse; 425 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 426 427 if (stateTrue && !stateFalse) { 428 ExplodedNode *N = C.generateSink(stateTrue); 429 if (!N) 430 return; 431 432 const char *description; 433 if (FuncII == Retain) 434 description = "Null pointer argument in call to CFRetain"; 435 else if (FuncII == Release) 436 description = "Null pointer argument in call to CFRelease"; 437 else if (FuncII == MakeCollectable) 438 description = "Null pointer argument in call to CFMakeCollectable"; 439 else 440 llvm_unreachable("impossible case"); 441 442 BugReport *report = new BugReport(*BT, description, N); 443 report->addRange(Arg->getSourceRange()); 444 bugreporter::trackNullOrUndefValue(N, Arg, *report); 445 C.emitReport(report); 446 return; 447 } 448 449 // From here on, we know the argument is non-null. 450 C.addTransition(stateFalse); 451} 452 453//===----------------------------------------------------------------------===// 454// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 455//===----------------------------------------------------------------------===// 456 457namespace { 458class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 459 mutable Selector releaseS; 460 mutable Selector retainS; 461 mutable Selector autoreleaseS; 462 mutable Selector drainS; 463 mutable OwningPtr<BugType> BT; 464 465public: 466 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 467}; 468} 469 470void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 471 CheckerContext &C) const { 472 473 if (!BT) { 474 BT.reset(new APIMisuse("message incorrectly sent to class instead of class " 475 "instance")); 476 477 ASTContext &Ctx = C.getASTContext(); 478 releaseS = GetNullarySelector("release", Ctx); 479 retainS = GetNullarySelector("retain", Ctx); 480 autoreleaseS = GetNullarySelector("autorelease", Ctx); 481 drainS = GetNullarySelector("drain", Ctx); 482 } 483 484 if (msg.isInstanceMessage()) 485 return; 486 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 487 assert(Class); 488 489 Selector S = msg.getSelector(); 490 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 491 return; 492 493 if (ExplodedNode *N = C.addTransition()) { 494 SmallString<200> buf; 495 llvm::raw_svector_ostream os(buf); 496 497 os << "The '" << S.getAsString() << "' message should be sent to instances " 498 "of class '" << Class->getName() 499 << "' and not the class directly"; 500 501 BugReport *report = new BugReport(*BT, os.str(), N); 502 report->addRange(msg.getSourceRange()); 503 C.emitReport(report); 504 } 505} 506 507//===----------------------------------------------------------------------===// 508// Check for passing non-Objective-C types to variadic methods that expect 509// only Objective-C types. 510//===----------------------------------------------------------------------===// 511 512namespace { 513class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 514 mutable Selector arrayWithObjectsS; 515 mutable Selector dictionaryWithObjectsAndKeysS; 516 mutable Selector setWithObjectsS; 517 mutable Selector orderedSetWithObjectsS; 518 mutable Selector initWithObjectsS; 519 mutable Selector initWithObjectsAndKeysS; 520 mutable OwningPtr<BugType> BT; 521 522 bool isVariadicMessage(const ObjCMethodCall &msg) const; 523 524public: 525 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 526}; 527} 528 529/// isVariadicMessage - Returns whether the given message is a variadic message, 530/// where all arguments must be Objective-C types. 531bool 532VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 533 const ObjCMethodDecl *MD = msg.getDecl(); 534 535 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 536 return false; 537 538 Selector S = msg.getSelector(); 539 540 if (msg.isInstanceMessage()) { 541 // FIXME: Ideally we'd look at the receiver interface here, but that's not 542 // useful for init, because alloc returns 'id'. In theory, this could lead 543 // to false positives, for example if there existed a class that had an 544 // initWithObjects: implementation that does accept non-Objective-C pointer 545 // types, but the chance of that happening is pretty small compared to the 546 // gains that this analysis gives. 547 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 548 549 switch (findKnownClass(Class)) { 550 case FC_NSArray: 551 case FC_NSOrderedSet: 552 case FC_NSSet: 553 return S == initWithObjectsS; 554 case FC_NSDictionary: 555 return S == initWithObjectsAndKeysS; 556 default: 557 return false; 558 } 559 } else { 560 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 561 562 switch (findKnownClass(Class)) { 563 case FC_NSArray: 564 return S == arrayWithObjectsS; 565 case FC_NSOrderedSet: 566 return S == orderedSetWithObjectsS; 567 case FC_NSSet: 568 return S == setWithObjectsS; 569 case FC_NSDictionary: 570 return S == dictionaryWithObjectsAndKeysS; 571 default: 572 return false; 573 } 574 } 575} 576 577void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 578 CheckerContext &C) const { 579 if (!BT) { 580 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " 581 "Objective-C pointer types")); 582 583 ASTContext &Ctx = C.getASTContext(); 584 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 585 dictionaryWithObjectsAndKeysS = 586 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 587 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 588 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 589 590 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 591 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 592 } 593 594 if (!isVariadicMessage(msg)) 595 return; 596 597 // We are not interested in the selector arguments since they have 598 // well-defined types, so the compiler will issue a warning for them. 599 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 600 601 // We're not interested in the last argument since it has to be nil or the 602 // compiler would have issued a warning for it elsewhere. 603 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 604 605 if (variadicArgsEnd <= variadicArgsBegin) 606 return; 607 608 // Verify that all arguments have Objective-C types. 609 llvm::Optional<ExplodedNode*> errorNode; 610 ProgramStateRef state = C.getState(); 611 612 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 613 QualType ArgTy = msg.getArgExpr(I)->getType(); 614 if (ArgTy->isObjCObjectPointerType()) 615 continue; 616 617 // Block pointers are treaded as Objective-C pointers. 618 if (ArgTy->isBlockPointerType()) 619 continue; 620 621 // Ignore pointer constants. 622 if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) 623 continue; 624 625 // Ignore pointer types annotated with 'NSObject' attribute. 626 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 627 continue; 628 629 // Ignore CF references, which can be toll-free bridged. 630 if (coreFoundation::isCFObjectRef(ArgTy)) 631 continue; 632 633 // Generate only one error node to use for all bug reports. 634 if (!errorNode.hasValue()) 635 errorNode = C.addTransition(); 636 637 if (!errorNode.getValue()) 638 continue; 639 640 SmallString<128> sbuf; 641 llvm::raw_svector_ostream os(sbuf); 642 643 StringRef TypeName = GetReceiverInterfaceName(msg); 644 if (!TypeName.empty()) 645 os << "Argument to '" << TypeName << "' method '"; 646 else 647 os << "Argument to method '"; 648 649 os << msg.getSelector().getAsString() 650 << "' should be an Objective-C pointer type, not '"; 651 ArgTy.print(os, C.getLangOpts()); 652 os << "'"; 653 654 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); 655 R->addRange(msg.getArgSourceRange(I)); 656 C.emitReport(R); 657 } 658} 659 660//===----------------------------------------------------------------------===// 661// Improves the modeling of loops over Cocoa collections. 662//===----------------------------------------------------------------------===// 663 664namespace { 665class ObjCLoopChecker 666 : public Checker<check::PostStmt<ObjCForCollectionStmt> > { 667 668public: 669 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 670}; 671} 672 673static bool isKnownNonNilCollectionType(QualType T) { 674 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 675 if (!PT) 676 return false; 677 678 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 679 if (!ID) 680 return false; 681 682 switch (findKnownClass(ID)) { 683 case FC_NSArray: 684 case FC_NSDictionary: 685 case FC_NSEnumerator: 686 case FC_NSOrderedSet: 687 case FC_NSSet: 688 return true; 689 default: 690 return false; 691 } 692} 693 694void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 695 CheckerContext &C) const { 696 ProgramStateRef State = C.getState(); 697 698 // Check if this is the branch for the end of the loop. 699 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); 700 if (CollectionSentinel.isZeroConstant()) 701 return; 702 703 // See if the collection is one where we /know/ the elements are non-nil. 704 const Expr *Collection = FCS->getCollection(); 705 if (!isKnownNonNilCollectionType(Collection->getType())) 706 return; 707 708 // FIXME: Copied from ExprEngineObjC. 709 const Stmt *Element = FCS->getElement(); 710 SVal ElementVar; 711 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 712 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 713 assert(ElemDecl->getInit() == 0); 714 ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); 715 } else { 716 ElementVar = State->getSVal(Element, C.getLocationContext()); 717 } 718 719 if (!isa<Loc>(ElementVar)) 720 return; 721 722 // Go ahead and assume the value is non-nil. 723 SVal Val = State->getSVal(cast<Loc>(ElementVar)); 724 State = State->assume(cast<DefinedOrUnknownSVal>(Val), true); 725 C.addTransition(State); 726} 727 728namespace { 729/// \class ObjCNonNilReturnValueChecker 730/// \brief The checker restricts the return values of APIs known to 731/// never (or almost never) return 'nil'. 732class ObjCNonNilReturnValueChecker 733 : public Checker<check::PostObjCMessage> { 734 mutable bool Initialized; 735 mutable Selector ObjectAtIndex; 736 mutable Selector ObjectAtIndexedSubscript; 737 738public: 739 ObjCNonNilReturnValueChecker() : Initialized(false) {} 740 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 741}; 742} 743 744static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 745 ProgramStateRef State, 746 CheckerContext &C) { 747 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 748 if (DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&Val)) 749 return State->assume(*DV, true); 750 return State; 751} 752 753void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 754 CheckerContext &C) 755 const { 756 ProgramStateRef State = C.getState(); 757 758 if (!Initialized) { 759 ASTContext &Ctx = C.getASTContext(); 760 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 761 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 762 } 763 764 // Check the receiver type. 765 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 766 767 // Assume that object returned from '[self init]' or '[super init]' is not 768 // 'nil' if we are processing an inlined function/method. 769 // 770 // A defensive callee will (and should) check if the object returned by 771 // '[super init]' is 'nil' before doing it's own initialization. However, 772 // since 'nil' is rarely returned in practice, we should not warn when the 773 // caller to the defensive constructor uses the object in contexts where 774 // 'nil' is not accepted. 775 if (!C.inTopFrame() && M.getDecl() && 776 M.getDecl()->getMethodFamily() == OMF_init && 777 M.isReceiverSelfOrSuper()) { 778 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 779 } 780 781 // Objects returned from 782 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 783 // are never 'nil'. 784 FoundationClass Cl = findKnownClass(Interface); 785 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 786 Selector Sel = M.getSelector(); 787 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 788 // Go ahead and assume the value is non-nil. 789 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 790 } 791 } 792 } 793 C.addTransition(State); 794} 795 796//===----------------------------------------------------------------------===// 797// Check registration. 798//===----------------------------------------------------------------------===// 799 800void ento::registerNilArgChecker(CheckerManager &mgr) { 801 mgr.registerChecker<NilArgChecker>(); 802} 803 804void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 805 mgr.registerChecker<CFNumberCreateChecker>(); 806} 807 808void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 809 mgr.registerChecker<CFRetainReleaseChecker>(); 810} 811 812void ento::registerClassReleaseChecker(CheckerManager &mgr) { 813 mgr.registerChecker<ClassReleaseChecker>(); 814} 815 816void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 817 mgr.registerChecker<VariadicMethodTypeChecker>(); 818} 819 820void ento::registerObjCLoopChecker(CheckerManager &mgr) { 821 mgr.registerChecker<ObjCLoopChecker>(); 822} 823 824void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 825 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 826} 827