BasicObjCFoundationChecks.cpp revision de507eaf3cb54d3cb234dc14499c10ab3373d15f
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/Calls.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 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg, 436 report)); 437 C.EmitReport(report); 438 return; 439 } 440 441 // From here on, we know the argument is non-null. 442 C.addTransition(stateFalse); 443} 444 445//===----------------------------------------------------------------------===// 446// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. 447//===----------------------------------------------------------------------===// 448 449namespace { 450class ClassReleaseChecker : public Checker<check::PreObjCMessage> { 451 mutable Selector releaseS; 452 mutable Selector retainS; 453 mutable Selector autoreleaseS; 454 mutable Selector drainS; 455 mutable OwningPtr<BugType> BT; 456 457public: 458 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 459}; 460} 461 462void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 463 CheckerContext &C) const { 464 465 if (!BT) { 466 BT.reset(new APIMisuse("message incorrectly sent to class instead of class " 467 "instance")); 468 469 ASTContext &Ctx = C.getASTContext(); 470 releaseS = GetNullarySelector("release", Ctx); 471 retainS = GetNullarySelector("retain", Ctx); 472 autoreleaseS = GetNullarySelector("autorelease", Ctx); 473 drainS = GetNullarySelector("drain", Ctx); 474 } 475 476 if (msg.isInstanceMessage()) 477 return; 478 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 479 assert(Class); 480 481 Selector S = msg.getSelector(); 482 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) 483 return; 484 485 if (ExplodedNode *N = C.addTransition()) { 486 SmallString<200> buf; 487 llvm::raw_svector_ostream os(buf); 488 489 os << "The '" << S.getAsString() << "' message should be sent to instances " 490 "of class '" << Class->getName() 491 << "' and not the class directly"; 492 493 BugReport *report = new BugReport(*BT, os.str(), N); 494 report->addRange(msg.getSourceRange()); 495 C.EmitReport(report); 496 } 497} 498 499//===----------------------------------------------------------------------===// 500// Check for passing non-Objective-C types to variadic methods that expect 501// only Objective-C types. 502//===----------------------------------------------------------------------===// 503 504namespace { 505class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { 506 mutable Selector arrayWithObjectsS; 507 mutable Selector dictionaryWithObjectsAndKeysS; 508 mutable Selector setWithObjectsS; 509 mutable Selector orderedSetWithObjectsS; 510 mutable Selector initWithObjectsS; 511 mutable Selector initWithObjectsAndKeysS; 512 mutable OwningPtr<BugType> BT; 513 514 bool isVariadicMessage(const ObjCMethodCall &msg) const; 515 516public: 517 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; 518}; 519} 520 521/// isVariadicMessage - Returns whether the given message is a variadic message, 522/// where all arguments must be Objective-C types. 523bool 524VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { 525 const ObjCMethodDecl *MD = msg.getDecl(); 526 527 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) 528 return false; 529 530 Selector S = msg.getSelector(); 531 532 if (msg.isInstanceMessage()) { 533 // FIXME: Ideally we'd look at the receiver interface here, but that's not 534 // useful for init, because alloc returns 'id'. In theory, this could lead 535 // to false positives, for example if there existed a class that had an 536 // initWithObjects: implementation that does accept non-Objective-C pointer 537 // types, but the chance of that happening is pretty small compared to the 538 // gains that this analysis gives. 539 const ObjCInterfaceDecl *Class = MD->getClassInterface(); 540 541 switch (findKnownClass(Class)) { 542 case FC_NSArray: 543 case FC_NSOrderedSet: 544 case FC_NSSet: 545 return S == initWithObjectsS; 546 case FC_NSDictionary: 547 return S == initWithObjectsAndKeysS; 548 default: 549 return false; 550 } 551 } else { 552 const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); 553 554 switch (findKnownClass(Class)) { 555 case FC_NSArray: 556 return S == arrayWithObjectsS; 557 case FC_NSOrderedSet: 558 return S == orderedSetWithObjectsS; 559 case FC_NSSet: 560 return S == setWithObjectsS; 561 case FC_NSDictionary: 562 return S == dictionaryWithObjectsAndKeysS; 563 default: 564 return false; 565 } 566 } 567} 568 569void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, 570 CheckerContext &C) const { 571 if (!BT) { 572 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all " 573 "Objective-C pointer types")); 574 575 ASTContext &Ctx = C.getASTContext(); 576 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); 577 dictionaryWithObjectsAndKeysS = 578 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); 579 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); 580 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); 581 582 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); 583 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); 584 } 585 586 if (!isVariadicMessage(msg)) 587 return; 588 589 // We are not interested in the selector arguments since they have 590 // well-defined types, so the compiler will issue a warning for them. 591 unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); 592 593 // We're not interested in the last argument since it has to be nil or the 594 // compiler would have issued a warning for it elsewhere. 595 unsigned variadicArgsEnd = msg.getNumArgs() - 1; 596 597 if (variadicArgsEnd <= variadicArgsBegin) 598 return; 599 600 // Verify that all arguments have Objective-C types. 601 llvm::Optional<ExplodedNode*> errorNode; 602 ProgramStateRef state = C.getState(); 603 604 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { 605 QualType ArgTy = msg.getArgExpr(I)->getType(); 606 if (ArgTy->isObjCObjectPointerType()) 607 continue; 608 609 // Block pointers are treaded as Objective-C pointers. 610 if (ArgTy->isBlockPointerType()) 611 continue; 612 613 // Ignore pointer constants. 614 if (isa<loc::ConcreteInt>(msg.getArgSVal(I))) 615 continue; 616 617 // Ignore pointer types annotated with 'NSObject' attribute. 618 if (C.getASTContext().isObjCNSObjectType(ArgTy)) 619 continue; 620 621 // Ignore CF references, which can be toll-free bridged. 622 if (coreFoundation::isCFObjectRef(ArgTy)) 623 continue; 624 625 // Generate only one error node to use for all bug reports. 626 if (!errorNode.hasValue()) 627 errorNode = C.addTransition(); 628 629 if (!errorNode.getValue()) 630 continue; 631 632 SmallString<128> sbuf; 633 llvm::raw_svector_ostream os(sbuf); 634 635 StringRef TypeName = GetReceiverInterfaceName(msg); 636 if (!TypeName.empty()) 637 os << "Argument to '" << TypeName << "' method '"; 638 else 639 os << "Argument to method '"; 640 641 os << msg.getSelector().getAsString() 642 << "' should be an Objective-C pointer type, not '"; 643 ArgTy.print(os, C.getLangOpts()); 644 os << "'"; 645 646 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue()); 647 R->addRange(msg.getArgSourceRange(I)); 648 C.EmitReport(R); 649 } 650} 651 652//===----------------------------------------------------------------------===// 653// Improves the modeling of loops over Cocoa collections. 654//===----------------------------------------------------------------------===// 655 656namespace { 657class ObjCLoopChecker 658 : public Checker<check::PostStmt<ObjCForCollectionStmt> > { 659 660public: 661 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; 662}; 663} 664 665static bool isKnownNonNilCollectionType(QualType T) { 666 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); 667 if (!PT) 668 return false; 669 670 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); 671 if (!ID) 672 return false; 673 674 switch (findKnownClass(ID)) { 675 case FC_NSArray: 676 case FC_NSDictionary: 677 case FC_NSEnumerator: 678 case FC_NSOrderedSet: 679 case FC_NSSet: 680 return true; 681 default: 682 return false; 683 } 684} 685 686void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, 687 CheckerContext &C) const { 688 ProgramStateRef State = C.getState(); 689 690 // Check if this is the branch for the end of the loop. 691 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext()); 692 if (CollectionSentinel.isZeroConstant()) 693 return; 694 695 // See if the collection is one where we /know/ the elements are non-nil. 696 const Expr *Collection = FCS->getCollection(); 697 if (!isKnownNonNilCollectionType(Collection->getType())) 698 return; 699 700 // FIXME: Copied from ExprEngineObjC. 701 const Stmt *Element = FCS->getElement(); 702 SVal ElementVar; 703 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { 704 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); 705 assert(ElemDecl->getInit() == 0); 706 ElementVar = State->getLValue(ElemDecl, C.getLocationContext()); 707 } else { 708 ElementVar = State->getSVal(Element, C.getLocationContext()); 709 } 710 711 if (!isa<Loc>(ElementVar)) 712 return; 713 714 // Go ahead and assume the value is non-nil. 715 SVal Val = State->getSVal(cast<Loc>(ElementVar)); 716 State = State->assume(cast<DefinedOrUnknownSVal>(Val), true); 717 C.addTransition(State); 718} 719 720 721//===----------------------------------------------------------------------===// 722// Check registration. 723//===----------------------------------------------------------------------===// 724 725void ento::registerNilArgChecker(CheckerManager &mgr) { 726 mgr.registerChecker<NilArgChecker>(); 727} 728 729void ento::registerCFNumberCreateChecker(CheckerManager &mgr) { 730 mgr.registerChecker<CFNumberCreateChecker>(); 731} 732 733void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { 734 mgr.registerChecker<CFRetainReleaseChecker>(); 735} 736 737void ento::registerClassReleaseChecker(CheckerManager &mgr) { 738 mgr.registerChecker<ClassReleaseChecker>(); 739} 740 741void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { 742 mgr.registerChecker<VariadicMethodTypeChecker>(); 743} 744 745void ento::registerObjCLoopChecker(CheckerManager &mgr) { 746 mgr.registerChecker<ObjCLoopChecker>(); 747} 748