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