xref: /openbsd-src/gnu/llvm/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision 12c855180aad702bbcca06e0398d774beeafb155)
1e5dd7070Spatrick //== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2e5dd7070Spatrick //
3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information.
5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e5dd7070Spatrick //
7e5dd7070Spatrick //===----------------------------------------------------------------------===//
8e5dd7070Spatrick //
9e5dd7070Spatrick //  This file defines BasicObjCFoundationChecks, a class that encapsulates
10e5dd7070Spatrick //  a set of simple checks to run on Objective-C code using Apple's Foundation
11e5dd7070Spatrick //  classes.
12e5dd7070Spatrick //
13e5dd7070Spatrick //===----------------------------------------------------------------------===//
14e5dd7070Spatrick 
15e5dd7070Spatrick #include "clang/AST/ASTContext.h"
16e5dd7070Spatrick #include "clang/AST/DeclObjC.h"
17e5dd7070Spatrick #include "clang/AST/Expr.h"
18e5dd7070Spatrick #include "clang/AST/ExprObjC.h"
19e5dd7070Spatrick #include "clang/AST/StmtObjC.h"
20e5dd7070Spatrick #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
21e5dd7070Spatrick #include "clang/Analysis/SelectorExtras.h"
22*12c85518Srobert #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
23e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h"
25e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26*12c85518Srobert #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
27e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
28e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
29e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
30e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
31e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
32e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
33e5dd7070Spatrick #include "llvm/ADT/SmallString.h"
34e5dd7070Spatrick #include "llvm/ADT/StringMap.h"
35e5dd7070Spatrick #include "llvm/Support/raw_ostream.h"
36*12c85518Srobert #include <optional>
37e5dd7070Spatrick 
38e5dd7070Spatrick using namespace clang;
39e5dd7070Spatrick using namespace ento;
40e5dd7070Spatrick using namespace llvm;
41e5dd7070Spatrick 
42e5dd7070Spatrick namespace {
43e5dd7070Spatrick class APIMisuse : public BugType {
44e5dd7070Spatrick public:
APIMisuse(const CheckerBase * checker,const char * name)45e5dd7070Spatrick   APIMisuse(const CheckerBase *checker, const char *name)
46e5dd7070Spatrick       : BugType(checker, name, "API Misuse (Apple)") {}
47e5dd7070Spatrick };
48e5dd7070Spatrick } // end anonymous namespace
49e5dd7070Spatrick 
50e5dd7070Spatrick //===----------------------------------------------------------------------===//
51e5dd7070Spatrick // Utility functions.
52e5dd7070Spatrick //===----------------------------------------------------------------------===//
53e5dd7070Spatrick 
GetReceiverInterfaceName(const ObjCMethodCall & msg)54e5dd7070Spatrick static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
55e5dd7070Spatrick   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
56e5dd7070Spatrick     return ID->getIdentifier()->getName();
57e5dd7070Spatrick   return StringRef();
58e5dd7070Spatrick }
59e5dd7070Spatrick 
60e5dd7070Spatrick enum FoundationClass {
61e5dd7070Spatrick   FC_None,
62e5dd7070Spatrick   FC_NSArray,
63e5dd7070Spatrick   FC_NSDictionary,
64e5dd7070Spatrick   FC_NSEnumerator,
65e5dd7070Spatrick   FC_NSNull,
66e5dd7070Spatrick   FC_NSOrderedSet,
67e5dd7070Spatrick   FC_NSSet,
68e5dd7070Spatrick   FC_NSString
69e5dd7070Spatrick };
70e5dd7070Spatrick 
findKnownClass(const ObjCInterfaceDecl * ID,bool IncludeSuperclasses=true)71e5dd7070Spatrick static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
72e5dd7070Spatrick                                       bool IncludeSuperclasses = true) {
73e5dd7070Spatrick   static llvm::StringMap<FoundationClass> Classes;
74e5dd7070Spatrick   if (Classes.empty()) {
75e5dd7070Spatrick     Classes["NSArray"] = FC_NSArray;
76e5dd7070Spatrick     Classes["NSDictionary"] = FC_NSDictionary;
77e5dd7070Spatrick     Classes["NSEnumerator"] = FC_NSEnumerator;
78e5dd7070Spatrick     Classes["NSNull"] = FC_NSNull;
79e5dd7070Spatrick     Classes["NSOrderedSet"] = FC_NSOrderedSet;
80e5dd7070Spatrick     Classes["NSSet"] = FC_NSSet;
81e5dd7070Spatrick     Classes["NSString"] = FC_NSString;
82e5dd7070Spatrick   }
83e5dd7070Spatrick 
84e5dd7070Spatrick   // FIXME: Should we cache this at all?
85e5dd7070Spatrick   FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
86e5dd7070Spatrick   if (result == FC_None && IncludeSuperclasses)
87e5dd7070Spatrick     if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
88e5dd7070Spatrick       return findKnownClass(Super);
89e5dd7070Spatrick 
90e5dd7070Spatrick   return result;
91e5dd7070Spatrick }
92e5dd7070Spatrick 
93e5dd7070Spatrick //===----------------------------------------------------------------------===//
94e5dd7070Spatrick // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
95e5dd7070Spatrick //===----------------------------------------------------------------------===//
96e5dd7070Spatrick 
97e5dd7070Spatrick namespace {
98e5dd7070Spatrick   class NilArgChecker : public Checker<check::PreObjCMessage,
99e5dd7070Spatrick                                        check::PostStmt<ObjCDictionaryLiteral>,
100e5dd7070Spatrick                                        check::PostStmt<ObjCArrayLiteral> > {
101e5dd7070Spatrick     mutable std::unique_ptr<APIMisuse> BT;
102e5dd7070Spatrick 
103e5dd7070Spatrick     mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
104e5dd7070Spatrick     mutable Selector ArrayWithObjectSel;
105e5dd7070Spatrick     mutable Selector AddObjectSel;
106e5dd7070Spatrick     mutable Selector InsertObjectAtIndexSel;
107e5dd7070Spatrick     mutable Selector ReplaceObjectAtIndexWithObjectSel;
108e5dd7070Spatrick     mutable Selector SetObjectAtIndexedSubscriptSel;
109e5dd7070Spatrick     mutable Selector ArrayByAddingObjectSel;
110e5dd7070Spatrick     mutable Selector DictionaryWithObjectForKeySel;
111e5dd7070Spatrick     mutable Selector SetObjectForKeySel;
112e5dd7070Spatrick     mutable Selector SetObjectForKeyedSubscriptSel;
113e5dd7070Spatrick     mutable Selector RemoveObjectForKeySel;
114e5dd7070Spatrick 
115e5dd7070Spatrick     void warnIfNilExpr(const Expr *E,
116e5dd7070Spatrick                        const char *Msg,
117e5dd7070Spatrick                        CheckerContext &C) const;
118e5dd7070Spatrick 
119e5dd7070Spatrick     void warnIfNilArg(CheckerContext &C,
120e5dd7070Spatrick                       const ObjCMethodCall &msg, unsigned Arg,
121e5dd7070Spatrick                       FoundationClass Class,
122e5dd7070Spatrick                       bool CanBeSubscript = false) const;
123e5dd7070Spatrick 
124e5dd7070Spatrick     void generateBugReport(ExplodedNode *N,
125e5dd7070Spatrick                            StringRef Msg,
126e5dd7070Spatrick                            SourceRange Range,
127e5dd7070Spatrick                            const Expr *Expr,
128e5dd7070Spatrick                            CheckerContext &C) const;
129e5dd7070Spatrick 
130e5dd7070Spatrick   public:
131e5dd7070Spatrick     void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
132e5dd7070Spatrick     void checkPostStmt(const ObjCDictionaryLiteral *DL,
133e5dd7070Spatrick                        CheckerContext &C) const;
134e5dd7070Spatrick     void checkPostStmt(const ObjCArrayLiteral *AL,
135e5dd7070Spatrick                        CheckerContext &C) const;
136e5dd7070Spatrick   };
137e5dd7070Spatrick } // end anonymous namespace
138e5dd7070Spatrick 
warnIfNilExpr(const Expr * E,const char * Msg,CheckerContext & C) const139e5dd7070Spatrick void NilArgChecker::warnIfNilExpr(const Expr *E,
140e5dd7070Spatrick                                   const char *Msg,
141e5dd7070Spatrick                                   CheckerContext &C) const {
142e5dd7070Spatrick   ProgramStateRef State = C.getState();
143e5dd7070Spatrick   if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
144e5dd7070Spatrick 
145e5dd7070Spatrick     if (ExplodedNode *N = C.generateErrorNode()) {
146e5dd7070Spatrick       generateBugReport(N, Msg, E->getSourceRange(), E, C);
147e5dd7070Spatrick     }
148e5dd7070Spatrick   }
149e5dd7070Spatrick }
150e5dd7070Spatrick 
warnIfNilArg(CheckerContext & C,const ObjCMethodCall & msg,unsigned int Arg,FoundationClass Class,bool CanBeSubscript) const151e5dd7070Spatrick void NilArgChecker::warnIfNilArg(CheckerContext &C,
152e5dd7070Spatrick                                  const ObjCMethodCall &msg,
153e5dd7070Spatrick                                  unsigned int Arg,
154e5dd7070Spatrick                                  FoundationClass Class,
155e5dd7070Spatrick                                  bool CanBeSubscript) const {
156e5dd7070Spatrick   // Check if the argument is nil.
157e5dd7070Spatrick   ProgramStateRef State = C.getState();
158e5dd7070Spatrick   if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
159e5dd7070Spatrick       return;
160e5dd7070Spatrick 
161e5dd7070Spatrick   // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
162e5dd7070Spatrick   // because it's called multiple times from some callers, so it'd cause
163e5dd7070Spatrick   // an unwanted state split if two or more non-fatal errors are thrown
164e5dd7070Spatrick   // within the same checker callback. For now we don't want to, but
165e5dd7070Spatrick   // it'll need to be fixed if we ever want to.
166e5dd7070Spatrick   if (ExplodedNode *N = C.generateErrorNode()) {
167e5dd7070Spatrick     SmallString<128> sbuf;
168e5dd7070Spatrick     llvm::raw_svector_ostream os(sbuf);
169e5dd7070Spatrick 
170e5dd7070Spatrick     if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
171e5dd7070Spatrick 
172e5dd7070Spatrick       if (Class == FC_NSArray) {
173e5dd7070Spatrick         os << "Array element cannot be nil";
174e5dd7070Spatrick       } else if (Class == FC_NSDictionary) {
175e5dd7070Spatrick         if (Arg == 0) {
176e5dd7070Spatrick           os << "Value stored into '";
177e5dd7070Spatrick           os << GetReceiverInterfaceName(msg) << "' cannot be nil";
178e5dd7070Spatrick         } else {
179e5dd7070Spatrick           assert(Arg == 1);
180e5dd7070Spatrick           os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
181e5dd7070Spatrick         }
182e5dd7070Spatrick       } else
183e5dd7070Spatrick         llvm_unreachable("Missing foundation class for the subscript expr");
184e5dd7070Spatrick 
185e5dd7070Spatrick     } else {
186e5dd7070Spatrick       if (Class == FC_NSDictionary) {
187e5dd7070Spatrick         if (Arg == 0)
188e5dd7070Spatrick           os << "Value argument ";
189e5dd7070Spatrick         else {
190e5dd7070Spatrick           assert(Arg == 1);
191e5dd7070Spatrick           os << "Key argument ";
192e5dd7070Spatrick         }
193e5dd7070Spatrick         os << "to '";
194e5dd7070Spatrick         msg.getSelector().print(os);
195e5dd7070Spatrick         os << "' cannot be nil";
196e5dd7070Spatrick       } else {
197e5dd7070Spatrick         os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
198e5dd7070Spatrick         msg.getSelector().print(os);
199e5dd7070Spatrick         os << "' cannot be nil";
200e5dd7070Spatrick       }
201e5dd7070Spatrick     }
202e5dd7070Spatrick 
203e5dd7070Spatrick     generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
204e5dd7070Spatrick                       msg.getArgExpr(Arg), C);
205e5dd7070Spatrick   }
206e5dd7070Spatrick }
207e5dd7070Spatrick 
generateBugReport(ExplodedNode * N,StringRef Msg,SourceRange Range,const Expr * E,CheckerContext & C) const208e5dd7070Spatrick void NilArgChecker::generateBugReport(ExplodedNode *N,
209e5dd7070Spatrick                                       StringRef Msg,
210e5dd7070Spatrick                                       SourceRange Range,
211e5dd7070Spatrick                                       const Expr *E,
212e5dd7070Spatrick                                       CheckerContext &C) const {
213e5dd7070Spatrick   if (!BT)
214e5dd7070Spatrick     BT.reset(new APIMisuse(this, "nil argument"));
215e5dd7070Spatrick 
216e5dd7070Spatrick   auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
217e5dd7070Spatrick   R->addRange(Range);
218e5dd7070Spatrick   bugreporter::trackExpressionValue(N, E, *R);
219e5dd7070Spatrick   C.emitReport(std::move(R));
220e5dd7070Spatrick }
221e5dd7070Spatrick 
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const222e5dd7070Spatrick void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
223e5dd7070Spatrick                                         CheckerContext &C) const {
224e5dd7070Spatrick   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
225e5dd7070Spatrick   if (!ID)
226e5dd7070Spatrick     return;
227e5dd7070Spatrick 
228e5dd7070Spatrick   FoundationClass Class = findKnownClass(ID);
229e5dd7070Spatrick 
230e5dd7070Spatrick   static const unsigned InvalidArgIndex = UINT_MAX;
231e5dd7070Spatrick   unsigned Arg = InvalidArgIndex;
232e5dd7070Spatrick   bool CanBeSubscript = false;
233e5dd7070Spatrick 
234e5dd7070Spatrick   if (Class == FC_NSString) {
235e5dd7070Spatrick     Selector S = msg.getSelector();
236e5dd7070Spatrick 
237e5dd7070Spatrick     if (S.isUnarySelector())
238e5dd7070Spatrick       return;
239e5dd7070Spatrick 
240e5dd7070Spatrick     if (StringSelectors.empty()) {
241e5dd7070Spatrick       ASTContext &Ctx = C.getASTContext();
242e5dd7070Spatrick       Selector Sels[] = {
243e5dd7070Spatrick           getKeywordSelector(Ctx, "caseInsensitiveCompare"),
244e5dd7070Spatrick           getKeywordSelector(Ctx, "compare"),
245e5dd7070Spatrick           getKeywordSelector(Ctx, "compare", "options"),
246e5dd7070Spatrick           getKeywordSelector(Ctx, "compare", "options", "range"),
247e5dd7070Spatrick           getKeywordSelector(Ctx, "compare", "options", "range", "locale"),
248e5dd7070Spatrick           getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"),
249e5dd7070Spatrick           getKeywordSelector(Ctx, "initWithFormat"),
250e5dd7070Spatrick           getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"),
251e5dd7070Spatrick           getKeywordSelector(Ctx, "localizedCompare"),
252e5dd7070Spatrick           getKeywordSelector(Ctx, "localizedStandardCompare"),
253e5dd7070Spatrick       };
254e5dd7070Spatrick       for (Selector KnownSel : Sels)
255e5dd7070Spatrick         StringSelectors[KnownSel] = 0;
256e5dd7070Spatrick     }
257e5dd7070Spatrick     auto I = StringSelectors.find(S);
258e5dd7070Spatrick     if (I == StringSelectors.end())
259e5dd7070Spatrick       return;
260e5dd7070Spatrick     Arg = I->second;
261e5dd7070Spatrick   } else if (Class == FC_NSArray) {
262e5dd7070Spatrick     Selector S = msg.getSelector();
263e5dd7070Spatrick 
264e5dd7070Spatrick     if (S.isUnarySelector())
265e5dd7070Spatrick       return;
266e5dd7070Spatrick 
267e5dd7070Spatrick     if (ArrayWithObjectSel.isNull()) {
268e5dd7070Spatrick       ASTContext &Ctx = C.getASTContext();
269e5dd7070Spatrick       ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject");
270e5dd7070Spatrick       AddObjectSel = getKeywordSelector(Ctx, "addObject");
271e5dd7070Spatrick       InsertObjectAtIndexSel =
272e5dd7070Spatrick           getKeywordSelector(Ctx, "insertObject", "atIndex");
273e5dd7070Spatrick       ReplaceObjectAtIndexWithObjectSel =
274e5dd7070Spatrick           getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject");
275e5dd7070Spatrick       SetObjectAtIndexedSubscriptSel =
276e5dd7070Spatrick           getKeywordSelector(Ctx, "setObject", "atIndexedSubscript");
277e5dd7070Spatrick       ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject");
278e5dd7070Spatrick     }
279e5dd7070Spatrick 
280e5dd7070Spatrick     if (S == ArrayWithObjectSel || S == AddObjectSel ||
281e5dd7070Spatrick         S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
282e5dd7070Spatrick       Arg = 0;
283e5dd7070Spatrick     } else if (S == SetObjectAtIndexedSubscriptSel) {
284e5dd7070Spatrick       Arg = 0;
285e5dd7070Spatrick       CanBeSubscript = true;
286e5dd7070Spatrick     } else if (S == ReplaceObjectAtIndexWithObjectSel) {
287e5dd7070Spatrick       Arg = 1;
288e5dd7070Spatrick     }
289e5dd7070Spatrick   } else if (Class == FC_NSDictionary) {
290e5dd7070Spatrick     Selector S = msg.getSelector();
291e5dd7070Spatrick 
292e5dd7070Spatrick     if (S.isUnarySelector())
293e5dd7070Spatrick       return;
294e5dd7070Spatrick 
295e5dd7070Spatrick     if (DictionaryWithObjectForKeySel.isNull()) {
296e5dd7070Spatrick       ASTContext &Ctx = C.getASTContext();
297e5dd7070Spatrick       DictionaryWithObjectForKeySel =
298e5dd7070Spatrick           getKeywordSelector(Ctx, "dictionaryWithObject", "forKey");
299e5dd7070Spatrick       SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey");
300e5dd7070Spatrick       SetObjectForKeyedSubscriptSel =
301e5dd7070Spatrick           getKeywordSelector(Ctx, "setObject", "forKeyedSubscript");
302e5dd7070Spatrick       RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey");
303e5dd7070Spatrick     }
304e5dd7070Spatrick 
305e5dd7070Spatrick     if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
306e5dd7070Spatrick       Arg = 0;
307e5dd7070Spatrick       warnIfNilArg(C, msg, /* Arg */1, Class);
308e5dd7070Spatrick     } else if (S == SetObjectForKeyedSubscriptSel) {
309e5dd7070Spatrick       CanBeSubscript = true;
310e5dd7070Spatrick       Arg = 1;
311e5dd7070Spatrick     } else if (S == RemoveObjectForKeySel) {
312e5dd7070Spatrick       Arg = 0;
313e5dd7070Spatrick     }
314e5dd7070Spatrick   }
315e5dd7070Spatrick 
316e5dd7070Spatrick   // If argument is '0', report a warning.
317e5dd7070Spatrick   if ((Arg != InvalidArgIndex))
318e5dd7070Spatrick     warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
319e5dd7070Spatrick }
320e5dd7070Spatrick 
checkPostStmt(const ObjCArrayLiteral * AL,CheckerContext & C) const321e5dd7070Spatrick void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
322e5dd7070Spatrick                                   CheckerContext &C) const {
323e5dd7070Spatrick   unsigned NumOfElements = AL->getNumElements();
324e5dd7070Spatrick   for (unsigned i = 0; i < NumOfElements; ++i) {
325e5dd7070Spatrick     warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
326e5dd7070Spatrick   }
327e5dd7070Spatrick }
328e5dd7070Spatrick 
checkPostStmt(const ObjCDictionaryLiteral * DL,CheckerContext & C) const329e5dd7070Spatrick void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
330e5dd7070Spatrick                                   CheckerContext &C) const {
331e5dd7070Spatrick   unsigned NumOfElements = DL->getNumElements();
332e5dd7070Spatrick   for (unsigned i = 0; i < NumOfElements; ++i) {
333e5dd7070Spatrick     ObjCDictionaryElement Element = DL->getKeyValueElement(i);
334e5dd7070Spatrick     warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
335e5dd7070Spatrick     warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
336e5dd7070Spatrick   }
337e5dd7070Spatrick }
338e5dd7070Spatrick 
339e5dd7070Spatrick //===----------------------------------------------------------------------===//
340e5dd7070Spatrick // Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
341e5dd7070Spatrick //===----------------------------------------------------------------------===//
342e5dd7070Spatrick 
343e5dd7070Spatrick namespace {
344e5dd7070Spatrick class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
345e5dd7070Spatrick   mutable std::unique_ptr<APIMisuse> BT;
346e5dd7070Spatrick   mutable IdentifierInfo *ICreate, *IGetValue;
347e5dd7070Spatrick public:
CFNumberChecker()348e5dd7070Spatrick   CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {}
349e5dd7070Spatrick 
350e5dd7070Spatrick   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
351e5dd7070Spatrick };
352e5dd7070Spatrick } // end anonymous namespace
353e5dd7070Spatrick 
354e5dd7070Spatrick enum CFNumberType {
355e5dd7070Spatrick   kCFNumberSInt8Type = 1,
356e5dd7070Spatrick   kCFNumberSInt16Type = 2,
357e5dd7070Spatrick   kCFNumberSInt32Type = 3,
358e5dd7070Spatrick   kCFNumberSInt64Type = 4,
359e5dd7070Spatrick   kCFNumberFloat32Type = 5,
360e5dd7070Spatrick   kCFNumberFloat64Type = 6,
361e5dd7070Spatrick   kCFNumberCharType = 7,
362e5dd7070Spatrick   kCFNumberShortType = 8,
363e5dd7070Spatrick   kCFNumberIntType = 9,
364e5dd7070Spatrick   kCFNumberLongType = 10,
365e5dd7070Spatrick   kCFNumberLongLongType = 11,
366e5dd7070Spatrick   kCFNumberFloatType = 12,
367e5dd7070Spatrick   kCFNumberDoubleType = 13,
368e5dd7070Spatrick   kCFNumberCFIndexType = 14,
369e5dd7070Spatrick   kCFNumberNSIntegerType = 15,
370e5dd7070Spatrick   kCFNumberCGFloatType = 16
371e5dd7070Spatrick };
372e5dd7070Spatrick 
GetCFNumberSize(ASTContext & Ctx,uint64_t i)373*12c85518Srobert static std::optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
374e5dd7070Spatrick   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
375e5dd7070Spatrick 
376e5dd7070Spatrick   if (i < kCFNumberCharType)
377e5dd7070Spatrick     return FixedSize[i-1];
378e5dd7070Spatrick 
379e5dd7070Spatrick   QualType T;
380e5dd7070Spatrick 
381e5dd7070Spatrick   switch (i) {
382e5dd7070Spatrick     case kCFNumberCharType:     T = Ctx.CharTy;     break;
383e5dd7070Spatrick     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
384e5dd7070Spatrick     case kCFNumberIntType:      T = Ctx.IntTy;      break;
385e5dd7070Spatrick     case kCFNumberLongType:     T = Ctx.LongTy;     break;
386e5dd7070Spatrick     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
387e5dd7070Spatrick     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
388e5dd7070Spatrick     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
389e5dd7070Spatrick     case kCFNumberCFIndexType:
390e5dd7070Spatrick     case kCFNumberNSIntegerType:
391e5dd7070Spatrick     case kCFNumberCGFloatType:
392e5dd7070Spatrick       // FIXME: We need a way to map from names to Type*.
393e5dd7070Spatrick     default:
394*12c85518Srobert       return std::nullopt;
395e5dd7070Spatrick   }
396e5dd7070Spatrick 
397e5dd7070Spatrick   return Ctx.getTypeSize(T);
398e5dd7070Spatrick }
399e5dd7070Spatrick 
400e5dd7070Spatrick #if 0
401e5dd7070Spatrick static const char* GetCFNumberTypeStr(uint64_t i) {
402e5dd7070Spatrick   static const char* Names[] = {
403e5dd7070Spatrick     "kCFNumberSInt8Type",
404e5dd7070Spatrick     "kCFNumberSInt16Type",
405e5dd7070Spatrick     "kCFNumberSInt32Type",
406e5dd7070Spatrick     "kCFNumberSInt64Type",
407e5dd7070Spatrick     "kCFNumberFloat32Type",
408e5dd7070Spatrick     "kCFNumberFloat64Type",
409e5dd7070Spatrick     "kCFNumberCharType",
410e5dd7070Spatrick     "kCFNumberShortType",
411e5dd7070Spatrick     "kCFNumberIntType",
412e5dd7070Spatrick     "kCFNumberLongType",
413e5dd7070Spatrick     "kCFNumberLongLongType",
414e5dd7070Spatrick     "kCFNumberFloatType",
415e5dd7070Spatrick     "kCFNumberDoubleType",
416e5dd7070Spatrick     "kCFNumberCFIndexType",
417e5dd7070Spatrick     "kCFNumberNSIntegerType",
418e5dd7070Spatrick     "kCFNumberCGFloatType"
419e5dd7070Spatrick   };
420e5dd7070Spatrick 
421e5dd7070Spatrick   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
422e5dd7070Spatrick }
423e5dd7070Spatrick #endif
424e5dd7070Spatrick 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const425e5dd7070Spatrick void CFNumberChecker::checkPreStmt(const CallExpr *CE,
426e5dd7070Spatrick                                          CheckerContext &C) const {
427e5dd7070Spatrick   ProgramStateRef state = C.getState();
428e5dd7070Spatrick   const FunctionDecl *FD = C.getCalleeDecl(CE);
429e5dd7070Spatrick   if (!FD)
430e5dd7070Spatrick     return;
431e5dd7070Spatrick 
432e5dd7070Spatrick   ASTContext &Ctx = C.getASTContext();
433e5dd7070Spatrick   if (!ICreate) {
434e5dd7070Spatrick     ICreate = &Ctx.Idents.get("CFNumberCreate");
435e5dd7070Spatrick     IGetValue = &Ctx.Idents.get("CFNumberGetValue");
436e5dd7070Spatrick   }
437e5dd7070Spatrick   if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) ||
438e5dd7070Spatrick       CE->getNumArgs() != 3)
439e5dd7070Spatrick     return;
440e5dd7070Spatrick 
441e5dd7070Spatrick   // Get the value of the "theType" argument.
442e5dd7070Spatrick   SVal TheTypeVal = C.getSVal(CE->getArg(1));
443e5dd7070Spatrick 
444e5dd7070Spatrick   // FIXME: We really should allow ranges of valid theType values, and
445e5dd7070Spatrick   //   bifurcate the state appropriately.
446*12c85518Srobert   std::optional<nonloc::ConcreteInt> V =
447*12c85518Srobert       dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
448e5dd7070Spatrick   if (!V)
449e5dd7070Spatrick     return;
450e5dd7070Spatrick 
451e5dd7070Spatrick   uint64_t NumberKind = V->getValue().getLimitedValue();
452*12c85518Srobert   std::optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
453e5dd7070Spatrick 
454e5dd7070Spatrick   // FIXME: In some cases we can emit an error.
455e5dd7070Spatrick   if (!OptCFNumberSize)
456e5dd7070Spatrick     return;
457e5dd7070Spatrick 
458e5dd7070Spatrick   uint64_t CFNumberSize = *OptCFNumberSize;
459e5dd7070Spatrick 
460e5dd7070Spatrick   // Look at the value of the integer being passed by reference.  Essentially
461e5dd7070Spatrick   // we want to catch cases where the value passed in is not equal to the
462e5dd7070Spatrick   // size of the type being created.
463e5dd7070Spatrick   SVal TheValueExpr = C.getSVal(CE->getArg(2));
464e5dd7070Spatrick 
465e5dd7070Spatrick   // FIXME: Eventually we should handle arbitrary locations.  We can do this
466e5dd7070Spatrick   //  by having an enhanced memory model that does low-level typing.
467*12c85518Srobert   std::optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
468e5dd7070Spatrick   if (!LV)
469e5dd7070Spatrick     return;
470e5dd7070Spatrick 
471e5dd7070Spatrick   const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
472e5dd7070Spatrick   if (!R)
473e5dd7070Spatrick     return;
474e5dd7070Spatrick 
475e5dd7070Spatrick   QualType T = Ctx.getCanonicalType(R->getValueType());
476e5dd7070Spatrick 
477e5dd7070Spatrick   // FIXME: If the pointee isn't an integer type, should we flag a warning?
478e5dd7070Spatrick   //  People can do weird stuff with pointers.
479e5dd7070Spatrick 
480e5dd7070Spatrick   if (!T->isIntegralOrEnumerationType())
481e5dd7070Spatrick     return;
482e5dd7070Spatrick 
483e5dd7070Spatrick   uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T);
484e5dd7070Spatrick 
485e5dd7070Spatrick   if (PrimitiveTypeSize == CFNumberSize)
486e5dd7070Spatrick     return;
487e5dd7070Spatrick 
488e5dd7070Spatrick   // FIXME: We can actually create an abstract "CFNumber" object that has
489e5dd7070Spatrick   //  the bits initialized to the provided values.
490e5dd7070Spatrick   ExplodedNode *N = C.generateNonFatalErrorNode();
491e5dd7070Spatrick   if (N) {
492e5dd7070Spatrick     SmallString<128> sbuf;
493e5dd7070Spatrick     llvm::raw_svector_ostream os(sbuf);
494e5dd7070Spatrick     bool isCreate = (FD->getIdentifier() == ICreate);
495e5dd7070Spatrick 
496e5dd7070Spatrick     if (isCreate) {
497e5dd7070Spatrick       os << (PrimitiveTypeSize == 8 ? "An " : "A ")
498e5dd7070Spatrick          << PrimitiveTypeSize << "-bit integer is used to initialize a "
499e5dd7070Spatrick          << "CFNumber object that represents "
500e5dd7070Spatrick          << (CFNumberSize == 8 ? "an " : "a ")
501e5dd7070Spatrick          << CFNumberSize << "-bit integer; ";
502e5dd7070Spatrick     } else {
503e5dd7070Spatrick       os << "A CFNumber object that represents "
504e5dd7070Spatrick          << (CFNumberSize == 8 ? "an " : "a ")
505e5dd7070Spatrick          << CFNumberSize << "-bit integer is used to initialize "
506e5dd7070Spatrick          << (PrimitiveTypeSize == 8 ? "an " : "a ")
507e5dd7070Spatrick          << PrimitiveTypeSize << "-bit integer; ";
508e5dd7070Spatrick     }
509e5dd7070Spatrick 
510e5dd7070Spatrick     if (PrimitiveTypeSize < CFNumberSize)
511e5dd7070Spatrick       os << (CFNumberSize - PrimitiveTypeSize)
512e5dd7070Spatrick       << " bits of the CFNumber value will "
513e5dd7070Spatrick       << (isCreate ? "be garbage." : "overwrite adjacent storage.");
514e5dd7070Spatrick     else
515e5dd7070Spatrick       os << (PrimitiveTypeSize - CFNumberSize)
516e5dd7070Spatrick       << " bits of the integer value will be "
517e5dd7070Spatrick       << (isCreate ? "lost." : "garbage.");
518e5dd7070Spatrick 
519e5dd7070Spatrick     if (!BT)
520e5dd7070Spatrick       BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
521e5dd7070Spatrick 
522e5dd7070Spatrick     auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
523e5dd7070Spatrick     report->addRange(CE->getArg(2)->getSourceRange());
524e5dd7070Spatrick     C.emitReport(std::move(report));
525e5dd7070Spatrick   }
526e5dd7070Spatrick }
527e5dd7070Spatrick 
528e5dd7070Spatrick //===----------------------------------------------------------------------===//
529e5dd7070Spatrick // CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
530e5dd7070Spatrick //===----------------------------------------------------------------------===//
531e5dd7070Spatrick 
532e5dd7070Spatrick namespace {
533e5dd7070Spatrick class CFRetainReleaseChecker : public Checker<check::PreCall> {
534e5dd7070Spatrick   mutable APIMisuse BT{this, "null passed to CF memory management function"};
535*12c85518Srobert   const CallDescriptionSet ModelledCalls = {
536*12c85518Srobert       {{"CFRetain"}, 1},
537*12c85518Srobert       {{"CFRelease"}, 1},
538*12c85518Srobert       {{"CFMakeCollectable"}, 1},
539*12c85518Srobert       {{"CFAutorelease"}, 1},
540*12c85518Srobert   };
541e5dd7070Spatrick 
542e5dd7070Spatrick public:
543e5dd7070Spatrick   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
544e5dd7070Spatrick };
545e5dd7070Spatrick } // end anonymous namespace
546e5dd7070Spatrick 
checkPreCall(const CallEvent & Call,CheckerContext & C) const547e5dd7070Spatrick void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
548e5dd7070Spatrick                                           CheckerContext &C) const {
549e5dd7070Spatrick   // TODO: Make this check part of CallDescription.
550e5dd7070Spatrick   if (!Call.isGlobalCFunction())
551e5dd7070Spatrick     return;
552e5dd7070Spatrick 
553e5dd7070Spatrick   // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
554*12c85518Srobert   if (!ModelledCalls.contains(Call))
555e5dd7070Spatrick     return;
556e5dd7070Spatrick 
557e5dd7070Spatrick   // Get the argument's value.
558e5dd7070Spatrick   SVal ArgVal = Call.getArgSVal(0);
559*12c85518Srobert   std::optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
560e5dd7070Spatrick   if (!DefArgVal)
561e5dd7070Spatrick     return;
562e5dd7070Spatrick 
563e5dd7070Spatrick   // Is it null?
564e5dd7070Spatrick   ProgramStateRef state = C.getState();
565e5dd7070Spatrick   ProgramStateRef stateNonNull, stateNull;
566e5dd7070Spatrick   std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
567e5dd7070Spatrick 
568e5dd7070Spatrick   if (!stateNonNull) {
569e5dd7070Spatrick     ExplodedNode *N = C.generateErrorNode(stateNull);
570e5dd7070Spatrick     if (!N)
571e5dd7070Spatrick       return;
572e5dd7070Spatrick 
573e5dd7070Spatrick     SmallString<64> Str;
574e5dd7070Spatrick     raw_svector_ostream OS(Str);
575e5dd7070Spatrick     OS << "Null pointer argument in call to "
576e5dd7070Spatrick        << cast<FunctionDecl>(Call.getDecl())->getName();
577e5dd7070Spatrick 
578e5dd7070Spatrick     auto report = std::make_unique<PathSensitiveBugReport>(BT, OS.str(), N);
579e5dd7070Spatrick     report->addRange(Call.getArgSourceRange(0));
580e5dd7070Spatrick     bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report);
581e5dd7070Spatrick     C.emitReport(std::move(report));
582e5dd7070Spatrick     return;
583e5dd7070Spatrick   }
584e5dd7070Spatrick 
585e5dd7070Spatrick   // From here on, we know the argument is non-null.
586e5dd7070Spatrick   C.addTransition(stateNonNull);
587e5dd7070Spatrick }
588e5dd7070Spatrick 
589e5dd7070Spatrick //===----------------------------------------------------------------------===//
590e5dd7070Spatrick // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
591e5dd7070Spatrick //===----------------------------------------------------------------------===//
592e5dd7070Spatrick 
593e5dd7070Spatrick namespace {
594e5dd7070Spatrick class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
595e5dd7070Spatrick   mutable Selector releaseS;
596e5dd7070Spatrick   mutable Selector retainS;
597e5dd7070Spatrick   mutable Selector autoreleaseS;
598e5dd7070Spatrick   mutable Selector drainS;
599e5dd7070Spatrick   mutable std::unique_ptr<BugType> BT;
600e5dd7070Spatrick 
601e5dd7070Spatrick public:
602e5dd7070Spatrick   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
603e5dd7070Spatrick };
604e5dd7070Spatrick } // end anonymous namespace
605e5dd7070Spatrick 
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const606e5dd7070Spatrick void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
607e5dd7070Spatrick                                               CheckerContext &C) const {
608e5dd7070Spatrick   if (!BT) {
609e5dd7070Spatrick     BT.reset(new APIMisuse(
610e5dd7070Spatrick         this, "message incorrectly sent to class instead of class instance"));
611e5dd7070Spatrick 
612e5dd7070Spatrick     ASTContext &Ctx = C.getASTContext();
613e5dd7070Spatrick     releaseS = GetNullarySelector("release", Ctx);
614e5dd7070Spatrick     retainS = GetNullarySelector("retain", Ctx);
615e5dd7070Spatrick     autoreleaseS = GetNullarySelector("autorelease", Ctx);
616e5dd7070Spatrick     drainS = GetNullarySelector("drain", Ctx);
617e5dd7070Spatrick   }
618e5dd7070Spatrick 
619e5dd7070Spatrick   if (msg.isInstanceMessage())
620e5dd7070Spatrick     return;
621e5dd7070Spatrick   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
622e5dd7070Spatrick   assert(Class);
623e5dd7070Spatrick 
624e5dd7070Spatrick   Selector S = msg.getSelector();
625e5dd7070Spatrick   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
626e5dd7070Spatrick     return;
627e5dd7070Spatrick 
628e5dd7070Spatrick   if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
629e5dd7070Spatrick     SmallString<200> buf;
630e5dd7070Spatrick     llvm::raw_svector_ostream os(buf);
631e5dd7070Spatrick 
632e5dd7070Spatrick     os << "The '";
633e5dd7070Spatrick     S.print(os);
634e5dd7070Spatrick     os << "' message should be sent to instances "
635e5dd7070Spatrick           "of class '" << Class->getName()
636e5dd7070Spatrick        << "' and not the class directly";
637e5dd7070Spatrick 
638e5dd7070Spatrick     auto report = std::make_unique<PathSensitiveBugReport>(*BT, os.str(), N);
639e5dd7070Spatrick     report->addRange(msg.getSourceRange());
640e5dd7070Spatrick     C.emitReport(std::move(report));
641e5dd7070Spatrick   }
642e5dd7070Spatrick }
643e5dd7070Spatrick 
644e5dd7070Spatrick //===----------------------------------------------------------------------===//
645e5dd7070Spatrick // Check for passing non-Objective-C types to variadic methods that expect
646e5dd7070Spatrick // only Objective-C types.
647e5dd7070Spatrick //===----------------------------------------------------------------------===//
648e5dd7070Spatrick 
649e5dd7070Spatrick namespace {
650e5dd7070Spatrick class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
651e5dd7070Spatrick   mutable Selector arrayWithObjectsS;
652e5dd7070Spatrick   mutable Selector dictionaryWithObjectsAndKeysS;
653e5dd7070Spatrick   mutable Selector setWithObjectsS;
654e5dd7070Spatrick   mutable Selector orderedSetWithObjectsS;
655e5dd7070Spatrick   mutable Selector initWithObjectsS;
656e5dd7070Spatrick   mutable Selector initWithObjectsAndKeysS;
657e5dd7070Spatrick   mutable std::unique_ptr<BugType> BT;
658e5dd7070Spatrick 
659e5dd7070Spatrick   bool isVariadicMessage(const ObjCMethodCall &msg) const;
660e5dd7070Spatrick 
661e5dd7070Spatrick public:
662e5dd7070Spatrick   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
663e5dd7070Spatrick };
664e5dd7070Spatrick } // end anonymous namespace
665e5dd7070Spatrick 
666e5dd7070Spatrick /// isVariadicMessage - Returns whether the given message is a variadic message,
667e5dd7070Spatrick /// where all arguments must be Objective-C types.
668e5dd7070Spatrick bool
isVariadicMessage(const ObjCMethodCall & msg) const669e5dd7070Spatrick VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
670e5dd7070Spatrick   const ObjCMethodDecl *MD = msg.getDecl();
671e5dd7070Spatrick 
672e5dd7070Spatrick   if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
673e5dd7070Spatrick     return false;
674e5dd7070Spatrick 
675e5dd7070Spatrick   Selector S = msg.getSelector();
676e5dd7070Spatrick 
677e5dd7070Spatrick   if (msg.isInstanceMessage()) {
678e5dd7070Spatrick     // FIXME: Ideally we'd look at the receiver interface here, but that's not
679e5dd7070Spatrick     // useful for init, because alloc returns 'id'. In theory, this could lead
680e5dd7070Spatrick     // to false positives, for example if there existed a class that had an
681e5dd7070Spatrick     // initWithObjects: implementation that does accept non-Objective-C pointer
682e5dd7070Spatrick     // types, but the chance of that happening is pretty small compared to the
683e5dd7070Spatrick     // gains that this analysis gives.
684e5dd7070Spatrick     const ObjCInterfaceDecl *Class = MD->getClassInterface();
685e5dd7070Spatrick 
686e5dd7070Spatrick     switch (findKnownClass(Class)) {
687e5dd7070Spatrick     case FC_NSArray:
688e5dd7070Spatrick     case FC_NSOrderedSet:
689e5dd7070Spatrick     case FC_NSSet:
690e5dd7070Spatrick       return S == initWithObjectsS;
691e5dd7070Spatrick     case FC_NSDictionary:
692e5dd7070Spatrick       return S == initWithObjectsAndKeysS;
693e5dd7070Spatrick     default:
694e5dd7070Spatrick       return false;
695e5dd7070Spatrick     }
696e5dd7070Spatrick   } else {
697e5dd7070Spatrick     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
698e5dd7070Spatrick 
699e5dd7070Spatrick     switch (findKnownClass(Class)) {
700e5dd7070Spatrick       case FC_NSArray:
701e5dd7070Spatrick         return S == arrayWithObjectsS;
702e5dd7070Spatrick       case FC_NSOrderedSet:
703e5dd7070Spatrick         return S == orderedSetWithObjectsS;
704e5dd7070Spatrick       case FC_NSSet:
705e5dd7070Spatrick         return S == setWithObjectsS;
706e5dd7070Spatrick       case FC_NSDictionary:
707e5dd7070Spatrick         return S == dictionaryWithObjectsAndKeysS;
708e5dd7070Spatrick       default:
709e5dd7070Spatrick         return false;
710e5dd7070Spatrick     }
711e5dd7070Spatrick   }
712e5dd7070Spatrick }
713e5dd7070Spatrick 
checkPreObjCMessage(const ObjCMethodCall & msg,CheckerContext & C) const714e5dd7070Spatrick void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
715e5dd7070Spatrick                                                     CheckerContext &C) const {
716e5dd7070Spatrick   if (!BT) {
717e5dd7070Spatrick     BT.reset(new APIMisuse(this,
718e5dd7070Spatrick                            "Arguments passed to variadic method aren't all "
719e5dd7070Spatrick                            "Objective-C pointer types"));
720e5dd7070Spatrick 
721e5dd7070Spatrick     ASTContext &Ctx = C.getASTContext();
722e5dd7070Spatrick     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
723e5dd7070Spatrick     dictionaryWithObjectsAndKeysS =
724e5dd7070Spatrick       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
725e5dd7070Spatrick     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
726e5dd7070Spatrick     orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
727e5dd7070Spatrick 
728e5dd7070Spatrick     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
729e5dd7070Spatrick     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
730e5dd7070Spatrick   }
731e5dd7070Spatrick 
732e5dd7070Spatrick   if (!isVariadicMessage(msg))
733e5dd7070Spatrick       return;
734e5dd7070Spatrick 
735e5dd7070Spatrick   // We are not interested in the selector arguments since they have
736e5dd7070Spatrick   // well-defined types, so the compiler will issue a warning for them.
737e5dd7070Spatrick   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
738e5dd7070Spatrick 
739e5dd7070Spatrick   // We're not interested in the last argument since it has to be nil or the
740e5dd7070Spatrick   // compiler would have issued a warning for it elsewhere.
741e5dd7070Spatrick   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
742e5dd7070Spatrick 
743e5dd7070Spatrick   if (variadicArgsEnd <= variadicArgsBegin)
744e5dd7070Spatrick     return;
745e5dd7070Spatrick 
746e5dd7070Spatrick   // Verify that all arguments have Objective-C types.
747*12c85518Srobert   std::optional<ExplodedNode *> errorNode;
748e5dd7070Spatrick 
749e5dd7070Spatrick   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
750e5dd7070Spatrick     QualType ArgTy = msg.getArgExpr(I)->getType();
751e5dd7070Spatrick     if (ArgTy->isObjCObjectPointerType())
752e5dd7070Spatrick       continue;
753e5dd7070Spatrick 
754e5dd7070Spatrick     // Block pointers are treaded as Objective-C pointers.
755e5dd7070Spatrick     if (ArgTy->isBlockPointerType())
756e5dd7070Spatrick       continue;
757e5dd7070Spatrick 
758e5dd7070Spatrick     // Ignore pointer constants.
759*12c85518Srobert     if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
760e5dd7070Spatrick       continue;
761e5dd7070Spatrick 
762e5dd7070Spatrick     // Ignore pointer types annotated with 'NSObject' attribute.
763e5dd7070Spatrick     if (C.getASTContext().isObjCNSObjectType(ArgTy))
764e5dd7070Spatrick       continue;
765e5dd7070Spatrick 
766e5dd7070Spatrick     // Ignore CF references, which can be toll-free bridged.
767e5dd7070Spatrick     if (coreFoundation::isCFObjectRef(ArgTy))
768e5dd7070Spatrick       continue;
769e5dd7070Spatrick 
770e5dd7070Spatrick     // Generate only one error node to use for all bug reports.
771*12c85518Srobert     if (!errorNode)
772e5dd7070Spatrick       errorNode = C.generateNonFatalErrorNode();
773e5dd7070Spatrick 
774*12c85518Srobert     if (!*errorNode)
775e5dd7070Spatrick       continue;
776e5dd7070Spatrick 
777e5dd7070Spatrick     SmallString<128> sbuf;
778e5dd7070Spatrick     llvm::raw_svector_ostream os(sbuf);
779e5dd7070Spatrick 
780e5dd7070Spatrick     StringRef TypeName = GetReceiverInterfaceName(msg);
781e5dd7070Spatrick     if (!TypeName.empty())
782e5dd7070Spatrick       os << "Argument to '" << TypeName << "' method '";
783e5dd7070Spatrick     else
784e5dd7070Spatrick       os << "Argument to method '";
785e5dd7070Spatrick 
786e5dd7070Spatrick     msg.getSelector().print(os);
787e5dd7070Spatrick     os << "' should be an Objective-C pointer type, not '";
788e5dd7070Spatrick     ArgTy.print(os, C.getLangOpts());
789e5dd7070Spatrick     os << "'";
790e5dd7070Spatrick 
791*12c85518Srobert     auto R =
792*12c85518Srobert         std::make_unique<PathSensitiveBugReport>(*BT, os.str(), *errorNode);
793e5dd7070Spatrick     R->addRange(msg.getArgSourceRange(I));
794e5dd7070Spatrick     C.emitReport(std::move(R));
795e5dd7070Spatrick   }
796e5dd7070Spatrick }
797e5dd7070Spatrick 
798e5dd7070Spatrick //===----------------------------------------------------------------------===//
799e5dd7070Spatrick // Improves the modeling of loops over Cocoa collections.
800e5dd7070Spatrick //===----------------------------------------------------------------------===//
801e5dd7070Spatrick 
802e5dd7070Spatrick // The map from container symbol to the container count symbol.
803e5dd7070Spatrick // We currently will remember the last container count symbol encountered.
804e5dd7070Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
805e5dd7070Spatrick REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
806e5dd7070Spatrick 
807e5dd7070Spatrick namespace {
808e5dd7070Spatrick class ObjCLoopChecker
809e5dd7070Spatrick   : public Checker<check::PostStmt<ObjCForCollectionStmt>,
810e5dd7070Spatrick                    check::PostObjCMessage,
811e5dd7070Spatrick                    check::DeadSymbols,
812e5dd7070Spatrick                    check::PointerEscape > {
813e5dd7070Spatrick   mutable IdentifierInfo *CountSelectorII;
814e5dd7070Spatrick 
815e5dd7070Spatrick   bool isCollectionCountMethod(const ObjCMethodCall &M,
816e5dd7070Spatrick                                CheckerContext &C) const;
817e5dd7070Spatrick 
818e5dd7070Spatrick public:
ObjCLoopChecker()819e5dd7070Spatrick   ObjCLoopChecker() : CountSelectorII(nullptr) {}
820e5dd7070Spatrick   void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
821e5dd7070Spatrick   void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
822e5dd7070Spatrick   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
823e5dd7070Spatrick   ProgramStateRef checkPointerEscape(ProgramStateRef State,
824e5dd7070Spatrick                                      const InvalidatedSymbols &Escaped,
825e5dd7070Spatrick                                      const CallEvent *Call,
826e5dd7070Spatrick                                      PointerEscapeKind Kind) const;
827e5dd7070Spatrick };
828e5dd7070Spatrick } // end anonymous namespace
829e5dd7070Spatrick 
isKnownNonNilCollectionType(QualType T)830e5dd7070Spatrick static bool isKnownNonNilCollectionType(QualType T) {
831e5dd7070Spatrick   const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
832e5dd7070Spatrick   if (!PT)
833e5dd7070Spatrick     return false;
834e5dd7070Spatrick 
835e5dd7070Spatrick   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
836e5dd7070Spatrick   if (!ID)
837e5dd7070Spatrick     return false;
838e5dd7070Spatrick 
839e5dd7070Spatrick   switch (findKnownClass(ID)) {
840e5dd7070Spatrick   case FC_NSArray:
841e5dd7070Spatrick   case FC_NSDictionary:
842e5dd7070Spatrick   case FC_NSEnumerator:
843e5dd7070Spatrick   case FC_NSOrderedSet:
844e5dd7070Spatrick   case FC_NSSet:
845e5dd7070Spatrick     return true;
846e5dd7070Spatrick   default:
847e5dd7070Spatrick     return false;
848e5dd7070Spatrick   }
849e5dd7070Spatrick }
850e5dd7070Spatrick 
851e5dd7070Spatrick /// Assumes that the collection is non-nil.
852e5dd7070Spatrick ///
853e5dd7070Spatrick /// If the collection is known to be nil, returns NULL to indicate an infeasible
854e5dd7070Spatrick /// path.
checkCollectionNonNil(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS)855e5dd7070Spatrick static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
856e5dd7070Spatrick                                              ProgramStateRef State,
857e5dd7070Spatrick                                              const ObjCForCollectionStmt *FCS) {
858e5dd7070Spatrick   if (!State)
859e5dd7070Spatrick     return nullptr;
860e5dd7070Spatrick 
861e5dd7070Spatrick   SVal CollectionVal = C.getSVal(FCS->getCollection());
862*12c85518Srobert   std::optional<DefinedSVal> KnownCollection =
863*12c85518Srobert       CollectionVal.getAs<DefinedSVal>();
864e5dd7070Spatrick   if (!KnownCollection)
865e5dd7070Spatrick     return State;
866e5dd7070Spatrick 
867e5dd7070Spatrick   ProgramStateRef StNonNil, StNil;
868e5dd7070Spatrick   std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
869e5dd7070Spatrick   if (StNil && !StNonNil) {
870e5dd7070Spatrick     // The collection is nil. This path is infeasible.
871e5dd7070Spatrick     return nullptr;
872e5dd7070Spatrick   }
873e5dd7070Spatrick 
874e5dd7070Spatrick   return StNonNil;
875e5dd7070Spatrick }
876e5dd7070Spatrick 
877e5dd7070Spatrick /// Assumes that the collection elements are non-nil.
878e5dd7070Spatrick ///
879e5dd7070Spatrick /// This only applies if the collection is one of those known not to contain
880e5dd7070Spatrick /// nil values.
checkElementNonNil(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS)881e5dd7070Spatrick static ProgramStateRef checkElementNonNil(CheckerContext &C,
882e5dd7070Spatrick                                           ProgramStateRef State,
883e5dd7070Spatrick                                           const ObjCForCollectionStmt *FCS) {
884e5dd7070Spatrick   if (!State)
885e5dd7070Spatrick     return nullptr;
886e5dd7070Spatrick 
887e5dd7070Spatrick   // See if the collection is one where we /know/ the elements are non-nil.
888e5dd7070Spatrick   if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
889e5dd7070Spatrick     return State;
890e5dd7070Spatrick 
891e5dd7070Spatrick   const LocationContext *LCtx = C.getLocationContext();
892e5dd7070Spatrick   const Stmt *Element = FCS->getElement();
893e5dd7070Spatrick 
894e5dd7070Spatrick   // FIXME: Copied from ExprEngineObjC.
895*12c85518Srobert   std::optional<Loc> ElementLoc;
896e5dd7070Spatrick   if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
897e5dd7070Spatrick     const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
898e5dd7070Spatrick     assert(ElemDecl->getInit() == nullptr);
899e5dd7070Spatrick     ElementLoc = State->getLValue(ElemDecl, LCtx);
900e5dd7070Spatrick   } else {
901e5dd7070Spatrick     ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
902e5dd7070Spatrick   }
903e5dd7070Spatrick 
904e5dd7070Spatrick   if (!ElementLoc)
905e5dd7070Spatrick     return State;
906e5dd7070Spatrick 
907e5dd7070Spatrick   // Go ahead and assume the value is non-nil.
908e5dd7070Spatrick   SVal Val = State->getSVal(*ElementLoc);
909*12c85518Srobert   return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
910e5dd7070Spatrick }
911e5dd7070Spatrick 
912e5dd7070Spatrick /// Returns NULL state if the collection is known to contain elements
913e5dd7070Spatrick /// (or is known not to contain elements if the Assumption parameter is false.)
914e5dd7070Spatrick static ProgramStateRef
assumeCollectionNonEmpty(CheckerContext & C,ProgramStateRef State,SymbolRef CollectionS,bool Assumption)915e5dd7070Spatrick assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
916e5dd7070Spatrick                          SymbolRef CollectionS, bool Assumption) {
917e5dd7070Spatrick   if (!State || !CollectionS)
918e5dd7070Spatrick     return State;
919e5dd7070Spatrick 
920e5dd7070Spatrick   const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
921e5dd7070Spatrick   if (!CountS) {
922e5dd7070Spatrick     const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
923e5dd7070Spatrick     if (!KnownNonEmpty)
924e5dd7070Spatrick       return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
925e5dd7070Spatrick     return (Assumption == *KnownNonEmpty) ? State : nullptr;
926e5dd7070Spatrick   }
927e5dd7070Spatrick 
928e5dd7070Spatrick   SValBuilder &SvalBuilder = C.getSValBuilder();
929e5dd7070Spatrick   SVal CountGreaterThanZeroVal =
930e5dd7070Spatrick     SvalBuilder.evalBinOp(State, BO_GT,
931e5dd7070Spatrick                           nonloc::SymbolVal(*CountS),
932e5dd7070Spatrick                           SvalBuilder.makeIntVal(0, (*CountS)->getType()),
933e5dd7070Spatrick                           SvalBuilder.getConditionType());
934*12c85518Srobert   std::optional<DefinedSVal> CountGreaterThanZero =
935e5dd7070Spatrick       CountGreaterThanZeroVal.getAs<DefinedSVal>();
936e5dd7070Spatrick   if (!CountGreaterThanZero) {
937e5dd7070Spatrick     // The SValBuilder cannot construct a valid SVal for this condition.
938e5dd7070Spatrick     // This means we cannot properly reason about it.
939e5dd7070Spatrick     return State;
940e5dd7070Spatrick   }
941e5dd7070Spatrick 
942e5dd7070Spatrick   return State->assume(*CountGreaterThanZero, Assumption);
943e5dd7070Spatrick }
944e5dd7070Spatrick 
945e5dd7070Spatrick static ProgramStateRef
assumeCollectionNonEmpty(CheckerContext & C,ProgramStateRef State,const ObjCForCollectionStmt * FCS,bool Assumption)946e5dd7070Spatrick assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
947e5dd7070Spatrick                          const ObjCForCollectionStmt *FCS,
948e5dd7070Spatrick                          bool Assumption) {
949e5dd7070Spatrick   if (!State)
950e5dd7070Spatrick     return nullptr;
951e5dd7070Spatrick 
952e5dd7070Spatrick   SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol();
953e5dd7070Spatrick   return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
954e5dd7070Spatrick }
955e5dd7070Spatrick 
956e5dd7070Spatrick /// If the fist block edge is a back edge, we are reentering the loop.
alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode * N,const ObjCForCollectionStmt * FCS)957e5dd7070Spatrick static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
958e5dd7070Spatrick                                              const ObjCForCollectionStmt *FCS) {
959e5dd7070Spatrick   if (!N)
960e5dd7070Spatrick     return false;
961e5dd7070Spatrick 
962e5dd7070Spatrick   ProgramPoint P = N->getLocation();
963*12c85518Srobert   if (std::optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
964e5dd7070Spatrick     return BE->getSrc()->getLoopTarget() == FCS;
965e5dd7070Spatrick   }
966e5dd7070Spatrick 
967e5dd7070Spatrick   // Keep looking for a block edge.
968e5dd7070Spatrick   for (ExplodedNode::const_pred_iterator I = N->pred_begin(),
969e5dd7070Spatrick                                          E = N->pred_end(); I != E; ++I) {
970e5dd7070Spatrick     if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS))
971e5dd7070Spatrick       return true;
972e5dd7070Spatrick   }
973e5dd7070Spatrick 
974e5dd7070Spatrick   return false;
975e5dd7070Spatrick }
976e5dd7070Spatrick 
checkPostStmt(const ObjCForCollectionStmt * FCS,CheckerContext & C) const977e5dd7070Spatrick void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
978e5dd7070Spatrick                                     CheckerContext &C) const {
979e5dd7070Spatrick   ProgramStateRef State = C.getState();
980e5dd7070Spatrick 
981e5dd7070Spatrick   // Check if this is the branch for the end of the loop.
982a9ac8606Spatrick   if (!ExprEngine::hasMoreIteration(State, FCS, C.getLocationContext())) {
983e5dd7070Spatrick     if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
984e5dd7070Spatrick       State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
985e5dd7070Spatrick 
986e5dd7070Spatrick   // Otherwise, this is a branch that goes through the loop body.
987e5dd7070Spatrick   } else {
988e5dd7070Spatrick     State = checkCollectionNonNil(C, State, FCS);
989e5dd7070Spatrick     State = checkElementNonNil(C, State, FCS);
990e5dd7070Spatrick     State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
991e5dd7070Spatrick   }
992e5dd7070Spatrick 
993e5dd7070Spatrick   if (!State)
994e5dd7070Spatrick     C.generateSink(C.getState(), C.getPredecessor());
995e5dd7070Spatrick   else if (State != C.getState())
996e5dd7070Spatrick     C.addTransition(State);
997e5dd7070Spatrick }
998e5dd7070Spatrick 
isCollectionCountMethod(const ObjCMethodCall & M,CheckerContext & C) const999e5dd7070Spatrick bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
1000e5dd7070Spatrick                                               CheckerContext &C) const {
1001e5dd7070Spatrick   Selector S = M.getSelector();
1002e5dd7070Spatrick   // Initialize the identifiers on first use.
1003e5dd7070Spatrick   if (!CountSelectorII)
1004e5dd7070Spatrick     CountSelectorII = &C.getASTContext().Idents.get("count");
1005e5dd7070Spatrick 
1006e5dd7070Spatrick   // If the method returns collection count, record the value.
1007e5dd7070Spatrick   return S.isUnarySelector() &&
1008e5dd7070Spatrick          (S.getIdentifierInfoForSlot(0) == CountSelectorII);
1009e5dd7070Spatrick }
1010e5dd7070Spatrick 
checkPostObjCMessage(const ObjCMethodCall & M,CheckerContext & C) const1011e5dd7070Spatrick void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1012e5dd7070Spatrick                                            CheckerContext &C) const {
1013e5dd7070Spatrick   if (!M.isInstanceMessage())
1014e5dd7070Spatrick     return;
1015e5dd7070Spatrick 
1016e5dd7070Spatrick   const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1017e5dd7070Spatrick   if (!ClassID)
1018e5dd7070Spatrick     return;
1019e5dd7070Spatrick 
1020e5dd7070Spatrick   FoundationClass Class = findKnownClass(ClassID);
1021e5dd7070Spatrick   if (Class != FC_NSDictionary &&
1022e5dd7070Spatrick       Class != FC_NSArray &&
1023e5dd7070Spatrick       Class != FC_NSSet &&
1024e5dd7070Spatrick       Class != FC_NSOrderedSet)
1025e5dd7070Spatrick     return;
1026e5dd7070Spatrick 
1027e5dd7070Spatrick   SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1028e5dd7070Spatrick   if (!ContainerS)
1029e5dd7070Spatrick     return;
1030e5dd7070Spatrick 
1031e5dd7070Spatrick   // If we are processing a call to "count", get the symbolic value returned by
1032e5dd7070Spatrick   // a call to "count" and add it to the map.
1033e5dd7070Spatrick   if (!isCollectionCountMethod(M, C))
1034e5dd7070Spatrick     return;
1035e5dd7070Spatrick 
1036e5dd7070Spatrick   const Expr *MsgExpr = M.getOriginExpr();
1037e5dd7070Spatrick   SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
1038e5dd7070Spatrick   if (CountS) {
1039e5dd7070Spatrick     ProgramStateRef State = C.getState();
1040e5dd7070Spatrick 
1041e5dd7070Spatrick     C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1042e5dd7070Spatrick     State = State->set<ContainerCountMap>(ContainerS, CountS);
1043e5dd7070Spatrick 
1044e5dd7070Spatrick     if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1045e5dd7070Spatrick       State = State->remove<ContainerNonEmptyMap>(ContainerS);
1046e5dd7070Spatrick       State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
1047e5dd7070Spatrick     }
1048e5dd7070Spatrick 
1049e5dd7070Spatrick     C.addTransition(State);
1050e5dd7070Spatrick   }
1051e5dd7070Spatrick }
1052e5dd7070Spatrick 
getMethodReceiverIfKnownImmutable(const CallEvent * Call)1053e5dd7070Spatrick static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1054e5dd7070Spatrick   const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1055e5dd7070Spatrick   if (!Message)
1056e5dd7070Spatrick     return nullptr;
1057e5dd7070Spatrick 
1058e5dd7070Spatrick   const ObjCMethodDecl *MD = Message->getDecl();
1059e5dd7070Spatrick   if (!MD)
1060e5dd7070Spatrick     return nullptr;
1061e5dd7070Spatrick 
1062e5dd7070Spatrick   const ObjCInterfaceDecl *StaticClass;
1063e5dd7070Spatrick   if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
1064e5dd7070Spatrick     // We can't find out where the method was declared without doing more work.
1065e5dd7070Spatrick     // Instead, see if the receiver is statically typed as a known immutable
1066e5dd7070Spatrick     // collection.
1067e5dd7070Spatrick     StaticClass = Message->getOriginExpr()->getReceiverInterface();
1068e5dd7070Spatrick   } else {
1069e5dd7070Spatrick     StaticClass = MD->getClassInterface();
1070e5dd7070Spatrick   }
1071e5dd7070Spatrick 
1072e5dd7070Spatrick   if (!StaticClass)
1073e5dd7070Spatrick     return nullptr;
1074e5dd7070Spatrick 
1075e5dd7070Spatrick   switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
1076e5dd7070Spatrick   case FC_None:
1077e5dd7070Spatrick     return nullptr;
1078e5dd7070Spatrick   case FC_NSArray:
1079e5dd7070Spatrick   case FC_NSDictionary:
1080e5dd7070Spatrick   case FC_NSEnumerator:
1081e5dd7070Spatrick   case FC_NSNull:
1082e5dd7070Spatrick   case FC_NSOrderedSet:
1083e5dd7070Spatrick   case FC_NSSet:
1084e5dd7070Spatrick   case FC_NSString:
1085e5dd7070Spatrick     break;
1086e5dd7070Spatrick   }
1087e5dd7070Spatrick 
1088e5dd7070Spatrick   return Message->getReceiverSVal().getAsSymbol();
1089e5dd7070Spatrick }
1090e5dd7070Spatrick 
1091e5dd7070Spatrick ProgramStateRef
checkPointerEscape(ProgramStateRef State,const InvalidatedSymbols & Escaped,const CallEvent * Call,PointerEscapeKind Kind) const1092e5dd7070Spatrick ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1093e5dd7070Spatrick                                     const InvalidatedSymbols &Escaped,
1094e5dd7070Spatrick                                     const CallEvent *Call,
1095e5dd7070Spatrick                                     PointerEscapeKind Kind) const {
1096e5dd7070Spatrick   SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1097e5dd7070Spatrick 
1098*12c85518Srobert   // Remove the invalidated symbols from the collection count map.
1099e5dd7070Spatrick   for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1100e5dd7070Spatrick        E = Escaped.end();
1101e5dd7070Spatrick        I != E; ++I) {
1102e5dd7070Spatrick     SymbolRef Sym = *I;
1103e5dd7070Spatrick 
1104e5dd7070Spatrick     // Don't invalidate this symbol's count if we know the method being called
1105e5dd7070Spatrick     // is declared on an immutable class. This isn't completely correct if the
1106e5dd7070Spatrick     // receiver is also passed as an argument, but in most uses of NSArray,
1107e5dd7070Spatrick     // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1108e5dd7070Spatrick     if (Sym == ImmutableReceiver)
1109e5dd7070Spatrick       continue;
1110e5dd7070Spatrick 
1111e5dd7070Spatrick     // The symbol escaped. Pessimistically, assume that the count could have
1112e5dd7070Spatrick     // changed.
1113e5dd7070Spatrick     State = State->remove<ContainerCountMap>(Sym);
1114e5dd7070Spatrick     State = State->remove<ContainerNonEmptyMap>(Sym);
1115e5dd7070Spatrick   }
1116e5dd7070Spatrick   return State;
1117e5dd7070Spatrick }
1118e5dd7070Spatrick 
checkDeadSymbols(SymbolReaper & SymReaper,CheckerContext & C) const1119e5dd7070Spatrick void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1120e5dd7070Spatrick                                        CheckerContext &C) const {
1121e5dd7070Spatrick   ProgramStateRef State = C.getState();
1122e5dd7070Spatrick 
1123e5dd7070Spatrick   // Remove the dead symbols from the collection count map.
1124e5dd7070Spatrick   ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1125e5dd7070Spatrick   for (ContainerCountMapTy::iterator I = Tracked.begin(),
1126e5dd7070Spatrick                                      E = Tracked.end(); I != E; ++I) {
1127e5dd7070Spatrick     SymbolRef Sym = I->first;
1128e5dd7070Spatrick     if (SymReaper.isDead(Sym)) {
1129e5dd7070Spatrick       State = State->remove<ContainerCountMap>(Sym);
1130e5dd7070Spatrick       State = State->remove<ContainerNonEmptyMap>(Sym);
1131e5dd7070Spatrick     }
1132e5dd7070Spatrick   }
1133e5dd7070Spatrick 
1134e5dd7070Spatrick   C.addTransition(State);
1135e5dd7070Spatrick }
1136e5dd7070Spatrick 
1137e5dd7070Spatrick namespace {
1138e5dd7070Spatrick /// \class ObjCNonNilReturnValueChecker
1139e5dd7070Spatrick /// The checker restricts the return values of APIs known to
1140e5dd7070Spatrick /// never (or almost never) return 'nil'.
1141e5dd7070Spatrick class ObjCNonNilReturnValueChecker
1142e5dd7070Spatrick   : public Checker<check::PostObjCMessage,
1143e5dd7070Spatrick                    check::PostStmt<ObjCArrayLiteral>,
1144e5dd7070Spatrick                    check::PostStmt<ObjCDictionaryLiteral>,
1145e5dd7070Spatrick                    check::PostStmt<ObjCBoxedExpr> > {
1146e5dd7070Spatrick     mutable bool Initialized;
1147e5dd7070Spatrick     mutable Selector ObjectAtIndex;
1148e5dd7070Spatrick     mutable Selector ObjectAtIndexedSubscript;
1149e5dd7070Spatrick     mutable Selector NullSelector;
1150e5dd7070Spatrick 
1151e5dd7070Spatrick public:
ObjCNonNilReturnValueChecker()1152e5dd7070Spatrick   ObjCNonNilReturnValueChecker() : Initialized(false) {}
1153e5dd7070Spatrick 
1154e5dd7070Spatrick   ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1155e5dd7070Spatrick                                       ProgramStateRef State,
1156e5dd7070Spatrick                                       CheckerContext &C) const;
assumeExprIsNonNull(const Expr * E,CheckerContext & C) const1157e5dd7070Spatrick   void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1158e5dd7070Spatrick     C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
1159e5dd7070Spatrick   }
1160e5dd7070Spatrick 
checkPostStmt(const ObjCArrayLiteral * E,CheckerContext & C) const1161e5dd7070Spatrick   void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1162e5dd7070Spatrick     assumeExprIsNonNull(E, C);
1163e5dd7070Spatrick   }
checkPostStmt(const ObjCDictionaryLiteral * E,CheckerContext & C) const1164e5dd7070Spatrick   void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1165e5dd7070Spatrick     assumeExprIsNonNull(E, C);
1166e5dd7070Spatrick   }
checkPostStmt(const ObjCBoxedExpr * E,CheckerContext & C) const1167e5dd7070Spatrick   void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1168e5dd7070Spatrick     assumeExprIsNonNull(E, C);
1169e5dd7070Spatrick   }
1170e5dd7070Spatrick 
1171e5dd7070Spatrick   void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1172e5dd7070Spatrick };
1173e5dd7070Spatrick } // end anonymous namespace
1174e5dd7070Spatrick 
1175e5dd7070Spatrick ProgramStateRef
assumeExprIsNonNull(const Expr * NonNullExpr,ProgramStateRef State,CheckerContext & C) const1176e5dd7070Spatrick ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1177e5dd7070Spatrick                                                   ProgramStateRef State,
1178e5dd7070Spatrick                                                   CheckerContext &C) const {
1179e5dd7070Spatrick   SVal Val = C.getSVal(NonNullExpr);
1180*12c85518Srobert   if (std::optional<DefinedOrUnknownSVal> DV =
1181*12c85518Srobert           Val.getAs<DefinedOrUnknownSVal>())
1182e5dd7070Spatrick     return State->assume(*DV, true);
1183e5dd7070Spatrick   return State;
1184e5dd7070Spatrick }
1185e5dd7070Spatrick 
checkPostObjCMessage(const ObjCMethodCall & M,CheckerContext & C) const1186e5dd7070Spatrick void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1187e5dd7070Spatrick                                                         CheckerContext &C)
1188e5dd7070Spatrick                                                         const {
1189e5dd7070Spatrick   ProgramStateRef State = C.getState();
1190e5dd7070Spatrick 
1191e5dd7070Spatrick   if (!Initialized) {
1192e5dd7070Spatrick     ASTContext &Ctx = C.getASTContext();
1193e5dd7070Spatrick     ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
1194e5dd7070Spatrick     ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
1195e5dd7070Spatrick     NullSelector = GetNullarySelector("null", Ctx);
1196e5dd7070Spatrick   }
1197e5dd7070Spatrick 
1198e5dd7070Spatrick   // Check the receiver type.
1199e5dd7070Spatrick   if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1200e5dd7070Spatrick 
1201e5dd7070Spatrick     // Assume that object returned from '[self init]' or '[super init]' is not
1202e5dd7070Spatrick     // 'nil' if we are processing an inlined function/method.
1203e5dd7070Spatrick     //
1204e5dd7070Spatrick     // A defensive callee will (and should) check if the object returned by
1205e5dd7070Spatrick     // '[super init]' is 'nil' before doing it's own initialization. However,
1206e5dd7070Spatrick     // since 'nil' is rarely returned in practice, we should not warn when the
1207e5dd7070Spatrick     // caller to the defensive constructor uses the object in contexts where
1208e5dd7070Spatrick     // 'nil' is not accepted.
1209e5dd7070Spatrick     if (!C.inTopFrame() && M.getDecl() &&
1210e5dd7070Spatrick         M.getDecl()->getMethodFamily() == OMF_init &&
1211e5dd7070Spatrick         M.isReceiverSelfOrSuper()) {
1212e5dd7070Spatrick       State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1213e5dd7070Spatrick     }
1214e5dd7070Spatrick 
1215e5dd7070Spatrick     FoundationClass Cl = findKnownClass(Interface);
1216e5dd7070Spatrick 
1217e5dd7070Spatrick     // Objects returned from
1218e5dd7070Spatrick     // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1219e5dd7070Spatrick     // are never 'nil'.
1220e5dd7070Spatrick     if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
1221e5dd7070Spatrick       Selector Sel = M.getSelector();
1222e5dd7070Spatrick       if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1223e5dd7070Spatrick         // Go ahead and assume the value is non-nil.
1224e5dd7070Spatrick         State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1225e5dd7070Spatrick       }
1226e5dd7070Spatrick     }
1227e5dd7070Spatrick 
1228e5dd7070Spatrick     // Objects returned from [NSNull null] are not nil.
1229e5dd7070Spatrick     if (Cl == FC_NSNull) {
1230e5dd7070Spatrick       if (M.getSelector() == NullSelector) {
1231e5dd7070Spatrick         // Go ahead and assume the value is non-nil.
1232e5dd7070Spatrick         State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1233e5dd7070Spatrick       }
1234e5dd7070Spatrick     }
1235e5dd7070Spatrick   }
1236e5dd7070Spatrick   C.addTransition(State);
1237e5dd7070Spatrick }
1238e5dd7070Spatrick 
1239e5dd7070Spatrick //===----------------------------------------------------------------------===//
1240e5dd7070Spatrick // Check registration.
1241e5dd7070Spatrick //===----------------------------------------------------------------------===//
1242e5dd7070Spatrick 
registerNilArgChecker(CheckerManager & mgr)1243e5dd7070Spatrick void ento::registerNilArgChecker(CheckerManager &mgr) {
1244e5dd7070Spatrick   mgr.registerChecker<NilArgChecker>();
1245e5dd7070Spatrick }
1246e5dd7070Spatrick 
shouldRegisterNilArgChecker(const CheckerManager & mgr)1247ec727ea7Spatrick bool ento::shouldRegisterNilArgChecker(const CheckerManager &mgr) {
1248e5dd7070Spatrick   return true;
1249e5dd7070Spatrick }
1250e5dd7070Spatrick 
registerCFNumberChecker(CheckerManager & mgr)1251e5dd7070Spatrick void ento::registerCFNumberChecker(CheckerManager &mgr) {
1252e5dd7070Spatrick   mgr.registerChecker<CFNumberChecker>();
1253e5dd7070Spatrick }
1254e5dd7070Spatrick 
shouldRegisterCFNumberChecker(const CheckerManager & mgr)1255ec727ea7Spatrick bool ento::shouldRegisterCFNumberChecker(const CheckerManager &mgr) {
1256e5dd7070Spatrick   return true;
1257e5dd7070Spatrick }
1258e5dd7070Spatrick 
registerCFRetainReleaseChecker(CheckerManager & mgr)1259e5dd7070Spatrick void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1260e5dd7070Spatrick   mgr.registerChecker<CFRetainReleaseChecker>();
1261e5dd7070Spatrick }
1262e5dd7070Spatrick 
shouldRegisterCFRetainReleaseChecker(const CheckerManager & mgr)1263ec727ea7Spatrick bool ento::shouldRegisterCFRetainReleaseChecker(const CheckerManager &mgr) {
1264e5dd7070Spatrick   return true;
1265e5dd7070Spatrick }
1266e5dd7070Spatrick 
registerClassReleaseChecker(CheckerManager & mgr)1267e5dd7070Spatrick void ento::registerClassReleaseChecker(CheckerManager &mgr) {
1268e5dd7070Spatrick   mgr.registerChecker<ClassReleaseChecker>();
1269e5dd7070Spatrick }
1270e5dd7070Spatrick 
shouldRegisterClassReleaseChecker(const CheckerManager & mgr)1271ec727ea7Spatrick bool ento::shouldRegisterClassReleaseChecker(const CheckerManager &mgr) {
1272e5dd7070Spatrick   return true;
1273e5dd7070Spatrick }
1274e5dd7070Spatrick 
registerVariadicMethodTypeChecker(CheckerManager & mgr)1275e5dd7070Spatrick void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1276e5dd7070Spatrick   mgr.registerChecker<VariadicMethodTypeChecker>();
1277e5dd7070Spatrick }
1278e5dd7070Spatrick 
shouldRegisterVariadicMethodTypeChecker(const CheckerManager & mgr)1279ec727ea7Spatrick bool ento::shouldRegisterVariadicMethodTypeChecker(const CheckerManager &mgr) {
1280e5dd7070Spatrick   return true;
1281e5dd7070Spatrick }
1282e5dd7070Spatrick 
registerObjCLoopChecker(CheckerManager & mgr)1283e5dd7070Spatrick void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1284e5dd7070Spatrick   mgr.registerChecker<ObjCLoopChecker>();
1285e5dd7070Spatrick }
1286e5dd7070Spatrick 
shouldRegisterObjCLoopChecker(const CheckerManager & mgr)1287ec727ea7Spatrick bool ento::shouldRegisterObjCLoopChecker(const CheckerManager &mgr) {
1288e5dd7070Spatrick   return true;
1289e5dd7070Spatrick }
1290e5dd7070Spatrick 
registerObjCNonNilReturnValueChecker(CheckerManager & mgr)1291e5dd7070Spatrick void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1292e5dd7070Spatrick   mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1293e5dd7070Spatrick }
1294e5dd7070Spatrick 
shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager & mgr)1295ec727ea7Spatrick bool ento::shouldRegisterObjCNonNilReturnValueChecker(const CheckerManager &mgr) {
1296e5dd7070Spatrick   return true;
1297e5dd7070Spatrick }
1298