blob: a6bcb6930a544aae41675bd4e56110fab4cb1ced [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
Zhongxing Xu27f17422008-10-17 05:57:07 +000086static inline bool isNil(SVal X) {
David Blaikie3a3c4e02013-02-21 06:05:05 +000087 return X.getAs<loc::ConcreteInt>().hasValue();
Ted Kremenek27156c82008-03-27 21:15:17 +000088}
89
Ted Kremenekc0414922008-03-27 07:25:52 +000090//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +000091// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenekc0414922008-03-27 07:25:52 +000092//===----------------------------------------------------------------------===//
93
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000094namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000095 class NilArgChecker : public Checker<check::PreObjCMessage> {
Dylan Noblesmithe2778992012-02-05 02:12:40 +000096 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000097
98 void WarnNilArg(CheckerContext &C,
Jordan Rose547060b2012-07-02 19:28:04 +000099 const ObjCMethodCall &msg, unsigned Arg) const;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000100
Benjamin Kramer2fc373e2010-10-22 16:33:16 +0000101 public:
Jordan Rose547060b2012-07-02 19:28:04 +0000102 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
Benjamin Kramer2fc373e2010-10-22 16:33:16 +0000103 };
104}
Mike Stump11289f42009-09-09 15:08:12 +0000105
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000106void NilArgChecker::WarnNilArg(CheckerContext &C,
Jordan Rose547060b2012-07-02 19:28:04 +0000107 const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000108 unsigned int Arg) const
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000109{
110 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000111 BT.reset(new APIMisuse("nil argument"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000112
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000113 if (ExplodedNode *N = C.generateSink()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000114 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000115 llvm::raw_svector_ostream os(sbuf);
Jordan Rose547060b2012-07-02 19:28:04 +0000116 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000117 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump11289f42009-09-09 15:08:12 +0000118
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000119 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000120 R->addRange(msg.getArgSourceRange(Arg));
Jordan Rosee10d5a72012-11-02 01:53:40 +0000121 C.emitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +0000122 }
Ted Kremenek276278e2008-03-27 22:05:32 +0000123}
124
Jordan Rose547060b2012-07-02 19:28:04 +0000125void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000126 CheckerContext &C) const {
Anders Carlsson3c50aea2011-03-08 20:05:26 +0000127 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
128 if (!ID)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000129 return;
Anna Zaks6457ad22013-03-18 20:46:56 +0000130
131 FoundationClass Class = findKnownClass(ID);
132
133 static const unsigned InvalidArgIndex = UINT_MAX;
134 unsigned Arg = InvalidArgIndex;
135
136 if (Class == FC_NSString) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000137 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000138
139 if (S.isUnarySelector())
140 return;
141
142 // FIXME: This is going to be really slow doing these checks with
143 // lexical comparisons.
144
145 std::string NameStr = S.getAsString();
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000146 StringRef Name(NameStr);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000147 assert(!Name.empty());
148
149 // FIXME: Checking for initWithFormat: will not work in most cases
150 // yet because [NSString alloc] returns id, not NSString*. We will
151 // need support for tracking expected-type information in the analyzer
152 // to find these errors.
153 if (Name == "caseInsensitiveCompare:" ||
154 Name == "compare:" ||
155 Name == "compare:options:" ||
156 Name == "compare:options:range:" ||
157 Name == "compare:options:range:locale:" ||
158 Name == "componentsSeparatedByCharactersInSet:" ||
159 Name == "initWithFormat:") {
Anna Zaks6457ad22013-03-18 20:46:56 +0000160 Arg = 0;
161 }
162 } else if (Class == FC_NSArray) {
163 Selector S = msg.getSelector();
164
165 if (S.isUnarySelector())
166 return;
167
168 if (S.getNameForSlot(0).equals("addObject")) {
169 Arg = 0;
170 } else if (S.getNameForSlot(0).equals("insertObject") &&
171 S.getNameForSlot(1).equals("atIndex")) {
172 Arg = 0;
173 } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") &&
174 S.getNameForSlot(1).equals("withObject")) {
175 Arg = 1;
176 } else if (S.getNameForSlot(0).equals("setObject") &&
177 S.getNameForSlot(1).equals("atIndexedSubscript")) {
178 Arg = 0;
179 } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
180 Arg = 0;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000181 }
182 }
Anna Zaks6457ad22013-03-18 20:46:56 +0000183
184 // If argument is '0', report a warning.
185 if ((Arg != InvalidArgIndex) && isNil(msg.getArgSVal(Arg)))
186 WarnNilArg(C, msg, Arg);
187
Ted Kremenekc0414922008-03-27 07:25:52 +0000188}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000189
190//===----------------------------------------------------------------------===//
191// Error reporting.
192//===----------------------------------------------------------------------===//
193
194namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000195class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000196 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000197 mutable IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000198public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000199 CFNumberCreateChecker() : II(0) {}
200
201 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
202
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000203private:
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000204 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000205 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000206};
207} // end anonymous namespace
208
209enum CFNumberType {
210 kCFNumberSInt8Type = 1,
211 kCFNumberSInt16Type = 2,
212 kCFNumberSInt32Type = 3,
213 kCFNumberSInt64Type = 4,
214 kCFNumberFloat32Type = 5,
215 kCFNumberFloat64Type = 6,
216 kCFNumberCharType = 7,
217 kCFNumberShortType = 8,
218 kCFNumberIntType = 9,
219 kCFNumberLongType = 10,
220 kCFNumberLongLongType = 11,
221 kCFNumberFloatType = 12,
222 kCFNumberDoubleType = 13,
223 kCFNumberCFIndexType = 14,
224 kCFNumberNSIntegerType = 15,
225 kCFNumberCGFloatType = 16
226};
227
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000228static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000229 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000230
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000231 if (i < kCFNumberCharType)
232 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000233
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000234 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000235
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000236 switch (i) {
237 case kCFNumberCharType: T = Ctx.CharTy; break;
238 case kCFNumberShortType: T = Ctx.ShortTy; break;
239 case kCFNumberIntType: T = Ctx.IntTy; break;
240 case kCFNumberLongType: T = Ctx.LongTy; break;
241 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
242 case kCFNumberFloatType: T = Ctx.FloatTy; break;
243 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
244 case kCFNumberCFIndexType:
245 case kCFNumberNSIntegerType:
246 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000247 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000248 default:
David Blaikie7a30dc52013-02-21 01:47:18 +0000249 return None;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000250 }
Mike Stump11289f42009-09-09 15:08:12 +0000251
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000252 return Ctx.getTypeSize(T);
253}
254
255#if 0
256static const char* GetCFNumberTypeStr(uint64_t i) {
257 static const char* Names[] = {
258 "kCFNumberSInt8Type",
259 "kCFNumberSInt16Type",
260 "kCFNumberSInt32Type",
261 "kCFNumberSInt64Type",
262 "kCFNumberFloat32Type",
263 "kCFNumberFloat64Type",
264 "kCFNumberCharType",
265 "kCFNumberShortType",
266 "kCFNumberIntType",
267 "kCFNumberLongType",
268 "kCFNumberLongLongType",
269 "kCFNumberFloatType",
270 "kCFNumberDoubleType",
271 "kCFNumberCFIndexType",
272 "kCFNumberNSIntegerType",
273 "kCFNumberCGFloatType"
274 };
Mike Stump11289f42009-09-09 15:08:12 +0000275
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000276 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
277}
278#endif
279
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000280void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
281 CheckerContext &C) const {
Ted Kremenek49b1e382012-01-26 21:29:00 +0000282 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000283 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000284 if (!FD)
285 return;
286
287 ASTContext &Ctx = C.getASTContext();
288 if (!II)
289 II = &Ctx.Idents.get("CFNumberCreate");
290
291 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
292 return;
Mike Stump11289f42009-09-09 15:08:12 +0000293
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000294 // Get the value of the "theType" argument.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000295 const LocationContext *LCtx = C.getLocationContext();
296 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000297
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000298 // FIXME: We really should allow ranges of valid theType values, and
299 // bifurcate the state appropriately.
David Blaikie05785d12013-02-20 22:23:23 +0000300 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000301 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000302 return;
Mike Stump11289f42009-09-09 15:08:12 +0000303
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000304 uint64_t NumberKind = V->getValue().getLimitedValue();
David Blaikie05785d12013-02-20 22:23:23 +0000305 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000306
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000307 // FIXME: In some cases we can emit an error.
David Blaikiee359f3c2013-02-20 22:23:03 +0000308 if (!OptTargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000309 return;
Mike Stump11289f42009-09-09 15:08:12 +0000310
David Blaikiee359f3c2013-02-20 22:23:03 +0000311 uint64_t TargetSize = *OptTargetSize;
312
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000313 // Look at the value of the integer being passed by reference. Essentially
314 // we want to catch cases where the value passed in is not equal to the
315 // size of the type being created.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000316 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000317
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000318 // FIXME: Eventually we should handle arbitrary locations. We can do this
319 // by having an enhanced memory model that does low-level typing.
David Blaikie05785d12013-02-20 22:23:23 +0000320 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000321 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000322 return;
Mike Stump11289f42009-09-09 15:08:12 +0000323
Ted Kremenek8df44b262011-08-12 20:02:48 +0000324 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000325 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000326 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000327
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000328 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000329
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000330 // FIXME: If the pointee isn't an integer type, should we flag a warning?
331 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000332
333 if (!T->isIntegerType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000334 return;
Mike Stump11289f42009-09-09 15:08:12 +0000335
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000336 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000337
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000338 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000339 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000340 return;
Mike Stump11289f42009-09-09 15:08:12 +0000341
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000342 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
343 // otherwise generate a regular node.
344 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000345 // FIXME: We can actually create an abstract "CFNumber" object that has
346 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000347 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000348 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaksda4c8d62011-10-26 21:06:34 +0000349 : C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000350 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000351 llvm::raw_svector_ostream os(sbuf);
352
353 os << (SourceSize == 8 ? "An " : "A ")
354 << SourceSize << " bit integer is used to initialize a CFNumber "
355 "object that represents "
356 << (TargetSize == 8 ? "an " : "a ")
357 << TargetSize << " bit integer. ";
358
359 if (SourceSize < TargetSize)
360 os << (TargetSize - SourceSize)
361 << " bits of the CFNumber value will be garbage." ;
362 else
363 os << (SourceSize - TargetSize)
364 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000365
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000366 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000367 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000368
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000369 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000370 report->addRange(CE->getArg(2)->getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000371 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000372 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000373}
374
Ted Kremenek1f352db2008-07-22 16:21:24 +0000375//===----------------------------------------------------------------------===//
Jordan Rose721567a2012-11-07 17:12:37 +0000376// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000377//===----------------------------------------------------------------------===//
378
379namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000380class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000381 mutable OwningPtr<APIMisuse> BT;
Jordan Rose721567a2012-11-07 17:12:37 +0000382 mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
Ted Kremenekc057f412009-07-14 00:43:42 +0000383public:
Jordan Rose721567a2012-11-07 17:12:37 +0000384 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000385 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenekc057f412009-07-14 00:43:42 +0000386};
387} // end anonymous namespace
388
389
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000390void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
391 CheckerContext &C) const {
Ted Kremenekc057f412009-07-14 00:43:42 +0000392 // If the CallExpr doesn't have exactly 1 argument just give up checking.
393 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000394 return;
Mike Stump11289f42009-09-09 15:08:12 +0000395
Ted Kremenek49b1e382012-01-26 21:29:00 +0000396 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000397 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000398 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000399 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000400
401 if (!BT) {
402 ASTContext &Ctx = C.getASTContext();
403 Retain = &Ctx.Idents.get("CFRetain");
404 Release = &Ctx.Idents.get("CFRelease");
Jordan Rose721567a2012-11-07 17:12:37 +0000405 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
406 BT.reset(
407 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000408 }
Mike Stump11289f42009-09-09 15:08:12 +0000409
Jordan Rose721567a2012-11-07 17:12:37 +0000410 // Check if we called CFRetain/CFRelease/CFMakeCollectable.
Mike Stump11289f42009-09-09 15:08:12 +0000411 const IdentifierInfo *FuncII = FD->getIdentifier();
Jordan Rose721567a2012-11-07 17:12:37 +0000412 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
Jordy Rose40c5c242010-07-06 02:34:42 +0000413 return;
Mike Stump11289f42009-09-09 15:08:12 +0000414
Jordy Rose40c5c242010-07-06 02:34:42 +0000415 // FIXME: The rest of this just checks that the argument is non-null.
Anna Zaksef893392013-03-09 03:23:14 +0000416 // It should probably be refactored and combined with NonNullParamChecker.
Jordy Rose40c5c242010-07-06 02:34:42 +0000417
418 // Get the argument's value.
419 const Expr *Arg = CE->getArg(0);
Ted Kremenek632e3b72012-01-06 22:09:28 +0000420 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000421 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000422 if (!DefArgVal)
423 return;
424
425 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000426 SValBuilder &svalBuilder = C.getSValBuilder();
David Blaikie2fdacbc2013-02-20 05:52:05 +0000427 DefinedSVal zero =
428 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000429
430 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000431 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000432
433 // Are they equal?
Ted Kremenek49b1e382012-01-26 21:29:00 +0000434 ProgramStateRef stateTrue, stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000435 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000436
437 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000438 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000439 if (!N)
440 return;
441
Jordan Rose721567a2012-11-07 17:12:37 +0000442 const char *description;
443 if (FuncII == Retain)
444 description = "Null pointer argument in call to CFRetain";
445 else if (FuncII == Release)
446 description = "Null pointer argument in call to CFRelease";
447 else if (FuncII == MakeCollectable)
448 description = "Null pointer argument in call to CFMakeCollectable";
449 else
450 llvm_unreachable("impossible case");
Ted Kremenekc057f412009-07-14 00:43:42 +0000451
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000452 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose40c5c242010-07-06 02:34:42 +0000453 report->addRange(Arg->getSourceRange());
Jordan Rosea0f7d352012-08-28 00:50:51 +0000454 bugreporter::trackNullOrUndefValue(N, Arg, *report);
Jordan Rosee10d5a72012-11-02 01:53:40 +0000455 C.emitReport(report);
Jordy Rose40c5c242010-07-06 02:34:42 +0000456 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000457 }
458
Jordy Rose40c5c242010-07-06 02:34:42 +0000459 // From here on, we know the argument is non-null.
Anna Zaksda4c8d62011-10-26 21:06:34 +0000460 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000461}
462
463//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000464// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
465//===----------------------------------------------------------------------===//
466
467namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000468class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000469 mutable Selector releaseS;
470 mutable Selector retainS;
471 mutable Selector autoreleaseS;
472 mutable Selector drainS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000473 mutable OwningPtr<BugType> BT;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000474
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000475public:
Jordan Rose547060b2012-07-02 19:28:04 +0000476 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000477};
478}
479
Jordan Rose547060b2012-07-02 19:28:04 +0000480void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000481 CheckerContext &C) const {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000482
483 if (!BT) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000484 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
485 "instance"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000486
487 ASTContext &Ctx = C.getASTContext();
488 releaseS = GetNullarySelector("release", Ctx);
489 retainS = GetNullarySelector("retain", Ctx);
490 autoreleaseS = GetNullarySelector("autorelease", Ctx);
491 drainS = GetNullarySelector("drain", Ctx);
492 }
493
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000494 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000495 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000496 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
497 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000498
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000499 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000500 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000501 return;
502
Anna Zaksda4c8d62011-10-26 21:06:34 +0000503 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000504 SmallString<200> buf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000505 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000506
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000507 os << "The '" << S.getAsString() << "' message should be sent to instances "
508 "of class '" << Class->getName()
509 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000510
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000511 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000512 report->addRange(msg.getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000513 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000514 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000515}
516
517//===----------------------------------------------------------------------===//
Anders Carlssond91d5f12011-03-13 20:35:21 +0000518// Check for passing non-Objective-C types to variadic methods that expect
519// only Objective-C types.
520//===----------------------------------------------------------------------===//
521
522namespace {
523class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
524 mutable Selector arrayWithObjectsS;
525 mutable Selector dictionaryWithObjectsAndKeysS;
526 mutable Selector setWithObjectsS;
Jordy Rosec0230d72012-04-06 19:06:01 +0000527 mutable Selector orderedSetWithObjectsS;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000528 mutable Selector initWithObjectsS;
529 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000530 mutable OwningPtr<BugType> BT;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000531
Jordan Rose547060b2012-07-02 19:28:04 +0000532 bool isVariadicMessage(const ObjCMethodCall &msg) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000533
534public:
Jordan Rose547060b2012-07-02 19:28:04 +0000535 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000536};
537}
538
539/// isVariadicMessage - Returns whether the given message is a variadic message,
540/// where all arguments must be Objective-C types.
541bool
Jordan Rose547060b2012-07-02 19:28:04 +0000542VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
543 const ObjCMethodDecl *MD = msg.getDecl();
Ted Kremenekced5fea2011-04-12 21:47:05 +0000544
545 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlssond91d5f12011-03-13 20:35:21 +0000546 return false;
547
548 Selector S = msg.getSelector();
549
550 if (msg.isInstanceMessage()) {
551 // FIXME: Ideally we'd look at the receiver interface here, but that's not
552 // useful for init, because alloc returns 'id'. In theory, this could lead
553 // to false positives, for example if there existed a class that had an
554 // initWithObjects: implementation that does accept non-Objective-C pointer
555 // types, but the chance of that happening is pretty small compared to the
556 // gains that this analysis gives.
557 const ObjCInterfaceDecl *Class = MD->getClassInterface();
558
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000559 switch (findKnownClass(Class)) {
560 case FC_NSArray:
561 case FC_NSOrderedSet:
562 case FC_NSSet:
563 return S == initWithObjectsS;
564 case FC_NSDictionary:
565 return S == initWithObjectsAndKeysS;
566 default:
567 return false;
568 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000569 } else {
570 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
571
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000572 switch (findKnownClass(Class)) {
573 case FC_NSArray:
574 return S == arrayWithObjectsS;
575 case FC_NSOrderedSet:
576 return S == orderedSetWithObjectsS;
577 case FC_NSSet:
578 return S == setWithObjectsS;
579 case FC_NSDictionary:
580 return S == dictionaryWithObjectsAndKeysS;
581 default:
582 return false;
583 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000584 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000585}
586
Jordan Rose547060b2012-07-02 19:28:04 +0000587void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Anders Carlssond91d5f12011-03-13 20:35:21 +0000588 CheckerContext &C) const {
589 if (!BT) {
590 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
591 "Objective-C pointer types"));
592
593 ASTContext &Ctx = C.getASTContext();
594 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
595 dictionaryWithObjectsAndKeysS =
596 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
597 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
Jordy Rosec0230d72012-04-06 19:06:01 +0000598 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000599
600 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
601 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
602 }
603
604 if (!isVariadicMessage(msg))
605 return;
606
607 // We are not interested in the selector arguments since they have
608 // well-defined types, so the compiler will issue a warning for them.
609 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
610
611 // We're not interested in the last argument since it has to be nil or the
612 // compiler would have issued a warning for it elsewhere.
613 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
614
615 if (variadicArgsEnd <= variadicArgsBegin)
616 return;
617
618 // Verify that all arguments have Objective-C types.
David Blaikie05785d12013-02-20 22:23:23 +0000619 Optional<ExplodedNode*> errorNode;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000620 ProgramStateRef state = C.getState();
Ted Kremenek066b2262011-03-14 19:50:37 +0000621
Anders Carlssond91d5f12011-03-13 20:35:21 +0000622 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
Jordan Rose547060b2012-07-02 19:28:04 +0000623 QualType ArgTy = msg.getArgExpr(I)->getType();
Anders Carlssond91d5f12011-03-13 20:35:21 +0000624 if (ArgTy->isObjCObjectPointerType())
625 continue;
626
Anders Carlssond1f65f62011-04-19 01:16:46 +0000627 // Block pointers are treaded as Objective-C pointers.
628 if (ArgTy->isBlockPointerType())
629 continue;
630
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000631 // Ignore pointer constants.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000632 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000633 continue;
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000634
Ted Kremenek70727342011-03-17 04:10:25 +0000635 // Ignore pointer types annotated with 'NSObject' attribute.
636 if (C.getASTContext().isObjCNSObjectType(ArgTy))
637 continue;
638
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000639 // Ignore CF references, which can be toll-free bridged.
Ted Kremenekc85964e2011-07-16 19:50:32 +0000640 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000641 continue;
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000642
Ted Kremenek066b2262011-03-14 19:50:37 +0000643 // Generate only one error node to use for all bug reports.
Jordan Rose547060b2012-07-02 19:28:04 +0000644 if (!errorNode.hasValue())
Anna Zaksda4c8d62011-10-26 21:06:34 +0000645 errorNode = C.addTransition();
Ted Kremenek066b2262011-03-14 19:50:37 +0000646
647 if (!errorNode.getValue())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000648 continue;
649
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000650 SmallString<128> sbuf;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000651 llvm::raw_svector_ostream os(sbuf);
652
Jordan Rose547060b2012-07-02 19:28:04 +0000653 StringRef TypeName = GetReceiverInterfaceName(msg);
654 if (!TypeName.empty())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000655 os << "Argument to '" << TypeName << "' method '";
656 else
657 os << "Argument to method '";
658
659 os << msg.getSelector().getAsString()
Jordan Rose547060b2012-07-02 19:28:04 +0000660 << "' should be an Objective-C pointer type, not '";
661 ArgTy.print(os, C.getLangOpts());
662 os << "'";
Anders Carlssond91d5f12011-03-13 20:35:21 +0000663
Jordan Rose547060b2012-07-02 19:28:04 +0000664 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
Anders Carlssond91d5f12011-03-13 20:35:21 +0000665 R->addRange(msg.getArgSourceRange(I));
Jordan Rosee10d5a72012-11-02 01:53:40 +0000666 C.emitReport(R);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000667 }
668}
669
670//===----------------------------------------------------------------------===//
Jordan Roseefef7602012-06-11 16:40:41 +0000671// Improves the modeling of loops over Cocoa collections.
672//===----------------------------------------------------------------------===//
673
674namespace {
675class ObjCLoopChecker
676 : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
677
678public:
679 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
680};
681}
682
683static bool isKnownNonNilCollectionType(QualType T) {
684 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
685 if (!PT)
686 return false;
687
688 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
689 if (!ID)
690 return false;
691
692 switch (findKnownClass(ID)) {
693 case FC_NSArray:
694 case FC_NSDictionary:
695 case FC_NSEnumerator:
696 case FC_NSOrderedSet:
697 case FC_NSSet:
698 return true;
699 default:
700 return false;
701 }
702}
703
704void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
705 CheckerContext &C) const {
706 ProgramStateRef State = C.getState();
707
708 // Check if this is the branch for the end of the loop.
709 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
710 if (CollectionSentinel.isZeroConstant())
711 return;
712
713 // See if the collection is one where we /know/ the elements are non-nil.
714 const Expr *Collection = FCS->getCollection();
715 if (!isKnownNonNilCollectionType(Collection->getType()))
716 return;
717
718 // FIXME: Copied from ExprEngineObjC.
719 const Stmt *Element = FCS->getElement();
720 SVal ElementVar;
721 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
722 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
723 assert(ElemDecl->getInit() == 0);
724 ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
725 } else {
726 ElementVar = State->getSVal(Element, C.getLocationContext());
727 }
728
David Blaikie2fdacbc2013-02-20 05:52:05 +0000729 if (!ElementVar.getAs<Loc>())
Jordan Roseefef7602012-06-11 16:40:41 +0000730 return;
731
732 // Go ahead and assume the value is non-nil.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000733 SVal Val = State->getSVal(ElementVar.castAs<Loc>());
734 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
Jordan Roseefef7602012-06-11 16:40:41 +0000735 C.addTransition(State);
736}
737
Anna Zaks5a5a1752012-08-22 21:19:56 +0000738namespace {
739/// \class ObjCNonNilReturnValueChecker
Anna Zaks4818bbe2012-08-30 19:40:52 +0000740/// \brief The checker restricts the return values of APIs known to
741/// never (or almost never) return 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000742class ObjCNonNilReturnValueChecker
743 : public Checker<check::PostObjCMessage> {
744 mutable bool Initialized;
745 mutable Selector ObjectAtIndex;
746 mutable Selector ObjectAtIndexedSubscript;
Anna Zaks9159e162012-08-22 22:47:58 +0000747
Anna Zaks5a5a1752012-08-22 21:19:56 +0000748public:
Anna Zaks9159e162012-08-22 22:47:58 +0000749 ObjCNonNilReturnValueChecker() : Initialized(false) {}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000750 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
751};
752}
753
Benjamin Kramer199f8da2012-09-10 11:57:16 +0000754static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
755 ProgramStateRef State,
756 CheckerContext &C) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000757 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000758 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
Anna Zaks830c48e2012-08-30 22:55:32 +0000759 return State->assume(*DV, true);
Anna Zaksb504f442012-08-30 22:42:41 +0000760 return State;
Anna Zaks4818bbe2012-08-30 19:40:52 +0000761}
762
Anna Zaks5a5a1752012-08-22 21:19:56 +0000763void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
764 CheckerContext &C)
Anna Zaks4818bbe2012-08-30 19:40:52 +0000765 const {
Anna Zaks5a5a1752012-08-22 21:19:56 +0000766 ProgramStateRef State = C.getState();
767
768 if (!Initialized) {
769 ASTContext &Ctx = C.getASTContext();
770 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
771 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
772 }
773
774 // Check the receiver type.
775 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000776
777 // Assume that object returned from '[self init]' or '[super init]' is not
778 // 'nil' if we are processing an inlined function/method.
779 //
780 // A defensive callee will (and should) check if the object returned by
781 // '[super init]' is 'nil' before doing it's own initialization. However,
782 // since 'nil' is rarely returned in practice, we should not warn when the
783 // caller to the defensive constructor uses the object in contexts where
784 // 'nil' is not accepted.
Anna Zaks49bb6502012-11-06 04:20:54 +0000785 if (!C.inTopFrame() && M.getDecl() &&
Anna Zaks4818bbe2012-08-30 19:40:52 +0000786 M.getDecl()->getMethodFamily() == OMF_init &&
787 M.isReceiverSelfOrSuper()) {
788 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
789 }
790
791 // Objects returned from
792 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
793 // are never 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000794 FoundationClass Cl = findKnownClass(Interface);
795 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
796 Selector Sel = M.getSelector();
797 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
798 // Go ahead and assume the value is non-nil.
Anna Zaks4818bbe2012-08-30 19:40:52 +0000799 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000800 }
801 }
802 }
Anna Zaks4818bbe2012-08-30 19:40:52 +0000803 C.addTransition(State);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000804}
Jordan Roseefef7602012-06-11 16:40:41 +0000805
806//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000807// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000808//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000809
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000810void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000811 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000812}
813
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000814void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000815 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000816}
817
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000818void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000819 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000820}
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000821
822void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000823 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000824}
Anders Carlssond91d5f12011-03-13 20:35:21 +0000825
826void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
827 mgr.registerChecker<VariadicMethodTypeChecker>();
828}
Jordan Roseefef7602012-06-11 16:40:41 +0000829
830void ento::registerObjCLoopChecker(CheckerManager &mgr) {
831 mgr.registerChecker<ObjCLoopChecker>();
832}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000833
834void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
835 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
836}