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