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