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