BasicObjCFoundationChecks.cpp revision 6bcf27bb9a4b5c3f79cb44c0e4654a6d7619ad89
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 "SelectorExtras.h" 18#include "clang/AST/ASTContext.h" 19#include "clang/AST/DeclObjC.h" 20#include "clang/AST/Expr.h" 21#include "clang/AST/ExprObjC.h" 22#include "clang/AST/StmtObjC.h" 23#include "clang/Analysis/DomainSpecific/CocoaConventions.h" 24#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 25#include "clang/StaticAnalyzer/Core/Checker.h" 26#include "clang/StaticAnalyzer/Core/CheckerManager.h" 27#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 28#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 29#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" 30#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" 31#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" 32#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" 33#include "llvm/ADT/SmallString.h" 34#include "llvm/ADT/StringMap.h" 35#include "llvm/Support/raw_ostream.h" 36 37using namespace clang; 38using namespace ento; 39 40namespace { 41class APIMisuse : public BugType { 42public: 43 APIMisuse(const CheckerBase *checker, const char *name) 44 : BugType(checker, name, "API Misuse (Apple)") {} 45}; 46} // end anonymous namespace 47 48//===----------------------------------------------------------------------===// 49// Utility functions. 50//===----------------------------------------------------------------------===// 51 52static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { 53 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) 54 return ID->getIdentifier()->getName(); 55 return StringRef(); 56} 57 58enum FoundationClass { 59 FC_None, 60 FC_NSArray, 61 FC_NSDictionary, 62 FC_NSEnumerator, 63 FC_NSNull, 64 FC_NSOrderedSet, 65 FC_NSSet, 66 FC_NSString 67}; 68 69static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, 70 bool IncludeSuperclasses = true) { 71 static llvm::StringMap<FoundationClass> Classes; 72 if (Classes.empty()) { 73 Classes["NSArray"] = FC_NSArray; 74 Classes["NSDictionary"] = FC_NSDictionary; 75 Classes["NSEnumerator"] = FC_NSEnumerator; 76 Classes["NSNull"] = FC_NSNull; 77 Classes["NSOrderedSet"] = FC_NSOrderedSet; 78 Classes["NSSet"] = FC_NSSet; 79 Classes["NSString"] = FC_NSString; 80 } 81 82 // FIXME: Should we cache this at all? 83 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); 84 if (result == FC_None && IncludeSuperclasses) 85 if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) 86 return findKnownClass(Super); 87 88 return result; 89} 90 91//===----------------------------------------------------------------------===// 92// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. 93//===----------------------------------------------------------------------===// 94 95namespace { 96 class NilArgChecker : public Checker<check::PreObjCMessage, 97 check::PostStmt<ObjCDictionaryLiteral>, 98 check::PostStmt<ObjCArrayLiteral> > { 99 mutable std::unique_ptr<APIMisuse> BT; 100 101 mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; 102 mutable Selector ArrayWithObjectSel; 103 mutable Selector AddObjectSel; 104 mutable Selector InsertObjectAtIndexSel; 105 mutable Selector ReplaceObjectAtIndexWithObjectSel; 106 mutable Selector SetObjectAtIndexedSubscriptSel; 107 mutable Selector ArrayByAddingObjectSel; 108 mutable Selector DictionaryWithObjectForKeySel; 109 mutable Selector SetObjectForKeySel; 110 mutable Selector SetObjectForKeyedSubscriptSel; 111 mutable Selector RemoveObjectForKeySel; 112 113 void warnIfNilExpr(const Expr *E, 114 const char *Msg, 115 CheckerContext &C) const; 116 117 void warnIfNilArg(CheckerContext &C, 118 const ObjCMethodCall &msg, unsigned Arg, 119 FoundationClass Class, 120 bool CanBeSubscript = false) const; 121 122 void generateBugReport(ExplodedNode *N, 123 StringRef Msg, 124 SourceRange Range, 125 const Expr *Expr, 126 CheckerContext &C) const; 127 128 public: 129 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 130 void checkPostStmt(const ObjCDictionaryLiteral *DL, 131 CheckerContext &C) const; 132 void checkPostStmt(const ObjCArrayLiteral *AL, 133 CheckerContext &C) const; 134 }; 135} 136 137void NilArgChecker::warnIfNilExpr(const Expr *E, 138 const char *Msg, 139 CheckerContext &C) const { 140 ProgramStateRef State = C.getState(); 141 if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { 142 143 if (ExplodedNode *N = C.generateSink()) { 144 generateBugReport(N, Msg, E->getSourceRange(), E, C); 145 } 146 147 } 148} 149 150void NilArgChecker::warnIfNilArg(CheckerContext &C, 151 const ObjCMethodCall &msg, 152 unsigned int Arg, 153 FoundationClass Class, 154 bool CanBeSubscript) const { 155 // Check if the argument is nil. 156 ProgramStateRef State = C.getState(); 157 if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) 158 return; 159 160 if (ExplodedNode *N = C.generateSink()) { 161 SmallString<128> sbuf; 162 llvm::raw_svector_ostream os(sbuf); 163 164 if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { 165 166 if (Class == FC_NSArray) { 167 os << "Array element cannot be nil"; 168 } else if (Class == FC_NSDictionary) { 169 if (Arg == 0) { 170 os << "Value stored into '"; 171 os << GetReceiverInterfaceName(msg) << "' cannot be nil"; 172 } else { 173 assert(Arg == 1); 174 os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; 175 } 176 } else 177 llvm_unreachable("Missing foundation class for the subscript expr"); 178 179 } else { 180 if (Class == FC_NSDictionary) { 181 if (Arg == 0) 182 os << "Value argument "; 183 else { 184 assert(Arg == 1); 185 os << "Key argument "; 186 } 187 os << "to '"; 188 msg.getSelector().print(os); 189 os << "' cannot be nil"; 190 } else { 191 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; 192 msg.getSelector().print(os); 193 os << "' cannot be nil"; 194 } 195 } 196 197 generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), 198 msg.getArgExpr(Arg), C); 199 } 200} 201 202void NilArgChecker::generateBugReport(ExplodedNode *N, 203 StringRef Msg, 204 SourceRange Range, 205 const Expr *E, 206 CheckerContext &C) const { 207 if (!BT) 208 BT.reset(new APIMisuse(this, "nil argument")); 209 210 BugReport *R = new BugReport(*BT, Msg, N); 211 R->addRange(Range); 212 bugreporter::trackNullOrUndefValue(N, E, *R); 213 C.emitReport(R); 214} 215 216void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 217 CheckerContext &C) const { 218 const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); 219 if (!ID) 220 return; 221 222 FoundationClass Class = findKnownClass(ID); 223 224 static const unsigned InvalidArgIndex = UINT_MAX; 225 unsigned Arg = InvalidArgIndex; 226 bool CanBeSubscript = false; 227 228 if (Class == FC_NSString) { 229 Selector S = msg.getSelector(); 230 231 if (S.isUnarySelector()) 232 return; 233 234 if (StringSelectors.empty()) { 235 ASTContext &Ctx = C.getASTContext(); 236 Selector Sels[] = { 237 getKeywordSelector(Ctx, "caseInsensitiveCompare", nullptr), 238 getKeywordSelector(Ctx, "compare", nullptr), 239 getKeywordSelector(Ctx, "compare", "options", nullptr), 240 getKeywordSelector(Ctx, "compare", "options", "range", nullptr), 241 getKeywordSelector(Ctx, "compare", "options", "range", "locale", 242 nullptr), 243 getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet", 244 nullptr), 245 getKeywordSelector(Ctx, "initWithFormat", 246 nullptr), 247 getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare", nullptr), 248 getKeywordSelector(Ctx, "localizedCompare", nullptr), 249 getKeywordSelector(Ctx, "localizedStandardCompare", nullptr), 250 }; 251 for (Selector KnownSel : Sels) 252 StringSelectors[KnownSel] = 0; 253 } 254 auto I = StringSelectors.find(S); 255 if (I == StringSelectors.end()) 256 return; 257 Arg = I->second; 258 } else if (Class == FC_NSArray) { 259 Selector S = msg.getSelector(); 260 261 if (S.isUnarySelector()) 262 return; 263 264 if (ArrayWithObjectSel.isNull()) { 265 ASTContext &Ctx = C.getASTContext(); 266 ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject", nullptr); 267 AddObjectSel = getKeywordSelector(Ctx, "addObject", nullptr); 268 InsertObjectAtIndexSel = 269 getKeywordSelector(Ctx, "insertObject", "atIndex", nullptr); 270 ReplaceObjectAtIndexWithObjectSel = 271 getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject", nullptr); 272 SetObjectAtIndexedSubscriptSel = 273 getKeywordSelector(Ctx, "setObject", "atIndexedSubscript", nullptr); 274 ArrayByAddingObjectSel = 275 getKeywordSelector(Ctx, "arrayByAddingObject", nullptr); 276 } 277 278 if (S == ArrayWithObjectSel || S == AddObjectSel || 279 S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) { 280 Arg = 0; 281 } else if (S == SetObjectAtIndexedSubscriptSel) { 282 Arg = 0; 283 CanBeSubscript = true; 284 } else if (S == ReplaceObjectAtIndexWithObjectSel) { 285 Arg = 1; 286 } 287 } else if (Class == FC_NSDictionary) { 288 Selector S = msg.getSelector(); 289 290 if (S.isUnarySelector()) 291 return; 292 293 if (DictionaryWithObjectForKeySel.isNull()) { 294 ASTContext &Ctx = C.getASTContext(); 295 DictionaryWithObjectForKeySel = 296 getKeywordSelector(Ctx, "dictionaryWithObject", "forKey", nullptr); 297 SetObjectForKeySel = 298 getKeywordSelector(Ctx, "setObject", "forKey", nullptr); 299 SetObjectForKeyedSubscriptSel = 300 getKeywordSelector(Ctx, "setObject", "forKeyedSubscript", nullptr); 301 RemoveObjectForKeySel = 302 getKeywordSelector(Ctx, "removeObjectForKey", nullptr); 303 } 304 305 if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) { 306 Arg = 0; 307 warnIfNilArg(C, msg, /* Arg */1, Class); 308 } else if (S == SetObjectForKeyedSubscriptSel) { 309 CanBeSubscript = true; 310 Arg = 0; 311 warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript); 312 } else if (S == RemoveObjectForKeySel) { 313 Arg = 0; 314 } 315 } 316 317 // If argument is '0', report a warning. 318 if ((Arg != InvalidArgIndex)) 319 warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); 320} 321 322void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, 323 CheckerContext &C) const { 324 unsigned NumOfElements = AL->getNumElements(); 325 for (unsigned i = 0; i < NumOfElements; ++i) { 326 warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); 327 } 328} 329 330void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, 331 CheckerContext &C) const { 332 unsigned NumOfElements = DL->getNumElements(); 333 for (unsigned i = 0; i < NumOfElements; ++i) { 334 ObjCDictionaryElement Element = DL->getKeyValueElement(i); 335 warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); 336 warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); 337 } 338} 339 340//===----------------------------------------------------------------------===// 341// Error reporting. 342//===----------------------------------------------------------------------===// 343 344namespace { 345class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > { 346 mutable std::unique_ptr<APIMisuse> BT; 347 mutable IdentifierInfo* II; 348public: 349 CFNumberCreateChecker() : II(nullptr) {} 350 351 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 352 353private: 354 void EmitError(const TypedRegion* R, const Expr *Ex, 355 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); 356}; 357} // end anonymous namespace 358 359enum CFNumberType { 360 kCFNumberSInt8Type = 1, 361 kCFNumberSInt16Type = 2, 362 kCFNumberSInt32Type = 3, 363 kCFNumberSInt64Type = 4, 364 kCFNumberFloat32Type = 5, 365 kCFNumberFloat64Type = 6, 366 kCFNumberCharType = 7, 367 kCFNumberShortType = 8, 368 kCFNumberIntType = 9, 369 kCFNumberLongType = 10, 370 kCFNumberLongLongType = 11, 371 kCFNumberFloatType = 12, 372 kCFNumberDoubleType = 13, 373 kCFNumberCFIndexType = 14, 374 kCFNumberNSIntegerType = 15, 375 kCFNumberCGFloatType = 16 376}; 377 378static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { 379 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; 380 381 if (i < kCFNumberCharType) 382 return FixedSize[i-1]; 383 384 QualType T; 385 386 switch (i) { 387 case kCFNumberCharType: T = Ctx.CharTy; break; 388 case kCFNumberShortType: T = Ctx.ShortTy; break; 389 case kCFNumberIntType: T = Ctx.IntTy; break; 390 case kCFNumberLongType: T = Ctx.LongTy; break; 391 case kCFNumberLongLongType: T = Ctx.LongLongTy; break; 392 case kCFNumberFloatType: T = Ctx.FloatTy; break; 393 case kCFNumberDoubleType: T = Ctx.DoubleTy; break; 394 case kCFNumberCFIndexType: 395 case kCFNumberNSIntegerType: 396 case kCFNumberCGFloatType: 397 // FIXME: We need a way to map from names to Type*. 398 default: 399 return None; 400 } 401 402 return Ctx.getTypeSize(T); 403} 404 405#if 0 406static const char* GetCFNumberTypeStr(uint64_t i) { 407 static const char* Names[] = { 408 "kCFNumberSInt8Type", 409 "kCFNumberSInt16Type", 410 "kCFNumberSInt32Type", 411 "kCFNumberSInt64Type", 412 "kCFNumberFloat32Type", 413 "kCFNumberFloat64Type", 414 "kCFNumberCharType", 415 "kCFNumberShortType", 416 "kCFNumberIntType", 417 "kCFNumberLongType", 418 "kCFNumberLongLongType", 419 "kCFNumberFloatType", 420 "kCFNumberDoubleType", 421 "kCFNumberCFIndexType", 422 "kCFNumberNSIntegerType", 423 "kCFNumberCGFloatType" 424 }; 425 426 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; 427} 428#endif 429 430void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE, 431 CheckerContext &C) const { 432 ProgramStateRef state = C.getState(); 433 const FunctionDecl *FD = C.getCalleeDecl(CE); 434 if (!FD) 435 return; 436 437 ASTContext &Ctx = C.getASTContext(); 438 if (!II) 439 II = &Ctx.Idents.get("CFNumberCreate"); 440 441 if (FD->getIdentifier() != II || CE->getNumArgs() != 3) 442 return; 443 444 // Get the value of the "theType" argument. 445 const LocationContext *LCtx = C.getLocationContext(); 446 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx); 447 448 // FIXME: We really should allow ranges of valid theType values, and 449 // bifurcate the state appropriately. 450 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); 451 if (!V) 452 return; 453 454 uint64_t NumberKind = V->getValue().getLimitedValue(); 455 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind); 456 457 // FIXME: In some cases we can emit an error. 458 if (!OptTargetSize) 459 return; 460 461 uint64_t TargetSize = *OptTargetSize; 462 463 // Look at the value of the integer being passed by reference. Essentially 464 // we want to catch cases where the value passed in is not equal to the 465 // size of the type being created. 466 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx); 467 468 // FIXME: Eventually we should handle arbitrary locations. We can do this 469 // by having an enhanced memory model that does low-level typing. 470 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); 471 if (!LV) 472 return; 473 474 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); 475 if (!R) 476 return; 477 478 QualType T = Ctx.getCanonicalType(R->getValueType()); 479 480 // FIXME: If the pointee isn't an integer type, should we flag a warning? 481 // People can do weird stuff with pointers. 482 483 if (!T->isIntegralOrEnumerationType()) 484 return; 485 486 uint64_t SourceSize = Ctx.getTypeSize(T); 487 488 // CHECK: is SourceSize == TargetSize 489 if (SourceSize == TargetSize) 490 return; 491 492 // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; 493 // otherwise generate a regular node. 494 // 495 // FIXME: We can actually create an abstract "CFNumber" object that has 496 // the bits initialized to the provided values. 497 // 498 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() 499 : C.addTransition()) { 500 SmallString<128> sbuf; 501 llvm::raw_svector_ostream os(sbuf); 502 503 os << (SourceSize == 8 ? "An " : "A ") 504 << SourceSize << " bit integer is used to initialize a CFNumber " 505 "object that represents " 506 << (TargetSize == 8 ? "an " : "a ") 507 << TargetSize << " bit integer. "; 508 509 if (SourceSize < TargetSize) 510 os << (TargetSize - SourceSize) 511 << " bits of the CFNumber value will be garbage." ; 512 else 513 os << (SourceSize - TargetSize) 514 << " bits of the input integer will be lost."; 515 516 if (!BT) 517 BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate")); 518 519 BugReport *report = new BugReport(*BT, os.str(), N); 520 report->addRange(CE->getArg(2)->getSourceRange()); 521 C.emitReport(report); 522 } 523} 524 525//===----------------------------------------------------------------------===// 526// CFRetain/CFRelease/CFMakeCollectable checking for null arguments. 527//===----------------------------------------------------------------------===// 528 529namespace { 530class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > { 531 mutable std::unique_ptr<APIMisuse> BT; 532 mutable IdentifierInfo *Retain, *Release, *MakeCollectable; 533public: 534 CFRetainReleaseChecker() 535 : Retain(nullptr), Release(nullptr), MakeCollectable(nullptr) {} 536 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 537}; 538} // end anonymous namespace 539 540 541void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE, 542 CheckerContext &C) const { 543 // If the CallExpr doesn't have exactly 1 argument just give up checking. 544 if (CE->getNumArgs() != 1) 545 return; 546 547 ProgramStateRef state = C.getState(); 548 const FunctionDecl *FD = C.getCalleeDecl(CE); 549 if (!FD) 550 return; 551 552 if (!BT) { 553 ASTContext &Ctx = C.getASTContext(); 554 Retain = &Ctx.Idents.get("CFRetain"); 555 Release = &Ctx.Idents.get("CFRelease"); 556 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable"); 557 BT.reset(new APIMisuse( 558 this, "null passed to CFRetain/CFRelease/CFMakeCollectable")); 559 } 560 561 // Check if we called CFRetain/CFRelease/CFMakeCollectable. 562 const IdentifierInfo *FuncII = FD->getIdentifier(); 563 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable)) 564 return; 565 566 // FIXME: The rest of this just checks that the argument is non-null. 567 // It should probably be refactored and combined with NonNullParamChecker. 568 569 // Get the argument's value. 570 const Expr *Arg = CE->getArg(0); 571 SVal ArgVal = state->getSVal(Arg, C.getLocationContext()); 572 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); 573 if (!DefArgVal) 574 return; 575 576 // Get a NULL value. 577 SValBuilder &svalBuilder = C.getSValBuilder(); 578 DefinedSVal zero = 579 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); 580 581 // Make an expression asserting that they're equal. 582 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); 583 584 // Are they equal? 585 ProgramStateRef stateTrue, stateFalse; 586 std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); 587 588 if (stateTrue && !stateFalse) { 589 ExplodedNode *N = C.generateSink(stateTrue); 590 if (!N) 591 return; 592 593 const char *description; 594 if (FuncII == Retain) 595 description = "Null pointer argument in call to CFRetain"; 596 else if (FuncII == Release) 597 description = "Null pointer argument in call to CFRelease"; 598 else if (FuncII == MakeCollectable) 599 description = "Null pointer argument in call to CFMakeCollectable"; 600 else 601 llvm_unreachable("impossible case"); 602 603 BugReport *report = new BugReport(*BT, description, N); 604 report->addRange(Arg->getSourceRange()); 605 bugreporter::trackNullOrUndefValue(N, Arg, *report); 606 C.emitReport(report); 607 return; 608 } 609 610 // From here on, we know the argument is non-null. 611 C.addTransition(stateFalse); 612} 613 614//===----------------------------------------------------------------------===// 615// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 616//===----------------------------------------------------------------------===// 617 618namespace { 619class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 620 mutable Selector releaseS; 621 mutable Selector retainS; 622 mutable Selector autoreleaseS; 623 mutable Selector drainS; 624 mutable std::unique_ptr<BugType> BT; 625 626public: 627 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 628}; 629} 630 631void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 632 CheckerContext &C) const { 633 634 if (!BT) { 635 BT.reset(new APIMisuse( 636 this, "message incorrectly sent to class instead of class instance")); 637 638 ASTContext &Ctx = C.getASTContext(); 639 releaseS = GetNullarySelector("release", Ctx); 640 retainS = GetNullarySelector("retain", Ctx); 641 autoreleaseS = GetNullarySelector("autorelease", Ctx); 642 drainS = GetNullarySelector("drain", Ctx); 643 } 644 645 if (msg.isInstanceMessage()) 646 return; 647 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 648 assert(Class); 649 650 Selector S = msg.getSelector(); 651 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 652 return; 653 654 if (ExplodedNode *N = C.addTransition()) { 655 SmallString<200> buf; 656 llvm::raw_svector_ostream os(buf); 657 658 os << "The '"; 659 S.print(os); 660 os << "' message should be sent to instances " 661 "of class '" << Class->getName() 662 << "' and not the class directly"; 663 664 BugReport *report = new BugReport(*BT, os.str(), N); 665 report->addRange(msg.getSourceRange()); 666 C.emitReport(report); 667 } 668} 669 670//===----------------------------------------------------------------------===// 671// Check for passing non-Objective-C types to variadic methods that expect 672// only Objective-C types. 673//===----------------------------------------------------------------------===// 674 675namespace { 676class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 677 mutable Selector arrayWithObjectsS; 678 mutable Selector dictionaryWithObjectsAndKeysS; 679 mutable Selector setWithObjectsS; 680 mutable Selector orderedSetWithObjectsS; 681 mutable Selector initWithObjectsS; 682 mutable Selector initWithObjectsAndKeysS; 683 mutable std::unique_ptr<BugType> BT; 684 685 bool isVariadicMessage(const ObjCMethodCall &msg) const; 686 687public: 688 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 689}; 690} 691 692/// isVariadicMessage - Returns whether the given message is a variadic message, 693/// where all arguments must be Objective-C types. 694bool 695VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 696 const ObjCMethodDecl *MD = msg.getDecl(); 697 698 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 699 return false; 700 701 Selector S = msg.getSelector(); 702 703 if (msg.isInstanceMessage()) { 704 // FIXME: Ideally we'd look at the receiver interface here, but that's not 705 // useful for init, because alloc returns 'id'. In theory, this could lead 706 // to false positives, for example if there existed a class that had an 707 // initWithObjects: implementation that does accept non-Objective-C pointer 708 // types, but the chance of that happening is pretty small compared to the 709 // gains that this analysis gives. 710 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 711 712 switch (findKnownClass(Class)) { 713 case FC_NSArray: 714 case FC_NSOrderedSet: 715 case FC_NSSet: 716 return S == initWithObjectsS; 717 case FC_NSDictionary: 718 return S == initWithObjectsAndKeysS; 719 default: 720 return false; 721 } 722 } else { 723 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 724 725 switch (findKnownClass(Class)) { 726 case FC_NSArray: 727 return S == arrayWithObjectsS; 728 case FC_NSOrderedSet: 729 return S == orderedSetWithObjectsS; 730 case FC_NSSet: 731 return S == setWithObjectsS; 732 case FC_NSDictionary: 733 return S == dictionaryWithObjectsAndKeysS; 734 default: 735 return false; 736 } 737 } 738} 739 740void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 741 CheckerContext &C) const { 742 if (!BT) { 743 BT.reset(new APIMisuse(this, 744 "Arguments passed to variadic method aren't all " 745 "Objective-C pointer types")); 746 747 ASTContext &Ctx = C.getASTContext(); 748 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 749 dictionaryWithObjectsAndKeysS = 750 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 751 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 752 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 753 754 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 755 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 756 } 757 758 if (!isVariadicMessage(msg)) 759 return; 760 761 // We are not interested in the selector arguments since they have 762 // well-defined types, so the compiler will issue a warning for them. 763 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 764 765 // We're not interested in the last argument since it has to be nil or the 766 // compiler would have issued a warning for it elsewhere. 767 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 768 769 if (variadicArgsEnd <= variadicArgsBegin) 770 return; 771 772 // Verify that all arguments have Objective-C types. 773 Optional<ExplodedNode*> errorNode; 774 775 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 776 QualType ArgTy = msg.getArgExpr(I)->getType(); 777 if (ArgTy->isObjCObjectPointerType()) 778 continue; 779 780 // Block pointers are treaded as Objective-C pointers. 781 if (ArgTy->isBlockPointerType()) 782 continue; 783 784 // Ignore pointer constants. 785 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) 786 continue; 787 788 // Ignore pointer types annotated with 'NSObject' attribute. 789 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 790 continue; 791 792 // Ignore CF references, which can be toll-free bridged. 793 if (coreFoundation::isCFObjectRef(ArgTy)) 794 continue; 795 796 // Generate only one error node to use for all bug reports. 797 if (!errorNode.hasValue()) 798 errorNode = C.addTransition(); 799 800 if (!errorNode.getValue()) 801 continue; 802 803 SmallString<128> sbuf; 804 llvm::raw_svector_ostream os(sbuf); 805 806 StringRef TypeName = GetReceiverInterfaceName(msg); 807 if (!TypeName.empty()) 808 os << "Argument to '" << TypeName << "' method '"; 809 else 810 os << "Argument to method '"; 811 812 msg.getSelector().print(os); 813 os << "' should be an Objective-C pointer type, not '"; 814 ArgTy.print(os, C.getLangOpts()); 815 os << "'"; 816 817 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); 818 R->addRange(msg.getArgSourceRange(I)); 819 C.emitReport(R); 820 } 821} 822 823//===----------------------------------------------------------------------===// 824// Improves the modeling of loops over Cocoa collections. 825//===----------------------------------------------------------------------===// 826 827// The map from container symbol to the container count symbol. 828// We currently will remember the last countainer count symbol encountered. 829REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) 830REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) 831 832namespace { 833class ObjCLoopChecker 834 : public Checker<check::PostStmt<ObjCForCollectionStmt>, 835 check::PostObjCMessage, 836 check::DeadSymbols, 837 check::PointerEscape > { 838 mutable IdentifierInfo *CountSelectorII; 839 840 bool isCollectionCountMethod(const ObjCMethodCall &M, 841 CheckerContext &C) const; 842 843public: 844 ObjCLoopChecker() : CountSelectorII(nullptr) {} 845 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 846 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 847 void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; 848 ProgramStateRef checkPointerEscape(ProgramStateRef State, 849 const InvalidatedSymbols &Escaped, 850 const CallEvent *Call, 851 PointerEscapeKind Kind) const; 852}; 853} 854 855static bool isKnownNonNilCollectionType(QualType T) { 856 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 857 if (!PT) 858 return false; 859 860 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 861 if (!ID) 862 return false; 863 864 switch (findKnownClass(ID)) { 865 case FC_NSArray: 866 case FC_NSDictionary: 867 case FC_NSEnumerator: 868 case FC_NSOrderedSet: 869 case FC_NSSet: 870 return true; 871 default: 872 return false; 873 } 874} 875 876/// Assumes that the collection is non-nil. 877/// 878/// If the collection is known to be nil, returns NULL to indicate an infeasible 879/// path. 880static ProgramStateRef checkCollectionNonNil(CheckerContext &C, 881 ProgramStateRef State, 882 const ObjCForCollectionStmt *FCS) { 883 if (!State) 884 return nullptr; 885 886 SVal CollectionVal = C.getSVal(FCS->getCollection()); 887 Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); 888 if (!KnownCollection) 889 return State; 890 891 ProgramStateRef StNonNil, StNil; 892 std::tie(StNonNil, StNil) = State->assume(*KnownCollection); 893 if (StNil && !StNonNil) { 894 // The collection is nil. This path is infeasible. 895 return nullptr; 896 } 897 898 return StNonNil; 899} 900 901/// Assumes that the collection elements are non-nil. 902/// 903/// This only applies if the collection is one of those known not to contain 904/// nil values. 905static ProgramStateRef checkElementNonNil(CheckerContext &C, 906 ProgramStateRef State, 907 const ObjCForCollectionStmt *FCS) { 908 if (!State) 909 return nullptr; 910 911 // See if the collection is one where we /know/ the elements are non-nil. 912 if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) 913 return State; 914 915 const LocationContext *LCtx = C.getLocationContext(); 916 const Stmt *Element = FCS->getElement(); 917 918 // FIXME: Copied from ExprEngineObjC. 919 Optional<Loc> ElementLoc; 920 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 921 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 922 assert(ElemDecl->getInit() == nullptr); 923 ElementLoc = State->getLValue(ElemDecl, LCtx); 924 } else { 925 ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); 926 } 927 928 if (!ElementLoc) 929 return State; 930 931 // Go ahead and assume the value is non-nil. 932 SVal Val = State->getSVal(*ElementLoc); 933 return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); 934} 935 936/// Returns NULL state if the collection is known to contain elements 937/// (or is known not to contain elements if the Assumption parameter is false.) 938static ProgramStateRef 939assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 940 SymbolRef CollectionS, bool Assumption) { 941 if (!State || !CollectionS) 942 return State; 943 944 const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); 945 if (!CountS) { 946 const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); 947 if (!KnownNonEmpty) 948 return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); 949 return (Assumption == *KnownNonEmpty) ? State : nullptr; 950 } 951 952 SValBuilder &SvalBuilder = C.getSValBuilder(); 953 SVal CountGreaterThanZeroVal = 954 SvalBuilder.evalBinOp(State, BO_GT, 955 nonloc::SymbolVal(*CountS), 956 SvalBuilder.makeIntVal(0, (*CountS)->getType()), 957 SvalBuilder.getConditionType()); 958 Optional<DefinedSVal> CountGreaterThanZero = 959 CountGreaterThanZeroVal.getAs<DefinedSVal>(); 960 if (!CountGreaterThanZero) { 961 // The SValBuilder cannot construct a valid SVal for this condition. 962 // This means we cannot properly reason about it. 963 return State; 964 } 965 966 return State->assume(*CountGreaterThanZero, Assumption); 967} 968 969static ProgramStateRef 970assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, 971 const ObjCForCollectionStmt *FCS, 972 bool Assumption) { 973 if (!State) 974 return nullptr; 975 976 SymbolRef CollectionS = 977 State->getSVal(FCS->getCollection(), C.getLocationContext()).getAsSymbol(); 978 return assumeCollectionNonEmpty(C, State, CollectionS, Assumption); 979} 980 981 982/// If the fist block edge is a back edge, we are reentering the loop. 983static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, 984 const ObjCForCollectionStmt *FCS) { 985 if (!N) 986 return false; 987 988 ProgramPoint P = N->getLocation(); 989 if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { 990 if (BE->getSrc()->getLoopTarget() == FCS) 991 return true; 992 return false; 993 } 994 995 // Keep looking for a block edge. 996 for (ExplodedNode::const_pred_iterator I = N->pred_begin(), 997 E = N->pred_end(); I != E; ++I) { 998 if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) 999 return true; 1000 } 1001 1002 return false; 1003} 1004 1005void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 1006 CheckerContext &C) const { 1007 ProgramStateRef State = C.getState(); 1008 1009 // Check if this is the branch for the end of the loop. 1010 SVal CollectionSentinel = C.getSVal(FCS); 1011 if (CollectionSentinel.isZeroConstant()) { 1012 if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) 1013 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); 1014 1015 // Otherwise, this is a branch that goes through the loop body. 1016 } else { 1017 State = checkCollectionNonNil(C, State, FCS); 1018 State = checkElementNonNil(C, State, FCS); 1019 State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); 1020 } 1021 1022 if (!State) 1023 C.generateSink(); 1024 else if (State != C.getState()) 1025 C.addTransition(State); 1026} 1027 1028bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, 1029 CheckerContext &C) const { 1030 Selector S = M.getSelector(); 1031 // Initialize the identifiers on first use. 1032 if (!CountSelectorII) 1033 CountSelectorII = &C.getASTContext().Idents.get("count"); 1034 1035 // If the method returns collection count, record the value. 1036 if (S.isUnarySelector() && 1037 (S.getIdentifierInfoForSlot(0) == CountSelectorII)) 1038 return true; 1039 1040 return false; 1041} 1042 1043void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1044 CheckerContext &C) const { 1045 if (!M.isInstanceMessage()) 1046 return; 1047 1048 const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); 1049 if (!ClassID) 1050 return; 1051 1052 FoundationClass Class = findKnownClass(ClassID); 1053 if (Class != FC_NSDictionary && 1054 Class != FC_NSArray && 1055 Class != FC_NSSet && 1056 Class != FC_NSOrderedSet) 1057 return; 1058 1059 SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); 1060 if (!ContainerS) 1061 return; 1062 1063 // If we are processing a call to "count", get the symbolic value returned by 1064 // a call to "count" and add it to the map. 1065 if (!isCollectionCountMethod(M, C)) 1066 return; 1067 1068 const Expr *MsgExpr = M.getOriginExpr(); 1069 SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); 1070 if (CountS) { 1071 ProgramStateRef State = C.getState(); 1072 1073 C.getSymbolManager().addSymbolDependency(ContainerS, CountS); 1074 State = State->set<ContainerCountMap>(ContainerS, CountS); 1075 1076 if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) { 1077 State = State->remove<ContainerNonEmptyMap>(ContainerS); 1078 State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty); 1079 } 1080 1081 C.addTransition(State); 1082 } 1083 return; 1084} 1085 1086static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) { 1087 const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call); 1088 if (!Message) 1089 return nullptr; 1090 1091 const ObjCMethodDecl *MD = Message->getDecl(); 1092 if (!MD) 1093 return nullptr; 1094 1095 const ObjCInterfaceDecl *StaticClass; 1096 if (isa<ObjCProtocolDecl>(MD->getDeclContext())) { 1097 // We can't find out where the method was declared without doing more work. 1098 // Instead, see if the receiver is statically typed as a known immutable 1099 // collection. 1100 StaticClass = Message->getOriginExpr()->getReceiverInterface(); 1101 } else { 1102 StaticClass = MD->getClassInterface(); 1103 } 1104 1105 if (!StaticClass) 1106 return nullptr; 1107 1108 switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) { 1109 case FC_None: 1110 return nullptr; 1111 case FC_NSArray: 1112 case FC_NSDictionary: 1113 case FC_NSEnumerator: 1114 case FC_NSNull: 1115 case FC_NSOrderedSet: 1116 case FC_NSSet: 1117 case FC_NSString: 1118 break; 1119 } 1120 1121 return Message->getReceiverSVal().getAsSymbol(); 1122} 1123 1124ProgramStateRef 1125ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, 1126 const InvalidatedSymbols &Escaped, 1127 const CallEvent *Call, 1128 PointerEscapeKind Kind) const { 1129 SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); 1130 1131 // Remove the invalidated symbols form the collection count map. 1132 for (InvalidatedSymbols::const_iterator I = Escaped.begin(), 1133 E = Escaped.end(); 1134 I != E; ++I) { 1135 SymbolRef Sym = *I; 1136 1137 // Don't invalidate this symbol's count if we know the method being called 1138 // is declared on an immutable class. This isn't completely correct if the 1139 // receiver is also passed as an argument, but in most uses of NSArray, 1140 // NSDictionary, etc. this isn't likely to happen in a dangerous way. 1141 if (Sym == ImmutableReceiver) 1142 continue; 1143 1144 // The symbol escaped. Pessimistically, assume that the count could have 1145 // changed. 1146 State = State->remove<ContainerCountMap>(Sym); 1147 State = State->remove<ContainerNonEmptyMap>(Sym); 1148 } 1149 return State; 1150} 1151 1152void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, 1153 CheckerContext &C) const { 1154 ProgramStateRef State = C.getState(); 1155 1156 // Remove the dead symbols from the collection count map. 1157 ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); 1158 for (ContainerCountMapTy::iterator I = Tracked.begin(), 1159 E = Tracked.end(); I != E; ++I) { 1160 SymbolRef Sym = I->first; 1161 if (SymReaper.isDead(Sym)) { 1162 State = State->remove<ContainerCountMap>(Sym); 1163 State = State->remove<ContainerNonEmptyMap>(Sym); 1164 } 1165 } 1166 1167 C.addTransition(State); 1168} 1169 1170namespace { 1171/// \class ObjCNonNilReturnValueChecker 1172/// \brief The checker restricts the return values of APIs known to 1173/// never (or almost never) return 'nil'. 1174class ObjCNonNilReturnValueChecker 1175 : public Checker<check::PostObjCMessage, 1176 check::PostStmt<ObjCArrayLiteral>, 1177 check::PostStmt<ObjCDictionaryLiteral>, 1178 check::PostStmt<ObjCBoxedExpr> > { 1179 mutable bool Initialized; 1180 mutable Selector ObjectAtIndex; 1181 mutable Selector ObjectAtIndexedSubscript; 1182 mutable Selector NullSelector; 1183 1184public: 1185 ObjCNonNilReturnValueChecker() : Initialized(false) {} 1186 1187 ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, 1188 ProgramStateRef State, 1189 CheckerContext &C) const; 1190 void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const { 1191 C.addTransition(assumeExprIsNonNull(E, C.getState(), C)); 1192 } 1193 1194 void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const { 1195 assumeExprIsNonNull(E, C); 1196 } 1197 void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const { 1198 assumeExprIsNonNull(E, C); 1199 } 1200 void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const { 1201 assumeExprIsNonNull(E, C); 1202 } 1203 1204 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; 1205}; 1206} 1207 1208ProgramStateRef 1209ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, 1210 ProgramStateRef State, 1211 CheckerContext &C) const { 1212 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext()); 1213 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) 1214 return State->assume(*DV, true); 1215 return State; 1216} 1217 1218void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, 1219 CheckerContext &C) 1220 const { 1221 ProgramStateRef State = C.getState(); 1222 1223 if (!Initialized) { 1224 ASTContext &Ctx = C.getASTContext(); 1225 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); 1226 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); 1227 NullSelector = GetNullarySelector("null", Ctx); 1228 } 1229 1230 // Check the receiver type. 1231 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { 1232 1233 // Assume that object returned from '[self init]' or '[super init]' is not 1234 // 'nil' if we are processing an inlined function/method. 1235 // 1236 // A defensive callee will (and should) check if the object returned by 1237 // '[super init]' is 'nil' before doing it's own initialization. However, 1238 // since 'nil' is rarely returned in practice, we should not warn when the 1239 // caller to the defensive constructor uses the object in contexts where 1240 // 'nil' is not accepted. 1241 if (!C.inTopFrame() && M.getDecl() && 1242 M.getDecl()->getMethodFamily() == OMF_init && 1243 M.isReceiverSelfOrSuper()) { 1244 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1245 } 1246 1247 FoundationClass Cl = findKnownClass(Interface); 1248 1249 // Objects returned from 1250 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] 1251 // are never 'nil'. 1252 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { 1253 Selector Sel = M.getSelector(); 1254 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { 1255 // Go ahead and assume the value is non-nil. 1256 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1257 } 1258 } 1259 1260 // Objects returned from [NSNull null] are not nil. 1261 if (Cl == FC_NSNull) { 1262 if (M.getSelector() == NullSelector) { 1263 // Go ahead and assume the value is non-nil. 1264 State = assumeExprIsNonNull(M.getOriginExpr(), State, C); 1265 } 1266 } 1267 } 1268 C.addTransition(State); 1269} 1270 1271//===----------------------------------------------------------------------===// 1272// Check registration. 1273//===----------------------------------------------------------------------===// 1274 1275void ento::registerNilArgChecker(CheckerManager &mgr) { 1276 mgr.registerChecker<NilArgChecker>(); 1277} 1278 1279void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 1280 mgr.registerChecker<CFNumberCreateChecker>(); 1281} 1282 1283void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 1284 mgr.registerChecker<CFRetainReleaseChecker>(); 1285} 1286 1287void ento::registerClassReleaseChecker(CheckerManager &mgr) { 1288 mgr.registerChecker<ClassReleaseChecker>(); 1289} 1290 1291void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 1292 mgr.registerChecker<VariadicMethodTypeChecker>(); 1293} 1294 1295void ento::registerObjCLoopChecker(CheckerManager &mgr) { 1296 mgr.registerChecker<ObjCLoopChecker>(); 1297} 1298 1299void 1300ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { 1301 mgr.registerChecker<ObjCNonNilReturnValueChecker>(); 1302} 1303