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