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