blob: fdb6bbbe52268eeeadb6a9bc6a41d802357e8174 [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;
130
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000131 if (findKnownClass(ID) == FC_NSString) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000132 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000133
134 if (S.isUnarySelector())
135 return;
136
137 // FIXME: This is going to be really slow doing these checks with
138 // lexical comparisons.
139
140 std::string NameStr = S.getAsString();
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000141 StringRef Name(NameStr);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000142 assert(!Name.empty());
143
144 // FIXME: Checking for initWithFormat: will not work in most cases
145 // yet because [NSString alloc] returns id, not NSString*. We will
146 // need support for tracking expected-type information in the analyzer
147 // to find these errors.
148 if (Name == "caseInsensitiveCompare:" ||
149 Name == "compare:" ||
150 Name == "compare:options:" ||
151 Name == "compare:options:range:" ||
152 Name == "compare:options:range:locale:" ||
153 Name == "componentsSeparatedByCharactersInSet:" ||
154 Name == "initWithFormat:") {
Jordan Rose547060b2012-07-02 19:28:04 +0000155 if (isNil(msg.getArgSVal(0)))
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000156 WarnNilArg(C, msg, 0);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000157 }
158 }
Ted Kremenekc0414922008-03-27 07:25:52 +0000159}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000160
161//===----------------------------------------------------------------------===//
162// Error reporting.
163//===----------------------------------------------------------------------===//
164
165namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000166class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000167 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000168 mutable IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000169public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000170 CFNumberCreateChecker() : II(0) {}
171
172 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
173
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000174private:
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000175 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000176 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000177};
178} // end anonymous namespace
179
180enum CFNumberType {
181 kCFNumberSInt8Type = 1,
182 kCFNumberSInt16Type = 2,
183 kCFNumberSInt32Type = 3,
184 kCFNumberSInt64Type = 4,
185 kCFNumberFloat32Type = 5,
186 kCFNumberFloat64Type = 6,
187 kCFNumberCharType = 7,
188 kCFNumberShortType = 8,
189 kCFNumberIntType = 9,
190 kCFNumberLongType = 10,
191 kCFNumberLongLongType = 11,
192 kCFNumberFloatType = 12,
193 kCFNumberDoubleType = 13,
194 kCFNumberCFIndexType = 14,
195 kCFNumberNSIntegerType = 15,
196 kCFNumberCGFloatType = 16
197};
198
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000199static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000200 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000201
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000202 if (i < kCFNumberCharType)
203 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000204
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000205 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000206
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000207 switch (i) {
208 case kCFNumberCharType: T = Ctx.CharTy; break;
209 case kCFNumberShortType: T = Ctx.ShortTy; break;
210 case kCFNumberIntType: T = Ctx.IntTy; break;
211 case kCFNumberLongType: T = Ctx.LongTy; break;
212 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
213 case kCFNumberFloatType: T = Ctx.FloatTy; break;
214 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
215 case kCFNumberCFIndexType:
216 case kCFNumberNSIntegerType:
217 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000218 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000219 default:
David Blaikie7a30dc52013-02-21 01:47:18 +0000220 return None;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000221 }
Mike Stump11289f42009-09-09 15:08:12 +0000222
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000223 return Ctx.getTypeSize(T);
224}
225
226#if 0
227static const char* GetCFNumberTypeStr(uint64_t i) {
228 static const char* Names[] = {
229 "kCFNumberSInt8Type",
230 "kCFNumberSInt16Type",
231 "kCFNumberSInt32Type",
232 "kCFNumberSInt64Type",
233 "kCFNumberFloat32Type",
234 "kCFNumberFloat64Type",
235 "kCFNumberCharType",
236 "kCFNumberShortType",
237 "kCFNumberIntType",
238 "kCFNumberLongType",
239 "kCFNumberLongLongType",
240 "kCFNumberFloatType",
241 "kCFNumberDoubleType",
242 "kCFNumberCFIndexType",
243 "kCFNumberNSIntegerType",
244 "kCFNumberCGFloatType"
245 };
Mike Stump11289f42009-09-09 15:08:12 +0000246
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000247 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
248}
249#endif
250
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000251void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
252 CheckerContext &C) const {
Ted Kremenek49b1e382012-01-26 21:29:00 +0000253 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000254 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000255 if (!FD)
256 return;
257
258 ASTContext &Ctx = C.getASTContext();
259 if (!II)
260 II = &Ctx.Idents.get("CFNumberCreate");
261
262 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
263 return;
Mike Stump11289f42009-09-09 15:08:12 +0000264
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000265 // Get the value of the "theType" argument.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000266 const LocationContext *LCtx = C.getLocationContext();
267 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000268
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000269 // FIXME: We really should allow ranges of valid theType values, and
270 // bifurcate the state appropriately.
David Blaikie05785d12013-02-20 22:23:23 +0000271 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000272 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000273 return;
Mike Stump11289f42009-09-09 15:08:12 +0000274
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000275 uint64_t NumberKind = V->getValue().getLimitedValue();
David Blaikie05785d12013-02-20 22:23:23 +0000276 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000277
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000278 // FIXME: In some cases we can emit an error.
David Blaikiee359f3c2013-02-20 22:23:03 +0000279 if (!OptTargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000280 return;
Mike Stump11289f42009-09-09 15:08:12 +0000281
David Blaikiee359f3c2013-02-20 22:23:03 +0000282 uint64_t TargetSize = *OptTargetSize;
283
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000284 // Look at the value of the integer being passed by reference. Essentially
285 // we want to catch cases where the value passed in is not equal to the
286 // size of the type being created.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000287 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000288
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000289 // FIXME: Eventually we should handle arbitrary locations. We can do this
290 // by having an enhanced memory model that does low-level typing.
David Blaikie05785d12013-02-20 22:23:23 +0000291 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000292 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000293 return;
Mike Stump11289f42009-09-09 15:08:12 +0000294
Ted Kremenek8df44b262011-08-12 20:02:48 +0000295 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000296 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000297 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000298
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000299 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000300
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000301 // FIXME: If the pointee isn't an integer type, should we flag a warning?
302 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000303
304 if (!T->isIntegerType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000305 return;
Mike Stump11289f42009-09-09 15:08:12 +0000306
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000307 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000308
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000309 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000310 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000311 return;
Mike Stump11289f42009-09-09 15:08:12 +0000312
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000313 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
314 // otherwise generate a regular node.
315 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000316 // FIXME: We can actually create an abstract "CFNumber" object that has
317 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000318 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000319 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaksda4c8d62011-10-26 21:06:34 +0000320 : C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000321 SmallString<128> sbuf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000322 llvm::raw_svector_ostream os(sbuf);
323
324 os << (SourceSize == 8 ? "An " : "A ")
325 << SourceSize << " bit integer is used to initialize a CFNumber "
326 "object that represents "
327 << (TargetSize == 8 ? "an " : "a ")
328 << TargetSize << " bit integer. ";
329
330 if (SourceSize < TargetSize)
331 os << (TargetSize - SourceSize)
332 << " bits of the CFNumber value will be garbage." ;
333 else
334 os << (SourceSize - TargetSize)
335 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000336
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000337 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000338 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000339
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000340 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000341 report->addRange(CE->getArg(2)->getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000342 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000343 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000344}
345
Ted Kremenek1f352db2008-07-22 16:21:24 +0000346//===----------------------------------------------------------------------===//
Jordan Rose721567a2012-11-07 17:12:37 +0000347// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000348//===----------------------------------------------------------------------===//
349
350namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000351class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000352 mutable OwningPtr<APIMisuse> BT;
Jordan Rose721567a2012-11-07 17:12:37 +0000353 mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
Ted Kremenekc057f412009-07-14 00:43:42 +0000354public:
Jordan Rose721567a2012-11-07 17:12:37 +0000355 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000356 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenekc057f412009-07-14 00:43:42 +0000357};
358} // end anonymous namespace
359
360
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000361void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
362 CheckerContext &C) const {
Ted Kremenekc057f412009-07-14 00:43:42 +0000363 // If the CallExpr doesn't have exactly 1 argument just give up checking.
364 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000365 return;
Mike Stump11289f42009-09-09 15:08:12 +0000366
Ted Kremenek49b1e382012-01-26 21:29:00 +0000367 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000368 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000369 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000370 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000371
372 if (!BT) {
373 ASTContext &Ctx = C.getASTContext();
374 Retain = &Ctx.Idents.get("CFRetain");
375 Release = &Ctx.Idents.get("CFRelease");
Jordan Rose721567a2012-11-07 17:12:37 +0000376 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
377 BT.reset(
378 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000379 }
Mike Stump11289f42009-09-09 15:08:12 +0000380
Jordan Rose721567a2012-11-07 17:12:37 +0000381 // Check if we called CFRetain/CFRelease/CFMakeCollectable.
Mike Stump11289f42009-09-09 15:08:12 +0000382 const IdentifierInfo *FuncII = FD->getIdentifier();
Jordan Rose721567a2012-11-07 17:12:37 +0000383 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
Jordy Rose40c5c242010-07-06 02:34:42 +0000384 return;
Mike Stump11289f42009-09-09 15:08:12 +0000385
Jordy Rose40c5c242010-07-06 02:34:42 +0000386 // FIXME: The rest of this just checks that the argument is non-null.
Anna Zaksef893392013-03-09 03:23:14 +0000387 // It should probably be refactored and combined with NonNullParamChecker.
Jordy Rose40c5c242010-07-06 02:34:42 +0000388
389 // Get the argument's value.
390 const Expr *Arg = CE->getArg(0);
Ted Kremenek632e3b72012-01-06 22:09:28 +0000391 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000392 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000393 if (!DefArgVal)
394 return;
395
396 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000397 SValBuilder &svalBuilder = C.getSValBuilder();
David Blaikie2fdacbc2013-02-20 05:52:05 +0000398 DefinedSVal zero =
399 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
Jordy Rose40c5c242010-07-06 02:34:42 +0000400
401 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000402 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000403
404 // Are they equal?
Ted Kremenek49b1e382012-01-26 21:29:00 +0000405 ProgramStateRef stateTrue, stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000406 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000407
408 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000409 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000410 if (!N)
411 return;
412
Jordan Rose721567a2012-11-07 17:12:37 +0000413 const char *description;
414 if (FuncII == Retain)
415 description = "Null pointer argument in call to CFRetain";
416 else if (FuncII == Release)
417 description = "Null pointer argument in call to CFRelease";
418 else if (FuncII == MakeCollectable)
419 description = "Null pointer argument in call to CFMakeCollectable";
420 else
421 llvm_unreachable("impossible case");
Ted Kremenekc057f412009-07-14 00:43:42 +0000422
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000423 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose40c5c242010-07-06 02:34:42 +0000424 report->addRange(Arg->getSourceRange());
Jordan Rosea0f7d352012-08-28 00:50:51 +0000425 bugreporter::trackNullOrUndefValue(N, Arg, *report);
Jordan Rosee10d5a72012-11-02 01:53:40 +0000426 C.emitReport(report);
Jordy Rose40c5c242010-07-06 02:34:42 +0000427 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000428 }
429
Jordy Rose40c5c242010-07-06 02:34:42 +0000430 // From here on, we know the argument is non-null.
Anna Zaksda4c8d62011-10-26 21:06:34 +0000431 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000432}
433
434//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000435// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
436//===----------------------------------------------------------------------===//
437
438namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000439class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000440 mutable Selector releaseS;
441 mutable Selector retainS;
442 mutable Selector autoreleaseS;
443 mutable Selector drainS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000444 mutable OwningPtr<BugType> BT;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000445
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000446public:
Jordan Rose547060b2012-07-02 19:28:04 +0000447 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000448};
449}
450
Jordan Rose547060b2012-07-02 19:28:04 +0000451void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000452 CheckerContext &C) const {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000453
454 if (!BT) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000455 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
456 "instance"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000457
458 ASTContext &Ctx = C.getASTContext();
459 releaseS = GetNullarySelector("release", Ctx);
460 retainS = GetNullarySelector("retain", Ctx);
461 autoreleaseS = GetNullarySelector("autorelease", Ctx);
462 drainS = GetNullarySelector("drain", Ctx);
463 }
464
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000465 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000466 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000467 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
468 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000469
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000470 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000471 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000472 return;
473
Anna Zaksda4c8d62011-10-26 21:06:34 +0000474 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000475 SmallString<200> buf;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000476 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000477
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000478 os << "The '" << S.getAsString() << "' message should be sent to instances "
479 "of class '" << Class->getName()
480 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000481
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000482 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000483 report->addRange(msg.getSourceRange());
Jordan Rosee10d5a72012-11-02 01:53:40 +0000484 C.emitReport(report);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000485 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000486}
487
488//===----------------------------------------------------------------------===//
Anders Carlssond91d5f12011-03-13 20:35:21 +0000489// Check for passing non-Objective-C types to variadic methods that expect
490// only Objective-C types.
491//===----------------------------------------------------------------------===//
492
493namespace {
494class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
495 mutable Selector arrayWithObjectsS;
496 mutable Selector dictionaryWithObjectsAndKeysS;
497 mutable Selector setWithObjectsS;
Jordy Rosec0230d72012-04-06 19:06:01 +0000498 mutable Selector orderedSetWithObjectsS;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000499 mutable Selector initWithObjectsS;
500 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmithe2778992012-02-05 02:12:40 +0000501 mutable OwningPtr<BugType> BT;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000502
Jordan Rose547060b2012-07-02 19:28:04 +0000503 bool isVariadicMessage(const ObjCMethodCall &msg) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000504
505public:
Jordan Rose547060b2012-07-02 19:28:04 +0000506 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000507};
508}
509
510/// isVariadicMessage - Returns whether the given message is a variadic message,
511/// where all arguments must be Objective-C types.
512bool
Jordan Rose547060b2012-07-02 19:28:04 +0000513VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
514 const ObjCMethodDecl *MD = msg.getDecl();
Ted Kremenekced5fea2011-04-12 21:47:05 +0000515
516 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlssond91d5f12011-03-13 20:35:21 +0000517 return false;
518
519 Selector S = msg.getSelector();
520
521 if (msg.isInstanceMessage()) {
522 // FIXME: Ideally we'd look at the receiver interface here, but that's not
523 // useful for init, because alloc returns 'id'. In theory, this could lead
524 // to false positives, for example if there existed a class that had an
525 // initWithObjects: implementation that does accept non-Objective-C pointer
526 // types, but the chance of that happening is pretty small compared to the
527 // gains that this analysis gives.
528 const ObjCInterfaceDecl *Class = MD->getClassInterface();
529
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000530 switch (findKnownClass(Class)) {
531 case FC_NSArray:
532 case FC_NSOrderedSet:
533 case FC_NSSet:
534 return S == initWithObjectsS;
535 case FC_NSDictionary:
536 return S == initWithObjectsAndKeysS;
537 default:
538 return false;
539 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000540 } else {
541 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
542
Jordan Rose3ba8ae32012-06-11 16:40:37 +0000543 switch (findKnownClass(Class)) {
544 case FC_NSArray:
545 return S == arrayWithObjectsS;
546 case FC_NSOrderedSet:
547 return S == orderedSetWithObjectsS;
548 case FC_NSSet:
549 return S == setWithObjectsS;
550 case FC_NSDictionary:
551 return S == dictionaryWithObjectsAndKeysS;
552 default:
553 return false;
554 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000555 }
Anders Carlssond91d5f12011-03-13 20:35:21 +0000556}
557
Jordan Rose547060b2012-07-02 19:28:04 +0000558void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Anders Carlssond91d5f12011-03-13 20:35:21 +0000559 CheckerContext &C) const {
560 if (!BT) {
561 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
562 "Objective-C pointer types"));
563
564 ASTContext &Ctx = C.getASTContext();
565 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
566 dictionaryWithObjectsAndKeysS =
567 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
568 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
Jordy Rosec0230d72012-04-06 19:06:01 +0000569 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000570
571 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
572 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
573 }
574
575 if (!isVariadicMessage(msg))
576 return;
577
578 // We are not interested in the selector arguments since they have
579 // well-defined types, so the compiler will issue a warning for them.
580 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
581
582 // We're not interested in the last argument since it has to be nil or the
583 // compiler would have issued a warning for it elsewhere.
584 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
585
586 if (variadicArgsEnd <= variadicArgsBegin)
587 return;
588
589 // Verify that all arguments have Objective-C types.
David Blaikie05785d12013-02-20 22:23:23 +0000590 Optional<ExplodedNode*> errorNode;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000591 ProgramStateRef state = C.getState();
Ted Kremenek066b2262011-03-14 19:50:37 +0000592
Anders Carlssond91d5f12011-03-13 20:35:21 +0000593 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
Jordan Rose547060b2012-07-02 19:28:04 +0000594 QualType ArgTy = msg.getArgExpr(I)->getType();
Anders Carlssond91d5f12011-03-13 20:35:21 +0000595 if (ArgTy->isObjCObjectPointerType())
596 continue;
597
Anders Carlssond1f65f62011-04-19 01:16:46 +0000598 // Block pointers are treaded as Objective-C pointers.
599 if (ArgTy->isBlockPointerType())
600 continue;
601
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000602 // Ignore pointer constants.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000603 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000604 continue;
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000605
Ted Kremenek70727342011-03-17 04:10:25 +0000606 // Ignore pointer types annotated with 'NSObject' attribute.
607 if (C.getASTContext().isObjCNSObjectType(ArgTy))
608 continue;
609
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000610 // Ignore CF references, which can be toll-free bridged.
Ted Kremenekc85964e2011-07-16 19:50:32 +0000611 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000612 continue;
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000613
Ted Kremenek066b2262011-03-14 19:50:37 +0000614 // Generate only one error node to use for all bug reports.
Jordan Rose547060b2012-07-02 19:28:04 +0000615 if (!errorNode.hasValue())
Anna Zaksda4c8d62011-10-26 21:06:34 +0000616 errorNode = C.addTransition();
Ted Kremenek066b2262011-03-14 19:50:37 +0000617
618 if (!errorNode.getValue())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000619 continue;
620
Dylan Noblesmith2c1dd272012-02-05 02:13:05 +0000621 SmallString<128> sbuf;
Anders Carlssond91d5f12011-03-13 20:35:21 +0000622 llvm::raw_svector_ostream os(sbuf);
623
Jordan Rose547060b2012-07-02 19:28:04 +0000624 StringRef TypeName = GetReceiverInterfaceName(msg);
625 if (!TypeName.empty())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000626 os << "Argument to '" << TypeName << "' method '";
627 else
628 os << "Argument to method '";
629
630 os << msg.getSelector().getAsString()
Jordan Rose547060b2012-07-02 19:28:04 +0000631 << "' should be an Objective-C pointer type, not '";
632 ArgTy.print(os, C.getLangOpts());
633 os << "'";
Anders Carlssond91d5f12011-03-13 20:35:21 +0000634
Jordan Rose547060b2012-07-02 19:28:04 +0000635 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
Anders Carlssond91d5f12011-03-13 20:35:21 +0000636 R->addRange(msg.getArgSourceRange(I));
Jordan Rosee10d5a72012-11-02 01:53:40 +0000637 C.emitReport(R);
Anders Carlssond91d5f12011-03-13 20:35:21 +0000638 }
639}
640
641//===----------------------------------------------------------------------===//
Jordan Roseefef7602012-06-11 16:40:41 +0000642// Improves the modeling of loops over Cocoa collections.
643//===----------------------------------------------------------------------===//
644
645namespace {
646class ObjCLoopChecker
647 : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
648
649public:
650 void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
651};
652}
653
654static bool isKnownNonNilCollectionType(QualType T) {
655 const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
656 if (!PT)
657 return false;
658
659 const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
660 if (!ID)
661 return false;
662
663 switch (findKnownClass(ID)) {
664 case FC_NSArray:
665 case FC_NSDictionary:
666 case FC_NSEnumerator:
667 case FC_NSOrderedSet:
668 case FC_NSSet:
669 return true;
670 default:
671 return false;
672 }
673}
674
675void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
676 CheckerContext &C) const {
677 ProgramStateRef State = C.getState();
678
679 // Check if this is the branch for the end of the loop.
680 SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
681 if (CollectionSentinel.isZeroConstant())
682 return;
683
684 // See if the collection is one where we /know/ the elements are non-nil.
685 const Expr *Collection = FCS->getCollection();
686 if (!isKnownNonNilCollectionType(Collection->getType()))
687 return;
688
689 // FIXME: Copied from ExprEngineObjC.
690 const Stmt *Element = FCS->getElement();
691 SVal ElementVar;
692 if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
693 const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
694 assert(ElemDecl->getInit() == 0);
695 ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
696 } else {
697 ElementVar = State->getSVal(Element, C.getLocationContext());
698 }
699
David Blaikie2fdacbc2013-02-20 05:52:05 +0000700 if (!ElementVar.getAs<Loc>())
Jordan Roseefef7602012-06-11 16:40:41 +0000701 return;
702
703 // Go ahead and assume the value is non-nil.
David Blaikie2fdacbc2013-02-20 05:52:05 +0000704 SVal Val = State->getSVal(ElementVar.castAs<Loc>());
705 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
Jordan Roseefef7602012-06-11 16:40:41 +0000706 C.addTransition(State);
707}
708
Anna Zaks5a5a1752012-08-22 21:19:56 +0000709namespace {
710/// \class ObjCNonNilReturnValueChecker
Anna Zaks4818bbe2012-08-30 19:40:52 +0000711/// \brief The checker restricts the return values of APIs known to
712/// never (or almost never) return 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000713class ObjCNonNilReturnValueChecker
714 : public Checker<check::PostObjCMessage> {
715 mutable bool Initialized;
716 mutable Selector ObjectAtIndex;
717 mutable Selector ObjectAtIndexedSubscript;
Anna Zaks9159e162012-08-22 22:47:58 +0000718
Anna Zaks5a5a1752012-08-22 21:19:56 +0000719public:
Anna Zaks9159e162012-08-22 22:47:58 +0000720 ObjCNonNilReturnValueChecker() : Initialized(false) {}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000721 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
722};
723}
724
Benjamin Kramer199f8da2012-09-10 11:57:16 +0000725static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
726 ProgramStateRef State,
727 CheckerContext &C) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000728 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
David Blaikie05785d12013-02-20 22:23:23 +0000729 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
Anna Zaks830c48e2012-08-30 22:55:32 +0000730 return State->assume(*DV, true);
Anna Zaksb504f442012-08-30 22:42:41 +0000731 return State;
Anna Zaks4818bbe2012-08-30 19:40:52 +0000732}
733
Anna Zaks5a5a1752012-08-22 21:19:56 +0000734void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
735 CheckerContext &C)
Anna Zaks4818bbe2012-08-30 19:40:52 +0000736 const {
Anna Zaks5a5a1752012-08-22 21:19:56 +0000737 ProgramStateRef State = C.getState();
738
739 if (!Initialized) {
740 ASTContext &Ctx = C.getASTContext();
741 ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
742 ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
743 }
744
745 // Check the receiver type.
746 if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
Anna Zaks4818bbe2012-08-30 19:40:52 +0000747
748 // Assume that object returned from '[self init]' or '[super init]' is not
749 // 'nil' if we are processing an inlined function/method.
750 //
751 // A defensive callee will (and should) check if the object returned by
752 // '[super init]' is 'nil' before doing it's own initialization. However,
753 // since 'nil' is rarely returned in practice, we should not warn when the
754 // caller to the defensive constructor uses the object in contexts where
755 // 'nil' is not accepted.
Anna Zaks49bb6502012-11-06 04:20:54 +0000756 if (!C.inTopFrame() && M.getDecl() &&
Anna Zaks4818bbe2012-08-30 19:40:52 +0000757 M.getDecl()->getMethodFamily() == OMF_init &&
758 M.isReceiverSelfOrSuper()) {
759 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
760 }
761
762 // Objects returned from
763 // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
764 // are never 'nil'.
Anna Zaks5a5a1752012-08-22 21:19:56 +0000765 FoundationClass Cl = findKnownClass(Interface);
766 if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
767 Selector Sel = M.getSelector();
768 if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
769 // Go ahead and assume the value is non-nil.
Anna Zaks4818bbe2012-08-30 19:40:52 +0000770 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000771 }
772 }
773 }
Anna Zaks4818bbe2012-08-30 19:40:52 +0000774 C.addTransition(State);
Anna Zaks5a5a1752012-08-22 21:19:56 +0000775}
Jordan Roseefef7602012-06-11 16:40:41 +0000776
777//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000778// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000779//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000780
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000781void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000782 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000783}
784
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000785void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000786 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000787}
788
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000789void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000790 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000791}
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000792
793void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000794 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000795}
Anders Carlssond91d5f12011-03-13 20:35:21 +0000796
797void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
798 mgr.registerChecker<VariadicMethodTypeChecker>();
799}
Jordan Roseefef7602012-06-11 16:40:41 +0000800
801void ento::registerObjCLoopChecker(CheckerManager &mgr) {
802 mgr.registerChecker<ObjCLoopChecker>();
803}
Anna Zaks5a5a1752012-08-22 21:19:56 +0000804
805void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
806 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
807}