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