BasicObjCFoundationChecks.cpp revision 243830
1218887Sdim//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim//  This file defines BasicObjCFoundationChecks, a class that encapsulates
11218887Sdim//  a set of simple checks to run on Objective-C code using Apple's Foundation
12218887Sdim//  classes.
13218887Sdim//
14218887Sdim//===----------------------------------------------------------------------===//
15218887Sdim
16218887Sdim#include "ClangSACheckers.h"
17221345Sdim#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
19218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20239462Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
21219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
23218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
24226633Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
25218887Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
27218887Sdim#include "clang/AST/DeclObjC.h"
28218887Sdim#include "clang/AST/Expr.h"
29218887Sdim#include "clang/AST/ExprObjC.h"
30239462Sdim#include "clang/AST/StmtObjC.h"
31218887Sdim#include "clang/AST/ASTContext.h"
32234353Sdim#include "llvm/ADT/SmallString.h"
33239462Sdim#include "llvm/ADT/StringMap.h"
34218887Sdim
35218887Sdimusing namespace clang;
36218887Sdimusing namespace ento;
37218887Sdim
38218887Sdimnamespace {
39218887Sdimclass APIMisuse : public BugType {
40218887Sdimpublic:
41218887Sdim  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
42218887Sdim};
43218887Sdim} // end anonymous namespace
44218887Sdim
45218887Sdim//===----------------------------------------------------------------------===//
46218887Sdim// Utility functions.
47218887Sdim//===----------------------------------------------------------------------===//
48218887Sdim
49239462Sdimstatic StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
50218887Sdim  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
51239462Sdim    return ID->getIdentifier()->getName();
52239462Sdim  return StringRef();
53218887Sdim}
54218887Sdim
55239462Sdimenum FoundationClass {
56239462Sdim  FC_None,
57239462Sdim  FC_NSArray,
58239462Sdim  FC_NSDictionary,
59239462Sdim  FC_NSEnumerator,
60239462Sdim  FC_NSOrderedSet,
61239462Sdim  FC_NSSet,
62239462Sdim  FC_NSString
63239462Sdim};
64218887Sdim
65239462Sdimstatic FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
66239462Sdim  static llvm::StringMap<FoundationClass> Classes;
67239462Sdim  if (Classes.empty()) {
68239462Sdim    Classes["NSArray"] = FC_NSArray;
69239462Sdim    Classes["NSDictionary"] = FC_NSDictionary;
70239462Sdim    Classes["NSEnumerator"] = FC_NSEnumerator;
71239462Sdim    Classes["NSOrderedSet"] = FC_NSOrderedSet;
72239462Sdim    Classes["NSSet"] = FC_NSSet;
73239462Sdim    Classes["NSString"] = FC_NSString;
74239462Sdim  }
75221345Sdim
76239462Sdim  // FIXME: Should we cache this at all?
77239462Sdim  FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
78239462Sdim  if (result == FC_None)
79239462Sdim    if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
80239462Sdim      return findKnownClass(Super);
81239462Sdim
82239462Sdim  return result;
83218887Sdim}
84218887Sdim
85218887Sdimstatic inline bool isNil(SVal X) {
86218887Sdim  return isa<loc::ConcreteInt>(X);
87218887Sdim}
88218887Sdim
89218887Sdim//===----------------------------------------------------------------------===//
90218887Sdim// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
91218887Sdim//===----------------------------------------------------------------------===//
92218887Sdim
93218887Sdimnamespace {
94221345Sdim  class NilArgChecker : public Checker<check::PreObjCMessage> {
95234353Sdim    mutable OwningPtr<APIMisuse> BT;
96219077Sdim
97219077Sdim    void WarnNilArg(CheckerContext &C,
98239462Sdim                    const ObjCMethodCall &msg, unsigned Arg) const;
99219077Sdim
100218887Sdim  public:
101239462Sdim    void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
102218887Sdim  };
103218887Sdim}
104218887Sdim
105218887Sdimvoid NilArgChecker::WarnNilArg(CheckerContext &C,
106239462Sdim                               const ObjCMethodCall &msg,
107219077Sdim                               unsigned int Arg) const
108218887Sdim{
109218887Sdim  if (!BT)
110219077Sdim    BT.reset(new APIMisuse("nil argument"));
111218887Sdim
112218887Sdim  if (ExplodedNode *N = C.generateSink()) {
113234353Sdim    SmallString<128> sbuf;
114218887Sdim    llvm::raw_svector_ostream os(sbuf);
115239462Sdim    os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
116218887Sdim       << msg.getSelector().getAsString() << "' cannot be nil";
117218887Sdim
118226633Sdim    BugReport *R = new BugReport(*BT, os.str(), N);
119218887Sdim    R->addRange(msg.getArgSourceRange(Arg));
120243830Sdim    C.emitReport(R);
121218887Sdim  }
122218887Sdim}
123218887Sdim
124239462Sdimvoid NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
125219077Sdim                                        CheckerContext &C) const {
126221345Sdim  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
127221345Sdim  if (!ID)
128218887Sdim    return;
129218887Sdim
130239462Sdim  if (findKnownClass(ID) == FC_NSString) {
131218887Sdim    Selector S = msg.getSelector();
132218887Sdim
133218887Sdim    if (S.isUnarySelector())
134218887Sdim      return;
135218887Sdim
136218887Sdim    // FIXME: This is going to be really slow doing these checks with
137218887Sdim    //  lexical comparisons.
138218887Sdim
139218887Sdim    std::string NameStr = S.getAsString();
140226633Sdim    StringRef Name(NameStr);
141218887Sdim    assert(!Name.empty());
142218887Sdim
143218887Sdim    // FIXME: Checking for initWithFormat: will not work in most cases
144218887Sdim    //  yet because [NSString alloc] returns id, not NSString*.  We will
145218887Sdim    //  need support for tracking expected-type information in the analyzer
146218887Sdim    //  to find these errors.
147218887Sdim    if (Name == "caseInsensitiveCompare:" ||
148218887Sdim        Name == "compare:" ||
149218887Sdim        Name == "compare:options:" ||
150218887Sdim        Name == "compare:options:range:" ||
151218887Sdim        Name == "compare:options:range:locale:" ||
152218887Sdim        Name == "componentsSeparatedByCharactersInSet:" ||
153218887Sdim        Name == "initWithFormat:") {
154239462Sdim      if (isNil(msg.getArgSVal(0)))
155218887Sdim        WarnNilArg(C, msg, 0);
156218887Sdim    }
157218887Sdim  }
158218887Sdim}
159218887Sdim
160218887Sdim//===----------------------------------------------------------------------===//
161218887Sdim// Error reporting.
162218887Sdim//===----------------------------------------------------------------------===//
163218887Sdim
164218887Sdimnamespace {
165221345Sdimclass CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
166234353Sdim  mutable OwningPtr<APIMisuse> BT;
167219077Sdim  mutable IdentifierInfo* II;
168218887Sdimpublic:
169219077Sdim  CFNumberCreateChecker() : II(0) {}
170219077Sdim
171219077Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
172219077Sdim
173218887Sdimprivate:
174226633Sdim  void EmitError(const TypedRegion* R, const Expr *Ex,
175218887Sdim                uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
176218887Sdim};
177218887Sdim} // end anonymous namespace
178218887Sdim
179218887Sdimenum CFNumberType {
180218887Sdim  kCFNumberSInt8Type = 1,
181218887Sdim  kCFNumberSInt16Type = 2,
182218887Sdim  kCFNumberSInt32Type = 3,
183218887Sdim  kCFNumberSInt64Type = 4,
184218887Sdim  kCFNumberFloat32Type = 5,
185218887Sdim  kCFNumberFloat64Type = 6,
186218887Sdim  kCFNumberCharType = 7,
187218887Sdim  kCFNumberShortType = 8,
188218887Sdim  kCFNumberIntType = 9,
189218887Sdim  kCFNumberLongType = 10,
190218887Sdim  kCFNumberLongLongType = 11,
191218887Sdim  kCFNumberFloatType = 12,
192218887Sdim  kCFNumberDoubleType = 13,
193218887Sdim  kCFNumberCFIndexType = 14,
194218887Sdim  kCFNumberNSIntegerType = 15,
195218887Sdim  kCFNumberCGFloatType = 16
196218887Sdim};
197218887Sdim
198218887Sdimnamespace {
199218887Sdim  template<typename T>
200218887Sdim  class Optional {
201218887Sdim    bool IsKnown;
202218887Sdim    T Val;
203218887Sdim  public:
204218887Sdim    Optional() : IsKnown(false), Val(0) {}
205218887Sdim    Optional(const T& val) : IsKnown(true), Val(val) {}
206218887Sdim
207218887Sdim    bool isKnown() const { return IsKnown; }
208218887Sdim
209218887Sdim    const T& getValue() const {
210218887Sdim      assert (isKnown());
211218887Sdim      return Val;
212218887Sdim    }
213218887Sdim
214218887Sdim    operator const T&() const {
215218887Sdim      return getValue();
216218887Sdim    }
217218887Sdim  };
218218887Sdim}
219218887Sdim
220226633Sdimstatic Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
221218887Sdim  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
222218887Sdim
223218887Sdim  if (i < kCFNumberCharType)
224218887Sdim    return FixedSize[i-1];
225218887Sdim
226218887Sdim  QualType T;
227218887Sdim
228218887Sdim  switch (i) {
229218887Sdim    case kCFNumberCharType:     T = Ctx.CharTy;     break;
230218887Sdim    case kCFNumberShortType:    T = Ctx.ShortTy;    break;
231218887Sdim    case kCFNumberIntType:      T = Ctx.IntTy;      break;
232218887Sdim    case kCFNumberLongType:     T = Ctx.LongTy;     break;
233218887Sdim    case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
234218887Sdim    case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
235218887Sdim    case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
236218887Sdim    case kCFNumberCFIndexType:
237218887Sdim    case kCFNumberNSIntegerType:
238218887Sdim    case kCFNumberCGFloatType:
239218887Sdim      // FIXME: We need a way to map from names to Type*.
240218887Sdim    default:
241218887Sdim      return Optional<uint64_t>();
242218887Sdim  }
243218887Sdim
244218887Sdim  return Ctx.getTypeSize(T);
245218887Sdim}
246218887Sdim
247218887Sdim#if 0
248218887Sdimstatic const char* GetCFNumberTypeStr(uint64_t i) {
249218887Sdim  static const char* Names[] = {
250218887Sdim    "kCFNumberSInt8Type",
251218887Sdim    "kCFNumberSInt16Type",
252218887Sdim    "kCFNumberSInt32Type",
253218887Sdim    "kCFNumberSInt64Type",
254218887Sdim    "kCFNumberFloat32Type",
255218887Sdim    "kCFNumberFloat64Type",
256218887Sdim    "kCFNumberCharType",
257218887Sdim    "kCFNumberShortType",
258218887Sdim    "kCFNumberIntType",
259218887Sdim    "kCFNumberLongType",
260218887Sdim    "kCFNumberLongLongType",
261218887Sdim    "kCFNumberFloatType",
262218887Sdim    "kCFNumberDoubleType",
263218887Sdim    "kCFNumberCFIndexType",
264218887Sdim    "kCFNumberNSIntegerType",
265218887Sdim    "kCFNumberCGFloatType"
266218887Sdim  };
267218887Sdim
268218887Sdim  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
269218887Sdim}
270218887Sdim#endif
271218887Sdim
272219077Sdimvoid CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
273219077Sdim                                         CheckerContext &C) const {
274234353Sdim  ProgramStateRef state = C.getState();
275234353Sdim  const FunctionDecl *FD = C.getCalleeDecl(CE);
276218887Sdim  if (!FD)
277218887Sdim    return;
278218887Sdim
279218887Sdim  ASTContext &Ctx = C.getASTContext();
280218887Sdim  if (!II)
281218887Sdim    II = &Ctx.Idents.get("CFNumberCreate");
282218887Sdim
283218887Sdim  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
284218887Sdim    return;
285218887Sdim
286218887Sdim  // Get the value of the "theType" argument.
287234353Sdim  const LocationContext *LCtx = C.getLocationContext();
288234353Sdim  SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
289218887Sdim
290218887Sdim  // FIXME: We really should allow ranges of valid theType values, and
291218887Sdim  //   bifurcate the state appropriately.
292218887Sdim  nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
293218887Sdim  if (!V)
294218887Sdim    return;
295218887Sdim
296218887Sdim  uint64_t NumberKind = V->getValue().getLimitedValue();
297218887Sdim  Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
298218887Sdim
299218887Sdim  // FIXME: In some cases we can emit an error.
300218887Sdim  if (!TargetSize.isKnown())
301218887Sdim    return;
302218887Sdim
303218887Sdim  // Look at the value of the integer being passed by reference.  Essentially
304218887Sdim  // we want to catch cases where the value passed in is not equal to the
305218887Sdim  // size of the type being created.
306234353Sdim  SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
307218887Sdim
308218887Sdim  // FIXME: Eventually we should handle arbitrary locations.  We can do this
309218887Sdim  //  by having an enhanced memory model that does low-level typing.
310218887Sdim  loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
311218887Sdim  if (!LV)
312218887Sdim    return;
313218887Sdim
314226633Sdim  const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
315218887Sdim  if (!R)
316218887Sdim    return;
317218887Sdim
318218887Sdim  QualType T = Ctx.getCanonicalType(R->getValueType());
319218887Sdim
320218887Sdim  // FIXME: If the pointee isn't an integer type, should we flag a warning?
321218887Sdim  //  People can do weird stuff with pointers.
322218887Sdim
323218887Sdim  if (!T->isIntegerType())
324218887Sdim    return;
325218887Sdim
326218887Sdim  uint64_t SourceSize = Ctx.getTypeSize(T);
327218887Sdim
328218887Sdim  // CHECK: is SourceSize == TargetSize
329218887Sdim  if (SourceSize == TargetSize)
330218887Sdim    return;
331218887Sdim
332218887Sdim  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
333218887Sdim  // otherwise generate a regular node.
334218887Sdim  //
335218887Sdim  // FIXME: We can actually create an abstract "CFNumber" object that has
336218887Sdim  //  the bits initialized to the provided values.
337218887Sdim  //
338218887Sdim  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
339234353Sdim                                                : C.addTransition()) {
340234353Sdim    SmallString<128> sbuf;
341218887Sdim    llvm::raw_svector_ostream os(sbuf);
342218887Sdim
343218887Sdim    os << (SourceSize == 8 ? "An " : "A ")
344218887Sdim       << SourceSize << " bit integer is used to initialize a CFNumber "
345218887Sdim                        "object that represents "
346218887Sdim       << (TargetSize == 8 ? "an " : "a ")
347218887Sdim       << TargetSize << " bit integer. ";
348218887Sdim
349218887Sdim    if (SourceSize < TargetSize)
350218887Sdim      os << (TargetSize - SourceSize)
351218887Sdim      << " bits of the CFNumber value will be garbage." ;
352218887Sdim    else
353218887Sdim      os << (SourceSize - TargetSize)
354218887Sdim      << " bits of the input integer will be lost.";
355218887Sdim
356218887Sdim    if (!BT)
357219077Sdim      BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
358218887Sdim
359226633Sdim    BugReport *report = new BugReport(*BT, os.str(), N);
360218887Sdim    report->addRange(CE->getArg(2)->getSourceRange());
361243830Sdim    C.emitReport(report);
362218887Sdim  }
363218887Sdim}
364218887Sdim
365218887Sdim//===----------------------------------------------------------------------===//
366243830Sdim// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
367218887Sdim//===----------------------------------------------------------------------===//
368218887Sdim
369218887Sdimnamespace {
370221345Sdimclass CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
371234353Sdim  mutable OwningPtr<APIMisuse> BT;
372243830Sdim  mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
373218887Sdimpublic:
374243830Sdim  CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
375226633Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
376218887Sdim};
377218887Sdim} // end anonymous namespace
378218887Sdim
379218887Sdim
380226633Sdimvoid CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
381226633Sdim                                          CheckerContext &C) const {
382218887Sdim  // If the CallExpr doesn't have exactly 1 argument just give up checking.
383218887Sdim  if (CE->getNumArgs() != 1)
384218887Sdim    return;
385218887Sdim
386234353Sdim  ProgramStateRef state = C.getState();
387234353Sdim  const FunctionDecl *FD = C.getCalleeDecl(CE);
388218887Sdim  if (!FD)
389218887Sdim    return;
390218887Sdim
391218887Sdim  if (!BT) {
392218887Sdim    ASTContext &Ctx = C.getASTContext();
393218887Sdim    Retain = &Ctx.Idents.get("CFRetain");
394218887Sdim    Release = &Ctx.Idents.get("CFRelease");
395243830Sdim    MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
396243830Sdim    BT.reset(
397243830Sdim      new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
398218887Sdim  }
399218887Sdim
400243830Sdim  // Check if we called CFRetain/CFRelease/CFMakeCollectable.
401218887Sdim  const IdentifierInfo *FuncII = FD->getIdentifier();
402243830Sdim  if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
403218887Sdim    return;
404218887Sdim
405218887Sdim  // FIXME: The rest of this just checks that the argument is non-null.
406218887Sdim  // It should probably be refactored and combined with AttrNonNullChecker.
407218887Sdim
408218887Sdim  // Get the argument's value.
409218887Sdim  const Expr *Arg = CE->getArg(0);
410234353Sdim  SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
411218887Sdim  DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
412218887Sdim  if (!DefArgVal)
413218887Sdim    return;
414218887Sdim
415218887Sdim  // Get a NULL value.
416218887Sdim  SValBuilder &svalBuilder = C.getSValBuilder();
417218887Sdim  DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
418218887Sdim
419218887Sdim  // Make an expression asserting that they're equal.
420218887Sdim  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
421218887Sdim
422218887Sdim  // Are they equal?
423234353Sdim  ProgramStateRef stateTrue, stateFalse;
424218887Sdim  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
425218887Sdim
426218887Sdim  if (stateTrue && !stateFalse) {
427218887Sdim    ExplodedNode *N = C.generateSink(stateTrue);
428218887Sdim    if (!N)
429218887Sdim      return;
430218887Sdim
431243830Sdim    const char *description;
432243830Sdim    if (FuncII == Retain)
433243830Sdim      description = "Null pointer argument in call to CFRetain";
434243830Sdim    else if (FuncII == Release)
435243830Sdim      description = "Null pointer argument in call to CFRelease";
436243830Sdim    else if (FuncII == MakeCollectable)
437243830Sdim      description = "Null pointer argument in call to CFMakeCollectable";
438243830Sdim    else
439243830Sdim      llvm_unreachable("impossible case");
440218887Sdim
441226633Sdim    BugReport *report = new BugReport(*BT, description, N);
442218887Sdim    report->addRange(Arg->getSourceRange());
443243830Sdim    bugreporter::trackNullOrUndefValue(N, Arg, *report);
444243830Sdim    C.emitReport(report);
445218887Sdim    return;
446218887Sdim  }
447218887Sdim
448218887Sdim  // From here on, we know the argument is non-null.
449218887Sdim  C.addTransition(stateFalse);
450218887Sdim}
451218887Sdim
452218887Sdim//===----------------------------------------------------------------------===//
453218887Sdim// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
454218887Sdim//===----------------------------------------------------------------------===//
455218887Sdim
456218887Sdimnamespace {
457221345Sdimclass ClassReleaseChecker : public Checker<check::PreObjCMessage> {
458219077Sdim  mutable Selector releaseS;
459219077Sdim  mutable Selector retainS;
460219077Sdim  mutable Selector autoreleaseS;
461219077Sdim  mutable Selector drainS;
462234353Sdim  mutable OwningPtr<BugType> BT;
463219077Sdim
464218887Sdimpublic:
465239462Sdim  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
466218887Sdim};
467218887Sdim}
468218887Sdim
469239462Sdimvoid ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
470219077Sdim                                              CheckerContext &C) const {
471218887Sdim
472218887Sdim  if (!BT) {
473219077Sdim    BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
474219077Sdim                           "instance"));
475218887Sdim
476218887Sdim    ASTContext &Ctx = C.getASTContext();
477218887Sdim    releaseS = GetNullarySelector("release", Ctx);
478218887Sdim    retainS = GetNullarySelector("retain", Ctx);
479218887Sdim    autoreleaseS = GetNullarySelector("autorelease", Ctx);
480218887Sdim    drainS = GetNullarySelector("drain", Ctx);
481218887Sdim  }
482218887Sdim
483218887Sdim  if (msg.isInstanceMessage())
484218887Sdim    return;
485218887Sdim  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
486218887Sdim  assert(Class);
487218887Sdim
488218887Sdim  Selector S = msg.getSelector();
489218887Sdim  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
490218887Sdim    return;
491218887Sdim
492234353Sdim  if (ExplodedNode *N = C.addTransition()) {
493234353Sdim    SmallString<200> buf;
494218887Sdim    llvm::raw_svector_ostream os(buf);
495218887Sdim
496218887Sdim    os << "The '" << S.getAsString() << "' message should be sent to instances "
497218887Sdim          "of class '" << Class->getName()
498218887Sdim       << "' and not the class directly";
499218887Sdim
500226633Sdim    BugReport *report = new BugReport(*BT, os.str(), N);
501218887Sdim    report->addRange(msg.getSourceRange());
502243830Sdim    C.emitReport(report);
503218887Sdim  }
504218887Sdim}
505218887Sdim
506218887Sdim//===----------------------------------------------------------------------===//
507221345Sdim// Check for passing non-Objective-C types to variadic methods that expect
508221345Sdim// only Objective-C types.
509221345Sdim//===----------------------------------------------------------------------===//
510221345Sdim
511221345Sdimnamespace {
512221345Sdimclass VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
513221345Sdim  mutable Selector arrayWithObjectsS;
514221345Sdim  mutable Selector dictionaryWithObjectsAndKeysS;
515221345Sdim  mutable Selector setWithObjectsS;
516234353Sdim  mutable Selector orderedSetWithObjectsS;
517221345Sdim  mutable Selector initWithObjectsS;
518221345Sdim  mutable Selector initWithObjectsAndKeysS;
519234353Sdim  mutable OwningPtr<BugType> BT;
520221345Sdim
521239462Sdim  bool isVariadicMessage(const ObjCMethodCall &msg) const;
522221345Sdim
523221345Sdimpublic:
524239462Sdim  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
525221345Sdim};
526221345Sdim}
527221345Sdim
528221345Sdim/// isVariadicMessage - Returns whether the given message is a variadic message,
529221345Sdim/// where all arguments must be Objective-C types.
530221345Sdimbool
531239462SdimVariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
532239462Sdim  const ObjCMethodDecl *MD = msg.getDecl();
533221345Sdim
534221345Sdim  if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
535221345Sdim    return false;
536221345Sdim
537221345Sdim  Selector S = msg.getSelector();
538221345Sdim
539221345Sdim  if (msg.isInstanceMessage()) {
540221345Sdim    // FIXME: Ideally we'd look at the receiver interface here, but that's not
541221345Sdim    // useful for init, because alloc returns 'id'. In theory, this could lead
542221345Sdim    // to false positives, for example if there existed a class that had an
543221345Sdim    // initWithObjects: implementation that does accept non-Objective-C pointer
544221345Sdim    // types, but the chance of that happening is pretty small compared to the
545221345Sdim    // gains that this analysis gives.
546221345Sdim    const ObjCInterfaceDecl *Class = MD->getClassInterface();
547221345Sdim
548239462Sdim    switch (findKnownClass(Class)) {
549239462Sdim    case FC_NSArray:
550239462Sdim    case FC_NSOrderedSet:
551239462Sdim    case FC_NSSet:
552239462Sdim      return S == initWithObjectsS;
553239462Sdim    case FC_NSDictionary:
554239462Sdim      return S == initWithObjectsAndKeysS;
555239462Sdim    default:
556239462Sdim      return false;
557239462Sdim    }
558221345Sdim  } else {
559221345Sdim    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
560221345Sdim
561239462Sdim    switch (findKnownClass(Class)) {
562239462Sdim      case FC_NSArray:
563239462Sdim        return S == arrayWithObjectsS;
564239462Sdim      case FC_NSOrderedSet:
565239462Sdim        return S == orderedSetWithObjectsS;
566239462Sdim      case FC_NSSet:
567239462Sdim        return S == setWithObjectsS;
568239462Sdim      case FC_NSDictionary:
569239462Sdim        return S == dictionaryWithObjectsAndKeysS;
570239462Sdim      default:
571239462Sdim        return false;
572239462Sdim    }
573221345Sdim  }
574221345Sdim}
575221345Sdim
576239462Sdimvoid VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
577221345Sdim                                                    CheckerContext &C) const {
578221345Sdim  if (!BT) {
579221345Sdim    BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
580221345Sdim                           "Objective-C pointer types"));
581221345Sdim
582221345Sdim    ASTContext &Ctx = C.getASTContext();
583221345Sdim    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
584221345Sdim    dictionaryWithObjectsAndKeysS =
585221345Sdim      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
586221345Sdim    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
587234353Sdim    orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
588221345Sdim
589221345Sdim    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
590221345Sdim    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
591221345Sdim  }
592221345Sdim
593221345Sdim  if (!isVariadicMessage(msg))
594221345Sdim      return;
595221345Sdim
596221345Sdim  // We are not interested in the selector arguments since they have
597221345Sdim  // well-defined types, so the compiler will issue a warning for them.
598221345Sdim  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
599221345Sdim
600221345Sdim  // We're not interested in the last argument since it has to be nil or the
601221345Sdim  // compiler would have issued a warning for it elsewhere.
602221345Sdim  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
603221345Sdim
604221345Sdim  if (variadicArgsEnd <= variadicArgsBegin)
605221345Sdim    return;
606221345Sdim
607221345Sdim  // Verify that all arguments have Objective-C types.
608221345Sdim  llvm::Optional<ExplodedNode*> errorNode;
609234353Sdim  ProgramStateRef state = C.getState();
610221345Sdim
611221345Sdim  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
612239462Sdim    QualType ArgTy = msg.getArgExpr(I)->getType();
613221345Sdim    if (ArgTy->isObjCObjectPointerType())
614221345Sdim      continue;
615221345Sdim
616221345Sdim    // Block pointers are treaded as Objective-C pointers.
617221345Sdim    if (ArgTy->isBlockPointerType())
618221345Sdim      continue;
619221345Sdim
620221345Sdim    // Ignore pointer constants.
621239462Sdim    if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
622221345Sdim      continue;
623221345Sdim
624221345Sdim    // Ignore pointer types annotated with 'NSObject' attribute.
625221345Sdim    if (C.getASTContext().isObjCNSObjectType(ArgTy))
626221345Sdim      continue;
627221345Sdim
628221345Sdim    // Ignore CF references, which can be toll-free bridged.
629224145Sdim    if (coreFoundation::isCFObjectRef(ArgTy))
630221345Sdim      continue;
631221345Sdim
632221345Sdim    // Generate only one error node to use for all bug reports.
633239462Sdim    if (!errorNode.hasValue())
634234353Sdim      errorNode = C.addTransition();
635221345Sdim
636221345Sdim    if (!errorNode.getValue())
637221345Sdim      continue;
638221345Sdim
639234353Sdim    SmallString<128> sbuf;
640221345Sdim    llvm::raw_svector_ostream os(sbuf);
641221345Sdim
642239462Sdim    StringRef TypeName = GetReceiverInterfaceName(msg);
643239462Sdim    if (!TypeName.empty())
644221345Sdim      os << "Argument to '" << TypeName << "' method '";
645221345Sdim    else
646221345Sdim      os << "Argument to method '";
647221345Sdim
648221345Sdim    os << msg.getSelector().getAsString()
649239462Sdim       << "' should be an Objective-C pointer type, not '";
650239462Sdim    ArgTy.print(os, C.getLangOpts());
651239462Sdim    os << "'";
652221345Sdim
653239462Sdim    BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
654221345Sdim    R->addRange(msg.getArgSourceRange(I));
655243830Sdim    C.emitReport(R);
656221345Sdim  }
657221345Sdim}
658221345Sdim
659221345Sdim//===----------------------------------------------------------------------===//
660239462Sdim// Improves the modeling of loops over Cocoa collections.
661239462Sdim//===----------------------------------------------------------------------===//
662239462Sdim
663239462Sdimnamespace {
664239462Sdimclass ObjCLoopChecker
665239462Sdim  : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
666239462Sdim
667239462Sdimpublic:
668239462Sdim  void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
669239462Sdim};
670239462Sdim}
671239462Sdim
672239462Sdimstatic bool isKnownNonNilCollectionType(QualType T) {
673239462Sdim  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
674239462Sdim  if (!PT)
675239462Sdim    return false;
676239462Sdim
677239462Sdim  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
678239462Sdim  if (!ID)
679239462Sdim    return false;
680239462Sdim
681239462Sdim  switch (findKnownClass(ID)) {
682239462Sdim  case FC_NSArray:
683239462Sdim  case FC_NSDictionary:
684239462Sdim  case FC_NSEnumerator:
685239462Sdim  case FC_NSOrderedSet:
686239462Sdim  case FC_NSSet:
687239462Sdim    return true;
688239462Sdim  default:
689239462Sdim    return false;
690239462Sdim  }
691239462Sdim}
692239462Sdim
693239462Sdimvoid ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
694239462Sdim                                    CheckerContext &C) const {
695239462Sdim  ProgramStateRef State = C.getState();
696239462Sdim
697239462Sdim  // Check if this is the branch for the end of the loop.
698239462Sdim  SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
699239462Sdim  if (CollectionSentinel.isZeroConstant())
700239462Sdim    return;
701239462Sdim
702239462Sdim  // See if the collection is one where we /know/ the elements are non-nil.
703239462Sdim  const Expr *Collection = FCS->getCollection();
704239462Sdim  if (!isKnownNonNilCollectionType(Collection->getType()))
705239462Sdim    return;
706239462Sdim
707239462Sdim  // FIXME: Copied from ExprEngineObjC.
708239462Sdim  const Stmt *Element = FCS->getElement();
709239462Sdim  SVal ElementVar;
710239462Sdim  if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
711239462Sdim    const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
712239462Sdim    assert(ElemDecl->getInit() == 0);
713239462Sdim    ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
714239462Sdim  } else {
715239462Sdim    ElementVar = State->getSVal(Element, C.getLocationContext());
716239462Sdim  }
717239462Sdim
718239462Sdim  if (!isa<Loc>(ElementVar))
719239462Sdim    return;
720239462Sdim
721239462Sdim  // Go ahead and assume the value is non-nil.
722239462Sdim  SVal Val = State->getSVal(cast<Loc>(ElementVar));
723239462Sdim  State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
724239462Sdim  C.addTransition(State);
725239462Sdim}
726239462Sdim
727243830Sdimnamespace {
728243830Sdim/// \class ObjCNonNilReturnValueChecker
729243830Sdim/// \brief The checker restricts the return values of APIs known to
730243830Sdim/// never (or almost never) return 'nil'.
731243830Sdimclass ObjCNonNilReturnValueChecker
732243830Sdim  : public Checker<check::PostObjCMessage> {
733243830Sdim    mutable bool Initialized;
734243830Sdim    mutable Selector ObjectAtIndex;
735243830Sdim    mutable Selector ObjectAtIndexedSubscript;
736239462Sdim
737243830Sdimpublic:
738243830Sdim  ObjCNonNilReturnValueChecker() : Initialized(false) {}
739243830Sdim  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
740243830Sdim};
741243830Sdim}
742243830Sdim
743243830Sdimstatic ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
744243830Sdim                                           ProgramStateRef State,
745243830Sdim                                           CheckerContext &C) {
746243830Sdim  SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
747243830Sdim  if (DefinedOrUnknownSVal *DV = dyn_cast<DefinedOrUnknownSVal>(&Val))
748243830Sdim    return State->assume(*DV, true);
749243830Sdim  return State;
750243830Sdim}
751243830Sdim
752243830Sdimvoid ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
753243830Sdim                                                        CheckerContext &C)
754243830Sdim                                                        const {
755243830Sdim  ProgramStateRef State = C.getState();
756243830Sdim
757243830Sdim  if (!Initialized) {
758243830Sdim    ASTContext &Ctx = C.getASTContext();
759243830Sdim    ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
760243830Sdim    ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
761243830Sdim  }
762243830Sdim
763243830Sdim  // Check the receiver type.
764243830Sdim  if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
765243830Sdim
766243830Sdim    // Assume that object returned from '[self init]' or '[super init]' is not
767243830Sdim    // 'nil' if we are processing an inlined function/method.
768243830Sdim    //
769243830Sdim    // A defensive callee will (and should) check if the object returned by
770243830Sdim    // '[super init]' is 'nil' before doing it's own initialization. However,
771243830Sdim    // since 'nil' is rarely returned in practice, we should not warn when the
772243830Sdim    // caller to the defensive constructor uses the object in contexts where
773243830Sdim    // 'nil' is not accepted.
774243830Sdim    if (!C.inTopFrame() && M.getDecl() &&
775243830Sdim        M.getDecl()->getMethodFamily() == OMF_init &&
776243830Sdim        M.isReceiverSelfOrSuper()) {
777243830Sdim      State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
778243830Sdim    }
779243830Sdim
780243830Sdim    // Objects returned from
781243830Sdim    // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
782243830Sdim    // are never 'nil'.
783243830Sdim    FoundationClass Cl = findKnownClass(Interface);
784243830Sdim    if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
785243830Sdim      Selector Sel = M.getSelector();
786243830Sdim      if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
787243830Sdim        // Go ahead and assume the value is non-nil.
788243830Sdim        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
789243830Sdim      }
790243830Sdim    }
791243830Sdim  }
792243830Sdim  C.addTransition(State);
793243830Sdim}
794243830Sdim
795239462Sdim//===----------------------------------------------------------------------===//
796218887Sdim// Check registration.
797218887Sdim//===----------------------------------------------------------------------===//
798218887Sdim
799218887Sdimvoid ento::registerNilArgChecker(CheckerManager &mgr) {
800219077Sdim  mgr.registerChecker<NilArgChecker>();
801218887Sdim}
802218887Sdim
803218887Sdimvoid ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
804219077Sdim  mgr.registerChecker<CFNumberCreateChecker>();
805218887Sdim}
806218887Sdim
807218887Sdimvoid ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
808219077Sdim  mgr.registerChecker<CFRetainReleaseChecker>();
809218887Sdim}
810218887Sdim
811218887Sdimvoid ento::registerClassReleaseChecker(CheckerManager &mgr) {
812219077Sdim  mgr.registerChecker<ClassReleaseChecker>();
813218887Sdim}
814221345Sdim
815221345Sdimvoid ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
816221345Sdim  mgr.registerChecker<VariadicMethodTypeChecker>();
817221345Sdim}
818239462Sdim
819239462Sdimvoid ento::registerObjCLoopChecker(CheckerManager &mgr) {
820239462Sdim  mgr.registerChecker<ObjCLoopChecker>();
821239462Sdim}
822243830Sdim
823243830Sdimvoid ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
824243830Sdim  mgr.registerChecker<ObjCNonNilReturnValueChecker>();
825243830Sdim}
826