BasicObjCFoundationChecks.cpp revision 66874fb18afbffb8b2ca05576851a64534be3352
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 X.getAs<loc::ConcreteInt>(); 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 199static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 200 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 201 202 if (i < kCFNumberCharType) 203 return FixedSize[i-1]; 204 205 QualType T; 206 207 switch (i) { 208 case kCFNumberCharType: T = Ctx.CharTy; break; 209 case kCFNumberShortType: T = Ctx.ShortTy; break; 210 case kCFNumberIntType: T = Ctx.IntTy; break; 211 case kCFNumberLongType: T = Ctx.LongTy; break; 212 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 213 case kCFNumberFloatType: T = Ctx.FloatTy; break; 214 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 215 case kCFNumberCFIndexType: 216 case kCFNumberNSIntegerType: 217 case kCFNumberCGFloatType: 218 // FIXME: We need a way to map from names to Type*. 219 default: 220 return None; 221 } 222 223 return Ctx.getTypeSize(T); 224} 225 226#if 0 227static const char* GetCFNumberTypeStr(uint64_t i) { 228 static const char* Names[] = { 229 "kCFNumberSInt8Type", 230 "kCFNumberSInt16Type", 231 "kCFNumberSInt32Type", 232 "kCFNumberSInt64Type", 233 "kCFNumberFloat32Type", 234 "kCFNumberFloat64Type", 235 "kCFNumberCharType", 236 "kCFNumberShortType", 237 "kCFNumberIntType", 238 "kCFNumberLongType", 239 "kCFNumberLongLongType", 240 "kCFNumberFloatType", 241 "kCFNumberDoubleType", 242 "kCFNumberCFIndexType", 243 "kCFNumberNSIntegerType", 244 "kCFNumberCGFloatType" 245 }; 246 247 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 248} 249#endif 250 251void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 252 CheckerContext &C) const { 253 ProgramStateRef state = C.getState(); 254 const FunctionDecl *FD = C.getCalleeDecl(CE); 255 if (!FD) 256 return; 257 258 ASTContext &Ctx = C.getASTContext(); 259 if (!II) 260 II = &Ctx.Idents.get("CFNumberCreate"); 261 262 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 263 return; 264 265 // Get the value of the "theType" argument. 266 const LocationContext *LCtx = C.getLocationContext(); 267 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); 268 269 // FIXME: We really should allow ranges of valid theType values, and 270 // bifurcate the state appropriately. 271 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); 272 if (!V) 273 return; 274 275 uint64_t NumberKind = V->getValue().getLimitedValue(); 276 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); 277 278 // FIXME: In some cases we can emit an error. 279 if (!OptTargetSize) 280 return; 281 282 uint64_t TargetSize = *OptTargetSize; 283 284 // Look at the value of the integer being passed by reference. Essentially 285 // we want to catch cases where the value passed in is not equal to the 286 // size of the type being created. 287 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); 288 289 // FIXME: Eventually we should handle arbitrary locations. We can do this 290 // by having an enhanced memory model that does low-level typing. 291 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); 292 if (!LV) 293 return; 294 295 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 296 if (!R) 297 return; 298 299 QualType T = Ctx.getCanonicalType(R->getValueType()); 300 301 // FIXME: If the pointee isn't an integer type, should we flag a warning? 302 // People can do weird stuff with pointers. 303 304 if (!T->isIntegerType()) 305 return; 306 307 uint64_t SourceSize = Ctx.getTypeSize(T); 308 309 // CHECK: is SourceSize == TargetSize 310 if (SourceSize == TargetSize) 311 return; 312 313 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 314 // otherwise generate a regular node. 315 // 316 // FIXME: We can actually create an abstract "CFNumber" object that has 317 // the bits initialized to the provided values. 318 // 319 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 320 : C.addTransition()) { 321 SmallString<128> sbuf; 322 llvm::raw_svector_ostream os(sbuf); 323 324 os << (SourceSize == 8 ? "An " : "A ") 325 << SourceSize << " bit integer is used to initialize a CFNumber " 326 "object that represents " 327 << (TargetSize == 8 ? "an " : "a ") 328 << TargetSize << " bit integer. "; 329 330 if (SourceSize < TargetSize) 331 os << (TargetSize - SourceSize) 332 << " bits of the CFNumber value will be garbage." ; 333 else 334 os << (SourceSize - TargetSize) 335 << " bits of the input integer will be lost."; 336 337 if (!BT) 338 BT.reset(new APIMisuse("Bad use of CFNumberCreate")); 339 340 BugReport *report = new BugReport(*BT, os.str(), N); 341 report->addRange(CE->getArg(2)->getSourceRange()); 342 C.emitReport(report); 343 } 344} 345 346//===----------------------------------------------------------------------===// 347// CFRetain/CFRelease/CFMakeCollectable checking for null arguments. 348//===----------------------------------------------------------------------===// 349 350namespace { 351class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 352 mutable OwningPtr<APIMisuse> BT; 353 mutable IdentifierInfo *Retain, *Release, *MakeCollectable; 354public: 355 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {} 356 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 357}; 358} // end anonymous namespace 359 360 361void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, 362 CheckerContext &C) const { 363 // If the CallExpr doesn't have exactly 1 argument just give up checking. 364 if (CE->getNumArgs() != 1) 365 return; 366 367 ProgramStateRef state = C.getState(); 368 const FunctionDecl *FD = C.getCalleeDecl(CE); 369 if (!FD) 370 return; 371 372 if (!BT) { 373 ASTContext &Ctx = C.getASTContext(); 374 Retain = &Ctx.Idents.get("CFRetain"); 375 Release = &Ctx.Idents.get("CFRelease"); 376 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); 377 BT.reset( 378 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable")); 379 } 380 381 // Check if we called CFRetain/CFRelease/CFMakeCollectable. 382 const IdentifierInfo *FuncII = FD->getIdentifier(); 383 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable)) 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, C.getLocationContext()); 392 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 393 if (!DefArgVal) 394 return; 395 396 // Get a NULL value. 397 SValBuilder &svalBuilder = C.getSValBuilder(); 398 DefinedSVal zero = 399 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); 400 401 // Make an expression asserting that they're equal. 402 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 403 404 // Are they equal? 405 ProgramStateRef stateTrue, stateFalse; 406 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 407 408 if (stateTrue && !stateFalse) { 409 ExplodedNode *N = C.generateSink(stateTrue); 410 if (!N) 411 return; 412 413 const char *description; 414 if (FuncII == Retain) 415 description = "Null pointer argument in call to CFRetain"; 416 else if (FuncII == Release) 417 description = "Null pointer argument in call to CFRelease"; 418 else if (FuncII == MakeCollectable) 419 description = "Null pointer argument in call to CFMakeCollectable"; 420 else 421 llvm_unreachable("impossible case"); 422 423 BugReport *report = new BugReport(*BT, description, N); 424 report->addRange(Arg->getSourceRange()); 425 bugreporter::trackNullOrUndefValue(N, Arg, *report); 426 C.emitReport(report); 427 return; 428 } 429 430 // From here on, we know the argument is non-null. 431 C.addTransition(stateFalse); 432} 433 434//===----------------------------------------------------------------------===// 435// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 436//===----------------------------------------------------------------------===// 437 438namespace { 439class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 440 mutable Selector releaseS; 441 mutable Selector retainS; 442 mutable Selector autoreleaseS; 443 mutable Selector drainS; 444 mutable OwningPtr<BugType> BT; 445 446public: 447 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 448}; 449} 450 451void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 452 CheckerContext &C) const { 453 454 if (!BT) { 455 BT.reset(new APIMisuse("message incorrectly sent to class instead of class " 456 "instance")); 457 458 ASTContext &Ctx = C.getASTContext(); 459 releaseS = GetNullarySelector("release", Ctx); 460 retainS = GetNullarySelector("retain", Ctx); 461 autoreleaseS = GetNullarySelector("autorelease", Ctx); 462 drainS = GetNullarySelector("drain", Ctx); 463 } 464 465 if (msg.isInstanceMessage()) 466 return; 467 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 468 assert(Class); 469 470 Selector S = msg.getSelector(); 471 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 472 return; 473 474 if (ExplodedNode *N = C.addTransition()) { 475 SmallString<200> buf; 476 llvm::raw_svector_ostream os(buf); 477 478 os << "The '" << S.getAsString() << "' message should be sent to instances " 479 "of class '" << Class->getName() 480 << "' and not the class directly"; 481 482 BugReport *report = new BugReport(*BT, os.str(), N); 483 report->addRange(msg.getSourceRange()); 484 C.emitReport(report); 485 } 486} 487 488//===----------------------------------------------------------------------===// 489// Check for passing non-Objective-C types to variadic methods that expect 490// only Objective-C types. 491//===----------------------------------------------------------------------===// 492 493namespace { 494class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 495 mutable Selector arrayWithObjectsS; 496 mutable Selector dictionaryWithObjectsAndKeysS; 497 mutable Selector setWithObjectsS; 498 mutable Selector orderedSetWithObjectsS; 499 mutable Selector initWithObjectsS; 500 mutable Selector initWithObjectsAndKeysS; 501 mutable OwningPtr<BugType> BT; 502 503 bool isVariadicMessage(const ObjCMethodCall &msg) const; 504 505public: 506 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 507}; 508} 509 510/// isVariadicMessage - Returns whether the given message is a variadic message, 511/// where all arguments must be Objective-C types. 512bool 513VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 514 const ObjCMethodDecl *MD = msg.getDecl(); 515 516 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 517 return false; 518 519 Selector S = msg.getSelector(); 520 521 if (msg.isInstanceMessage()) { 522 // FIXME: Ideally we'd look at the receiver interface here, but that's not 523 // useful for init, because alloc returns 'id'. In theory, this could lead 524 // to false positives, for example if there existed a class that had an 525 // initWithObjects: implementation that does accept non-Objective-C pointer 526 // types, but the chance of that happening is pretty small compared to the 527 // gains that this analysis gives. 528 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 529 530 switch (findKnownClass(Class)) { 531 case FC_NSArray: 532 case FC_NSOrderedSet: 533 case FC_NSSet: 534 return S == initWithObjectsS; 535 case FC_NSDictionary: 536 return S == initWithObjectsAndKeysS; 537 default: 538 return false; 539 } 540 } else { 541 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 542 543 switch (findKnownClass(Class)) { 544 case FC_NSArray: 545 return S == arrayWithObjectsS; 546 case FC_NSOrderedSet: 547 return S == orderedSetWithObjectsS; 548 case FC_NSSet: 549 return S == setWithObjectsS; 550 case FC_NSDictionary: 551 return S == dictionaryWithObjectsAndKeysS; 552 default: 553 return false; 554 } 555 } 556} 557 558void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &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 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 570 571 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 572 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 573 } 574 575 if (!isVariadicMessage(msg)) 576 return; 577 578 // We are not interested in the selector arguments since they have 579 // well-defined types, so the compiler will issue a warning for them. 580 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 581 582 // We're not interested in the last argument since it has to be nil or the 583 // compiler would have issued a warning for it elsewhere. 584 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 585 586 if (variadicArgsEnd <= variadicArgsBegin) 587 return; 588 589 // Verify that all arguments have Objective-C types. 590 Optional<ExplodedNode*> errorNode; 591 ProgramStateRef state = C.getState(); 592 593 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 594 QualType ArgTy = msg.getArgExpr(I)->getType(); 595 if (ArgTy->isObjCObjectPointerType()) 596 continue; 597 598 // Block pointers are treaded as Objective-C pointers. 599 if (ArgTy->isBlockPointerType()) 600 continue; 601 602 // Ignore pointer constants. 603 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 604 continue; 605 606 // Ignore pointer types annotated with 'NSObject' attribute. 607 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 608 continue; 609 610 // Ignore CF references, which can be toll-free bridged. 611 if (coreFoundation::isCFObjectRef(ArgTy)) 612 continue; 613 614 // Generate only one error node to use for all bug reports. 615 if (!errorNode.hasValue()) 616 errorNode = C.addTransition(); 617 618 if (!errorNode.getValue()) 619 continue; 620 621 SmallString<128> sbuf; 622 llvm::raw_svector_ostream os(sbuf); 623 624 StringRef TypeName = GetReceiverInterfaceName(msg); 625 if (!TypeName.empty()) 626 os << "Argument to '" << TypeName << "' method '"; 627 else 628 os << "Argument to method '"; 629 630 os << msg.getSelector().getAsString() 631 << "' should be an Objective-C pointer type, not '"; 632 ArgTy.print(os, C.getLangOpts()); 633 os << "'"; 634 635 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); 636 R->addRange(msg.getArgSourceRange(I)); 637 C.emitReport(R); 638 } 639} 640 641//===----------------------------------------------------------------------===// 642// Improves the modeling of loops over Cocoa collections. 643//===----------------------------------------------------------------------===// 644 645namespace { 646class ObjCLoopChecker 647 : public Checker<check::PostStmt<ObjCForCollectionStmt> > { 648 649public: 650 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 651}; 652} 653 654static bool isKnownNonNilCollectionType(QualType T) { 655 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 656 if (!PT) 657 return false; 658 659 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 660 if (!ID) 661 return false; 662 663 switch (findKnownClass(ID)) { 664 case FC_NSArray: 665 case FC_NSDictionary: 666 case FC_NSEnumerator: 667 case FC_NSOrderedSet: 668 case FC_NSSet: 669 return true; 670 default: 671 return false; 672 } 673} 674 675void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 676 CheckerContext &C) const { 677 ProgramStateRef State = C.getState(); 678 679 // Check if this is the branch for the end of the loop. 680 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); 681 if (CollectionSentinel.isZeroConstant()) 682 return; 683 684 // See if the collection is one where we /know/ the elements are non-nil. 685 const Expr *Collection = FCS->getCollection(); 686 if (!isKnownNonNilCollectionType(Collection->getType())) 687 return; 688 689 // FIXME: Copied from ExprEngineObjC. 690 const Stmt *Element = FCS->getElement(); 691 SVal ElementVar; 692 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 693 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 694 assert(ElemDecl->getInit() == 0); 695 ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); 696 } else { 697 ElementVar = State->getSVal(Element, C.getLocationContext()); 698 } 699 700 if (!ElementVar.getAs<Loc>()) 701 return; 702 703 // Go ahead and assume the value is non-nil. 704 SVal Val = State->getSVal(ElementVar.castAs<Loc>()); 705 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 706 C.addTransition(State); 707} 708 709namespace { 710/// \class ObjCNonNilReturnValueChecker 711/// \brief The checker restricts the return values of APIs known to 712/// never (or almost never) return 'nil'. 713class ObjCNonNilReturnValueChecker 714 : public Checker<check::PostObjCMessage> { 715 mutable bool Initialized; 716 mutable Selector ObjectAtIndex; 717 mutable Selector ObjectAtIndexedSubscript; 718 719public: 720 ObjCNonNilReturnValueChecker() : Initialized(false) {} 721 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 722}; 723} 724 725static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 726 ProgramStateRef State, 727 CheckerContext &C) { 728 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 729 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 730 return State->assume(*DV, true); 731 return State; 732} 733 734void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 735 CheckerContext &C) 736 const { 737 ProgramStateRef State = C.getState(); 738 739 if (!Initialized) { 740 ASTContext &Ctx = C.getASTContext(); 741 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 742 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 743 } 744 745 // Check the receiver type. 746 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 747 748 // Assume that object returned from '[self init]' or '[super init]' is not 749 // 'nil' if we are processing an inlined function/method. 750 // 751 // A defensive callee will (and should) check if the object returned by 752 // '[super init]' is 'nil' before doing it's own initialization. However, 753 // since 'nil' is rarely returned in practice, we should not warn when the 754 // caller to the defensive constructor uses the object in contexts where 755 // 'nil' is not accepted. 756 if (!C.inTopFrame() && M.getDecl() && 757 M.getDecl()->getMethodFamily() == OMF_init && 758 M.isReceiverSelfOrSuper()) { 759 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 760 } 761 762 // Objects returned from 763 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 764 // are never 'nil'. 765 FoundationClass Cl = findKnownClass(Interface); 766 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 767 Selector Sel = M.getSelector(); 768 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 769 // Go ahead and assume the value is non-nil. 770 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 771 } 772 } 773 } 774 C.addTransition(State); 775} 776 777//===----------------------------------------------------------------------===// 778// Check registration. 779//===----------------------------------------------------------------------===// 780 781void ento::registerNilArgChecker(CheckerManager &mgr) { 782 mgr.registerChecker<NilArgChecker>(); 783} 784 785void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 786 mgr.registerChecker<CFNumberCreateChecker>(); 787} 788 789void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 790 mgr.registerChecker<CFRetainReleaseChecker>(); 791} 792 793void ento::registerClassReleaseChecker(CheckerManager &mgr) { 794 mgr.registerChecker<ClassReleaseChecker>(); 795} 796 797void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 798 mgr.registerChecker<VariadicMethodTypeChecker>(); 799} 800 801void ento::registerObjCLoopChecker(CheckerManager &mgr) { 802 mgr.registerChecker<ObjCLoopChecker>(); 803} 804 805void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 806 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 807} 808