BasicObjCFoundationChecks.cpp revision 1895a0a6936001374f66adbdfcf8abe5edf912ea
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/CheckerContext.h"
21#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
22#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
23#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.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 const char* GetReceiverNameType(const ObjCMessage &msg) {
50  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
51    return ID->getIdentifier()->getNameStart();
52  return 0;
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 ObjCMessage &msg, unsigned Arg) const;
99
100  public:
101    void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
102  };
103}
104
105void NilArgChecker::WarnNilArg(CheckerContext &C,
106                               const ObjCMessage &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 '" << GetReceiverNameType(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(ObjCMessage 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, C.getLocationContext(), C.getState())))
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(ObjCMessage msg, CheckerContext &C) const;
459};
460}
461
462void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage 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 ObjCMessage &msg) const;
515
516public:
517  void checkPreObjCMessage(ObjCMessage 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 ObjCMessage &msg) const {
525  const ObjCMethodDecl *MD = msg.getMethodDecl();
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(ObjCMessage 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.getArgType(I);
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, C.getLocationContext(),
615                                             state)))
616      continue;
617
618    // Ignore pointer types annotated with 'NSObject' attribute.
619    if (C.getASTContext().isObjCNSObjectType(ArgTy))
620      continue;
621
622    // Ignore CF references, which can be toll-free bridged.
623    if (coreFoundation::isCFObjectRef(ArgTy))
624      continue;
625
626    // Generate only one error node to use for all bug reports.
627    if (!errorNode.hasValue()) {
628      errorNode = C.addTransition();
629    }
630
631    if (!errorNode.getValue())
632      continue;
633
634    SmallString<128> sbuf;
635    llvm::raw_svector_ostream os(sbuf);
636
637    if (const char *TypeName = GetReceiverNameType(msg))
638      os << "Argument to '" << TypeName << "' method '";
639    else
640      os << "Argument to method '";
641
642    os << msg.getSelector().getAsString()
643      << "' should be an Objective-C pointer type, not '"
644      << ArgTy.getAsString() << "'";
645
646    BugReport *R = new BugReport(*BT, os.str(),
647                                             errorNode.getValue());
648    R->addRange(msg.getArgSourceRange(I));
649    C.EmitReport(R);
650  }
651}
652
653//===----------------------------------------------------------------------===//
654// Improves the modeling of loops over Cocoa collections.
655//===----------------------------------------------------------------------===//
656
657namespace {
658class ObjCLoopChecker
659  : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
660
661public:
662  void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
663};
664}
665
666static bool isKnownNonNilCollectionType(QualType T) {
667  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
668  if (!PT)
669    return false;
670
671  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
672  if (!ID)
673    return false;
674
675  switch (findKnownClass(ID)) {
676  case FC_NSArray:
677  case FC_NSDictionary:
678  case FC_NSEnumerator:
679  case FC_NSOrderedSet:
680  case FC_NSSet:
681    return true;
682  default:
683    return false;
684  }
685}
686
687void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
688                                    CheckerContext &C) const {
689  ProgramStateRef State = C.getState();
690
691  // Check if this is the branch for the end of the loop.
692  SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
693  if (CollectionSentinel.isZeroConstant())
694    return;
695
696  // See if the collection is one where we /know/ the elements are non-nil.
697  const Expr *Collection = FCS->getCollection();
698  if (!isKnownNonNilCollectionType(Collection->getType()))
699    return;
700
701  // FIXME: Copied from ExprEngineObjC.
702  const Stmt *Element = FCS->getElement();
703  SVal ElementVar;
704  if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
705    const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
706    assert(ElemDecl->getInit() == 0);
707    ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
708  } else {
709    ElementVar = State->getSVal(Element, C.getLocationContext());
710  }
711
712  if (!isa<Loc>(ElementVar))
713    return;
714
715  // Go ahead and assume the value is non-nil.
716  SVal Val = State->getSVal(cast<Loc>(ElementVar));
717  State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
718  C.addTransition(State);
719}
720
721
722//===----------------------------------------------------------------------===//
723// Check registration.
724//===----------------------------------------------------------------------===//
725
726void ento::registerNilArgChecker(CheckerManager &mgr) {
727  mgr.registerChecker<NilArgChecker>();
728}
729
730void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
731  mgr.registerChecker<CFNumberCreateChecker>();
732}
733
734void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
735  mgr.registerChecker<CFRetainReleaseChecker>();
736}
737
738void ento::registerClassReleaseChecker(CheckerManager &mgr) {
739  mgr.registerChecker<ClassReleaseChecker>();
740}
741
742void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
743  mgr.registerChecker<VariadicMethodTypeChecker>();
744}
745
746void ento::registerObjCLoopChecker(CheckerManager &mgr) {
747  mgr.registerChecker<ObjCLoopChecker>();
748}
749