blob: cbe144be9b40ac174e9d5974cf78be4a7a62a584 [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"
Ted Kremenek6fa1dae2011-03-17 04:01:35 +000017#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000018#include "clang/StaticAnalyzer/Core/Checker.h"
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +000019#include "clang/StaticAnalyzer/Core/CheckerManager.h"
Argyrios Kyrtzidisdff865d2011-02-23 01:05:36 +000020#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000021#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000022#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
Jordy Rose087611e2011-09-02 08:02:59 +000023#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
Ted Kremenek001fd5b2011-08-15 22:09:50 +000024#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
Ted Kremenekf8cbac42011-02-10 01:03:03 +000025#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000027#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000028#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000029#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000030#include "clang/AST/ASTContext.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000031
Ted Kremenekc0414922008-03-27 07:25:52 +000032using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000033using namespace ento;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000034
Ted Kremenekbd2c8002010-10-20 23:38:56 +000035namespace {
36class APIMisuse : public BugType {
37public:
38 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
39};
40} // end anonymous namespace
41
42//===----------------------------------------------------------------------===//
43// Utility functions.
44//===----------------------------------------------------------------------===//
45
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000046static const char* GetReceiverNameType(const ObjCMessage &msg) {
Anders Carlsson3c50aea2011-03-08 20:05:26 +000047 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
48 return ID->getIdentifier()->getNameStart();
49 return 0;
Ted Kremenek276278e2008-03-27 22:05:32 +000050}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000051
Anders Carlsson3c50aea2011-03-08 20:05:26 +000052static bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
Chris Lattner0e62c1c2011-07-23 10:55:15 +000053 StringRef ClassName) {
Anders Carlsson3c50aea2011-03-08 20:05:26 +000054 if (ID->getIdentifier()->getName() == ClassName)
55 return true;
56
57 if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
58 return isReceiverClassOrSuperclass(Super, ClassName);
59
60 return false;
Ted Kremenekc0414922008-03-27 07:25:52 +000061}
62
Zhongxing Xu27f17422008-10-17 05:57:07 +000063static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +000064 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +000065}
66
Ted Kremenekc0414922008-03-27 07:25:52 +000067//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +000068// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenekc0414922008-03-27 07:25:52 +000069//===----------------------------------------------------------------------===//
70
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000071namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +000072 class NilArgChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000073 mutable llvm::OwningPtr<APIMisuse> BT;
74
75 void WarnNilArg(CheckerContext &C,
76 const ObjCMessage &msg, unsigned Arg) const;
77
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000078 public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000079 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000080 };
81}
Mike Stump11289f42009-09-09 15:08:12 +000082
Ted Kremenekbd2c8002010-10-20 23:38:56 +000083void NilArgChecker::WarnNilArg(CheckerContext &C,
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000084 const ObjCMessage &msg,
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000085 unsigned int Arg) const
Ted Kremenekbd2c8002010-10-20 23:38:56 +000086{
87 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +000088 BT.reset(new APIMisuse("nil argument"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +000089
Ted Kremenek750b7ac2010-12-20 21:19:09 +000090 if (ExplodedNode *N = C.generateSink()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +000091 llvm::SmallString<128> sbuf;
92 llvm::raw_svector_ostream os(sbuf);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000093 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
94 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump11289f42009-09-09 15:08:12 +000095
Anna Zaks3a6bdf82011-08-17 23:00:25 +000096 BugReport *R = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000097 R->addRange(msg.getArgSourceRange(Arg));
Ted Kremenekbd2c8002010-10-20 23:38:56 +000098 C.EmitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +000099 }
Ted Kremenek276278e2008-03-27 22:05:32 +0000100}
101
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000102void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
103 CheckerContext &C) const {
Anders Carlsson3c50aea2011-03-08 20:05:26 +0000104 const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
105 if (!ID)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000106 return;
107
Anders Carlsson3c50aea2011-03-08 20:05:26 +0000108 if (isReceiverClassOrSuperclass(ID, "NSString")) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000109 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000110
111 if (S.isUnarySelector())
112 return;
113
114 // FIXME: This is going to be really slow doing these checks with
115 // lexical comparisons.
116
117 std::string NameStr = S.getAsString();
Chris Lattner0e62c1c2011-07-23 10:55:15 +0000118 StringRef Name(NameStr);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000119 assert(!Name.empty());
120
121 // FIXME: Checking for initWithFormat: will not work in most cases
122 // yet because [NSString alloc] returns id, not NSString*. We will
123 // need support for tracking expected-type information in the analyzer
124 // to find these errors.
125 if (Name == "caseInsensitiveCompare:" ||
126 Name == "compare:" ||
127 Name == "compare:options:" ||
128 Name == "compare:options:range:" ||
129 Name == "compare:options:range:locale:" ||
130 Name == "componentsSeparatedByCharactersInSet:" ||
131 Name == "initWithFormat:") {
Ted Kremenek632e3b72012-01-06 22:09:28 +0000132 if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000133 WarnNilArg(C, msg, 0);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000134 }
135 }
Ted Kremenekc0414922008-03-27 07:25:52 +0000136}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000137
138//===----------------------------------------------------------------------===//
139// Error reporting.
140//===----------------------------------------------------------------------===//
141
142namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000143class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000144 mutable llvm::OwningPtr<APIMisuse> BT;
145 mutable IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000146public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000147 CFNumberCreateChecker() : II(0) {}
148
149 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
150
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000151private:
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000152 void EmitError(const TypedRegion* R, const Expr *Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000153 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000154};
155} // end anonymous namespace
156
157enum CFNumberType {
158 kCFNumberSInt8Type = 1,
159 kCFNumberSInt16Type = 2,
160 kCFNumberSInt32Type = 3,
161 kCFNumberSInt64Type = 4,
162 kCFNumberFloat32Type = 5,
163 kCFNumberFloat64Type = 6,
164 kCFNumberCharType = 7,
165 kCFNumberShortType = 8,
166 kCFNumberIntType = 9,
167 kCFNumberLongType = 10,
168 kCFNumberLongLongType = 11,
169 kCFNumberFloatType = 12,
170 kCFNumberDoubleType = 13,
171 kCFNumberCFIndexType = 14,
172 kCFNumberNSIntegerType = 15,
173 kCFNumberCGFloatType = 16
174};
175
176namespace {
177 template<typename T>
178 class Optional {
179 bool IsKnown;
180 T Val;
181 public:
182 Optional() : IsKnown(false), Val(0) {}
183 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000184
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000185 bool isKnown() const { return IsKnown; }
186
187 const T& getValue() const {
188 assert (isKnown());
189 return Val;
190 }
191
192 operator const T&() const {
193 return getValue();
194 }
195 };
196}
197
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000198static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000199 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000200
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000201 if (i < kCFNumberCharType)
202 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000203
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000204 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000205
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000206 switch (i) {
207 case kCFNumberCharType: T = Ctx.CharTy; break;
208 case kCFNumberShortType: T = Ctx.ShortTy; break;
209 case kCFNumberIntType: T = Ctx.IntTy; break;
210 case kCFNumberLongType: T = Ctx.LongTy; break;
211 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
212 case kCFNumberFloatType: T = Ctx.FloatTy; break;
213 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
214 case kCFNumberCFIndexType:
215 case kCFNumberNSIntegerType:
216 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000217 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000218 default:
219 return Optional<uint64_t>();
220 }
Mike Stump11289f42009-09-09 15:08:12 +0000221
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000222 return Ctx.getTypeSize(T);
223}
224
225#if 0
226static const char* GetCFNumberTypeStr(uint64_t i) {
227 static const char* Names[] = {
228 "kCFNumberSInt8Type",
229 "kCFNumberSInt16Type",
230 "kCFNumberSInt32Type",
231 "kCFNumberSInt64Type",
232 "kCFNumberFloat32Type",
233 "kCFNumberFloat64Type",
234 "kCFNumberCharType",
235 "kCFNumberShortType",
236 "kCFNumberIntType",
237 "kCFNumberLongType",
238 "kCFNumberLongLongType",
239 "kCFNumberFloatType",
240 "kCFNumberDoubleType",
241 "kCFNumberCFIndexType",
242 "kCFNumberNSIntegerType",
243 "kCFNumberCGFloatType"
244 };
Mike Stump11289f42009-09-09 15:08:12 +0000245
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000246 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
247}
248#endif
249
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000250void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
251 CheckerContext &C) const {
Ted Kremenek49b1e382012-01-26 21:29:00 +0000252 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000253 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000254 if (!FD)
255 return;
256
257 ASTContext &Ctx = C.getASTContext();
258 if (!II)
259 II = &Ctx.Idents.get("CFNumberCreate");
260
261 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
262 return;
Mike Stump11289f42009-09-09 15:08:12 +0000263
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000264 // Get the value of the "theType" argument.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000265 const LocationContext *LCtx = C.getLocationContext();
266 SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000267
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000268 // FIXME: We really should allow ranges of valid theType values, and
269 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000270 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000271 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000272 return;
Mike Stump11289f42009-09-09 15:08:12 +0000273
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000274 uint64_t NumberKind = V->getValue().getLimitedValue();
275 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000276
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000277 // FIXME: In some cases we can emit an error.
278 if (!TargetSize.isKnown())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000279 return;
Mike Stump11289f42009-09-09 15:08:12 +0000280
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000281 // Look at the value of the integer being passed by reference. Essentially
282 // we want to catch cases where the value passed in is not equal to the
283 // size of the type being created.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000284 SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
Mike Stump11289f42009-09-09 15:08:12 +0000285
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000286 // FIXME: Eventually we should handle arbitrary locations. We can do this
287 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000288 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000289 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000290 return;
Mike Stump11289f42009-09-09 15:08:12 +0000291
Ted Kremenek8df44b262011-08-12 20:02:48 +0000292 const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000293 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000294 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000295
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000296 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000297
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000298 // FIXME: If the pointee isn't an integer type, should we flag a warning?
299 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000300
301 if (!T->isIntegerType())
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 SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000305
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000306 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000307 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000308 return;
Mike Stump11289f42009-09-09 15:08:12 +0000309
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000310 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
311 // otherwise generate a regular node.
312 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000313 // FIXME: We can actually create an abstract "CFNumber" object that has
314 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000315 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000316 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
Anna Zaksda4c8d62011-10-26 21:06:34 +0000317 : C.addTransition()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000318 llvm::SmallString<128> sbuf;
319 llvm::raw_svector_ostream os(sbuf);
320
321 os << (SourceSize == 8 ? "An " : "A ")
322 << SourceSize << " bit integer is used to initialize a CFNumber "
323 "object that represents "
324 << (TargetSize == 8 ? "an " : "a ")
325 << TargetSize << " bit integer. ";
326
327 if (SourceSize < TargetSize)
328 os << (TargetSize - SourceSize)
329 << " bits of the CFNumber value will be garbage." ;
330 else
331 os << (SourceSize - TargetSize)
332 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000333
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000334 if (!BT)
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000335 BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000336
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000337 BugReport *report = new BugReport(*BT, os.str(), N);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000338 report->addRange(CE->getArg(2)->getSourceRange());
339 C.EmitReport(report);
340 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000341}
342
Ted Kremenek1f352db2008-07-22 16:21:24 +0000343//===----------------------------------------------------------------------===//
Jordy Rose40c5c242010-07-06 02:34:42 +0000344// CFRetain/CFRelease checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000345//===----------------------------------------------------------------------===//
346
347namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000348class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000349 mutable llvm::OwningPtr<APIMisuse> BT;
350 mutable IdentifierInfo *Retain, *Release;
Ted Kremenekc057f412009-07-14 00:43:42 +0000351public:
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000352 CFRetainReleaseChecker(): Retain(0), Release(0) {}
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000353 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
Ted Kremenekc057f412009-07-14 00:43:42 +0000354};
355} // end anonymous namespace
356
357
Ted Kremenek5ef32db2011-08-12 23:37:29 +0000358void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
359 CheckerContext &C) const {
Ted Kremenekc057f412009-07-14 00:43:42 +0000360 // If the CallExpr doesn't have exactly 1 argument just give up checking.
361 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000362 return;
Mike Stump11289f42009-09-09 15:08:12 +0000363
Ted Kremenek49b1e382012-01-26 21:29:00 +0000364 ProgramStateRef state = C.getState();
Anna Zaksc6aa5312011-12-01 05:57:37 +0000365 const FunctionDecl *FD = C.getCalleeDecl(CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000366 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000367 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000368
369 if (!BT) {
370 ASTContext &Ctx = C.getASTContext();
371 Retain = &Ctx.Idents.get("CFRetain");
372 Release = &Ctx.Idents.get("CFRelease");
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000373 BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000374 }
Mike Stump11289f42009-09-09 15:08:12 +0000375
Jordy Rose40c5c242010-07-06 02:34:42 +0000376 // Check if we called CFRetain/CFRelease.
Mike Stump11289f42009-09-09 15:08:12 +0000377 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000378 if (!(FuncII == Retain || FuncII == Release))
Jordy Rose40c5c242010-07-06 02:34:42 +0000379 return;
Mike Stump11289f42009-09-09 15:08:12 +0000380
Jordy Rose40c5c242010-07-06 02:34:42 +0000381 // FIXME: The rest of this just checks that the argument is non-null.
382 // It should probably be refactored and combined with AttrNonNullChecker.
383
384 // Get the argument's value.
385 const Expr *Arg = CE->getArg(0);
Ted Kremenek632e3b72012-01-06 22:09:28 +0000386 SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
Jordy Rose40c5c242010-07-06 02:34:42 +0000387 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
388 if (!DefArgVal)
389 return;
390
391 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000392 SValBuilder &svalBuilder = C.getSValBuilder();
393 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
Jordy Rose40c5c242010-07-06 02:34:42 +0000394
395 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000396 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000397
398 // Are they equal?
Ted Kremenek49b1e382012-01-26 21:29:00 +0000399 ProgramStateRef stateTrue, stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000400 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000401
402 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000403 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000404 if (!N)
405 return;
406
Ted Kremenekc057f412009-07-14 00:43:42 +0000407 const char *description = (FuncII == Retain)
408 ? "Null pointer argument in call to CFRetain"
409 : "Null pointer argument in call to CFRelease";
410
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000411 BugReport *report = new BugReport(*BT, description, N);
Jordy Rose40c5c242010-07-06 02:34:42 +0000412 report->addRange(Arg->getSourceRange());
Anna Zaksf86615c2011-08-19 22:33:38 +0000413 report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
Jordy Rose40c5c242010-07-06 02:34:42 +0000414 C.EmitReport(report);
415 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000416 }
417
Jordy Rose40c5c242010-07-06 02:34:42 +0000418 // From here on, we know the argument is non-null.
Anna Zaksda4c8d62011-10-26 21:06:34 +0000419 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000420}
421
422//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000423// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
424//===----------------------------------------------------------------------===//
425
426namespace {
Argyrios Kyrtzidis6a5674f2011-03-01 01:16:21 +0000427class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000428 mutable Selector releaseS;
429 mutable Selector retainS;
430 mutable Selector autoreleaseS;
431 mutable Selector drainS;
432 mutable llvm::OwningPtr<BugType> BT;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000433
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000434public:
435 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000436};
437}
438
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000439void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
440 CheckerContext &C) const {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000441
442 if (!BT) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000443 BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
444 "instance"));
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000445
446 ASTContext &Ctx = C.getASTContext();
447 releaseS = GetNullarySelector("release", Ctx);
448 retainS = GetNullarySelector("retain", Ctx);
449 autoreleaseS = GetNullarySelector("autorelease", Ctx);
450 drainS = GetNullarySelector("drain", Ctx);
451 }
452
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000453 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000454 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000455 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
456 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000457
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000458 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000459 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000460 return;
461
Anna Zaksda4c8d62011-10-26 21:06:34 +0000462 if (ExplodedNode *N = C.addTransition()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000463 llvm::SmallString<200> buf;
464 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000465
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000466 os << "The '" << S.getAsString() << "' message should be sent to instances "
467 "of class '" << Class->getName()
468 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000469
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000470 BugReport *report = new BugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000471 report->addRange(msg.getSourceRange());
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000472 C.EmitReport(report);
473 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000474}
475
476//===----------------------------------------------------------------------===//
Anders Carlssond91d5f12011-03-13 20:35:21 +0000477// Check for passing non-Objective-C types to variadic methods that expect
478// only Objective-C types.
479//===----------------------------------------------------------------------===//
480
481namespace {
482class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
483 mutable Selector arrayWithObjectsS;
484 mutable Selector dictionaryWithObjectsAndKeysS;
485 mutable Selector setWithObjectsS;
486 mutable Selector initWithObjectsS;
487 mutable Selector initWithObjectsAndKeysS;
488 mutable llvm::OwningPtr<BugType> BT;
489
490 bool isVariadicMessage(const ObjCMessage &msg) const;
491
492public:
493 void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
494};
495}
496
497/// isVariadicMessage - Returns whether the given message is a variadic message,
498/// where all arguments must be Objective-C types.
499bool
500VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
501 const ObjCMethodDecl *MD = msg.getMethodDecl();
Ted Kremenekced5fea2011-04-12 21:47:05 +0000502
503 if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
Anders Carlssond91d5f12011-03-13 20:35:21 +0000504 return false;
505
506 Selector S = msg.getSelector();
507
508 if (msg.isInstanceMessage()) {
509 // FIXME: Ideally we'd look at the receiver interface here, but that's not
510 // useful for init, because alloc returns 'id'. In theory, this could lead
511 // to false positives, for example if there existed a class that had an
512 // initWithObjects: implementation that does accept non-Objective-C pointer
513 // types, but the chance of that happening is pretty small compared to the
514 // gains that this analysis gives.
515 const ObjCInterfaceDecl *Class = MD->getClassInterface();
516
517 // -[NSArray initWithObjects:]
518 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
519 S == initWithObjectsS)
520 return true;
521
522 // -[NSDictionary initWithObjectsAndKeys:]
523 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
524 S == initWithObjectsAndKeysS)
525 return true;
526
527 // -[NSSet initWithObjects:]
528 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
529 S == initWithObjectsS)
530 return true;
531 } else {
532 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
533
534 // -[NSArray arrayWithObjects:]
535 if (isReceiverClassOrSuperclass(Class, "NSArray") &&
536 S == arrayWithObjectsS)
537 return true;
538
539 // -[NSDictionary dictionaryWithObjectsAndKeys:]
540 if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
541 S == dictionaryWithObjectsAndKeysS)
542 return true;
543
544 // -[NSSet setWithObjects:]
545 if (isReceiverClassOrSuperclass(Class, "NSSet") &&
546 S == setWithObjectsS)
547 return true;
548 }
549
550 return false;
551}
552
553void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
554 CheckerContext &C) const {
555 if (!BT) {
556 BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
557 "Objective-C pointer types"));
558
559 ASTContext &Ctx = C.getASTContext();
560 arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
561 dictionaryWithObjectsAndKeysS =
562 GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
563 setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
564
565 initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
566 initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
567 }
568
569 if (!isVariadicMessage(msg))
570 return;
571
572 // We are not interested in the selector arguments since they have
573 // well-defined types, so the compiler will issue a warning for them.
574 unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
575
576 // We're not interested in the last argument since it has to be nil or the
577 // compiler would have issued a warning for it elsewhere.
578 unsigned variadicArgsEnd = msg.getNumArgs() - 1;
579
580 if (variadicArgsEnd <= variadicArgsBegin)
581 return;
582
583 // Verify that all arguments have Objective-C types.
Ted Kremenek066b2262011-03-14 19:50:37 +0000584 llvm::Optional<ExplodedNode*> errorNode;
Ted Kremenek49b1e382012-01-26 21:29:00 +0000585 ProgramStateRef state = C.getState();
Ted Kremenek066b2262011-03-14 19:50:37 +0000586
Anders Carlssond91d5f12011-03-13 20:35:21 +0000587 for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
588 QualType ArgTy = msg.getArgType(I);
589 if (ArgTy->isObjCObjectPointerType())
590 continue;
591
Anders Carlssond1f65f62011-04-19 01:16:46 +0000592 // Block pointers are treaded as Objective-C pointers.
593 if (ArgTy->isBlockPointerType())
594 continue;
595
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000596 // Ignore pointer constants.
Ted Kremenek632e3b72012-01-06 22:09:28 +0000597 if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
598 state)))
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000599 continue;
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000600
Ted Kremenek70727342011-03-17 04:10:25 +0000601 // Ignore pointer types annotated with 'NSObject' attribute.
602 if (C.getASTContext().isObjCNSObjectType(ArgTy))
603 continue;
604
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000605 // Ignore CF references, which can be toll-free bridged.
Ted Kremenekc85964e2011-07-16 19:50:32 +0000606 if (coreFoundation::isCFObjectRef(ArgTy))
Ted Kremenek6fa1dae2011-03-17 04:01:35 +0000607 continue;
Ted Kremenek4ceebbf2011-03-16 00:22:51 +0000608
Ted Kremenek066b2262011-03-14 19:50:37 +0000609 // Generate only one error node to use for all bug reports.
610 if (!errorNode.hasValue()) {
Anna Zaksda4c8d62011-10-26 21:06:34 +0000611 errorNode = C.addTransition();
Ted Kremenek066b2262011-03-14 19:50:37 +0000612 }
613
614 if (!errorNode.getValue())
Anders Carlssond91d5f12011-03-13 20:35:21 +0000615 continue;
616
617 llvm::SmallString<128> sbuf;
618 llvm::raw_svector_ostream os(sbuf);
619
620 if (const char *TypeName = GetReceiverNameType(msg))
621 os << "Argument to '" << TypeName << "' method '";
622 else
623 os << "Argument to method '";
624
625 os << msg.getSelector().getAsString()
626 << "' should be an Objective-C pointer type, not '"
627 << ArgTy.getAsString() << "'";
628
Anna Zaks3a6bdf82011-08-17 23:00:25 +0000629 BugReport *R = new BugReport(*BT, os.str(),
Ted Kremenek066b2262011-03-14 19:50:37 +0000630 errorNode.getValue());
Anders Carlssond91d5f12011-03-13 20:35:21 +0000631 R->addRange(msg.getArgSourceRange(I));
632 C.EmitReport(R);
633 }
634}
635
636//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000637// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000638//===----------------------------------------------------------------------===//
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000639
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000640void ento::registerNilArgChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000641 mgr.registerChecker<NilArgChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000642}
643
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000644void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000645 mgr.registerChecker<CFNumberCreateChecker>();
Argyrios Kyrtzidis9d4d4f92011-02-16 01:40:52 +0000646}
647
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000648void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000649 mgr.registerChecker<CFRetainReleaseChecker>();
Ted Kremenek1f352db2008-07-22 16:21:24 +0000650}
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000651
652void ento::registerClassReleaseChecker(CheckerManager &mgr) {
Argyrios Kyrtzidisdd058d82011-02-23 00:16:10 +0000653 mgr.registerChecker<ClassReleaseChecker>();
Argyrios Kyrtzidis507ff532011-02-17 21:39:17 +0000654}
Anders Carlssond91d5f12011-03-13 20:35:21 +0000655
656void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
657 mgr.registerChecker<VariadicMethodTypeChecker>();
658}