blob: fb43964a89cefb5d0c541e7874dfb6989568f6a1 [file] [log] [blame]
Ted Kremenekc0414922008-03-27 07:25:52 +00001//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines BasicObjCFoundationChecks, a class that encapsulates
11// a set of simple checks to run on Objective-C code using Apple's Foundation
12// classes.
13//
14//===----------------------------------------------------------------------===//
15
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +000016#include "ClangSACheckers.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000017#include "clang/AST/ASTContext.h"
18#include "clang/AST/DeclObjC.h"
19#include "clang/AST/Expr.h"
20#include "clang/AST/ExprObjC.h"
21#include "clang/AST/StmtObjC.h"
Ted Kremenek6fa1dae2011-03-17 04:01:35 +000022#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000023#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000024#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +000025#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Jordan Rose4f7df9b2012-07-26 21:39:41 +000026#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +000027#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000028#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000029#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000030#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000031#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
Benjamin Kramer49038022012-02-04 13:45:25 +000032#include "llvm/ADT/SmallString.h"
Jordan Rose3ba8ae32012-06-11 16:40:37 +000033#include "llvm/ADT/StringMap.h"
Benjamin Kramer444a1302012-12-01 17:12:56 +000034#include "llvm/Support/raw_ostream.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000035
Ted Kremenekc0414922008-03-27 07:25:52 +000036using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000037using namespace ento;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000038
Ted Kremenekbd2c8002010-10-20 23:38:56 +000039namespace {
40class APIMisuse : public BugType {
41public:
42 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
43};
44} // end anonymous namespace
45
46//===----------------------------------------------------------------------===//
47// Utility functions.
48//===----------------------------------------------------------------------===//
49
Jordan Rose547060b2012-07-02 19:28:04 +000050static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
Anders Carlsson3c50aea2011-03-08 20:05:26 +000051 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
Jordan Rose547060b2012-07-02 19:28:04 +000052 return ID->getIdentifier()->getName();
53 return StringRef();
Ted Kremenek276278e2008-03-27 22:05:32 +000054}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000055
Jordan Rose3ba8ae32012-06-11 16:40:37 +000056enum FoundationClass {
57 FC_None,
58 FC_NSArray,
59 FC_NSDictionary,
60 FC_NSEnumerator,
61 FC_NSOrderedSet,
62 FC_NSSet,
63 FC_NSString
64};
Anders Carlsson3c50aea2011-03-08 20:05:26 +000065
Jordan Rose3ba8ae32012-06-11 16:40:37 +000066static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
67 static llvm::StringMap<FoundationClass> Classes;
68 if (Classes.empty()) {
69 Classes["NSArray"] = FC_NSArray;
70 Classes["NSDictionary"] = FC_NSDictionary;
71 Classes["NSEnumerator"] = FC_NSEnumerator;
72 Classes["NSOrderedSet"] = FC_NSOrderedSet;
73 Classes["NSSet"] = FC_NSSet;
74 Classes["NSString"] = FC_NSString;
75 }
Anders Carlsson3c50aea2011-03-08 20:05:26 +000076
Jordan Rose3ba8ae32012-06-11 16:40:37 +000077 // FIXME: Should we cache this at all?
78 FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
79 if (result == FC_None)
80 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
81 return findKnownClass(Super);
82
83 return result;
Ted Kremenekc0414922008-03-27 07:25:52 +000084}
85
86//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +000087// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenekc0414922008-03-27 07:25:52 +000088//===----------------------------------------------------------------------===//
89
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000090namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000091 class NilArgChecker : public Checker<check::PreObjCMessage> {
Dylan Noblesmithe2778992012-02-05 02:12:40 +000092 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000093
Anna Zaks130df4b2013-03-23 00:39:21 +000094 void WarnIfNilArg(CheckerContext &C,
95 const ObjCMethodCall &msg, unsigned Arg,
96 FoundationClass Class,
97 bool CanBeSubscript = false) const;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000098
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000099 public:
Jordan Rose547060b2012-07-02 19:28:04 +0000100 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
Benjamin Kramer2fc373e2010-10-22 16:33:16 +0000101 };
102}
Mike Stump11289f42009-09-09 15:08:12 +0000103
Anna Zaks130df4b2013-03-23 00:39:21 +0000104void NilArgChecker::WarnIfNilArg(CheckerContext &C,
105 const ObjCMethodCall &msg,
106 unsigned int Arg,
107 FoundationClass Class,
108 bool CanBeSubscript) const {
109 // Check if the argument is nil.
110 ProgramStateRef State = C.getState();
111 if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
112 return;
113
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000114 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000115 BT.reset(new APIMisuse("nil argument"));
Anna Zaks130df4b2013-03-23 00:39:21 +0000116
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000117 if (ExplodedNode *N = C.generateSink()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000118 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000119 llvm::raw_svector_ostream os(sbuf);
Anna Zaks130df4b2013-03-23 00:39:21 +0000120
121 if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
122
123 if (Class == FC_NSArray) {
124 os << "Array element cannot be nil";
125 } else if (Class == FC_NSDictionary) {
126 if (Arg == 0)
127 os << "Dictionary object cannot be nil";
128 else {
129 assert(Arg == 1);
130 os << "Dictionary key cannot be nil";
131 }
132 } else
133 llvm_unreachable("Missing foundation class for the subscript expr");
134
135 } else {
136 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
137 << msg.getSelector().getAsString() << "' cannot be nil";
138 }
Mike Stump11289f42009-09-09 15:08:12 +0000139
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000140 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000141 R->addRange(msg.getArgSourceRange(Arg));
Jordan Rosee10d5a72012-11-02 01:53:40 +0000142 C.emitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +0000143 }
Ted Kremenek276278e2008-03-27 22:05:32 +0000144}
145
Jordan Rose547060b2012-07-02 19:28:04 +0000146void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000147 CheckerContext &C) const {
Anders Carlsson3c50aea2011-03-08 20:05:26 +0000148 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
149 if (!ID)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000150 return;
Anna Zaks6457ad22013-03-18 20:46:56 +0000151
152 FoundationClass Class = findKnownClass(ID);
153
154 static const unsigned InvalidArgIndex = UINT_MAX;
155 unsigned Arg = InvalidArgIndex;
Anna Zaks130df4b2013-03-23 00:39:21 +0000156 bool CanBeSubscript = false;
157
Anna Zaks6457ad22013-03-18 20:46:56 +0000158 if (Class == FC_NSString) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000159 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000160
161 if (S.isUnarySelector())
162 return;
163
164 // FIXME: This is going to be really slow doing these checks with
165 // lexical comparisons.
166
167 std::string NameStr = S.getAsString();
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000168 StringRef Name(NameStr);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000169 assert(!Name.empty());
170
171 // FIXME: Checking for initWithFormat: will not work in most cases
172 // yet because [NSString alloc] returns id, not NSString*. We will
173 // need support for tracking expected-type information in the analyzer
174 // to find these errors.
175 if (Name == "caseInsensitiveCompare:" ||
176 Name == "compare:" ||
177 Name == "compare:options:" ||
178 Name == "compare:options:range:" ||
179 Name == "compare:options:range:locale:" ||
180 Name == "componentsSeparatedByCharactersInSet:" ||
181 Name == "initWithFormat:") {
Anna Zaks6457ad22013-03-18 20:46:56 +0000182 Arg = 0;
183 }
184 } else if (Class == FC_NSArray) {
185 Selector S = msg.getSelector();
186
187 if (S.isUnarySelector())
188 return;
189
190 if (S.getNameForSlot(0).equals("addObject")) {
191 Arg = 0;
192 } else if (S.getNameForSlot(0).equals("insertObject") &&
193 S.getNameForSlot(1).equals("atIndex")) {
194 Arg = 0;
195 } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") &&
196 S.getNameForSlot(1).equals("withObject")) {
197 Arg = 1;
198 } else if (S.getNameForSlot(0).equals("setObject") &&
199 S.getNameForSlot(1).equals("atIndexedSubscript")) {
200 Arg = 0;
Anna Zaks130df4b2013-03-23 00:39:21 +0000201 CanBeSubscript = true;
Anna Zaks6457ad22013-03-18 20:46:56 +0000202 } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
203 Arg = 0;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000204 }
Anna Zaks130df4b2013-03-23 00:39:21 +0000205 } else if (Class == FC_NSDictionary) {
206 Selector S = msg.getSelector();
207
208 if (S.isUnarySelector())
209 return;
210
211 if (S.getNameForSlot(0).equals("dictionaryWithObject") &&
212 S.getNameForSlot(1).equals("forKey")) {
213 Arg = 0;
214 WarnIfNilArg(C, msg, /* Arg */1, Class);
215 } else if (S.getNameForSlot(0).equals("setObject") &&
216 S.getNameForSlot(1).equals("forKey")) {
217 Arg = 0;
218 WarnIfNilArg(C, msg, /* Arg */1, Class);
219 } else if (S.getNameForSlot(0).equals("setObject") &&
220 S.getNameForSlot(1).equals("forKeyedSubscript")) {
221 CanBeSubscript = true;
222 Arg = 0;
223 WarnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript);
224 } else if (S.getNameForSlot(0).equals("removeObjectForKey")) {
225 Arg = 0;
226 }
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000227 }
Anna Zaks6457ad22013-03-18 20:46:56 +0000228
Anna Zaks130df4b2013-03-23 00:39:21 +0000229
Anna Zaks6457ad22013-03-18 20:46:56 +0000230 // If argument is '0', report a warning.
Anna Zaks130df4b2013-03-23 00:39:21 +0000231 if ((Arg != InvalidArgIndex))
232 WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
Anna Zaks6457ad22013-03-18 20:46:56 +0000233
Ted Kremenekc0414922008-03-27 07:25:52 +0000234}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000235
236//===----------------------------------------------------------------------===//
237// Error reporting.
238//===----------------------------------------------------------------------===//
239
240namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000241class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000242 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000243 mutable IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000244public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000245 CFNumberCreateChecker() : II(0) {}
246
247 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
248
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000249private:
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000250 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000251 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000252};
253} // end anonymous namespace
254
255enum CFNumberType {
256 kCFNumberSInt8Type = 1,
257 kCFNumberSInt16Type = 2,
258 kCFNumberSInt32Type = 3,
259 kCFNumberSInt64Type = 4,
260 kCFNumberFloat32Type = 5,
261 kCFNumberFloat64Type = 6,
262 kCFNumberCharType = 7,
263 kCFNumberShortType = 8,
264 kCFNumberIntType = 9,
265 kCFNumberLongType = 10,
266 kCFNumberLongLongType = 11,
267 kCFNumberFloatType = 12,
268 kCFNumberDoubleType = 13,
269 kCFNumberCFIndexType = 14,
270 kCFNumberNSIntegerType = 15,
271 kCFNumberCGFloatType = 16
272};
273
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000274static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000275 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000276
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000277 if (i < kCFNumberCharType)
278 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000279
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000280 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000281
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000282 switch (i) {
283 case kCFNumberCharType: T = Ctx.CharTy; break;
284 case kCFNumberShortType: T = Ctx.ShortTy; break;
285 case kCFNumberIntType: T = Ctx.IntTy; break;
286 case kCFNumberLongType: T = Ctx.LongTy; break;
287 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
288 case kCFNumberFloatType: T = Ctx.FloatTy; break;
289 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
290 case kCFNumberCFIndexType:
291 case kCFNumberNSIntegerType:
292 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000293 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000294 default:
David Blaikie7a30dc52013-02-21 01:47:18 +0000295 return None;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000296 }
Mike Stump11289f42009-09-09 15:08:12 +0000297
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000298 return Ctx.getTypeSize(T);
299}
300
301#if 0
302static const char* GetCFNumberTypeStr(uint64_t i) {
303 static const char* Names[] = {
304 "kCFNumberSInt8Type",
305 "kCFNumberSInt16Type",
306 "kCFNumberSInt32Type",
307 "kCFNumberSInt64Type",
308 "kCFNumberFloat32Type",
309 "kCFNumberFloat64Type",
310 "kCFNumberCharType",
311 "kCFNumberShortType",
312 "kCFNumberIntType",
313 "kCFNumberLongType",
314 "kCFNumberLongLongType",
315 "kCFNumberFloatType",
316 "kCFNumberDoubleType",
317 "kCFNumberCFIndexType",
318 "kCFNumberNSIntegerType",
319 "kCFNumberCGFloatType"
320 };
Mike Stump11289f42009-09-09 15:08:12 +0000321
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000322 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
323}
324#endif
325
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000326void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
327 CheckerContext &C) const {
Ted Kremenek49b1e382012-01-26 21:29:00 +0000328 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000329 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000330 if (!FD)
331 return;
332
333 ASTContext &Ctx = C.getASTContext();
334 if (!II)
335 II = &Ctx.Idents.get("CFNumberCreate");
336
337 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
338 return;
Mike Stump11289f42009-09-09 15:08:12 +0000339
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000340 // Get the value of the "theType" argument.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000341 const LocationContext *LCtx = C.getLocationContext();
342 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000343
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000344 // FIXME: We really should allow ranges of valid theType values, and
345 // bifurcate the state appropriately.
David Blaikie05785d12013-02-20 22:23:23 +0000346 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000347 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000348 return;
Mike Stump11289f42009-09-09 15:08:12 +0000349
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000350 uint64_t NumberKind = V->getValue().getLimitedValue();
David Blaikie05785d12013-02-20 22:23:23 +0000351 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000352
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000353 // FIXME: In some cases we can emit an error.
David Blaikiee359f3c2013-02-20 22:23:03 +0000354 if (!OptTargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000355 return;
Mike Stump11289f42009-09-09 15:08:12 +0000356
David Blaikiee359f3c2013-02-20 22:23:03 +0000357 uint64_t TargetSize = *OptTargetSize;
358
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000359 // Look at the value of the integer being passed by reference. Essentially
360 // we want to catch cases where the value passed in is not equal to the
361 // size of the type being created.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000362 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000363
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000364 // FIXME: Eventually we should handle arbitrary locations. We can do this
365 // by having an enhanced memory model that does low-level typing.
David Blaikie05785d12013-02-20 22:23:23 +0000366 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000367 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000368 return;
Mike Stump11289f42009-09-09 15:08:12 +0000369
Ted Kremenek8df44b262011-08-12 20:02:48 +0000370 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000371 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000372 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000373
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000374 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000375
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000376 // FIXME: If the pointee isn't an integer type, should we flag a warning?
377 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000378
379 if (!T->isIntegerType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000380 return;
Mike Stump11289f42009-09-09 15:08:12 +0000381
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000382 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000383
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000384 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000385 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000386 return;
Mike Stump11289f42009-09-09 15:08:12 +0000387
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000388 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
389 // otherwise generate a regular node.
390 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000391 // FIXME: We can actually create an abstract "CFNumber" object that has
392 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000393 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000394 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaksda4c8d62011-10-26 21:06:34 +0000395 : C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000396 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000397 llvm::raw_svector_ostream os(sbuf);
398
399 os << (SourceSize == 8 ? "An " : "A ")
400 << SourceSize << " bit integer is used to initialize a CFNumber "
401 "object that represents "
402 << (TargetSize == 8 ? "an " : "a ")
403 << TargetSize << " bit integer. ";
404
405 if (SourceSize < TargetSize)
406 os << (TargetSize - SourceSize)
407 << " bits of the CFNumber value will be garbage." ;
408 else
409 os << (SourceSize - TargetSize)
410 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000411
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000412 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000413 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000414
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000415 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000416 report->addRange(CE->getArg(2)->getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000417 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000418 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000419}
420
Ted Kremenek1f352db2008-07-22 16:21:24 +0000421//===----------------------------------------------------------------------===//
Jordan Rose721567a2012-11-07 17:12:37 +0000422// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000423//===----------------------------------------------------------------------===//
424
425namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000426class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000427 mutable OwningPtr<APIMisuse> BT;
Jordan Rose721567a2012-11-07 17:12:37 +0000428 mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
Ted Kremenekc057f412009-07-14 00:43:42 +0000429public:
Jordan Rose721567a2012-11-07 17:12:37 +0000430 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000431 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenekc057f412009-07-14 00:43:42 +0000432};
433} // end anonymous namespace
434
435
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000436void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
437 CheckerContext &C) const {
Ted Kremenekc057f412009-07-14 00:43:42 +0000438 // If the CallExpr doesn't have exactly 1 argument just give up checking.
439 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000440 return;
Mike Stump11289f42009-09-09 15:08:12 +0000441
Ted Kremenek49b1e382012-01-26 21:29:00 +0000442 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000443 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000444 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000445 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000446
447 if (!BT) {
448 ASTContext &Ctx = C.getASTContext();
449 Retain = &Ctx.Idents.get("CFRetain");
450 Release = &Ctx.Idents.get("CFRelease");
Jordan Rose721567a2012-11-07 17:12:37 +0000451 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
452 BT.reset(
453 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000454 }
Mike Stump11289f42009-09-09 15:08:12 +0000455
Jordan Rose721567a2012-11-07 17:12:37 +0000456 // Check if we called CFRetain/CFRelease/CFMakeCollectable.
Mike Stump11289f42009-09-09 15:08:12 +0000457 const IdentifierInfo *FuncII = FD->getIdentifier();
Jordan Rose721567a2012-11-07 17:12:37 +0000458 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
Jordy Rose40c5c242010-07-06 02:34:42 +0000459 return;
Mike Stump11289f42009-09-09 15:08:12 +0000460
Jordy Rose40c5c242010-07-06 02:34:42 +0000461 // FIXME: The rest of this just checks that the argument is non-null.
Anna Zaksef893392013-03-09 03:23:14 +0000462 // It should probably be refactored and combined with NonNullParamChecker.
Jordy Rose40c5c242010-07-06 02:34:42 +0000463
464 // Get the argument's value.
465 const Expr *Arg = CE->getArg(0);
Ted Kremenek632e3b72012-01-06 22:09:28 +0000466 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000467 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000468 if (!DefArgVal)
469 return;
470
471 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000472 SValBuilder &svalBuilder = C.getSValBuilder();
David Blaikie2fdacbc2013-02-20 05:52:05 +0000473 DefinedSVal zero =
474 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000475
476 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000477 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000478
479 // Are they equal?
Ted Kremenek49b1e382012-01-26 21:29:00 +0000480 ProgramStateRef stateTrue, stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000481 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000482
483 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000484 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000485 if (!N)
486 return;
487
Jordan Rose721567a2012-11-07 17:12:37 +0000488 const char *description;
489 if (FuncII == Retain)
490 description = "Null pointer argument in call to CFRetain";
491 else if (FuncII == Release)
492 description = "Null pointer argument in call to CFRelease";
493 else if (FuncII == MakeCollectable)
494 description = "Null pointer argument in call to CFMakeCollectable";
495 else
496 llvm_unreachable("impossible case");
Ted Kremenekc057f412009-07-14 00:43:42 +0000497
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000498 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose40c5c242010-07-06 02:34:42 +0000499 report->addRange(Arg->getSourceRange());
Jordan Rosea0f7d352012-08-28 00:50:51 +0000500 bugreporter::trackNullOrUndefValue(N, Arg, *report);
Jordan Rosee10d5a72012-11-02 01:53:40 +0000501 C.emitReport(report);
Jordy Rose40c5c242010-07-06 02:34:42 +0000502 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000503 }
504
Jordy Rose40c5c242010-07-06 02:34:42 +0000505 // From here on, we know the argument is non-null.
Anna Zaksda4c8d62011-10-26 21:06:34 +0000506 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000507}
508
509//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000510// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
511//===----------------------------------------------------------------------===//
512
513namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000514class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000515 mutable Selector releaseS;
516 mutable Selector retainS;
517 mutable Selector autoreleaseS;
518 mutable Selector drainS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000519 mutable OwningPtr<BugType> BT;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000520
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000521public:
Jordan Rose547060b2012-07-02 19:28:04 +0000522 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000523};
524}
525
Jordan Rose547060b2012-07-02 19:28:04 +0000526void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000527 CheckerContext &C) const {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000528
529 if (!BT) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000530 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
531 "instance"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000532
533 ASTContext &Ctx = C.getASTContext();
534 releaseS = GetNullarySelector("release", Ctx);
535 retainS = GetNullarySelector("retain", Ctx);
536 autoreleaseS = GetNullarySelector("autorelease", Ctx);
537 drainS = GetNullarySelector("drain", Ctx);
538 }
539
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000540 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000541 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000542 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
543 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000544
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000545 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000546 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000547 return;
548
Anna Zaksda4c8d62011-10-26 21:06:34 +0000549 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000550 SmallString<200> buf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000551 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000552
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000553 os << "The '" << S.getAsString() << "' message should be sent to instances "
554 "of class '" << Class->getName()
555 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000556
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000557 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000558 report->addRange(msg.getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000559 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000560 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000561}
562
563//===----------------------------------------------------------------------===//
Anders Carlssond91d5f12011-03-13 20:35:21 +0000564// Check for passing non-Objective-C types to variadic methods that expect
565// only Objective-C types.
566//===----------------------------------------------------------------------===//
567
568namespace {
569class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
570 mutable Selector arrayWithObjectsS;
571 mutable Selector dictionaryWithObjectsAndKeysS;
572 mutable Selector setWithObjectsS;
Jordy Rosec0230d72012-04-06 19:06:01 +0000573 mutable Selector orderedSetWithObjectsS;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000574 mutable Selector initWithObjectsS;
575 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000576 mutable OwningPtr<BugType> BT;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000577
Jordan Rose547060b2012-07-02 19:28:04 +0000578 bool isVariadicMessage(const ObjCMethodCall &msg) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000579
580public:
Jordan Rose547060b2012-07-02 19:28:04 +0000581 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000582};
583}
584
585/// isVariadicMessage - Returns whether the given message is a variadic message,
586/// where all arguments must be Objective-C types.
587bool
Jordan Rose547060b2012-07-02 19:28:04 +0000588VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
589 const ObjCMethodDecl *MD = msg.getDecl();
Ted Kremenekced5fea2011-04-12 21:47:05 +0000590
591 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlssond91d5f12011-03-13 20:35:21 +0000592 return false;
593
594 Selector S = msg.getSelector();
595
596 if (msg.isInstanceMessage()) {
597 // FIXME: Ideally we'd look at the receiver interface here, but that's not
598 // useful for init, because alloc returns 'id'. In theory, this could lead
599 // to false positives, for example if there existed a class that had an
600 // initWithObjects: implementation that does accept non-Objective-C pointer
601 // types, but the chance of that happening is pretty small compared to the
602 // gains that this analysis gives.
603 const ObjCInterfaceDecl *Class = MD->getClassInterface();
604
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000605 switch (findKnownClass(Class)) {
606 case FC_NSArray:
607 case FC_NSOrderedSet:
608 case FC_NSSet:
609 return S == initWithObjectsS;
610 case FC_NSDictionary:
611 return S == initWithObjectsAndKeysS;
612 default:
613 return false;
614 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000615 } else {
616 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
617
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000618 switch (findKnownClass(Class)) {
619 case FC_NSArray:
620 return S == arrayWithObjectsS;
621 case FC_NSOrderedSet:
622 return S == orderedSetWithObjectsS;
623 case FC_NSSet:
624 return S == setWithObjectsS;
625 case FC_NSDictionary:
626 return S == dictionaryWithObjectsAndKeysS;
627 default:
628 return false;
629 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000630 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000631}
632
Jordan Rose547060b2012-07-02 19:28:04 +0000633void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Anders Carlssond91d5f12011-03-13 20:35:21 +0000634 CheckerContext &C) const {
635 if (!BT) {
636 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
637 "Objective-C pointer types"));
638
639 ASTContext &Ctx = C.getASTContext();
640 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
641 dictionaryWithObjectsAndKeysS =
642 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
643 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
Jordy Rosec0230d72012-04-06 19:06:01 +0000644 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000645
646 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
647 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
648 }
649
650 if (!isVariadicMessage(msg))
651 return;
652
653 // We are not interested in the selector arguments since they have
654 // well-defined types, so the compiler will issue a warning for them.
655 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
656
657 // We're not interested in the last argument since it has to be nil or the
658 // compiler would have issued a warning for it elsewhere.
659 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
660
661 if (variadicArgsEnd <= variadicArgsBegin)
662 return;
663
664 // Verify that all arguments have Objective-C types.
David Blaikie05785d12013-02-20 22:23:23 +0000665 Optional<ExplodedNode*> errorNode;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000666 ProgramStateRef state = C.getState();
Ted Kremenek066b2262011-03-14 19:50:37 +0000667
Anders Carlssond91d5f12011-03-13 20:35:21 +0000668 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
Jordan Rose547060b2012-07-02 19:28:04 +0000669 QualType ArgTy = msg.getArgExpr(I)->getType();
Anders Carlssond91d5f12011-03-13 20:35:21 +0000670 if (ArgTy->isObjCObjectPointerType())
671 continue;
672
Anders Carlssond1f65f62011-04-19 01:16:46 +0000673 // Block pointers are treaded as Objective-C pointers.
674 if (ArgTy->isBlockPointerType())
675 continue;
676
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000677 // Ignore pointer constants.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000678 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000679 continue;
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000680
Ted Kremenek70727342011-03-17 04:10:25 +0000681 // Ignore pointer types annotated with 'NSObject' attribute.
682 if (C.getASTContext().isObjCNSObjectType(ArgTy))
683 continue;
684
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000685 // Ignore CF references, which can be toll-free bridged.
Ted Kremenekc85964e2011-07-16 19:50:32 +0000686 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000687 continue;
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000688
Ted Kremenek066b2262011-03-14 19:50:37 +0000689 // Generate only one error node to use for all bug reports.
Jordan Rose547060b2012-07-02 19:28:04 +0000690 if (!errorNode.hasValue())
Anna Zaksda4c8d62011-10-26 21:06:34 +0000691 errorNode = C.addTransition();
Ted Kremenek066b2262011-03-14 19:50:37 +0000692
693 if (!errorNode.getValue())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000694 continue;
695
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000696 SmallString<128> sbuf;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000697 llvm::raw_svector_ostream os(sbuf);
698
Jordan Rose547060b2012-07-02 19:28:04 +0000699 StringRef TypeName = GetReceiverInterfaceName(msg);
700 if (!TypeName.empty())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000701 os << "Argument to '" << TypeName << "' method '";
702 else
703 os << "Argument to method '";
704
705 os << msg.getSelector().getAsString()
Jordan Rose547060b2012-07-02 19:28:04 +0000706 << "' should be an Objective-C pointer type, not '";
707 ArgTy.print(os, C.getLangOpts());
708 os << "'";
Anders Carlssond91d5f12011-03-13 20:35:21 +0000709
Jordan Rose547060b2012-07-02 19:28:04 +0000710 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
Anders Carlssond91d5f12011-03-13 20:35:21 +0000711 R->addRange(msg.getArgSourceRange(I));
Jordan Rosee10d5a72012-11-02 01:53:40 +0000712 C.emitReport(R);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000713 }
714}
715
716//===----------------------------------------------------------------------===//
Jordan Roseefef7602012-06-11 16:40:41 +0000717// Improves the modeling of loops over Cocoa collections.
718//===----------------------------------------------------------------------===//
719
720namespace {
721class ObjCLoopChecker
722 : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
723
724public:
725 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
726};
727}
728
729static bool isKnownNonNilCollectionType(QualType T) {
730 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
731 if (!PT)
732 return false;
733
734 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
735 if (!ID)
736 return false;
737
738 switch (findKnownClass(ID)) {
739 case FC_NSArray:
740 case FC_NSDictionary:
741 case FC_NSEnumerator:
742 case FC_NSOrderedSet:
743 case FC_NSSet:
744 return true;
745 default:
746 return false;
747 }
748}
749
750void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
751 CheckerContext &C) const {
752 ProgramStateRef State = C.getState();
753
754 // Check if this is the branch for the end of the loop.
755 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
756 if (CollectionSentinel.isZeroConstant())
757 return;
758
759 // See if the collection is one where we /know/ the elements are non-nil.
760 const Expr *Collection = FCS->getCollection();
761 if (!isKnownNonNilCollectionType(Collection->getType()))
762 return;
763
764 // FIXME: Copied from ExprEngineObjC.
765 const Stmt *Element = FCS->getElement();
766 SVal ElementVar;
767 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
768 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
769 assert(ElemDecl->getInit() == 0);
770 ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
771 } else {
772 ElementVar = State->getSVal(Element, C.getLocationContext());
773 }
774
David Blaikie2fdacbc2013-02-20 05:52:05 +0000775 if (!ElementVar.getAs<Loc>())
Jordan Roseefef7602012-06-11 16:40:41 +0000776 return;
777
778 // Go ahead and assume the value is non-nil.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000779 SVal Val = State->getSVal(ElementVar.castAs<Loc>());
780 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
Jordan Roseefef7602012-06-11 16:40:41 +0000781 C.addTransition(State);
782}
783
Anna Zaks5a5a1752012-08-22 21:19:56 +0000784namespace {
785/// \class ObjCNonNilReturnValueChecker
Anna Zaks4818bbe2012-08-30 19:40:52 +0000786/// \brief The checker restricts the return values of APIs known to
787/// never (or almost never) return 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000788class ObjCNonNilReturnValueChecker
789 : public Checker<check::PostObjCMessage> {
790 mutable bool Initialized;
791 mutable Selector ObjectAtIndex;
792 mutable Selector ObjectAtIndexedSubscript;
Anna Zaks9159e162012-08-22 22:47:58 +0000793
Anna Zaks5a5a1752012-08-22 21:19:56 +0000794public:
Anna Zaks9159e162012-08-22 22:47:58 +0000795 ObjCNonNilReturnValueChecker() : Initialized(false) {}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000796 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
797};
798}
799
Benjamin Kramer199f8da2012-09-10 11:57:16 +0000800static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
801 ProgramStateRef State,
802 CheckerContext &C) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000803 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000804 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
Anna Zaks830c48e2012-08-30 22:55:32 +0000805 return State->assume(*DV, true);
Anna Zaksb504f442012-08-30 22:42:41 +0000806 return State;
Anna Zaks4818bbe2012-08-30 19:40:52 +0000807}
808
Anna Zaks5a5a1752012-08-22 21:19:56 +0000809void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
810 CheckerContext &C)
Anna Zaks4818bbe2012-08-30 19:40:52 +0000811 const {
Anna Zaks5a5a1752012-08-22 21:19:56 +0000812 ProgramStateRef State = C.getState();
813
814 if (!Initialized) {
815 ASTContext &Ctx = C.getASTContext();
816 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
817 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
818 }
819
820 // Check the receiver type.
821 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000822
823 // Assume that object returned from '[self init]' or '[super init]' is not
824 // 'nil' if we are processing an inlined function/method.
825 //
826 // A defensive callee will (and should) check if the object returned by
827 // '[super init]' is 'nil' before doing it's own initialization. However,
828 // since 'nil' is rarely returned in practice, we should not warn when the
829 // caller to the defensive constructor uses the object in contexts where
830 // 'nil' is not accepted.
Anna Zaks49bb6502012-11-06 04:20:54 +0000831 if (!C.inTopFrame() && M.getDecl() &&
Anna Zaks4818bbe2012-08-30 19:40:52 +0000832 M.getDecl()->getMethodFamily() == OMF_init &&
833 M.isReceiverSelfOrSuper()) {
834 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
835 }
836
837 // Objects returned from
838 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
839 // are never 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000840 FoundationClass Cl = findKnownClass(Interface);
841 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
842 Selector Sel = M.getSelector();
843 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
844 // Go ahead and assume the value is non-nil.
Anna Zaks4818bbe2012-08-30 19:40:52 +0000845 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000846 }
847 }
848 }
Anna Zaks4818bbe2012-08-30 19:40:52 +0000849 C.addTransition(State);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000850}
Jordan Roseefef7602012-06-11 16:40:41 +0000851
852//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000853// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000854//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000855
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000856void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000857 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000858}
859
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000860void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000861 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000862}
863
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000864void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000865 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000866}
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000867
868void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000869 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000870}
Anders Carlssond91d5f12011-03-13 20:35:21 +0000871
872void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
873 mgr.registerChecker<VariadicMethodTypeChecker>();
874}
Jordan Roseefef7602012-06-11 16:40:41 +0000875
876void ento::registerObjCLoopChecker(CheckerManager &mgr) {
877 mgr.registerChecker<ObjCLoopChecker>();
878}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000879
880void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
881 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
882}