blob: e0c3962cb668bcbc692b0a36d549a8a5742a7e2c [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) {
Anna Zaks4d1e3042013-04-05 23:50:18 +0000126 if (Arg == 0) {
Ted Kremeneke06df462013-04-08 18:09:16 +0000127 os << "Value stored into '";
Anna Zaks4d1e3042013-04-05 23:50:18 +0000128 os << GetReceiverInterfaceName(msg) << "' cannot be nil";
129 } else {
Anna Zaks130df4b2013-03-23 00:39:21 +0000130 assert(Arg == 1);
Anna Zaks4d1e3042013-04-05 23:50:18 +0000131 os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
Anna Zaks130df4b2013-03-23 00:39:21 +0000132 }
133 } else
134 llvm_unreachable("Missing foundation class for the subscript expr");
135
136 } else {
Anna Zaks4d1e3042013-04-05 23:50:18 +0000137 if (Class == FC_NSDictionary) {
138 if (Arg == 0)
139 os << "Value argument ";
140 else {
141 assert(Arg == 1);
142 os << "Key argument ";
143 }
144 os << "to '" << msg.getSelector().getAsString() << "' cannot be nil";
145 } else {
146 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
147 << msg.getSelector().getAsString() << "' cannot be nil";
148 }
Anna Zaks130df4b2013-03-23 00:39:21 +0000149 }
Mike Stump11289f42009-09-09 15:08:12 +0000150
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000151 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000152 R->addRange(msg.getArgSourceRange(Arg));
Anna Zaksbd8f60d2013-03-26 23:58:49 +0000153 bugreporter::trackNullOrUndefValue(N, msg.getArgExpr(Arg), *R);
Jordan Rosee10d5a72012-11-02 01:53:40 +0000154 C.emitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +0000155 }
Ted Kremenek276278e2008-03-27 22:05:32 +0000156}
157
Jordan Rose547060b2012-07-02 19:28:04 +0000158void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000159 CheckerContext &C) const {
Anders Carlsson3c50aea2011-03-08 20:05:26 +0000160 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
161 if (!ID)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000162 return;
Anna Zaks6457ad22013-03-18 20:46:56 +0000163
164 FoundationClass Class = findKnownClass(ID);
165
166 static const unsigned InvalidArgIndex = UINT_MAX;
167 unsigned Arg = InvalidArgIndex;
Anna Zaks130df4b2013-03-23 00:39:21 +0000168 bool CanBeSubscript = false;
169
Anna Zaks6457ad22013-03-18 20:46:56 +0000170 if (Class == FC_NSString) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000171 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000172
173 if (S.isUnarySelector())
174 return;
175
176 // FIXME: This is going to be really slow doing these checks with
177 // lexical comparisons.
178
179 std::string NameStr = S.getAsString();
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000180 StringRef Name(NameStr);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000181 assert(!Name.empty());
182
183 // FIXME: Checking for initWithFormat: will not work in most cases
184 // yet because [NSString alloc] returns id, not NSString*. We will
185 // need support for tracking expected-type information in the analyzer
186 // to find these errors.
187 if (Name == "caseInsensitiveCompare:" ||
188 Name == "compare:" ||
189 Name == "compare:options:" ||
190 Name == "compare:options:range:" ||
191 Name == "compare:options:range:locale:" ||
192 Name == "componentsSeparatedByCharactersInSet:" ||
193 Name == "initWithFormat:") {
Anna Zaks6457ad22013-03-18 20:46:56 +0000194 Arg = 0;
195 }
196 } else if (Class == FC_NSArray) {
197 Selector S = msg.getSelector();
198
199 if (S.isUnarySelector())
200 return;
201
202 if (S.getNameForSlot(0).equals("addObject")) {
203 Arg = 0;
204 } else if (S.getNameForSlot(0).equals("insertObject") &&
205 S.getNameForSlot(1).equals("atIndex")) {
206 Arg = 0;
207 } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") &&
208 S.getNameForSlot(1).equals("withObject")) {
209 Arg = 1;
210 } else if (S.getNameForSlot(0).equals("setObject") &&
211 S.getNameForSlot(1).equals("atIndexedSubscript")) {
212 Arg = 0;
Anna Zaks130df4b2013-03-23 00:39:21 +0000213 CanBeSubscript = true;
Anna Zaks6457ad22013-03-18 20:46:56 +0000214 } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
215 Arg = 0;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000216 }
Anna Zaks130df4b2013-03-23 00:39:21 +0000217 } else if (Class == FC_NSDictionary) {
218 Selector S = msg.getSelector();
219
220 if (S.isUnarySelector())
221 return;
222
223 if (S.getNameForSlot(0).equals("dictionaryWithObject") &&
224 S.getNameForSlot(1).equals("forKey")) {
225 Arg = 0;
226 WarnIfNilArg(C, msg, /* Arg */1, Class);
227 } else if (S.getNameForSlot(0).equals("setObject") &&
228 S.getNameForSlot(1).equals("forKey")) {
229 Arg = 0;
230 WarnIfNilArg(C, msg, /* Arg */1, Class);
231 } else if (S.getNameForSlot(0).equals("setObject") &&
232 S.getNameForSlot(1).equals("forKeyedSubscript")) {
233 CanBeSubscript = true;
234 Arg = 0;
235 WarnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript);
236 } else if (S.getNameForSlot(0).equals("removeObjectForKey")) {
237 Arg = 0;
238 }
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000239 }
Anna Zaks6457ad22013-03-18 20:46:56 +0000240
Anna Zaks130df4b2013-03-23 00:39:21 +0000241
Anna Zaks6457ad22013-03-18 20:46:56 +0000242 // If argument is '0', report a warning.
Anna Zaks130df4b2013-03-23 00:39:21 +0000243 if ((Arg != InvalidArgIndex))
244 WarnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
Anna Zaks6457ad22013-03-18 20:46:56 +0000245
Ted Kremenekc0414922008-03-27 07:25:52 +0000246}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000247
248//===----------------------------------------------------------------------===//
249// Error reporting.
250//===----------------------------------------------------------------------===//
251
252namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000253class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000254 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000255 mutable IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000256public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000257 CFNumberCreateChecker() : II(0) {}
258
259 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
260
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000261private:
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000262 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000263 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000264};
265} // end anonymous namespace
266
267enum CFNumberType {
268 kCFNumberSInt8Type = 1,
269 kCFNumberSInt16Type = 2,
270 kCFNumberSInt32Type = 3,
271 kCFNumberSInt64Type = 4,
272 kCFNumberFloat32Type = 5,
273 kCFNumberFloat64Type = 6,
274 kCFNumberCharType = 7,
275 kCFNumberShortType = 8,
276 kCFNumberIntType = 9,
277 kCFNumberLongType = 10,
278 kCFNumberLongLongType = 11,
279 kCFNumberFloatType = 12,
280 kCFNumberDoubleType = 13,
281 kCFNumberCFIndexType = 14,
282 kCFNumberNSIntegerType = 15,
283 kCFNumberCGFloatType = 16
284};
285
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000286static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000287 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000288
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000289 if (i < kCFNumberCharType)
290 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000291
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000292 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000293
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000294 switch (i) {
295 case kCFNumberCharType: T = Ctx.CharTy; break;
296 case kCFNumberShortType: T = Ctx.ShortTy; break;
297 case kCFNumberIntType: T = Ctx.IntTy; break;
298 case kCFNumberLongType: T = Ctx.LongTy; break;
299 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
300 case kCFNumberFloatType: T = Ctx.FloatTy; break;
301 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
302 case kCFNumberCFIndexType:
303 case kCFNumberNSIntegerType:
304 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000305 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000306 default:
David Blaikie7a30dc52013-02-21 01:47:18 +0000307 return None;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000308 }
Mike Stump11289f42009-09-09 15:08:12 +0000309
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000310 return Ctx.getTypeSize(T);
311}
312
313#if 0
314static const char* GetCFNumberTypeStr(uint64_t i) {
315 static const char* Names[] = {
316 "kCFNumberSInt8Type",
317 "kCFNumberSInt16Type",
318 "kCFNumberSInt32Type",
319 "kCFNumberSInt64Type",
320 "kCFNumberFloat32Type",
321 "kCFNumberFloat64Type",
322 "kCFNumberCharType",
323 "kCFNumberShortType",
324 "kCFNumberIntType",
325 "kCFNumberLongType",
326 "kCFNumberLongLongType",
327 "kCFNumberFloatType",
328 "kCFNumberDoubleType",
329 "kCFNumberCFIndexType",
330 "kCFNumberNSIntegerType",
331 "kCFNumberCGFloatType"
332 };
Mike Stump11289f42009-09-09 15:08:12 +0000333
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000334 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
335}
336#endif
337
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000338void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
339 CheckerContext &C) const {
Ted Kremenek49b1e382012-01-26 21:29:00 +0000340 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000341 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000342 if (!FD)
343 return;
344
345 ASTContext &Ctx = C.getASTContext();
346 if (!II)
347 II = &Ctx.Idents.get("CFNumberCreate");
348
349 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
350 return;
Mike Stump11289f42009-09-09 15:08:12 +0000351
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000352 // Get the value of the "theType" argument.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000353 const LocationContext *LCtx = C.getLocationContext();
354 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000355
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000356 // FIXME: We really should allow ranges of valid theType values, and
357 // bifurcate the state appropriately.
David Blaikie05785d12013-02-20 22:23:23 +0000358 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000359 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000360 return;
Mike Stump11289f42009-09-09 15:08:12 +0000361
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000362 uint64_t NumberKind = V->getValue().getLimitedValue();
David Blaikie05785d12013-02-20 22:23:23 +0000363 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000364
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000365 // FIXME: In some cases we can emit an error.
David Blaikiee359f3c2013-02-20 22:23:03 +0000366 if (!OptTargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000367 return;
Mike Stump11289f42009-09-09 15:08:12 +0000368
David Blaikiee359f3c2013-02-20 22:23:03 +0000369 uint64_t TargetSize = *OptTargetSize;
370
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000371 // Look at the value of the integer being passed by reference. Essentially
372 // we want to catch cases where the value passed in is not equal to the
373 // size of the type being created.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000374 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000375
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000376 // FIXME: Eventually we should handle arbitrary locations. We can do this
377 // by having an enhanced memory model that does low-level typing.
David Blaikie05785d12013-02-20 22:23:23 +0000378 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000379 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000380 return;
Mike Stump11289f42009-09-09 15:08:12 +0000381
Ted Kremenek8df44b262011-08-12 20:02:48 +0000382 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000383 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000384 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000385
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000386 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000387
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000388 // FIXME: If the pointee isn't an integer type, should we flag a warning?
389 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000390
Jordan Rose61e221f2013-04-09 02:30:33 +0000391 if (!T->isIntegralOrEnumerationType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000392 return;
Mike Stump11289f42009-09-09 15:08:12 +0000393
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000394 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000395
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000396 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000397 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000398 return;
Mike Stump11289f42009-09-09 15:08:12 +0000399
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000400 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
401 // otherwise generate a regular node.
402 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000403 // FIXME: We can actually create an abstract "CFNumber" object that has
404 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000405 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000406 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaksda4c8d62011-10-26 21:06:34 +0000407 : C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000408 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000409 llvm::raw_svector_ostream os(sbuf);
410
411 os << (SourceSize == 8 ? "An " : "A ")
412 << SourceSize << " bit integer is used to initialize a CFNumber "
413 "object that represents "
414 << (TargetSize == 8 ? "an " : "a ")
415 << TargetSize << " bit integer. ";
416
417 if (SourceSize < TargetSize)
418 os << (TargetSize - SourceSize)
419 << " bits of the CFNumber value will be garbage." ;
420 else
421 os << (SourceSize - TargetSize)
422 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000423
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000424 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000425 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000426
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000427 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000428 report->addRange(CE->getArg(2)->getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000429 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000430 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000431}
432
Ted Kremenek1f352db2008-07-22 16:21:24 +0000433//===----------------------------------------------------------------------===//
Jordan Rose721567a2012-11-07 17:12:37 +0000434// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000435//===----------------------------------------------------------------------===//
436
437namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000438class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000439 mutable OwningPtr<APIMisuse> BT;
Jordan Rose721567a2012-11-07 17:12:37 +0000440 mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
Ted Kremenekc057f412009-07-14 00:43:42 +0000441public:
Jordan Rose721567a2012-11-07 17:12:37 +0000442 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000443 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenekc057f412009-07-14 00:43:42 +0000444};
445} // end anonymous namespace
446
447
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000448void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
449 CheckerContext &C) const {
Ted Kremenekc057f412009-07-14 00:43:42 +0000450 // If the CallExpr doesn't have exactly 1 argument just give up checking.
451 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000452 return;
Mike Stump11289f42009-09-09 15:08:12 +0000453
Ted Kremenek49b1e382012-01-26 21:29:00 +0000454 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000455 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000456 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000457 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000458
459 if (!BT) {
460 ASTContext &Ctx = C.getASTContext();
461 Retain = &Ctx.Idents.get("CFRetain");
462 Release = &Ctx.Idents.get("CFRelease");
Jordan Rose721567a2012-11-07 17:12:37 +0000463 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
464 BT.reset(
465 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000466 }
Mike Stump11289f42009-09-09 15:08:12 +0000467
Jordan Rose721567a2012-11-07 17:12:37 +0000468 // Check if we called CFRetain/CFRelease/CFMakeCollectable.
Mike Stump11289f42009-09-09 15:08:12 +0000469 const IdentifierInfo *FuncII = FD->getIdentifier();
Jordan Rose721567a2012-11-07 17:12:37 +0000470 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
Jordy Rose40c5c242010-07-06 02:34:42 +0000471 return;
Mike Stump11289f42009-09-09 15:08:12 +0000472
Jordy Rose40c5c242010-07-06 02:34:42 +0000473 // FIXME: The rest of this just checks that the argument is non-null.
Anna Zaksef893392013-03-09 03:23:14 +0000474 // It should probably be refactored and combined with NonNullParamChecker.
Jordy Rose40c5c242010-07-06 02:34:42 +0000475
476 // Get the argument's value.
477 const Expr *Arg = CE->getArg(0);
Ted Kremenek632e3b72012-01-06 22:09:28 +0000478 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000479 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000480 if (!DefArgVal)
481 return;
482
483 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000484 SValBuilder &svalBuilder = C.getSValBuilder();
David Blaikie2fdacbc2013-02-20 05:52:05 +0000485 DefinedSVal zero =
486 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000487
488 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000489 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000490
491 // Are they equal?
Ted Kremenek49b1e382012-01-26 21:29:00 +0000492 ProgramStateRef stateTrue, stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000493 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000494
495 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000496 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000497 if (!N)
498 return;
499
Jordan Rose721567a2012-11-07 17:12:37 +0000500 const char *description;
501 if (FuncII == Retain)
502 description = "Null pointer argument in call to CFRetain";
503 else if (FuncII == Release)
504 description = "Null pointer argument in call to CFRelease";
505 else if (FuncII == MakeCollectable)
506 description = "Null pointer argument in call to CFMakeCollectable";
507 else
508 llvm_unreachable("impossible case");
Ted Kremenekc057f412009-07-14 00:43:42 +0000509
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000510 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose40c5c242010-07-06 02:34:42 +0000511 report->addRange(Arg->getSourceRange());
Jordan Rosea0f7d352012-08-28 00:50:51 +0000512 bugreporter::trackNullOrUndefValue(N, Arg, *report);
Jordan Rosee10d5a72012-11-02 01:53:40 +0000513 C.emitReport(report);
Jordy Rose40c5c242010-07-06 02:34:42 +0000514 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000515 }
516
Jordy Rose40c5c242010-07-06 02:34:42 +0000517 // From here on, we know the argument is non-null.
Anna Zaksda4c8d62011-10-26 21:06:34 +0000518 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000519}
520
521//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000522// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
523//===----------------------------------------------------------------------===//
524
525namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000526class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000527 mutable Selector releaseS;
528 mutable Selector retainS;
529 mutable Selector autoreleaseS;
530 mutable Selector drainS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000531 mutable OwningPtr<BugType> BT;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000532
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000533public:
Jordan Rose547060b2012-07-02 19:28:04 +0000534 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000535};
536}
537
Jordan Rose547060b2012-07-02 19:28:04 +0000538void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000539 CheckerContext &C) const {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000540
541 if (!BT) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000542 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
543 "instance"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000544
545 ASTContext &Ctx = C.getASTContext();
546 releaseS = GetNullarySelector("release", Ctx);
547 retainS = GetNullarySelector("retain", Ctx);
548 autoreleaseS = GetNullarySelector("autorelease", Ctx);
549 drainS = GetNullarySelector("drain", Ctx);
550 }
551
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000552 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000553 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000554 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
555 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000556
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000557 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000558 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000559 return;
560
Anna Zaksda4c8d62011-10-26 21:06:34 +0000561 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000562 SmallString<200> buf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000563 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000564
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000565 os << "The '" << S.getAsString() << "' message should be sent to instances "
566 "of class '" << Class->getName()
567 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000568
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000569 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000570 report->addRange(msg.getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000571 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000572 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000573}
574
575//===----------------------------------------------------------------------===//
Anders Carlssond91d5f12011-03-13 20:35:21 +0000576// Check for passing non-Objective-C types to variadic methods that expect
577// only Objective-C types.
578//===----------------------------------------------------------------------===//
579
580namespace {
581class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
582 mutable Selector arrayWithObjectsS;
583 mutable Selector dictionaryWithObjectsAndKeysS;
584 mutable Selector setWithObjectsS;
Jordy Rosec0230d72012-04-06 19:06:01 +0000585 mutable Selector orderedSetWithObjectsS;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000586 mutable Selector initWithObjectsS;
587 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000588 mutable OwningPtr<BugType> BT;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000589
Jordan Rose547060b2012-07-02 19:28:04 +0000590 bool isVariadicMessage(const ObjCMethodCall &msg) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000591
592public:
Jordan Rose547060b2012-07-02 19:28:04 +0000593 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000594};
595}
596
597/// isVariadicMessage - Returns whether the given message is a variadic message,
598/// where all arguments must be Objective-C types.
599bool
Jordan Rose547060b2012-07-02 19:28:04 +0000600VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
601 const ObjCMethodDecl *MD = msg.getDecl();
Ted Kremenekced5fea2011-04-12 21:47:05 +0000602
603 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlssond91d5f12011-03-13 20:35:21 +0000604 return false;
605
606 Selector S = msg.getSelector();
607
608 if (msg.isInstanceMessage()) {
609 // FIXME: Ideally we'd look at the receiver interface here, but that's not
610 // useful for init, because alloc returns 'id'. In theory, this could lead
611 // to false positives, for example if there existed a class that had an
612 // initWithObjects: implementation that does accept non-Objective-C pointer
613 // types, but the chance of that happening is pretty small compared to the
614 // gains that this analysis gives.
615 const ObjCInterfaceDecl *Class = MD->getClassInterface();
616
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000617 switch (findKnownClass(Class)) {
618 case FC_NSArray:
619 case FC_NSOrderedSet:
620 case FC_NSSet:
621 return S == initWithObjectsS;
622 case FC_NSDictionary:
623 return S == initWithObjectsAndKeysS;
624 default:
625 return false;
626 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000627 } else {
628 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
629
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000630 switch (findKnownClass(Class)) {
631 case FC_NSArray:
632 return S == arrayWithObjectsS;
633 case FC_NSOrderedSet:
634 return S == orderedSetWithObjectsS;
635 case FC_NSSet:
636 return S == setWithObjectsS;
637 case FC_NSDictionary:
638 return S == dictionaryWithObjectsAndKeysS;
639 default:
640 return false;
641 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000642 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000643}
644
Jordan Rose547060b2012-07-02 19:28:04 +0000645void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Anders Carlssond91d5f12011-03-13 20:35:21 +0000646 CheckerContext &C) const {
647 if (!BT) {
648 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
649 "Objective-C pointer types"));
650
651 ASTContext &Ctx = C.getASTContext();
652 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
653 dictionaryWithObjectsAndKeysS =
654 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
655 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
Jordy Rosec0230d72012-04-06 19:06:01 +0000656 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000657
658 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
659 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
660 }
661
662 if (!isVariadicMessage(msg))
663 return;
664
665 // We are not interested in the selector arguments since they have
666 // well-defined types, so the compiler will issue a warning for them.
667 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
668
669 // We're not interested in the last argument since it has to be nil or the
670 // compiler would have issued a warning for it elsewhere.
671 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
672
673 if (variadicArgsEnd <= variadicArgsBegin)
674 return;
675
676 // Verify that all arguments have Objective-C types.
David Blaikie05785d12013-02-20 22:23:23 +0000677 Optional<ExplodedNode*> errorNode;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000678 ProgramStateRef state = C.getState();
Ted Kremenek066b2262011-03-14 19:50:37 +0000679
Anders Carlssond91d5f12011-03-13 20:35:21 +0000680 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
Jordan Rose547060b2012-07-02 19:28:04 +0000681 QualType ArgTy = msg.getArgExpr(I)->getType();
Anders Carlssond91d5f12011-03-13 20:35:21 +0000682 if (ArgTy->isObjCObjectPointerType())
683 continue;
684
Anders Carlssond1f65f62011-04-19 01:16:46 +0000685 // Block pointers are treaded as Objective-C pointers.
686 if (ArgTy->isBlockPointerType())
687 continue;
688
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000689 // Ignore pointer constants.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000690 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000691 continue;
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000692
Ted Kremenek70727342011-03-17 04:10:25 +0000693 // Ignore pointer types annotated with 'NSObject' attribute.
694 if (C.getASTContext().isObjCNSObjectType(ArgTy))
695 continue;
696
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000697 // Ignore CF references, which can be toll-free bridged.
Ted Kremenekc85964e2011-07-16 19:50:32 +0000698 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000699 continue;
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000700
Ted Kremenek066b2262011-03-14 19:50:37 +0000701 // Generate only one error node to use for all bug reports.
Jordan Rose547060b2012-07-02 19:28:04 +0000702 if (!errorNode.hasValue())
Anna Zaksda4c8d62011-10-26 21:06:34 +0000703 errorNode = C.addTransition();
Ted Kremenek066b2262011-03-14 19:50:37 +0000704
705 if (!errorNode.getValue())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000706 continue;
707
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000708 SmallString<128> sbuf;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000709 llvm::raw_svector_ostream os(sbuf);
710
Jordan Rose547060b2012-07-02 19:28:04 +0000711 StringRef TypeName = GetReceiverInterfaceName(msg);
712 if (!TypeName.empty())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000713 os << "Argument to '" << TypeName << "' method '";
714 else
715 os << "Argument to method '";
716
717 os << msg.getSelector().getAsString()
Jordan Rose547060b2012-07-02 19:28:04 +0000718 << "' should be an Objective-C pointer type, not '";
719 ArgTy.print(os, C.getLangOpts());
720 os << "'";
Anders Carlssond91d5f12011-03-13 20:35:21 +0000721
Jordan Rose547060b2012-07-02 19:28:04 +0000722 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
Anders Carlssond91d5f12011-03-13 20:35:21 +0000723 R->addRange(msg.getArgSourceRange(I));
Jordan Rosee10d5a72012-11-02 01:53:40 +0000724 C.emitReport(R);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000725 }
726}
727
728//===----------------------------------------------------------------------===//
Jordan Roseefef7602012-06-11 16:40:41 +0000729// Improves the modeling of loops over Cocoa collections.
730//===----------------------------------------------------------------------===//
731
732namespace {
733class ObjCLoopChecker
734 : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
735
736public:
737 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
738};
739}
740
741static bool isKnownNonNilCollectionType(QualType T) {
742 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
743 if (!PT)
744 return false;
745
746 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
747 if (!ID)
748 return false;
749
750 switch (findKnownClass(ID)) {
751 case FC_NSArray:
752 case FC_NSDictionary:
753 case FC_NSEnumerator:
754 case FC_NSOrderedSet:
755 case FC_NSSet:
756 return true;
757 default:
758 return false;
759 }
760}
761
762void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
763 CheckerContext &C) const {
764 ProgramStateRef State = C.getState();
765
766 // Check if this is the branch for the end of the loop.
767 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
768 if (CollectionSentinel.isZeroConstant())
769 return;
770
771 // See if the collection is one where we /know/ the elements are non-nil.
772 const Expr *Collection = FCS->getCollection();
773 if (!isKnownNonNilCollectionType(Collection->getType()))
774 return;
775
776 // FIXME: Copied from ExprEngineObjC.
777 const Stmt *Element = FCS->getElement();
778 SVal ElementVar;
779 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
780 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
781 assert(ElemDecl->getInit() == 0);
782 ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
783 } else {
784 ElementVar = State->getSVal(Element, C.getLocationContext());
785 }
786
David Blaikie2fdacbc2013-02-20 05:52:05 +0000787 if (!ElementVar.getAs<Loc>())
Jordan Roseefef7602012-06-11 16:40:41 +0000788 return;
789
790 // Go ahead and assume the value is non-nil.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000791 SVal Val = State->getSVal(ElementVar.castAs<Loc>());
792 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
Jordan Roseefef7602012-06-11 16:40:41 +0000793 C.addTransition(State);
794}
795
Anna Zaks5a5a1752012-08-22 21:19:56 +0000796namespace {
797/// \class ObjCNonNilReturnValueChecker
Anna Zaks4818bbe2012-08-30 19:40:52 +0000798/// \brief The checker restricts the return values of APIs known to
799/// never (or almost never) return 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000800class ObjCNonNilReturnValueChecker
801 : public Checker<check::PostObjCMessage> {
802 mutable bool Initialized;
803 mutable Selector ObjectAtIndex;
804 mutable Selector ObjectAtIndexedSubscript;
Anna Zaks9159e162012-08-22 22:47:58 +0000805
Anna Zaks5a5a1752012-08-22 21:19:56 +0000806public:
Anna Zaks9159e162012-08-22 22:47:58 +0000807 ObjCNonNilReturnValueChecker() : Initialized(false) {}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000808 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
809};
810}
811
Benjamin Kramer199f8da2012-09-10 11:57:16 +0000812static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
813 ProgramStateRef State,
814 CheckerContext &C) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000815 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000816 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
Anna Zaks830c48e2012-08-30 22:55:32 +0000817 return State->assume(*DV, true);
Anna Zaksb504f442012-08-30 22:42:41 +0000818 return State;
Anna Zaks4818bbe2012-08-30 19:40:52 +0000819}
820
Anna Zaks5a5a1752012-08-22 21:19:56 +0000821void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
822 CheckerContext &C)
Anna Zaks4818bbe2012-08-30 19:40:52 +0000823 const {
Anna Zaks5a5a1752012-08-22 21:19:56 +0000824 ProgramStateRef State = C.getState();
825
826 if (!Initialized) {
827 ASTContext &Ctx = C.getASTContext();
828 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
829 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
830 }
831
832 // Check the receiver type.
833 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000834
835 // Assume that object returned from '[self init]' or '[super init]' is not
836 // 'nil' if we are processing an inlined function/method.
837 //
838 // A defensive callee will (and should) check if the object returned by
839 // '[super init]' is 'nil' before doing it's own initialization. However,
840 // since 'nil' is rarely returned in practice, we should not warn when the
841 // caller to the defensive constructor uses the object in contexts where
842 // 'nil' is not accepted.
Anna Zaks49bb6502012-11-06 04:20:54 +0000843 if (!C.inTopFrame() && M.getDecl() &&
Anna Zaks4818bbe2012-08-30 19:40:52 +0000844 M.getDecl()->getMethodFamily() == OMF_init &&
845 M.isReceiverSelfOrSuper()) {
846 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
847 }
848
849 // Objects returned from
850 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
851 // are never 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000852 FoundationClass Cl = findKnownClass(Interface);
853 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
854 Selector Sel = M.getSelector();
855 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
856 // Go ahead and assume the value is non-nil.
Anna Zaks4818bbe2012-08-30 19:40:52 +0000857 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000858 }
859 }
860 }
Anna Zaks4818bbe2012-08-30 19:40:52 +0000861 C.addTransition(State);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000862}
Jordan Roseefef7602012-06-11 16:40:41 +0000863
864//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000865// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000866//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000867
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000868void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000869 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000870}
871
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000872void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000873 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000874}
875
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000876void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000877 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000878}
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000879
880void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000881 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000882}
Anders Carlssond91d5f12011-03-13 20:35:21 +0000883
884void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
885 mgr.registerChecker<VariadicMethodTypeChecker>();
886}
Jordan Roseefef7602012-06-11 16:40:41 +0000887
888void ento::registerObjCLoopChecker(CheckerManager &mgr) {
889 mgr.registerChecker<ObjCLoopChecker>();
890}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000891
892void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
893 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
894}