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