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