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