blob: 26dbb7f2506eb9274deca60d476b06f49c219ea4 [file] [log] [blame]
Ted Kremenek99c6ad32008-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 Kyrtzidis0b1ba622011-02-16 01:40:52 +000016#include "ClangSACheckers.h"
Chandler Carruth55fc8732012-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 Kremenek928c4152011-03-17 04:01:35 +000022#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000023#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000024#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +000025#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Jordan Rosef540c542012-07-26 21:39:41 +000026#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
Argyrios Kyrtzidis983326f2011-02-23 01:05:36 +000027#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000028#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000029#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Ted Kremenek9b663712011-02-10 01:03:03 +000030#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
Chandler Carruth55fc8732012-12-04 09:13:33 +000031#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
Benjamin Kramer8fe83e12012-02-04 13:45:25 +000032#include "llvm/ADT/SmallString.h"
Jordan Rose9765ea92012-06-11 16:40:37 +000033#include "llvm/ADT/StringMap.h"
Benjamin Kramera93d0f22012-12-01 17:12:56 +000034#include "llvm/Support/raw_ostream.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000035
Ted Kremenek99c6ad32008-03-27 07:25:52 +000036using namespace clang;
Ted Kremenek9ef65372010-12-23 07:20:52 +000037using namespace ento;
Ted Kremenek52755612008-03-27 17:17:22 +000038
Ted Kremenek2ce2baa2010-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 Rosede507ea2012-07-02 19:28:04 +000050static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
Anders Carlssonb62bdce2011-03-08 20:05:26 +000051 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
Jordan Rosede507ea2012-07-02 19:28:04 +000052 return ID->getIdentifier()->getName();
53 return StringRef();
Ted Kremenek4ba62832008-03-27 22:05:32 +000054}
Ted Kremenek52755612008-03-27 17:17:22 +000055
Jordan Rose9765ea92012-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 Carlssonb62bdce2011-03-08 20:05:26 +000065
Jordan Rose9765ea92012-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 Carlssonb62bdce2011-03-08 20:05:26 +000076
Jordan Rose9765ea92012-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 Kremenek99c6ad32008-03-27 07:25:52 +000084}
85
Zhongxing Xu1c96b242008-10-17 05:57:07 +000086static inline bool isNil(SVal X) {
David Blaikie0adb1752013-02-21 06:05:05 +000087 return X.getAs<loc::ConcreteInt>().hasValue();
Ted Kremeneke5d5c202008-03-27 21:15:17 +000088}
89
Ted Kremenek99c6ad32008-03-27 07:25:52 +000090//===----------------------------------------------------------------------===//
Ted Kremenek2ce2baa2010-10-20 23:38:56 +000091// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenek99c6ad32008-03-27 07:25:52 +000092//===----------------------------------------------------------------------===//
93
Benjamin Kramercb9c0742010-10-22 16:33:16 +000094namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +000095 class NilArgChecker : public Checker<check::PreObjCMessage> {
Dylan Noblesmith6f42b622012-02-05 02:12:40 +000096 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +000097
98 void WarnNilArg(CheckerContext &C,
Jordan Rosede507ea2012-07-02 19:28:04 +000099 const ObjCMethodCall &msg, unsigned Arg) const;
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000100
Benjamin Kramercb9c0742010-10-22 16:33:16 +0000101 public:
Jordan Rosede507ea2012-07-02 19:28:04 +0000102 void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
Benjamin Kramercb9c0742010-10-22 16:33:16 +0000103 };
104}
Mike Stump1eb44332009-09-09 15:08:12 +0000105
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000106void NilArgChecker::WarnNilArg(CheckerContext &C,
Jordan Rosede507ea2012-07-02 19:28:04 +0000107 const ObjCMethodCall &msg,
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000108 unsigned int Arg) const
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000109{
110 if (!BT)
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000111 BT.reset(new APIMisuse("nil argument"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000112
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000113 if (ExplodedNode *N = C.generateSink()) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000114 SmallString<128> sbuf;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000115 llvm::raw_svector_ostream os(sbuf);
Jordan Rosede507ea2012-07-02 19:28:04 +0000116 os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000117 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump1eb44332009-09-09 15:08:12 +0000118
Anna Zakse172e8b2011-08-17 23:00:25 +0000119 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000120 R->addRange(msg.getArgSourceRange(Arg));
Jordan Rose785950e2012-11-02 01:53:40 +0000121 C.emitReport(R);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000122 }
Ted Kremenek4ba62832008-03-27 22:05:32 +0000123}
124
Jordan Rosede507ea2012-07-02 19:28:04 +0000125void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000126 CheckerContext &C) const {
Anders Carlssonb62bdce2011-03-08 20:05:26 +0000127 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
128 if (!ID)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000129 return;
130
Jordan Rose9765ea92012-06-11 16:40:37 +0000131 if (findKnownClass(ID) == FC_NSString) {
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000132 Selector S = msg.getSelector();
Ted Kremenek2ce2baa2010-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 Lattner5f9e2722011-07-23 10:55:15 +0000141 StringRef Name(NameStr);
Ted Kremenek2ce2baa2010-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 Rosede507ea2012-07-02 19:28:04 +0000155 if (isNil(msg.getArgSVal(0)))
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000156 WarnNilArg(C, msg, 0);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000157 }
158 }
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000159}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000160
161//===----------------------------------------------------------------------===//
162// Error reporting.
163//===----------------------------------------------------------------------===//
164
165namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000166class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000167 mutable OwningPtr<APIMisuse> BT;
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000168 mutable IdentifierInfo* II;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000169public:
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000170 CFNumberCreateChecker() : II(0) {}
171
172 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
173
Ted Kremenek04bc8762008-06-26 23:59:48 +0000174private:
Ted Kremenek9c378f72011-08-12 23:37:29 +0000175 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump1eb44332009-09-09 15:08:12 +0000176 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenek04bc8762008-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 Kremenek9c378f72011-08-12 23:37:29 +0000199static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopes2550d702009-12-23 17:49:57 +0000200 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump1eb44332009-09-09 15:08:12 +0000201
Ted Kremenek04bc8762008-06-26 23:59:48 +0000202 if (i < kCFNumberCharType)
203 return FixedSize[i-1];
Mike Stump1eb44332009-09-09 15:08:12 +0000204
Ted Kremenek04bc8762008-06-26 23:59:48 +0000205 QualType T;
Mike Stump1eb44332009-09-09 15:08:12 +0000206
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000218 // FIXME: We need a way to map from names to Type*.
Ted Kremenek04bc8762008-06-26 23:59:48 +0000219 default:
David Blaikie66874fb2013-02-21 01:47:18 +0000220 return None;
Ted Kremenek04bc8762008-06-26 23:59:48 +0000221 }
Mike Stump1eb44332009-09-09 15:08:12 +0000222
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000246
Ted Kremenek04bc8762008-06-26 23:59:48 +0000247 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
248}
249#endif
250
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000251void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
252 CheckerContext &C) const {
Ted Kremenek8bef8232012-01-26 21:29:00 +0000253 ProgramStateRef state = C.getState();
Anna Zaksb805c8f2011-12-01 05:57:37 +0000254 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenek2ce2baa2010-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 Stump1eb44332009-09-09 15:08:12 +0000264
Ted Kremenek04bc8762008-06-26 23:59:48 +0000265 // Get the value of the "theType" argument.
Ted Kremenek5eca4822012-01-06 22:09:28 +0000266 const LocationContext *LCtx = C.getLocationContext();
267 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump1eb44332009-09-09 15:08:12 +0000268
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000269 // FIXME: We really should allow ranges of valid theType values, and
270 // bifurcate the state appropriately.
David Blaikiedc84cd52013-02-20 22:23:23 +0000271 Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000272 if (!V)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000273 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000274
Ted Kremenek04bc8762008-06-26 23:59:48 +0000275 uint64_t NumberKind = V->getValue().getLimitedValue();
David Blaikiedc84cd52013-02-20 22:23:23 +0000276 Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump1eb44332009-09-09 15:08:12 +0000277
Ted Kremenek04bc8762008-06-26 23:59:48 +0000278 // FIXME: In some cases we can emit an error.
David Blaikie9e85b292013-02-20 22:23:03 +0000279 if (!OptTargetSize)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000280 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000281
David Blaikie9e85b292013-02-20 22:23:03 +0000282 uint64_t TargetSize = *OptTargetSize;
283
Ted Kremenek04bc8762008-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 Kremenek5eca4822012-01-06 22:09:28 +0000287 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump1eb44332009-09-09 15:08:12 +0000288
Ted Kremenek04bc8762008-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 Blaikiedc84cd52013-02-20 22:23:23 +0000291 Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
Ted Kremenek04bc8762008-06-26 23:59:48 +0000292 if (!LV)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000293 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000294
Ted Kremenek96979342011-08-12 20:02:48 +0000295 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000296 if (!R)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000297 return;
Ted Kremenek5e77eba2009-07-29 18:17:40 +0000298
Zhongxing Xu018220c2010-08-11 06:10:55 +0000299 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump1eb44332009-09-09 15:08:12 +0000300
Ted Kremenek04bc8762008-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 Stump1eb44332009-09-09 15:08:12 +0000303
304 if (!T->isIntegerType())
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000305 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000306
Ted Kremenek04bc8762008-06-26 23:59:48 +0000307 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump1eb44332009-09-09 15:08:12 +0000308
Ted Kremenek04bc8762008-06-26 23:59:48 +0000309 // CHECK: is SourceSize == TargetSize
Ted Kremenek04bc8762008-06-26 23:59:48 +0000310 if (SourceSize == TargetSize)
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000311 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000312
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000313 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
314 // otherwise generate a regular node.
315 //
Ted Kremenek04bc8762008-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 Kremenek2ce2baa2010-10-20 23:38:56 +0000318 //
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000319 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaks0bd6b112011-10-26 21:06:34 +0000320 : C.addTransition()) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000321 SmallString<128> sbuf;
Ted Kremenek2ce2baa2010-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 Kremenek04bc8762008-06-26 23:59:48 +0000336
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000337 if (!BT)
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000338 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000339
Anna Zakse172e8b2011-08-17 23:00:25 +0000340 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000341 report->addRange(CE->getArg(2)->getSourceRange());
Jordan Rose785950e2012-11-02 01:53:40 +0000342 C.emitReport(report);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000343 }
Ted Kremenek04bc8762008-06-26 23:59:48 +0000344}
345
Ted Kremenek78d46242008-07-22 16:21:24 +0000346//===----------------------------------------------------------------------===//
Jordan Rose0fe4d402012-11-07 17:12:37 +0000347// CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000348//===----------------------------------------------------------------------===//
349
350namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000351class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000352 mutable OwningPtr<APIMisuse> BT;
Jordan Rose0fe4d402012-11-07 17:12:37 +0000353 mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000354public:
Jordan Rose0fe4d402012-11-07 17:12:37 +0000355 CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
Ted Kremenek9c378f72011-08-12 23:37:29 +0000356 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000357};
358} // end anonymous namespace
359
360
Ted Kremenek9c378f72011-08-12 23:37:29 +0000361void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
362 CheckerContext &C) const {
Ted Kremenek79b4f7d2009-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 Rose61fb55c2010-07-06 02:34:42 +0000365 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000366
Ted Kremenek8bef8232012-01-26 21:29:00 +0000367 ProgramStateRef state = C.getState();
Anna Zaksb805c8f2011-12-01 05:57:37 +0000368 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000369 if (!FD)
Jordy Rose61fb55c2010-07-06 02:34:42 +0000370 return;
Ted Kremenek2ce2baa2010-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 Rose0fe4d402012-11-07 17:12:37 +0000376 MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
377 BT.reset(
378 new APIMisuse("null passed to CFRetain/CFRelease/CFMakeCollectable"));
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000379 }
Mike Stump1eb44332009-09-09 15:08:12 +0000380
Jordan Rose0fe4d402012-11-07 17:12:37 +0000381 // Check if we called CFRetain/CFRelease/CFMakeCollectable.
Mike Stump1eb44332009-09-09 15:08:12 +0000382 const IdentifierInfo *FuncII = FD->getIdentifier();
Jordan Rose0fe4d402012-11-07 17:12:37 +0000383 if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
Jordy Rose61fb55c2010-07-06 02:34:42 +0000384 return;
Mike Stump1eb44332009-09-09 15:08:12 +0000385
Jordy Rose61fb55c2010-07-06 02:34:42 +0000386 // FIXME: The rest of this just checks that the argument is non-null.
387 // It should probably be refactored and combined with AttrNonNullChecker.
388
389 // Get the argument's value.
390 const Expr *Arg = CE->getArg(0);
Ted Kremenek5eca4822012-01-06 22:09:28 +0000391 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
David Blaikiedc84cd52013-02-20 22:23:23 +0000392 Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
Jordy Rose61fb55c2010-07-06 02:34:42 +0000393 if (!DefArgVal)
394 return;
395
396 // Get a NULL value.
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000397 SValBuilder &svalBuilder = C.getSValBuilder();
David Blaikie5251abe2013-02-20 05:52:05 +0000398 DefinedSVal zero =
399 svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
Jordy Rose61fb55c2010-07-06 02:34:42 +0000400
401 // Make an expression asserting that they're equal.
Ted Kremenekc8413fd2010-12-02 07:49:45 +0000402 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000403
404 // Are they equal?
Ted Kremenek8bef8232012-01-26 21:29:00 +0000405 ProgramStateRef stateTrue, stateFalse;
Ted Kremenek28f47b92010-12-01 22:16:56 +0000406 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000407
408 if (stateTrue && !stateFalse) {
Ted Kremenekd048c6e2010-12-20 21:19:09 +0000409 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000410 if (!N)
411 return;
412
Jordan Rose0fe4d402012-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 Kremenek79b4f7d2009-07-14 00:43:42 +0000422
Anna Zakse172e8b2011-08-17 23:00:25 +0000423 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000424 report->addRange(Arg->getSourceRange());
Jordan Rosea1f81bb2012-08-28 00:50:51 +0000425 bugreporter::trackNullOrUndefValue(N, Arg, *report);
Jordan Rose785950e2012-11-02 01:53:40 +0000426 C.emitReport(report);
Jordy Rose61fb55c2010-07-06 02:34:42 +0000427 return;
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000428 }
429
Jordy Rose61fb55c2010-07-06 02:34:42 +0000430 // From here on, we know the argument is non-null.
Anna Zaks0bd6b112011-10-26 21:06:34 +0000431 C.addTransition(stateFalse);
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000432}
433
434//===----------------------------------------------------------------------===//
Ted Kremenek50e837b2009-11-20 05:27:05 +0000435// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
436//===----------------------------------------------------------------------===//
437
438namespace {
Argyrios Kyrtzidisec8605f2011-03-01 01:16:21 +0000439class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000440 mutable Selector releaseS;
441 mutable Selector retainS;
442 mutable Selector autoreleaseS;
443 mutable Selector drainS;
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000444 mutable OwningPtr<BugType> BT;
Ted Kremenek50e837b2009-11-20 05:27:05 +0000445
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000446public:
Jordan Rosede507ea2012-07-02 19:28:04 +0000447 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Ted Kremenek50e837b2009-11-20 05:27:05 +0000448};
449}
450
Jordan Rosede507ea2012-07-02 19:28:04 +0000451void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000452 CheckerContext &C) const {
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000453
454 if (!BT) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000455 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
456 "instance"));
Ted Kremenek2ce2baa2010-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 Kyrtzidis432424d2011-01-25 00:03:53 +0000465 if (msg.isInstanceMessage())
Ted Kremenek50e837b2009-11-20 05:27:05 +0000466 return;
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000467 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
468 assert(Class);
Douglas Gregor04badcf2010-04-21 00:45:42 +0000469
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000470 Selector S = msg.getSelector();
Benjamin Kramer921ddc42009-11-20 10:03:00 +0000471 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremenek50e837b2009-11-20 05:27:05 +0000472 return;
473
Anna Zaks0bd6b112011-10-26 21:06:34 +0000474 if (ExplodedNode *N = C.addTransition()) {
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000475 SmallString<200> buf;
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000476 llvm::raw_svector_ostream os(buf);
Ted Kremenek19d67b52009-11-23 22:22:01 +0000477
Ted Kremenek2ce2baa2010-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 Kremenek50e837b2009-11-20 05:27:05 +0000481
Anna Zakse172e8b2011-08-17 23:00:25 +0000482 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis432424d2011-01-25 00:03:53 +0000483 report->addRange(msg.getSourceRange());
Jordan Rose785950e2012-11-02 01:53:40 +0000484 C.emitReport(report);
Ted Kremenek2ce2baa2010-10-20 23:38:56 +0000485 }
Ted Kremenek50e837b2009-11-20 05:27:05 +0000486}
487
488//===----------------------------------------------------------------------===//
Anders Carlsson4597b7b2011-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 Rosef439e002012-04-06 19:06:01 +0000498 mutable Selector orderedSetWithObjectsS;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000499 mutable Selector initWithObjectsS;
500 mutable Selector initWithObjectsAndKeysS;
Dylan Noblesmith6f42b622012-02-05 02:12:40 +0000501 mutable OwningPtr<BugType> BT;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000502
Jordan Rosede507ea2012-07-02 19:28:04 +0000503 bool isVariadicMessage(const ObjCMethodCall &msg) const;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000504
505public:
Jordan Rosede507ea2012-07-02 19:28:04 +0000506 void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
Anders Carlsson4597b7b2011-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 Rosede507ea2012-07-02 19:28:04 +0000513VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
514 const ObjCMethodDecl *MD = msg.getDecl();
Ted Kremenek9281efe2011-04-12 21:47:05 +0000515
516 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlsson4597b7b2011-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 Rose9765ea92012-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 Carlsson4597b7b2011-03-13 20:35:21 +0000540 } else {
541 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
542
Jordan Rose9765ea92012-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 Carlsson4597b7b2011-03-13 20:35:21 +0000555 }
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000556}
557
Jordan Rosede507ea2012-07-02 19:28:04 +0000558void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
Anders Carlsson4597b7b2011-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 Rosef439e002012-04-06 19:06:01 +0000569 orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
Anders Carlsson4597b7b2011-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 Blaikiedc84cd52013-02-20 22:23:23 +0000590 Optional<ExplodedNode*> errorNode;
Ted Kremenek8bef8232012-01-26 21:29:00 +0000591 ProgramStateRef state = C.getState();
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000592
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000593 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
Jordan Rosede507ea2012-07-02 19:28:04 +0000594 QualType ArgTy = msg.getArgExpr(I)->getType();
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000595 if (ArgTy->isObjCObjectPointerType())
596 continue;
597
Anders Carlssonf05982b2011-04-19 01:16:46 +0000598 // Block pointers are treaded as Objective-C pointers.
599 if (ArgTy->isBlockPointerType())
600 continue;
601
Ted Kremenekd5fde212011-03-16 00:22:51 +0000602 // Ignore pointer constants.
David Blaikie5251abe2013-02-20 05:52:05 +0000603 if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
Ted Kremenekd5fde212011-03-16 00:22:51 +0000604 continue;
Ted Kremenek928c4152011-03-17 04:01:35 +0000605
Ted Kremenekf3f92932011-03-17 04:10:25 +0000606 // Ignore pointer types annotated with 'NSObject' attribute.
607 if (C.getASTContext().isObjCNSObjectType(ArgTy))
608 continue;
609
Ted Kremenek928c4152011-03-17 04:01:35 +0000610 // Ignore CF references, which can be toll-free bridged.
Ted Kremenek05560482011-07-16 19:50:32 +0000611 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek928c4152011-03-17 04:01:35 +0000612 continue;
Ted Kremenekd5fde212011-03-16 00:22:51 +0000613
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000614 // Generate only one error node to use for all bug reports.
Jordan Rosede507ea2012-07-02 19:28:04 +0000615 if (!errorNode.hasValue())
Anna Zaks0bd6b112011-10-26 21:06:34 +0000616 errorNode = C.addTransition();
Ted Kremenek6fb5c1f2011-03-14 19:50:37 +0000617
618 if (!errorNode.getValue())
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000619 continue;
620
Dylan Noblesmithf7ccbad2012-02-05 02:13:05 +0000621 SmallString<128> sbuf;
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000622 llvm::raw_svector_ostream os(sbuf);
623
Jordan Rosede507ea2012-07-02 19:28:04 +0000624 StringRef TypeName = GetReceiverInterfaceName(msg);
625 if (!TypeName.empty())
Anders Carlsson4597b7b2011-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 Rosede507ea2012-07-02 19:28:04 +0000631 << "' should be an Objective-C pointer type, not '";
632 ArgTy.print(os, C.getLangOpts());
633 os << "'";
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000634
Jordan Rosede507ea2012-07-02 19:28:04 +0000635 BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000636 R->addRange(msg.getArgSourceRange(I));
Jordan Rose785950e2012-11-02 01:53:40 +0000637 C.emitReport(R);
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000638 }
639}
640
641//===----------------------------------------------------------------------===//
Jordan Rose1895a0a2012-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 Blaikie5251abe2013-02-20 05:52:05 +0000700 if (!ElementVar.getAs<Loc>())
Jordan Rose1895a0a2012-06-11 16:40:41 +0000701 return;
702
703 // Go ahead and assume the value is non-nil.
David Blaikie5251abe2013-02-20 05:52:05 +0000704 SVal Val = State->getSVal(ElementVar.castAs<Loc>());
705 State = State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
Jordan Rose1895a0a2012-06-11 16:40:41 +0000706 C.addTransition(State);
707}
708
Anna Zaks26663612012-08-22 21:19:56 +0000709namespace {
710/// \class ObjCNonNilReturnValueChecker
Anna Zaks05fcbd32012-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 Zaks26663612012-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 Zaks769bc072012-08-22 22:47:58 +0000718
Anna Zaks26663612012-08-22 21:19:56 +0000719public:
Anna Zaks769bc072012-08-22 22:47:58 +0000720 ObjCNonNilReturnValueChecker() : Initialized(false) {}
Anna Zaks26663612012-08-22 21:19:56 +0000721 void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
722};
723}
724
Benjamin Kramerda885362012-09-10 11:57:16 +0000725static ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
726 ProgramStateRef State,
727 CheckerContext &C) {
Anna Zaks05fcbd32012-08-30 19:40:52 +0000728 SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
David Blaikiedc84cd52013-02-20 22:23:23 +0000729 if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
Anna Zaks43d39742012-08-30 22:55:32 +0000730 return State->assume(*DV, true);
Anna Zaksdc601f42012-08-30 22:42:41 +0000731 return State;
Anna Zaks05fcbd32012-08-30 19:40:52 +0000732}
733
Anna Zaks26663612012-08-22 21:19:56 +0000734void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
735 CheckerContext &C)
Anna Zaks05fcbd32012-08-30 19:40:52 +0000736 const {
Anna Zaks26663612012-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 Zaks05fcbd32012-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 Zaksd1ad5e52012-11-06 04:20:54 +0000756 if (!C.inTopFrame() && M.getDecl() &&
Anna Zaks05fcbd32012-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 Zaks26663612012-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 Zaks05fcbd32012-08-30 19:40:52 +0000770 State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
Anna Zaks26663612012-08-22 21:19:56 +0000771 }
772 }
773 }
Anna Zaks05fcbd32012-08-30 19:40:52 +0000774 C.addTransition(State);
Anna Zaks26663612012-08-22 21:19:56 +0000775}
Jordan Rose1895a0a2012-06-11 16:40:41 +0000776
777//===----------------------------------------------------------------------===//
Ted Kremenek78d46242008-07-22 16:21:24 +0000778// Check registration.
Ted Kremenek79b4f7d2009-07-14 00:43:42 +0000779//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000780
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000781void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000782 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000783}
784
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000785void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000786 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis0b1ba622011-02-16 01:40:52 +0000787}
788
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000789void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000790 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek78d46242008-07-22 16:21:24 +0000791}
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000792
793void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidis74eed0e2011-02-23 00:16:10 +0000794 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis695fb502011-02-17 21:39:17 +0000795}
Anders Carlsson4597b7b2011-03-13 20:35:21 +0000796
797void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
798 mgr.registerChecker<VariadicMethodTypeChecker>();
799}
Jordan Rose1895a0a2012-06-11 16:40:41 +0000800
801void ento::registerObjCLoopChecker(CheckerManager &mgr) {
802 mgr.registerChecker<ObjCLoopChecker>();
803}
Anna Zaks26663612012-08-22 21:19:56 +0000804
805void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
806 mgr.registerChecker<ObjCNonNilReturnValueChecker>();
807}