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"
17276479Sdim#include "SelectorExtras.h"
18249423Sdim#include "clang/AST/ASTContext.h"
19249423Sdim#include "clang/AST/DeclObjC.h"
20249423Sdim#include "clang/AST/Expr.h"
21249423Sdim#include "clang/AST/ExprObjC.h"
22249423Sdim#include "clang/AST/StmtObjC.h"
23221345Sdim#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
24249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
25221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
26218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
27239462Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30218887Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31249423Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32226633Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33234353Sdim#include "llvm/ADT/SmallString.h"
34239462Sdim#include "llvm/ADT/StringMap.h"
35249423Sdim#include "llvm/Support/raw_ostream.h"
36218887Sdim
37218887Sdimusing namespace clang;
38218887Sdimusing namespace ento;
39218887Sdim
40218887Sdimnamespace {
41218887Sdimclass APIMisuse : public BugType {
42218887Sdimpublic:
43276479Sdim  APIMisuse(const CheckerBase *checker, const char *name)
44276479Sdim      : BugType(checker, name, "API Misuse (Apple)") {}
45218887Sdim};
46218887Sdim} // end anonymous namespace
47218887Sdim
48218887Sdim//===----------------------------------------------------------------------===//
49218887Sdim// Utility functions.
50218887Sdim//===----------------------------------------------------------------------===//
51218887Sdim
52239462Sdimstatic StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
53218887Sdim  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
54239462Sdim    return ID->getIdentifier()->getName();
55239462Sdim  return StringRef();
56218887Sdim}
57218887Sdim
58239462Sdimenum FoundationClass {
59239462Sdim  FC_None,
60239462Sdim  FC_NSArray,
61239462Sdim  FC_NSDictionary,
62239462Sdim  FC_NSEnumerator,
63261991Sdim  FC_NSNull,
64239462Sdim  FC_NSOrderedSet,
65239462Sdim  FC_NSSet,
66239462Sdim  FC_NSString
67239462Sdim};
68218887Sdim
69261991Sdimstatic FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
70261991Sdim                                      bool IncludeSuperclasses = true) {
71239462Sdim  static llvm::StringMap<FoundationClass> Classes;
72239462Sdim  if (Classes.empty()) {
73239462Sdim    Classes["NSArray"] = FC_NSArray;
74239462Sdim    Classes["NSDictionary"] = FC_NSDictionary;
75239462Sdim    Classes["NSEnumerator"] = FC_NSEnumerator;
76261991Sdim    Classes["NSNull"] = FC_NSNull;
77239462Sdim    Classes["NSOrderedSet"] = FC_NSOrderedSet;
78239462Sdim    Classes["NSSet"] = FC_NSSet;
79239462Sdim    Classes["NSString"] = FC_NSString;
80239462Sdim  }
81221345Sdim
82239462Sdim  // FIXME: Should we cache this at all?
83239462Sdim  FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
84261991Sdim  if (result == FC_None && IncludeSuperclasses)
85239462Sdim    if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
86239462Sdim      return findKnownClass(Super);
87239462Sdim
88239462Sdim  return result;
89218887Sdim}
90218887Sdim
91218887Sdim//===----------------------------------------------------------------------===//
92218887Sdim// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
93218887Sdim//===----------------------------------------------------------------------===//
94218887Sdim
95218887Sdimnamespace {
96261991Sdim  class NilArgChecker : public Checker<check::PreObjCMessage,
97261991Sdim                                       check::PostStmt<ObjCDictionaryLiteral>,
98261991Sdim                                       check::PostStmt<ObjCArrayLiteral> > {
99276479Sdim    mutable std::unique_ptr<APIMisuse> BT;
100219077Sdim
101276479Sdim    mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
102276479Sdim    mutable Selector ArrayWithObjectSel;
103276479Sdim    mutable Selector AddObjectSel;
104276479Sdim    mutable Selector InsertObjectAtIndexSel;
105276479Sdim    mutable Selector ReplaceObjectAtIndexWithObjectSel;
106276479Sdim    mutable Selector SetObjectAtIndexedSubscriptSel;
107276479Sdim    mutable Selector ArrayByAddingObjectSel;
108276479Sdim    mutable Selector DictionaryWithObjectForKeySel;
109276479Sdim    mutable Selector SetObjectForKeySel;
110276479Sdim    mutable Selector SetObjectForKeyedSubscriptSel;
111276479Sdim    mutable Selector RemoveObjectForKeySel;
112276479Sdim
113261991Sdim    void warnIfNilExpr(const Expr *E,
114261991Sdim                       const char *Msg,
115261991Sdim                       CheckerContext &C) const;
116219077Sdim
117261991Sdim    void warnIfNilArg(CheckerContext &C,
118261991Sdim                      const ObjCMethodCall &msg, unsigned Arg,
119261991Sdim                      FoundationClass Class,
120261991Sdim                      bool CanBeSubscript = false) const;
121261991Sdim
122261991Sdim    void generateBugReport(ExplodedNode *N,
123261991Sdim                           StringRef Msg,
124261991Sdim                           SourceRange Range,
125261991Sdim                           const Expr *Expr,
126261991Sdim                           CheckerContext &C) const;
127261991Sdim
128218887Sdim  public:
129239462Sdim    void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
130261991Sdim    void checkPostStmt(const ObjCDictionaryLiteral *DL,
131261991Sdim                       CheckerContext &C) const;
132261991Sdim    void checkPostStmt(const ObjCArrayLiteral *AL,
133261991Sdim                       CheckerContext &C) const;
134218887Sdim  };
135218887Sdim}
136218887Sdim
137261991Sdimvoid NilArgChecker::warnIfNilExpr(const Expr *E,
138261991Sdim                                  const char *Msg,
139261991Sdim                                  CheckerContext &C) const {
140261991Sdim  ProgramStateRef State = C.getState();
141261991Sdim  if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
142261991Sdim
143296417Sdim    if (ExplodedNode *N = C.generateErrorNode()) {
144261991Sdim      generateBugReport(N, Msg, E->getSourceRange(), E, C);
145261991Sdim    }
146296417Sdim
147261991Sdim  }
148261991Sdim}
149261991Sdim
150261991Sdimvoid NilArgChecker::warnIfNilArg(CheckerContext &C,
151249423Sdim                                 const ObjCMethodCall &msg,
152249423Sdim                                 unsigned int Arg,
153249423Sdim                                 FoundationClass Class,
154249423Sdim                                 bool CanBeSubscript) const {
155249423Sdim  // Check if the argument is nil.
156249423Sdim  ProgramStateRef State = C.getState();
157249423Sdim  if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
158249423Sdim      return;
159296417Sdim
160296417Sdim  if (ExplodedNode *N = C.generateErrorNode()) {
161234353Sdim    SmallString<128> sbuf;
162218887Sdim    llvm::raw_svector_ostream os(sbuf);
163218887Sdim
164249423Sdim    if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
165249423Sdim
166249423Sdim      if (Class == FC_NSArray) {
167249423Sdim        os << "Array element cannot be nil";
168249423Sdim      } else if (Class == FC_NSDictionary) {
169251662Sdim        if (Arg == 0) {
170251662Sdim          os << "Value stored into '";
171251662Sdim          os << GetReceiverInterfaceName(msg) << "' cannot be nil";
172251662Sdim        } else {
173249423Sdim          assert(Arg == 1);
174251662Sdim          os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
175249423Sdim        }
176249423Sdim      } else
177249423Sdim        llvm_unreachable("Missing foundation class for the subscript expr");
178249423Sdim
179249423Sdim    } else {
180251662Sdim      if (Class == FC_NSDictionary) {
181251662Sdim        if (Arg == 0)
182251662Sdim          os << "Value argument ";
183251662Sdim        else {
184251662Sdim          assert(Arg == 1);
185251662Sdim          os << "Key argument ";
186251662Sdim        }
187276479Sdim        os << "to '";
188276479Sdim        msg.getSelector().print(os);
189276479Sdim        os << "' cannot be nil";
190251662Sdim      } else {
191276479Sdim        os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
192276479Sdim        msg.getSelector().print(os);
193276479Sdim        os << "' cannot be nil";
194251662Sdim      }
195249423Sdim    }
196296417Sdim
197261991Sdim    generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
198261991Sdim                      msg.getArgExpr(Arg), C);
199218887Sdim  }
200218887Sdim}
201218887Sdim
202261991Sdimvoid NilArgChecker::generateBugReport(ExplodedNode *N,
203261991Sdim                                      StringRef Msg,
204261991Sdim                                      SourceRange Range,
205261991Sdim                                      const Expr *E,
206261991Sdim                                      CheckerContext &C) const {
207261991Sdim  if (!BT)
208276479Sdim    BT.reset(new APIMisuse(this, "nil argument"));
209261991Sdim
210288943Sdim  auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
211261991Sdim  R->addRange(Range);
212261991Sdim  bugreporter::trackNullOrUndefValue(N, E, *R);
213288943Sdim  C.emitReport(std::move(R));
214261991Sdim}
215261991Sdim
216239462Sdimvoid NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
217219077Sdim                                        CheckerContext &C) const {
218221345Sdim  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
219221345Sdim  if (!ID)
220218887Sdim    return;
221249423Sdim
222249423Sdim  FoundationClass Class = findKnownClass(ID);
223249423Sdim
224249423Sdim  static const unsigned InvalidArgIndex = UINT_MAX;
225249423Sdim  unsigned Arg = InvalidArgIndex;
226249423Sdim  bool CanBeSubscript = false;
227296417Sdim
228249423Sdim  if (Class == FC_NSString) {
229218887Sdim    Selector S = msg.getSelector();
230276479Sdim
231218887Sdim    if (S.isUnarySelector())
232218887Sdim      return;
233276479Sdim
234276479Sdim    if (StringSelectors.empty()) {
235276479Sdim      ASTContext &Ctx = C.getASTContext();
236276479Sdim      Selector Sels[] = {
237276479Sdim        getKeywordSelector(Ctx, "caseInsensitiveCompare", nullptr),
238276479Sdim        getKeywordSelector(Ctx, "compare", nullptr),
239276479Sdim        getKeywordSelector(Ctx, "compare", "options", nullptr),
240276479Sdim        getKeywordSelector(Ctx, "compare", "options", "range", nullptr),
241276479Sdim        getKeywordSelector(Ctx, "compare", "options", "range", "locale",
242276479Sdim                           nullptr),
243276479Sdim        getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet",
244276479Sdim                           nullptr),
245276479Sdim        getKeywordSelector(Ctx, "initWithFormat",
246276479Sdim                           nullptr),
247276479Sdim        getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare", nullptr),
248276479Sdim        getKeywordSelector(Ctx, "localizedCompare", nullptr),
249276479Sdim        getKeywordSelector(Ctx, "localizedStandardCompare", nullptr),
250276479Sdim      };
251276479Sdim      for (Selector KnownSel : Sels)
252276479Sdim        StringSelectors[KnownSel] = 0;
253218887Sdim    }
254276479Sdim    auto I = StringSelectors.find(S);
255276479Sdim    if (I == StringSelectors.end())
256276479Sdim      return;
257276479Sdim    Arg = I->second;
258249423Sdim  } else if (Class == FC_NSArray) {
259249423Sdim    Selector S = msg.getSelector();
260249423Sdim
261249423Sdim    if (S.isUnarySelector())
262249423Sdim      return;
263249423Sdim
264276479Sdim    if (ArrayWithObjectSel.isNull()) {
265276479Sdim      ASTContext &Ctx = C.getASTContext();
266276479Sdim      ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject", nullptr);
267276479Sdim      AddObjectSel = getKeywordSelector(Ctx, "addObject", nullptr);
268276479Sdim      InsertObjectAtIndexSel =
269276479Sdim        getKeywordSelector(Ctx, "insertObject", "atIndex", nullptr);
270276479Sdim      ReplaceObjectAtIndexWithObjectSel =
271276479Sdim        getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject", nullptr);
272276479Sdim      SetObjectAtIndexedSubscriptSel =
273276479Sdim        getKeywordSelector(Ctx, "setObject", "atIndexedSubscript", nullptr);
274276479Sdim      ArrayByAddingObjectSel =
275276479Sdim        getKeywordSelector(Ctx, "arrayByAddingObject", nullptr);
276276479Sdim    }
277276479Sdim
278276479Sdim    if (S == ArrayWithObjectSel || S == AddObjectSel ||
279276479Sdim        S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
280249423Sdim      Arg = 0;
281276479Sdim    } else if (S == SetObjectAtIndexedSubscriptSel) {
282249423Sdim      Arg = 0;
283276479Sdim      CanBeSubscript = true;
284276479Sdim    } else if (S == ReplaceObjectAtIndexWithObjectSel) {
285249423Sdim      Arg = 1;
286249423Sdim    }
287249423Sdim  } else if (Class == FC_NSDictionary) {
288249423Sdim    Selector S = msg.getSelector();
289249423Sdim
290249423Sdim    if (S.isUnarySelector())
291249423Sdim      return;
292249423Sdim
293276479Sdim    if (DictionaryWithObjectForKeySel.isNull()) {
294276479Sdim      ASTContext &Ctx = C.getASTContext();
295276479Sdim      DictionaryWithObjectForKeySel =
296276479Sdim        getKeywordSelector(Ctx, "dictionaryWithObject", "forKey", nullptr);
297276479Sdim      SetObjectForKeySel =
298276479Sdim        getKeywordSelector(Ctx, "setObject", "forKey", nullptr);
299276479Sdim      SetObjectForKeyedSubscriptSel =
300276479Sdim        getKeywordSelector(Ctx, "setObject", "forKeyedSubscript", nullptr);
301276479Sdim      RemoveObjectForKeySel =
302276479Sdim        getKeywordSelector(Ctx, "removeObjectForKey", nullptr);
303276479Sdim    }
304276479Sdim
305276479Sdim    if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
306249423Sdim      Arg = 0;
307261991Sdim      warnIfNilArg(C, msg, /* Arg */1, Class);
308276479Sdim    } else if (S == SetObjectForKeyedSubscriptSel) {
309249423Sdim      CanBeSubscript = true;
310296417Sdim      Arg = 1;
311276479Sdim    } else if (S == RemoveObjectForKeySel) {
312249423Sdim      Arg = 0;
313249423Sdim    }
314218887Sdim  }
315249423Sdim
316249423Sdim  // If argument is '0', report a warning.
317249423Sdim  if ((Arg != InvalidArgIndex))
318261991Sdim    warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
319218887Sdim}
320218887Sdim
321261991Sdimvoid NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
322261991Sdim                                  CheckerContext &C) const {
323261991Sdim  unsigned NumOfElements = AL->getNumElements();
324261991Sdim  for (unsigned i = 0; i < NumOfElements; ++i) {
325261991Sdim    warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
326261991Sdim  }
327261991Sdim}
328261991Sdim
329261991Sdimvoid NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
330261991Sdim                                  CheckerContext &C) const {
331261991Sdim  unsigned NumOfElements = DL->getNumElements();
332261991Sdim  for (unsigned i = 0; i < NumOfElements; ++i) {
333261991Sdim    ObjCDictionaryElement Element = DL->getKeyValueElement(i);
334261991Sdim    warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
335261991Sdim    warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
336261991Sdim  }
337261991Sdim}
338261991Sdim
339218887Sdim//===----------------------------------------------------------------------===//
340218887Sdim// Error reporting.
341218887Sdim//===----------------------------------------------------------------------===//
342218887Sdim
343218887Sdimnamespace {
344221345Sdimclass CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
345276479Sdim  mutable std::unique_ptr<APIMisuse> BT;
346219077Sdim  mutable IdentifierInfo* II;
347218887Sdimpublic:
348276479Sdim  CFNumberCreateChecker() : II(nullptr) {}
349219077Sdim
350219077Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
351219077Sdim
352218887Sdimprivate:
353226633Sdim  void EmitError(const TypedRegion* R, const Expr *Ex,
354218887Sdim                uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
355218887Sdim};
356218887Sdim} // end anonymous namespace
357218887Sdim
358218887Sdimenum CFNumberType {
359218887Sdim  kCFNumberSInt8Type = 1,
360218887Sdim  kCFNumberSInt16Type = 2,
361218887Sdim  kCFNumberSInt32Type = 3,
362218887Sdim  kCFNumberSInt64Type = 4,
363218887Sdim  kCFNumberFloat32Type = 5,
364218887Sdim  kCFNumberFloat64Type = 6,
365218887Sdim  kCFNumberCharType = 7,
366218887Sdim  kCFNumberShortType = 8,
367218887Sdim  kCFNumberIntType = 9,
368218887Sdim  kCFNumberLongType = 10,
369218887Sdim  kCFNumberLongLongType = 11,
370218887Sdim  kCFNumberFloatType = 12,
371218887Sdim  kCFNumberDoubleType = 13,
372218887Sdim  kCFNumberCFIndexType = 14,
373218887Sdim  kCFNumberNSIntegerType = 15,
374218887Sdim  kCFNumberCGFloatType = 16
375218887Sdim};
376218887Sdim
377226633Sdimstatic Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
378218887Sdim  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
379218887Sdim
380218887Sdim  if (i < kCFNumberCharType)
381218887Sdim    return FixedSize[i-1];
382218887Sdim
383218887Sdim  QualType T;
384218887Sdim
385218887Sdim  switch (i) {
386218887Sdim    case kCFNumberCharType:     T = Ctx.CharTy;     break;
387218887Sdim    case kCFNumberShortType:    T = Ctx.ShortTy;    break;
388218887Sdim    case kCFNumberIntType:      T = Ctx.IntTy;      break;
389218887Sdim    case kCFNumberLongType:     T = Ctx.LongTy;     break;
390218887Sdim    case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
391218887Sdim    case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
392218887Sdim    case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
393218887Sdim    case kCFNumberCFIndexType:
394218887Sdim    case kCFNumberNSIntegerType:
395218887Sdim    case kCFNumberCGFloatType:
396218887Sdim      // FIXME: We need a way to map from names to Type*.
397218887Sdim    default:
398249423Sdim      return None;
399218887Sdim  }
400218887Sdim
401218887Sdim  return Ctx.getTypeSize(T);
402218887Sdim}
403218887Sdim
404218887Sdim#if 0
405218887Sdimstatic const char* GetCFNumberTypeStr(uint64_t i) {
406218887Sdim  static const char* Names[] = {
407218887Sdim    "kCFNumberSInt8Type",
408218887Sdim    "kCFNumberSInt16Type",
409218887Sdim    "kCFNumberSInt32Type",
410218887Sdim    "kCFNumberSInt64Type",
411218887Sdim    "kCFNumberFloat32Type",
412218887Sdim    "kCFNumberFloat64Type",
413218887Sdim    "kCFNumberCharType",
414218887Sdim    "kCFNumberShortType",
415218887Sdim    "kCFNumberIntType",
416218887Sdim    "kCFNumberLongType",
417218887Sdim    "kCFNumberLongLongType",
418218887Sdim    "kCFNumberFloatType",
419218887Sdim    "kCFNumberDoubleType",
420218887Sdim    "kCFNumberCFIndexType",
421218887Sdim    "kCFNumberNSIntegerType",
422218887Sdim    "kCFNumberCGFloatType"
423218887Sdim  };
424218887Sdim
425218887Sdim  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
426218887Sdim}
427218887Sdim#endif
428218887Sdim
429219077Sdimvoid CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
430219077Sdim                                         CheckerContext &C) const {
431234353Sdim  ProgramStateRef state = C.getState();
432234353Sdim  const FunctionDecl *FD = C.getCalleeDecl(CE);
433218887Sdim  if (!FD)
434218887Sdim    return;
435296417Sdim
436218887Sdim  ASTContext &Ctx = C.getASTContext();
437218887Sdim  if (!II)
438218887Sdim    II = &Ctx.Idents.get("CFNumberCreate");
439218887Sdim
440218887Sdim  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
441218887Sdim    return;
442218887Sdim
443218887Sdim  // Get the value of the "theType" argument.
444234353Sdim  const LocationContext *LCtx = C.getLocationContext();
445234353Sdim  SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
446218887Sdim
447218887Sdim  // FIXME: We really should allow ranges of valid theType values, and
448218887Sdim  //   bifurcate the state appropriately.
449249423Sdim  Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
450218887Sdim  if (!V)
451218887Sdim    return;
452218887Sdim
453218887Sdim  uint64_t NumberKind = V->getValue().getLimitedValue();
454249423Sdim  Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
455218887Sdim
456218887Sdim  // FIXME: In some cases we can emit an error.
457249423Sdim  if (!OptTargetSize)
458218887Sdim    return;
459218887Sdim
460249423Sdim  uint64_t TargetSize = *OptTargetSize;
461249423Sdim
462218887Sdim  // Look at the value of the integer being passed by reference.  Essentially
463218887Sdim  // we want to catch cases where the value passed in is not equal to the
464218887Sdim  // size of the type being created.
465234353Sdim  SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
466218887Sdim
467218887Sdim  // FIXME: Eventually we should handle arbitrary locations.  We can do this
468218887Sdim  //  by having an enhanced memory model that does low-level typing.
469249423Sdim  Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
470218887Sdim  if (!LV)
471218887Sdim    return;
472218887Sdim
473226633Sdim  const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
474218887Sdim  if (!R)
475218887Sdim    return;
476218887Sdim
477218887Sdim  QualType T = Ctx.getCanonicalType(R->getValueType());
478218887Sdim
479218887Sdim  // FIXME: If the pointee isn't an integer type, should we flag a warning?
480218887Sdim  //  People can do weird stuff with pointers.
481218887Sdim
482251662Sdim  if (!T->isIntegralOrEnumerationType())
483218887Sdim    return;
484218887Sdim
485218887Sdim  uint64_t SourceSize = Ctx.getTypeSize(T);
486218887Sdim
487218887Sdim  // CHECK: is SourceSize == TargetSize
488218887Sdim  if (SourceSize == TargetSize)
489218887Sdim    return;
490218887Sdim
491296417Sdim  // Generate an error.  Only generate a sink error node
492296417Sdim  // if 'SourceSize < TargetSize'; otherwise generate a non-fatal error node.
493218887Sdim  //
494218887Sdim  // FIXME: We can actually create an abstract "CFNumber" object that has
495218887Sdim  //  the bits initialized to the provided values.
496218887Sdim  //
497296417Sdim  ExplodedNode *N = SourceSize < TargetSize ? C.generateErrorNode()
498296417Sdim                                            : C.generateNonFatalErrorNode();
499296417Sdim  if (N) {
500234353Sdim    SmallString<128> sbuf;
501218887Sdim    llvm::raw_svector_ostream os(sbuf);
502296417Sdim
503218887Sdim    os << (SourceSize == 8 ? "An " : "A ")
504218887Sdim       << SourceSize << " bit integer is used to initialize a CFNumber "
505218887Sdim                        "object that represents "
506218887Sdim       << (TargetSize == 8 ? "an " : "a ")
507218887Sdim       << TargetSize << " bit integer. ";
508296417Sdim
509218887Sdim    if (SourceSize < TargetSize)
510218887Sdim      os << (TargetSize - SourceSize)
511218887Sdim      << " bits of the CFNumber value will be garbage." ;
512218887Sdim    else
513218887Sdim      os << (SourceSize - TargetSize)
514218887Sdim      << " bits of the input integer will be lost.";
515218887Sdim
516218887Sdim    if (!BT)
517276479Sdim      BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate"));
518276479Sdim
519288943Sdim    auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
520218887Sdim    report->addRange(CE->getArg(2)->getSourceRange());
521288943Sdim    C.emitReport(std::move(report));
522218887Sdim  }
523218887Sdim}
524218887Sdim
525218887Sdim//===----------------------------------------------------------------------===//
526276479Sdim// CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
527218887Sdim//===----------------------------------------------------------------------===//
528218887Sdim
529218887Sdimnamespace {
530221345Sdimclass CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
531276479Sdim  mutable std::unique_ptr<APIMisuse> BT;
532276479Sdim  mutable IdentifierInfo *Retain, *Release, *MakeCollectable, *Autorelease;
533218887Sdimpublic:
534276479Sdim  CFRetainReleaseChecker()
535276479Sdim      : Retain(nullptr), Release(nullptr), MakeCollectable(nullptr),
536276479Sdim        Autorelease(nullptr) {}
537226633Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
538218887Sdim};
539218887Sdim} // end anonymous namespace
540218887Sdim
541218887Sdim
542226633Sdimvoid CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
543226633Sdim                                          CheckerContext &C) const {
544218887Sdim  // If the CallExpr doesn't have exactly 1 argument just give up checking.
545218887Sdim  if (CE->getNumArgs() != 1)
546218887Sdim    return;
547218887Sdim
548234353Sdim  ProgramStateRef state = C.getState();
549234353Sdim  const FunctionDecl *FD = C.getCalleeDecl(CE);
550218887Sdim  if (!FD)
551218887Sdim    return;
552296417Sdim
553218887Sdim  if (!BT) {
554218887Sdim    ASTContext &Ctx = C.getASTContext();
555218887Sdim    Retain = &Ctx.Idents.get("CFRetain");
556218887Sdim    Release = &Ctx.Idents.get("CFRelease");
557243830Sdim    MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
558276479Sdim    Autorelease = &Ctx.Idents.get("CFAutorelease");
559276479Sdim    BT.reset(new APIMisuse(
560276479Sdim        this, "null passed to CF memory management function"));
561218887Sdim  }
562218887Sdim
563276479Sdim  // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
564218887Sdim  const IdentifierInfo *FuncII = FD->getIdentifier();
565276479Sdim  if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable ||
566276479Sdim        FuncII == Autorelease))
567218887Sdim    return;
568218887Sdim
569218887Sdim  // FIXME: The rest of this just checks that the argument is non-null.
570249423Sdim  // It should probably be refactored and combined with NonNullParamChecker.
571218887Sdim
572218887Sdim  // Get the argument's value.
573218887Sdim  const Expr *Arg = CE->getArg(0);
574234353Sdim  SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
575249423Sdim  Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
576218887Sdim  if (!DefArgVal)
577218887Sdim    return;
578218887Sdim
579218887Sdim  // Get a NULL value.
580218887Sdim  SValBuilder &svalBuilder = C.getSValBuilder();
581249423Sdim  DefinedSVal zero =
582249423Sdim      svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
583218887Sdim
584218887Sdim  // Make an expression asserting that they're equal.
585218887Sdim  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
586218887Sdim
587218887Sdim  // Are they equal?
588234353Sdim  ProgramStateRef stateTrue, stateFalse;
589276479Sdim  std::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
590218887Sdim
591218887Sdim  if (stateTrue && !stateFalse) {
592296417Sdim    ExplodedNode *N = C.generateErrorNode(stateTrue);
593218887Sdim    if (!N)
594218887Sdim      return;
595218887Sdim
596243830Sdim    const char *description;
597243830Sdim    if (FuncII == Retain)
598243830Sdim      description = "Null pointer argument in call to CFRetain";
599243830Sdim    else if (FuncII == Release)
600243830Sdim      description = "Null pointer argument in call to CFRelease";
601243830Sdim    else if (FuncII == MakeCollectable)
602243830Sdim      description = "Null pointer argument in call to CFMakeCollectable";
603276479Sdim    else if (FuncII == Autorelease)
604276479Sdim      description = "Null pointer argument in call to CFAutorelease";
605243830Sdim    else
606243830Sdim      llvm_unreachable("impossible case");
607218887Sdim
608288943Sdim    auto report = llvm::make_unique<BugReport>(*BT, description, N);
609218887Sdim    report->addRange(Arg->getSourceRange());
610243830Sdim    bugreporter::trackNullOrUndefValue(N, Arg, *report);
611288943Sdim    C.emitReport(std::move(report));
612218887Sdim    return;
613218887Sdim  }
614218887Sdim
615218887Sdim  // From here on, we know the argument is non-null.
616218887Sdim  C.addTransition(stateFalse);
617218887Sdim}
618218887Sdim
619218887Sdim//===----------------------------------------------------------------------===//
620218887Sdim// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
621218887Sdim//===----------------------------------------------------------------------===//
622218887Sdim
623218887Sdimnamespace {
624221345Sdimclass ClassReleaseChecker : public Checker<check::PreObjCMessage> {
625219077Sdim  mutable Selector releaseS;
626219077Sdim  mutable Selector retainS;
627219077Sdim  mutable Selector autoreleaseS;
628219077Sdim  mutable Selector drainS;
629276479Sdim  mutable std::unique_ptr<BugType> BT;
630219077Sdim
631218887Sdimpublic:
632239462Sdim  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
633218887Sdim};
634218887Sdim}
635218887Sdim
636239462Sdimvoid ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
637219077Sdim                                              CheckerContext &C) const {
638296417Sdim
639218887Sdim  if (!BT) {
640276479Sdim    BT.reset(new APIMisuse(
641276479Sdim        this, "message incorrectly sent to class instead of class instance"));
642276479Sdim
643218887Sdim    ASTContext &Ctx = C.getASTContext();
644218887Sdim    releaseS = GetNullarySelector("release", Ctx);
645218887Sdim    retainS = GetNullarySelector("retain", Ctx);
646218887Sdim    autoreleaseS = GetNullarySelector("autorelease", Ctx);
647218887Sdim    drainS = GetNullarySelector("drain", Ctx);
648218887Sdim  }
649296417Sdim
650218887Sdim  if (msg.isInstanceMessage())
651218887Sdim    return;
652218887Sdim  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
653218887Sdim  assert(Class);
654218887Sdim
655218887Sdim  Selector S = msg.getSelector();
656218887Sdim  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
657218887Sdim    return;
658296417Sdim
659296417Sdim  if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
660234353Sdim    SmallString<200> buf;
661218887Sdim    llvm::raw_svector_ostream os(buf);
662218887Sdim
663276479Sdim    os << "The '";
664276479Sdim    S.print(os);
665276479Sdim    os << "' message should be sent to instances "
666218887Sdim          "of class '" << Class->getName()
667218887Sdim       << "' and not the class directly";
668296417Sdim
669288943Sdim    auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
670218887Sdim    report->addRange(msg.getSourceRange());
671288943Sdim    C.emitReport(std::move(report));
672218887Sdim  }
673218887Sdim}
674218887Sdim
675218887Sdim//===----------------------------------------------------------------------===//
676221345Sdim// Check for passing non-Objective-C types to variadic methods that expect
677221345Sdim// only Objective-C types.
678221345Sdim//===----------------------------------------------------------------------===//
679221345Sdim
680221345Sdimnamespace {
681221345Sdimclass VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
682221345Sdim  mutable Selector arrayWithObjectsS;
683221345Sdim  mutable Selector dictionaryWithObjectsAndKeysS;
684221345Sdim  mutable Selector setWithObjectsS;
685234353Sdim  mutable Selector orderedSetWithObjectsS;
686221345Sdim  mutable Selector initWithObjectsS;
687221345Sdim  mutable Selector initWithObjectsAndKeysS;
688276479Sdim  mutable std::unique_ptr<BugType> BT;
689221345Sdim
690239462Sdim  bool isVariadicMessage(const ObjCMethodCall &msg) const;
691221345Sdim
692221345Sdimpublic:
693239462Sdim  void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
694221345Sdim};
695221345Sdim}
696221345Sdim
697221345Sdim/// isVariadicMessage - Returns whether the given message is a variadic message,
698221345Sdim/// where all arguments must be Objective-C types.
699221345Sdimbool
700239462SdimVariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
701239462Sdim  const ObjCMethodDecl *MD = msg.getDecl();
702296417Sdim
703221345Sdim  if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
704221345Sdim    return false;
705296417Sdim
706221345Sdim  Selector S = msg.getSelector();
707296417Sdim
708221345Sdim  if (msg.isInstanceMessage()) {
709221345Sdim    // FIXME: Ideally we'd look at the receiver interface here, but that's not
710221345Sdim    // useful for init, because alloc returns 'id'. In theory, this could lead
711221345Sdim    // to false positives, for example if there existed a class that had an
712221345Sdim    // initWithObjects: implementation that does accept non-Objective-C pointer
713221345Sdim    // types, but the chance of that happening is pretty small compared to the
714221345Sdim    // gains that this analysis gives.
715221345Sdim    const ObjCInterfaceDecl *Class = MD->getClassInterface();
716221345Sdim
717239462Sdim    switch (findKnownClass(Class)) {
718239462Sdim    case FC_NSArray:
719239462Sdim    case FC_NSOrderedSet:
720239462Sdim    case FC_NSSet:
721239462Sdim      return S == initWithObjectsS;
722239462Sdim    case FC_NSDictionary:
723239462Sdim      return S == initWithObjectsAndKeysS;
724239462Sdim    default:
725239462Sdim      return false;
726239462Sdim    }
727221345Sdim  } else {
728221345Sdim    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
729221345Sdim
730239462Sdim    switch (findKnownClass(Class)) {
731239462Sdim      case FC_NSArray:
732239462Sdim        return S == arrayWithObjectsS;
733239462Sdim      case FC_NSOrderedSet:
734239462Sdim        return S == orderedSetWithObjectsS;
735239462Sdim      case FC_NSSet:
736239462Sdim        return S == setWithObjectsS;
737239462Sdim      case FC_NSDictionary:
738239462Sdim        return S == dictionaryWithObjectsAndKeysS;
739239462Sdim      default:
740239462Sdim        return false;
741239462Sdim    }
742221345Sdim  }
743221345Sdim}
744221345Sdim
745239462Sdimvoid VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
746221345Sdim                                                    CheckerContext &C) const {
747221345Sdim  if (!BT) {
748276479Sdim    BT.reset(new APIMisuse(this,
749276479Sdim                           "Arguments passed to variadic method aren't all "
750221345Sdim                           "Objective-C pointer types"));
751221345Sdim
752221345Sdim    ASTContext &Ctx = C.getASTContext();
753221345Sdim    arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
754296417Sdim    dictionaryWithObjectsAndKeysS =
755221345Sdim      GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
756221345Sdim    setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
757234353Sdim    orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
758221345Sdim
759221345Sdim    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
760221345Sdim    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
761221345Sdim  }
762221345Sdim
763221345Sdim  if (!isVariadicMessage(msg))
764221345Sdim      return;
765221345Sdim
766221345Sdim  // We are not interested in the selector arguments since they have
767221345Sdim  // well-defined types, so the compiler will issue a warning for them.
768221345Sdim  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
769221345Sdim
770221345Sdim  // We're not interested in the last argument since it has to be nil or the
771221345Sdim  // compiler would have issued a warning for it elsewhere.
772221345Sdim  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
773221345Sdim
774221345Sdim  if (variadicArgsEnd <= variadicArgsBegin)
775221345Sdim    return;
776221345Sdim
777221345Sdim  // Verify that all arguments have Objective-C types.
778249423Sdim  Optional<ExplodedNode*> errorNode;
779276479Sdim
780221345Sdim  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
781239462Sdim    QualType ArgTy = msg.getArgExpr(I)->getType();
782221345Sdim    if (ArgTy->isObjCObjectPointerType())
783221345Sdim      continue;
784221345Sdim
785221345Sdim    // Block pointers are treaded as Objective-C pointers.
786221345Sdim    if (ArgTy->isBlockPointerType())
787221345Sdim      continue;
788221345Sdim
789221345Sdim    // Ignore pointer constants.
790249423Sdim    if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
791221345Sdim      continue;
792296417Sdim
793221345Sdim    // Ignore pointer types annotated with 'NSObject' attribute.
794221345Sdim    if (C.getASTContext().isObjCNSObjectType(ArgTy))
795221345Sdim      continue;
796296417Sdim
797221345Sdim    // Ignore CF references, which can be toll-free bridged.
798224145Sdim    if (coreFoundation::isCFObjectRef(ArgTy))
799221345Sdim      continue;
800221345Sdim
801221345Sdim    // Generate only one error node to use for all bug reports.
802239462Sdim    if (!errorNode.hasValue())
803296417Sdim      errorNode = C.generateNonFatalErrorNode();
804221345Sdim
805221345Sdim    if (!errorNode.getValue())
806221345Sdim      continue;
807221345Sdim
808234353Sdim    SmallString<128> sbuf;
809221345Sdim    llvm::raw_svector_ostream os(sbuf);
810221345Sdim
811239462Sdim    StringRef TypeName = GetReceiverInterfaceName(msg);
812239462Sdim    if (!TypeName.empty())
813221345Sdim      os << "Argument to '" << TypeName << "' method '";
814221345Sdim    else
815221345Sdim      os << "Argument to method '";
816221345Sdim
817276479Sdim    msg.getSelector().print(os);
818276479Sdim    os << "' should be an Objective-C pointer type, not '";
819239462Sdim    ArgTy.print(os, C.getLangOpts());
820239462Sdim    os << "'";
821221345Sdim
822288943Sdim    auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue());
823221345Sdim    R->addRange(msg.getArgSourceRange(I));
824288943Sdim    C.emitReport(std::move(R));
825221345Sdim  }
826221345Sdim}
827221345Sdim
828221345Sdim//===----------------------------------------------------------------------===//
829239462Sdim// Improves the modeling of loops over Cocoa collections.
830239462Sdim//===----------------------------------------------------------------------===//
831239462Sdim
832261991Sdim// The map from container symbol to the container count symbol.
833261991Sdim// We currently will remember the last countainer count symbol encountered.
834261991SdimREGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
835261991SdimREGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
836261991Sdim
837239462Sdimnamespace {
838239462Sdimclass ObjCLoopChecker
839261991Sdim  : public Checker<check::PostStmt<ObjCForCollectionStmt>,
840261991Sdim                   check::PostObjCMessage,
841261991Sdim                   check::DeadSymbols,
842261991Sdim                   check::PointerEscape > {
843261991Sdim  mutable IdentifierInfo *CountSelectorII;
844261991Sdim
845261991Sdim  bool isCollectionCountMethod(const ObjCMethodCall &M,
846261991Sdim                               CheckerContext &C) const;
847261991Sdim
848239462Sdimpublic:
849276479Sdim  ObjCLoopChecker() : CountSelectorII(nullptr) {}
850239462Sdim  void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
851261991Sdim  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
852261991Sdim  void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
853261991Sdim  ProgramStateRef checkPointerEscape(ProgramStateRef State,
854261991Sdim                                     const InvalidatedSymbols &Escaped,
855261991Sdim                                     const CallEvent *Call,
856261991Sdim                                     PointerEscapeKind Kind) const;
857239462Sdim};
858239462Sdim}
859239462Sdim
860239462Sdimstatic bool isKnownNonNilCollectionType(QualType T) {
861239462Sdim  const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
862239462Sdim  if (!PT)
863239462Sdim    return false;
864296417Sdim
865239462Sdim  const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
866239462Sdim  if (!ID)
867239462Sdim    return false;
868239462Sdim
869239462Sdim  switch (findKnownClass(ID)) {
870239462Sdim  case FC_NSArray:
871239462Sdim  case FC_NSDictionary:
872239462Sdim  case FC_NSEnumerator:
873239462Sdim  case FC_NSOrderedSet:
874239462Sdim  case FC_NSSet:
875239462Sdim    return true;
876239462Sdim  default:
877239462Sdim    return false;
878239462Sdim  }
879239462Sdim}
880239462Sdim
881251662Sdim/// Assumes that the collection is non-nil.
882251662Sdim///
883251662Sdim/// If the collection is known to be nil, returns NULL to indicate an infeasible
884251662Sdim/// path.
885251662Sdimstatic ProgramStateRef checkCollectionNonNil(CheckerContext &C,
886251662Sdim                                             ProgramStateRef State,
887251662Sdim                                             const ObjCForCollectionStmt *FCS) {
888251662Sdim  if (!State)
889276479Sdim    return nullptr;
890251662Sdim
891251662Sdim  SVal CollectionVal = C.getSVal(FCS->getCollection());
892251662Sdim  Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
893251662Sdim  if (!KnownCollection)
894251662Sdim    return State;
895251662Sdim
896251662Sdim  ProgramStateRef StNonNil, StNil;
897276479Sdim  std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
898251662Sdim  if (StNil && !StNonNil) {
899251662Sdim    // The collection is nil. This path is infeasible.
900276479Sdim    return nullptr;
901251662Sdim  }
902251662Sdim
903251662Sdim  return StNonNil;
904251662Sdim}
905251662Sdim
906251662Sdim/// Assumes that the collection elements are non-nil.
907251662Sdim///
908251662Sdim/// This only applies if the collection is one of those known not to contain
909251662Sdim/// nil values.
910251662Sdimstatic ProgramStateRef checkElementNonNil(CheckerContext &C,
911251662Sdim                                          ProgramStateRef State,
912251662Sdim                                          const ObjCForCollectionStmt *FCS) {
913251662Sdim  if (!State)
914276479Sdim    return nullptr;
915251662Sdim
916239462Sdim  // See if the collection is one where we /know/ the elements are non-nil.
917251662Sdim  if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
918251662Sdim    return State;
919251662Sdim
920251662Sdim  const LocationContext *LCtx = C.getLocationContext();
921251662Sdim  const Stmt *Element = FCS->getElement();
922251662Sdim
923239462Sdim  // FIXME: Copied from ExprEngineObjC.
924251662Sdim  Optional<Loc> ElementLoc;
925239462Sdim  if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
926239462Sdim    const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
927276479Sdim    assert(ElemDecl->getInit() == nullptr);
928251662Sdim    ElementLoc = State->getLValue(ElemDecl, LCtx);
929239462Sdim  } else {
930251662Sdim    ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
931239462Sdim  }
932239462Sdim
933251662Sdim  if (!ElementLoc)
934251662Sdim    return State;
935239462Sdim
936239462Sdim  // Go ahead and assume the value is non-nil.
937251662Sdim  SVal Val = State->getSVal(*ElementLoc);
938251662Sdim  return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
939239462Sdim}
940239462Sdim
941261991Sdim/// Returns NULL state if the collection is known to contain elements
942261991Sdim/// (or is known not to contain elements if the Assumption parameter is false.)
943261991Sdimstatic ProgramStateRef
944261991SdimassumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
945261991Sdim                         SymbolRef CollectionS, bool Assumption) {
946261991Sdim  if (!State || !CollectionS)
947261991Sdim    return State;
948261991Sdim
949261991Sdim  const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
950261991Sdim  if (!CountS) {
951261991Sdim    const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
952261991Sdim    if (!KnownNonEmpty)
953261991Sdim      return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
954276479Sdim    return (Assumption == *KnownNonEmpty) ? State : nullptr;
955261991Sdim  }
956261991Sdim
957261991Sdim  SValBuilder &SvalBuilder = C.getSValBuilder();
958261991Sdim  SVal CountGreaterThanZeroVal =
959261991Sdim    SvalBuilder.evalBinOp(State, BO_GT,
960261991Sdim                          nonloc::SymbolVal(*CountS),
961261991Sdim                          SvalBuilder.makeIntVal(0, (*CountS)->getType()),
962261991Sdim                          SvalBuilder.getConditionType());
963261991Sdim  Optional<DefinedSVal> CountGreaterThanZero =
964261991Sdim    CountGreaterThanZeroVal.getAs<DefinedSVal>();
965261991Sdim  if (!CountGreaterThanZero) {
966261991Sdim    // The SValBuilder cannot construct a valid SVal for this condition.
967261991Sdim    // This means we cannot properly reason about it.
968261991Sdim    return State;
969261991Sdim  }
970261991Sdim
971261991Sdim  return State->assume(*CountGreaterThanZero, Assumption);
972261991Sdim}
973261991Sdim
974261991Sdimstatic ProgramStateRef
975261991SdimassumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
976261991Sdim                         const ObjCForCollectionStmt *FCS,
977261991Sdim                         bool Assumption) {
978261991Sdim  if (!State)
979276479Sdim    return nullptr;
980261991Sdim
981261991Sdim  SymbolRef CollectionS =
982261991Sdim    State->getSVal(FCS->getCollection(), C.getLocationContext()).getAsSymbol();
983261991Sdim  return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
984261991Sdim}
985261991Sdim
986261991Sdim
987261991Sdim/// If the fist block edge is a back edge, we are reentering the loop.
988261991Sdimstatic bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
989261991Sdim                                             const ObjCForCollectionStmt *FCS) {
990261991Sdim  if (!N)
991261991Sdim    return false;
992261991Sdim
993261991Sdim  ProgramPoint P = N->getLocation();
994261991Sdim  if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
995296417Sdim    return BE->getSrc()->getLoopTarget() == FCS;
996261991Sdim  }
997261991Sdim
998261991Sdim  // Keep looking for a block edge.
999261991Sdim  for (ExplodedNode::const_pred_iterator I = N->pred_begin(),
1000261991Sdim                                         E = N->pred_end(); I != E; ++I) {
1001261991Sdim    if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS))
1002261991Sdim      return true;
1003261991Sdim  }
1004261991Sdim
1005261991Sdim  return false;
1006261991Sdim}
1007261991Sdim
1008251662Sdimvoid ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
1009251662Sdim                                    CheckerContext &C) const {
1010261991Sdim  ProgramStateRef State = C.getState();
1011261991Sdim
1012251662Sdim  // Check if this is the branch for the end of the loop.
1013251662Sdim  SVal CollectionSentinel = C.getSVal(FCS);
1014261991Sdim  if (CollectionSentinel.isZeroConstant()) {
1015261991Sdim    if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
1016261991Sdim      State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
1017251662Sdim
1018261991Sdim  // Otherwise, this is a branch that goes through the loop body.
1019261991Sdim  } else {
1020261991Sdim    State = checkCollectionNonNil(C, State, FCS);
1021261991Sdim    State = checkElementNonNil(C, State, FCS);
1022261991Sdim    State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
1023261991Sdim  }
1024296417Sdim
1025251662Sdim  if (!State)
1026296417Sdim    C.generateSink(C.getState(), C.getPredecessor());
1027251662Sdim  else if (State != C.getState())
1028251662Sdim    C.addTransition(State);
1029251662Sdim}
1030251662Sdim
1031261991Sdimbool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
1032261991Sdim                                              CheckerContext &C) const {
1033261991Sdim  Selector S = M.getSelector();
1034261991Sdim  // Initialize the identifiers on first use.
1035261991Sdim  if (!CountSelectorII)
1036261991Sdim    CountSelectorII = &C.getASTContext().Idents.get("count");
1037261991Sdim
1038261991Sdim  // If the method returns collection count, record the value.
1039296417Sdim  return S.isUnarySelector() &&
1040296417Sdim         (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1041261991Sdim}
1042261991Sdim
1043261991Sdimvoid ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1044261991Sdim                                           CheckerContext &C) const {
1045261991Sdim  if (!M.isInstanceMessage())
1046261991Sdim    return;
1047261991Sdim
1048261991Sdim  const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1049261991Sdim  if (!ClassID)
1050261991Sdim    return;
1051261991Sdim
1052261991Sdim  FoundationClass Class = findKnownClass(ClassID);
1053261991Sdim  if (Class != FC_NSDictionary &&
1054261991Sdim      Class != FC_NSArray &&
1055261991Sdim      Class != FC_NSSet &&
1056261991Sdim      Class != FC_NSOrderedSet)
1057261991Sdim    return;
1058261991Sdim
1059261991Sdim  SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1060261991Sdim  if (!ContainerS)
1061261991Sdim    return;
1062261991Sdim
1063261991Sdim  // If we are processing a call to "count", get the symbolic value returned by
1064261991Sdim  // a call to "count" and add it to the map.
1065261991Sdim  if (!isCollectionCountMethod(M, C))
1066261991Sdim    return;
1067296417Sdim
1068261991Sdim  const Expr *MsgExpr = M.getOriginExpr();
1069261991Sdim  SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
1070261991Sdim  if (CountS) {
1071261991Sdim    ProgramStateRef State = C.getState();
1072261991Sdim
1073261991Sdim    C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1074261991Sdim    State = State->set<ContainerCountMap>(ContainerS, CountS);
1075261991Sdim
1076261991Sdim    if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1077261991Sdim      State = State->remove<ContainerNonEmptyMap>(ContainerS);
1078261991Sdim      State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
1079261991Sdim    }
1080261991Sdim
1081261991Sdim    C.addTransition(State);
1082261991Sdim  }
1083261991Sdim  return;
1084261991Sdim}
1085261991Sdim
1086261991Sdimstatic SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1087261991Sdim  const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1088261991Sdim  if (!Message)
1089276479Sdim    return nullptr;
1090261991Sdim
1091261991Sdim  const ObjCMethodDecl *MD = Message->getDecl();
1092261991Sdim  if (!MD)
1093276479Sdim    return nullptr;
1094261991Sdim
1095261991Sdim  const ObjCInterfaceDecl *StaticClass;
1096261991Sdim  if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
1097261991Sdim    // We can't find out where the method was declared without doing more work.
1098261991Sdim    // Instead, see if the receiver is statically typed as a known immutable
1099261991Sdim    // collection.
1100261991Sdim    StaticClass = Message->getOriginExpr()->getReceiverInterface();
1101261991Sdim  } else {
1102261991Sdim    StaticClass = MD->getClassInterface();
1103261991Sdim  }
1104261991Sdim
1105261991Sdim  if (!StaticClass)
1106276479Sdim    return nullptr;
1107261991Sdim
1108261991Sdim  switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
1109261991Sdim  case FC_None:
1110276479Sdim    return nullptr;
1111261991Sdim  case FC_NSArray:
1112261991Sdim  case FC_NSDictionary:
1113261991Sdim  case FC_NSEnumerator:
1114261991Sdim  case FC_NSNull:
1115261991Sdim  case FC_NSOrderedSet:
1116261991Sdim  case FC_NSSet:
1117261991Sdim  case FC_NSString:
1118261991Sdim    break;
1119261991Sdim  }
1120261991Sdim
1121261991Sdim  return Message->getReceiverSVal().getAsSymbol();
1122261991Sdim}
1123261991Sdim
1124261991SdimProgramStateRef
1125261991SdimObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1126261991Sdim                                    const InvalidatedSymbols &Escaped,
1127261991Sdim                                    const CallEvent *Call,
1128261991Sdim                                    PointerEscapeKind Kind) const {
1129261991Sdim  SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1130261991Sdim
1131261991Sdim  // Remove the invalidated symbols form the collection count map.
1132261991Sdim  for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1133261991Sdim       E = Escaped.end();
1134261991Sdim       I != E; ++I) {
1135261991Sdim    SymbolRef Sym = *I;
1136261991Sdim
1137261991Sdim    // Don't invalidate this symbol's count if we know the method being called
1138261991Sdim    // is declared on an immutable class. This isn't completely correct if the
1139261991Sdim    // receiver is also passed as an argument, but in most uses of NSArray,
1140261991Sdim    // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1141261991Sdim    if (Sym == ImmutableReceiver)
1142261991Sdim      continue;
1143261991Sdim
1144261991Sdim    // The symbol escaped. Pessimistically, assume that the count could have
1145261991Sdim    // changed.
1146261991Sdim    State = State->remove<ContainerCountMap>(Sym);
1147261991Sdim    State = State->remove<ContainerNonEmptyMap>(Sym);
1148261991Sdim  }
1149261991Sdim  return State;
1150261991Sdim}
1151261991Sdim
1152261991Sdimvoid ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1153261991Sdim                                       CheckerContext &C) const {
1154261991Sdim  ProgramStateRef State = C.getState();
1155261991Sdim
1156261991Sdim  // Remove the dead symbols from the collection count map.
1157261991Sdim  ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1158261991Sdim  for (ContainerCountMapTy::iterator I = Tracked.begin(),
1159261991Sdim                                     E = Tracked.end(); I != E; ++I) {
1160261991Sdim    SymbolRef Sym = I->first;
1161261991Sdim    if (SymReaper.isDead(Sym)) {
1162261991Sdim      State = State->remove<ContainerCountMap>(Sym);
1163261991Sdim      State = State->remove<ContainerNonEmptyMap>(Sym);
1164261991Sdim    }
1165261991Sdim  }
1166261991Sdim
1167261991Sdim  C.addTransition(State);
1168261991Sdim}
1169261991Sdim
1170243830Sdimnamespace {
1171243830Sdim/// \class ObjCNonNilReturnValueChecker
1172243830Sdim/// \brief The checker restricts the return values of APIs known to
1173243830Sdim/// never (or almost never) return 'nil'.
1174243830Sdimclass ObjCNonNilReturnValueChecker
1175276479Sdim  : public Checker<check::PostObjCMessage,
1176276479Sdim                   check::PostStmt<ObjCArrayLiteral>,
1177276479Sdim                   check::PostStmt<ObjCDictionaryLiteral>,
1178276479Sdim                   check::PostStmt<ObjCBoxedExpr> > {
1179243830Sdim    mutable bool Initialized;
1180243830Sdim    mutable Selector ObjectAtIndex;
1181243830Sdim    mutable Selector ObjectAtIndexedSubscript;
1182261991Sdim    mutable Selector NullSelector;
1183239462Sdim
1184243830Sdimpublic:
1185243830Sdim  ObjCNonNilReturnValueChecker() : Initialized(false) {}
1186276479Sdim
1187276479Sdim  ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1188276479Sdim                                      ProgramStateRef State,
1189276479Sdim                                      CheckerContext &C) const;
1190276479Sdim  void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1191276479Sdim    C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
1192276479Sdim  }
1193276479Sdim
1194276479Sdim  void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1195276479Sdim    assumeExprIsNonNull(E, C);
1196276479Sdim  }
1197276479Sdim  void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1198276479Sdim    assumeExprIsNonNull(E, C);
1199276479Sdim  }
1200276479Sdim  void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1201276479Sdim    assumeExprIsNonNull(E, C);
1202276479Sdim  }
1203276479Sdim
1204243830Sdim  void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1205243830Sdim};
1206243830Sdim}
1207243830Sdim
1208276479SdimProgramStateRef
1209276479SdimObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1210276479Sdim                                                  ProgramStateRef State,
1211276479Sdim                                                  CheckerContext &C) const {
1212243830Sdim  SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
1213249423Sdim  if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
1214243830Sdim    return State->assume(*DV, true);
1215243830Sdim  return State;
1216243830Sdim}
1217243830Sdim
1218243830Sdimvoid ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1219243830Sdim                                                        CheckerContext &C)
1220243830Sdim                                                        const {
1221243830Sdim  ProgramStateRef State = C.getState();
1222243830Sdim
1223243830Sdim  if (!Initialized) {
1224243830Sdim    ASTContext &Ctx = C.getASTContext();
1225243830Sdim    ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
1226243830Sdim    ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
1227261991Sdim    NullSelector = GetNullarySelector("null", Ctx);
1228243830Sdim  }
1229243830Sdim
1230243830Sdim  // Check the receiver type.
1231243830Sdim  if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1232243830Sdim
1233243830Sdim    // Assume that object returned from '[self init]' or '[super init]' is not
1234243830Sdim    // 'nil' if we are processing an inlined function/method.
1235243830Sdim    //
1236243830Sdim    // A defensive callee will (and should) check if the object returned by
1237243830Sdim    // '[super init]' is 'nil' before doing it's own initialization. However,
1238243830Sdim    // since 'nil' is rarely returned in practice, we should not warn when the
1239243830Sdim    // caller to the defensive constructor uses the object in contexts where
1240243830Sdim    // 'nil' is not accepted.
1241243830Sdim    if (!C.inTopFrame() && M.getDecl() &&
1242243830Sdim        M.getDecl()->getMethodFamily() == OMF_init &&
1243243830Sdim        M.isReceiverSelfOrSuper()) {
1244243830Sdim      State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1245243830Sdim    }
1246243830Sdim
1247261991Sdim    FoundationClass Cl = findKnownClass(Interface);
1248261991Sdim
1249243830Sdim    // Objects returned from
1250243830Sdim    // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1251243830Sdim    // are never 'nil'.
1252243830Sdim    if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
1253243830Sdim      Selector Sel = M.getSelector();
1254243830Sdim      if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1255243830Sdim        // Go ahead and assume the value is non-nil.
1256243830Sdim        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1257243830Sdim      }
1258243830Sdim    }
1259261991Sdim
1260261991Sdim    // Objects returned from [NSNull null] are not nil.
1261261991Sdim    if (Cl == FC_NSNull) {
1262261991Sdim      if (M.getSelector() == NullSelector) {
1263261991Sdim        // Go ahead and assume the value is non-nil.
1264261991Sdim        State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1265261991Sdim      }
1266261991Sdim    }
1267243830Sdim  }
1268243830Sdim  C.addTransition(State);
1269243830Sdim}
1270243830Sdim
1271239462Sdim//===----------------------------------------------------------------------===//
1272218887Sdim// Check registration.
1273218887Sdim//===----------------------------------------------------------------------===//
1274218887Sdim
1275218887Sdimvoid ento::registerNilArgChecker(CheckerManager &mgr) {
1276219077Sdim  mgr.registerChecker<NilArgChecker>();
1277218887Sdim}
1278218887Sdim
1279218887Sdimvoid ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
1280219077Sdim  mgr.registerChecker<CFNumberCreateChecker>();
1281218887Sdim}
1282218887Sdim
1283218887Sdimvoid ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1284219077Sdim  mgr.registerChecker<CFRetainReleaseChecker>();
1285218887Sdim}
1286218887Sdim
1287218887Sdimvoid ento::registerClassReleaseChecker(CheckerManager &mgr) {
1288219077Sdim  mgr.registerChecker<ClassReleaseChecker>();
1289218887Sdim}
1290221345Sdim
1291221345Sdimvoid ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1292221345Sdim  mgr.registerChecker<VariadicMethodTypeChecker>();
1293221345Sdim}
1294239462Sdim
1295239462Sdimvoid ento::registerObjCLoopChecker(CheckerManager &mgr) {
1296239462Sdim  mgr.registerChecker<ObjCLoopChecker>();
1297239462Sdim}
1298243830Sdim
1299276479Sdimvoid
1300276479Sdimento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1301243830Sdim  mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1302243830Sdim}
1303