BasicObjCFoundationChecks.cpp revision 0fe4d400ab05995727440620c25fe1d185b4e046
1cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
205436638acc7c010349a69c3395f1a57c642dc62Ying Wang//
305436638acc7c010349a69c3395f1a57c642dc62Ying Wang//                     The LLVM Compiler Infrastructure
4cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//
5cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project// This file is distributed under the University of Illinois Open Source
6cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project// License. See LICENSE.TXT for details.
705436638acc7c010349a69c3395f1a57c642dc62Ying Wang//
8cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//===----------------------------------------------------------------------===//
9cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//
10cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//  This file defines BasicObjCFoundationChecks, a class that encapsulates
11cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//  a set of simple checks to run on Objective-C code using Apple's Foundation
12cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//  classes.
13cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//
14cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//===----------------------------------------------------------------------===//
15cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project
1605436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "ClangSACheckers.h"
1705436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
1805436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/Checker.h"
1905436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/CheckerManager.h"
2005436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
2105436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2205436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
2305436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
2405436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2505436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
2605436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
2705436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/AST/DeclObjC.h"
2805436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "clang/AST/Expr.h"
29cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project#include "clang/AST/ExprObjC.h"
30cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project#include "clang/AST/StmtObjC.h"
31cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project#include "clang/AST/ASTContext.h"
32cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project#include "llvm/ADT/SmallString.h"
3305436638acc7c010349a69c3395f1a57c642dc62Ying Wang#include "llvm/ADT/StringMap.h"
3405436638acc7c010349a69c3395f1a57c642dc62Ying Wang
35cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Projectusing namespace clang;
36cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Projectusing namespace ento;
37cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project
38cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Projectnamespace {
39cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Projectclass APIMisuse : public BugType {
40cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Projectpublic:
4105436638acc7c010349a69c3395f1a57c642dc62Ying Wang  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
4205436638acc7c010349a69c3395f1a57c642dc62Ying Wang};
4305436638acc7c010349a69c3395f1a57c642dc62Ying Wang} // end anonymous namespace
44cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project
45cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project//===----------------------------------------------------------------------===//
4605436638acc7c010349a69c3395f1a57c642dc62Ying Wang// Utility functions.
4705436638acc7c010349a69c3395f1a57c642dc62Ying Wang//===----------------------------------------------------------------------===//
4805436638acc7c010349a69c3395f1a57c642dc62Ying Wang
4905436638acc7c010349a69c3395f1a57c642dc62Ying Wangstatic StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
5005436638acc7c010349a69c3395f1a57c642dc62Ying Wang  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
5105436638acc7c010349a69c3395f1a57c642dc62Ying Wang    return ID->getIdentifier()->getName();
5205436638acc7c010349a69c3395f1a57c642dc62Ying Wang  return StringRef();
5305436638acc7c010349a69c3395f1a57c642dc62Ying Wang}
5405436638acc7c010349a69c3395f1a57c642dc62Ying Wang
5505436638acc7c010349a69c3395f1a57c642dc62Ying Wangenum FoundationClass {
56cea198a11f15a2eb071d98491ca9a8bc8cebfbc4The Android Open Source Project  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/CFMakeCollectable checking for null arguments.
367//===----------------------------------------------------------------------===//
368
369namespace {
370class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
371  mutable OwningPtr<APIMisuse> BT;
372  mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
373public:
374  CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(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    MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
396    BT.reset(
397      new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
398  }
399
400  // Check if we called CFRetain/CFRelease/CFMakeCollectable.
401  const IdentifierInfo *FuncII = FD->getIdentifier();
402  if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
403    return;
404
405  // FIXME: The rest of this just checks that the argument is non-null.
406  // It should probably be refactored and combined with AttrNonNullChecker.
407
408  // Get the argument's value.
409  const Expr *Arg = CE->getArg(0);
410  SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
411  DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
412  if (!DefArgVal)
413    return;
414
415  // Get a NULL value.
416  SValBuilder &svalBuilder = C.getSValBuilder();
417  DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
418
419  // Make an expression asserting that they're equal.
420  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
421
422  // Are they equal?
423  ProgramStateRef stateTrue, stateFalse;
424  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
425
426  if (stateTrue && !stateFalse) {
427    ExplodedNode *N = C.generateSink(stateTrue);
428    if (!N)
429      return;
430
431    const char *description;
432    if (FuncII == Retain)
433      description = "Null pointer argument in call to CFRetain";
434    else if (FuncII == Release)
435      description = "Null pointer argument in call to CFRelease";
436    else if (FuncII == MakeCollectable)
437      description = "Null pointer argument in call to CFMakeCollectable";
438    else
439      llvm_unreachable("impossible case");
440
441    BugReport *report = new BugReport(*BT, description, N);
442    report->addRange(Arg->getSourceRange());
443    bugreporter::trackNullOrUndefValue(N, Arg, *report);
444    C.emitReport(report);
445    return;
446  }
447
448  // From here on, we know the argument is non-null.
449  C.addTransition(stateFalse);
450}
451
452//===----------------------------------------------------------------------===//
453// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
454//===----------------------------------------------------------------------===//
455
456namespace {
457class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
458  mutable Selector releaseS;
459  mutable Selector retainS;
460  mutable Selector autoreleaseS;
461  mutable Selector drainS;
462  mutable OwningPtr<BugType> BT;
463
464public:
465  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
466};
467}
468
469void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
470                                              CheckerContext &C) const {
471
472  if (!BT) {
473    BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
474                           "instance"));
475
476    ASTContext &Ctx = C.getASTContext();
477    releaseS = GetNullarySelector("release", Ctx);
478    retainS = GetNullarySelector("retain", Ctx);
479    autoreleaseS = GetNullarySelector("autorelease", Ctx);
480    drainS = GetNullarySelector("drain", Ctx);
481  }
482
483  if (msg.isInstanceMessage())
484    return;
485  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
486  assert(Class);
487
488  Selector S = msg.getSelector();
489  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
490    return;
491
492  if (ExplodedNode *N = C.addTransition()) {
493    SmallString<200> buf;
494    llvm::raw_svector_ostream os(buf);
495
496    os << "The '" << S.getAsString() << "' message should be sent to instances "
497          "of class '" << Class->getName()
498       << "' and not the class directly";
499
500    BugReport *report = new BugReport(*BT, os.str(), N);
501    report->addRange(msg.getSourceRange());
502    C.emitReport(report);
503  }
504}
505
506//===----------------------------------------------------------------------===//
507// Check for passing non-Objective-C types to variadic methods that expect
508// only Objective-C types.
509//===----------------------------------------------------------------------===//
510
511namespace {
512class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
513  mutable Selector arrayWithObjectsS;
514  mutable Selector dictionaryWithObjectsAndKeysS;
515  mutable Selector setWithObjectsS;
516  mutable Selector orderedSetWithObjectsS;
517  mutable Selector initWithObjectsS;
518  mutable Selector initWithObjectsAndKeysS;
519  mutable OwningPtr<BugType> BT;
520
521  bool isVariadicMessage(const ObjCMethodCall &msg) const;
522
523public:
524  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
525};
526}
527
528/// isVariadicMessage - Returns whether the given message is a variadic message,
529/// where all arguments must be Objective-C types.
530bool
531VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
532  const ObjCMethodDecl *MD = msg.getDecl();
533
534  if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
535    return false;
536
537  Selector S = msg.getSelector();
538
539  if (msg.isInstanceMessage()) {
540    // FIXME: Ideally we'd look at the receiver interface here, but that's not
541    // useful for init, because alloc returns 'id'. In theory, this could lead
542    // to false positives, for example if there existed a class that had an
543    // initWithObjects: implementation that does accept non-Objective-C pointer
544    // types, but the chance of that happening is pretty small compared to the
545    // gains that this analysis gives.
546    const ObjCInterfaceDecl *Class = MD->getClassInterface();
547
548    switch (findKnownClass(Class)) {
549    case FC_NSArray:
550    case FC_NSOrderedSet:
551    case FC_NSSet:
552      return S == initWithObjectsS;
553    case FC_NSDictionary:
554      return S == initWithObjectsAndKeysS;
555    default:
556      return false;
557    }
558  } else {
559    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
560
561    switch (findKnownClass(Class)) {
562      case FC_NSArray:
563        return S == arrayWithObjectsS;
564      case FC_NSOrderedSet:
565        return S == orderedSetWithObjectsS;
566      case FC_NSSet:
567        return S == setWithObjectsS;
568      case FC_NSDictionary:
569        return S == dictionaryWithObjectsAndKeysS;
570      default:
571        return false;
572    }
573  }
574}
575
576void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
577                                                    CheckerContext &C) const {
578  if (!BT) {
579    BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
580                           "Objective-C pointer types"));
581
582    ASTContext &Ctx = C.getASTContext();
583    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
584    dictionaryWithObjectsAndKeysS =
585      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
586    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
587    orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
588
589    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
590    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
591  }
592
593  if (!isVariadicMessage(msg))
594      return;
595
596  // We are not interested in the selector arguments since they have
597  // well-defined types, so the compiler will issue a warning for them.
598  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
599
600  // We're not interested in the last argument since it has to be nil or the
601  // compiler would have issued a warning for it elsewhere.
602  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
603
604  if (variadicArgsEnd <= variadicArgsBegin)
605    return;
606
607  // Verify that all arguments have Objective-C types.
608  llvm::Optional<ExplodedNode*> errorNode;
609  ProgramStateRef state = C.getState();
610
611  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
612    QualType ArgTy = msg.getArgExpr(I)->getType();
613    if (ArgTy->isObjCObjectPointerType())
614      continue;
615
616    // Block pointers are treaded as Objective-C pointers.
617    if (ArgTy->isBlockPointerType())
618      continue;
619
620    // Ignore pointer constants.
621    if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
622      continue;
623
624    // Ignore pointer types annotated with 'NSObject' attribute.
625    if (C.getASTContext().isObjCNSObjectType(ArgTy))
626      continue;
627
628    // Ignore CF references, which can be toll-free bridged.
629    if (coreFoundation::isCFObjectRef(ArgTy))
630      continue;
631
632    // Generate only one error node to use for all bug reports.
633    if (!errorNode.hasValue())
634      errorNode = C.addTransition();
635
636    if (!errorNode.getValue())
637      continue;
638
639    SmallString<128> sbuf;
640    llvm::raw_svector_ostream os(sbuf);
641
642    StringRef TypeName = GetReceiverInterfaceName(msg);
643    if (!TypeName.empty())
644      os << "Argument to '" << TypeName << "' method '";
645    else
646      os << "Argument to method '";
647
648    os << msg.getSelector().getAsString()
649       << "' should be an Objective-C pointer type, not '";
650    ArgTy.print(os, C.getLangOpts());
651    os << "'";
652
653    BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
654    R->addRange(msg.getArgSourceRange(I));
655    C.emitReport(R);
656  }
657}
658
659//===----------------------------------------------------------------------===//
660// Improves the modeling of loops over Cocoa collections.
661//===----------------------------------------------------------------------===//
662
663namespace {
664class ObjCLoopChecker
665  : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
666
667public:
668  void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
669};
670}
671
672static bool isKnownNonNilCollectionType(QualType T) {
673  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
674  if (!PT)
675    return false;
676
677  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
678  if (!ID)
679    return false;
680
681  switch (findKnownClass(ID)) {
682  case FC_NSArray:
683  case FC_NSDictionary:
684  case FC_NSEnumerator:
685  case FC_NSOrderedSet:
686  case FC_NSSet:
687    return true;
688  default:
689    return false;
690  }
691}
692
693void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
694                                    CheckerContext &C) const {
695  ProgramStateRef State = C.getState();
696
697  // Check if this is the branch for the end of the loop.
698  SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
699  if (CollectionSentinel.isZeroConstant())
700    return;
701
702  // See if the collection is one where we /know/ the elements are non-nil.
703  const Expr *Collection = FCS->getCollection();
704  if (!isKnownNonNilCollectionType(Collection->getType()))
705    return;
706
707  // FIXME: Copied from ExprEngineObjC.
708  const Stmt *Element = FCS->getElement();
709  SVal ElementVar;
710  if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
711    const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
712    assert(ElemDecl->getInit() == 0);
713    ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
714  } else {
715    ElementVar = State->getSVal(Element, C.getLocationContext());
716  }
717
718  if (!isa<Loc>(ElementVar))
719    return;
720
721  // Go ahead and assume the value is non-nil.
722  SVal Val = State->getSVal(cast<Loc>(ElementVar));
723  State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
724  C.addTransition(State);
725}
726
727namespace {
728/// \class ObjCNonNilReturnValueChecker
729/// \brief The checker restricts the return values of APIs known to
730/// never (or almost never) return 'nil'.
731class ObjCNonNilReturnValueChecker
732  : public Checker<check::PostObjCMessage> {
733    mutable bool Initialized;
734    mutable Selector ObjectAtIndex;
735    mutable Selector ObjectAtIndexedSubscript;
736
737public:
738  ObjCNonNilReturnValueChecker() : Initialized(false) {}
739  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
740};
741}
742
743static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
744                                           ProgramStateRef State,
745                                           CheckerContext &C) {
746  SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
747  if (DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&Val))
748    return State->assume(*DV, true);
749  return State;
750}
751
752void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
753                                                        CheckerContext &C)
754                                                        const {
755  ProgramStateRef State = C.getState();
756
757  if (!Initialized) {
758    ASTContext &Ctx = C.getASTContext();
759    ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
760    ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
761  }
762
763  // Check the receiver type.
764  if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
765
766    // Assume that object returned from '[self init]' or '[super init]' is not
767    // 'nil' if we are processing an inlined function/method.
768    //
769    // A defensive callee will (and should) check if the object returned by
770    // '[super init]' is 'nil' before doing it's own initialization. However,
771    // since 'nil' is rarely returned in practice, we should not warn when the
772    // caller to the defensive constructor uses the object in contexts where
773    // 'nil' is not accepted.
774    if (!C.inTopFrame() && M.getDecl() &&
775        M.getDecl()->getMethodFamily() == OMF_init &&
776        M.isReceiverSelfOrSuper()) {
777      State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
778    }
779
780    // Objects returned from
781    // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
782    // are never 'nil'.
783    FoundationClass Cl = findKnownClass(Interface);
784    if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
785      Selector Sel = M.getSelector();
786      if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
787        // Go ahead and assume the value is non-nil.
788        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
789      }
790    }
791  }
792  C.addTransition(State);
793}
794
795//===----------------------------------------------------------------------===//
796// Check registration.
797//===----------------------------------------------------------------------===//
798
799void ento::registerNilArgChecker(CheckerManager &mgr) {
800  mgr.registerChecker<NilArgChecker>();
801}
802
803void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
804  mgr.registerChecker<CFNumberCreateChecker>();
805}
806
807void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
808  mgr.registerChecker<CFRetainReleaseChecker>();
809}
810
811void ento::registerClassReleaseChecker(CheckerManager &mgr) {
812  mgr.registerChecker<ClassReleaseChecker>();
813}
814
815void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
816  mgr.registerChecker<VariadicMethodTypeChecker>();
817}
818
819void ento::registerObjCLoopChecker(CheckerManager &mgr) {
820  mgr.registerChecker<ObjCLoopChecker>();
821}
822
823void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
824  mgr.registerChecker<ObjCNonNilReturnValueChecker>();
825}
826