blob: 8fa7e24cab006c477d4a034b28cd26276e70c7ea [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
Ted Kremeneka4d60b62008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekd99bd552010-12-23 19:38:26 +000018#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h"
19#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
20#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h"
21#include "clang/StaticAnalyzer/PathSensitive/GRState.h"
22#include "clang/StaticAnalyzer/BugReporter/BugType.h"
23#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h"
24#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h"
25#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
Daniel Dunbar6e8aa532008-08-11 05:35:13 +000026#include "clang/AST/DeclObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000027#include "clang/AST/Expr.h"
Steve Naroff021ca182008-05-29 21:12:08 +000028#include "clang/AST/ExprObjC.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000029#include "clang/AST/ASTContext.h"
Ted Kremenekc0414922008-03-27 07:25:52 +000030
Ted Kremenekc0414922008-03-27 07:25:52 +000031using namespace clang;
Ted Kremenek98857c92010-12-23 07:20:52 +000032using namespace ento;
Ted Kremeneka4d60b62008-03-27 17:17:22 +000033
Ted Kremenekbd2c8002010-10-20 23:38:56 +000034namespace {
35class APIMisuse : public BugType {
36public:
37 APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
38};
39} // end anonymous namespace
40
41//===----------------------------------------------------------------------===//
42// Utility functions.
43//===----------------------------------------------------------------------===//
44
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000045static const ObjCInterfaceType* GetReceiverType(const ObjCMessage &msg) {
46 if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
Argyrios Kyrtzidis8e169a52011-01-25 00:03:45 +000047 return ID->getTypeForDecl()->getAs<ObjCInterfaceType>();
Ted Kremenekf20e2282008-04-30 22:48:21 +000048 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000049}
50
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000051static const char* GetReceiverNameType(const ObjCMessage &msg) {
52 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(msg))
Daniel Dunbar2c422dc92009-10-18 20:26:12 +000053 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
54 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000055}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000056
Ted Kremenekbd2c8002010-10-20 23:38:56 +000057static bool isNSString(llvm::StringRef ClassName) {
58 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenekc0414922008-03-27 07:25:52 +000059}
60
Zhongxing Xu27f17422008-10-17 05:57:07 +000061static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +000062 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +000063}
64
Ted Kremenekc0414922008-03-27 07:25:52 +000065//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +000066// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenekc0414922008-03-27 07:25:52 +000067//===----------------------------------------------------------------------===//
68
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000069namespace {
70 class NilArgChecker : public CheckerVisitor<NilArgChecker> {
71 APIMisuse *BT;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000072 void WarnNilArg(CheckerContext &C, const ObjCMessage &msg, unsigned Arg);
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000073 public:
74 NilArgChecker() : BT(0) {}
75 static void *getTag() { static int x = 0; return &x; }
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000076 void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000077 };
78}
Mike Stump11289f42009-09-09 15:08:12 +000079
Ted Kremenekbd2c8002010-10-20 23:38:56 +000080void NilArgChecker::WarnNilArg(CheckerContext &C,
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000081 const ObjCMessage &msg,
Ted Kremenekbd2c8002010-10-20 23:38:56 +000082 unsigned int Arg)
83{
84 if (!BT)
85 BT = new APIMisuse("nil argument");
86
Ted Kremenek750b7ac2010-12-20 21:19:09 +000087 if (ExplodedNode *N = C.generateSink()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +000088 llvm::SmallString<128> sbuf;
89 llvm::raw_svector_ostream os(sbuf);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000090 os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
91 << msg.getSelector().getAsString() << "' cannot be nil";
Mike Stump11289f42009-09-09 15:08:12 +000092
Ted Kremenekbd2c8002010-10-20 23:38:56 +000093 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000094 R->addRange(msg.getArgSourceRange(Arg));
Ted Kremenekbd2c8002010-10-20 23:38:56 +000095 C.EmitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +000096 }
Ted Kremenek276278e2008-03-27 22:05:32 +000097}
98
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +000099void NilArgChecker::preVisitObjCMessage(CheckerContext &C,
100 ObjCMessage msg)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000101{
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000102 const ObjCInterfaceType *ReceiverType = GetReceiverType(msg);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000103 if (!ReceiverType)
104 return;
105
106 if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000107 Selector S = msg.getSelector();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000108
109 if (S.isUnarySelector())
110 return;
111
112 // FIXME: This is going to be really slow doing these checks with
113 // lexical comparisons.
114
115 std::string NameStr = S.getAsString();
116 llvm::StringRef Name(NameStr);
117 assert(!Name.empty());
118
119 // FIXME: Checking for initWithFormat: will not work in most cases
120 // yet because [NSString alloc] returns id, not NSString*. We will
121 // need support for tracking expected-type information in the analyzer
122 // to find these errors.
123 if (Name == "caseInsensitiveCompare:" ||
124 Name == "compare:" ||
125 Name == "compare:options:" ||
126 Name == "compare:options:range:" ||
127 Name == "compare:options:range:locale:" ||
128 Name == "componentsSeparatedByCharactersInSet:" ||
129 Name == "initWithFormat:") {
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000130 if (isNil(msg.getArgSVal(0, C.getState())))
131 WarnNilArg(C, msg, 0);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000132 }
133 }
Ted Kremenekc0414922008-03-27 07:25:52 +0000134}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000135
136//===----------------------------------------------------------------------===//
137// Error reporting.
138//===----------------------------------------------------------------------===//
139
140namespace {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000141class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000142 APIMisuse* BT;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000143 IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000144public:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000145 CFNumberCreateChecker() : BT(0), II(0) {}
146 ~CFNumberCreateChecker() {}
147 static void *getTag() { static int x = 0; return &x; }
148 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000149private:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000150 void EmitError(const TypedRegion* R, const Expr* Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000151 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000152};
153} // end anonymous namespace
154
155enum CFNumberType {
156 kCFNumberSInt8Type = 1,
157 kCFNumberSInt16Type = 2,
158 kCFNumberSInt32Type = 3,
159 kCFNumberSInt64Type = 4,
160 kCFNumberFloat32Type = 5,
161 kCFNumberFloat64Type = 6,
162 kCFNumberCharType = 7,
163 kCFNumberShortType = 8,
164 kCFNumberIntType = 9,
165 kCFNumberLongType = 10,
166 kCFNumberLongLongType = 11,
167 kCFNumberFloatType = 12,
168 kCFNumberDoubleType = 13,
169 kCFNumberCFIndexType = 14,
170 kCFNumberNSIntegerType = 15,
171 kCFNumberCGFloatType = 16
172};
173
174namespace {
175 template<typename T>
176 class Optional {
177 bool IsKnown;
178 T Val;
179 public:
180 Optional() : IsKnown(false), Val(0) {}
181 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000182
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000183 bool isKnown() const { return IsKnown; }
184
185 const T& getValue() const {
186 assert (isKnown());
187 return Val;
188 }
189
190 operator const T&() const {
191 return getValue();
192 }
193 };
194}
195
196static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000197 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000198
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000199 if (i < kCFNumberCharType)
200 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000201
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000202 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000203
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000204 switch (i) {
205 case kCFNumberCharType: T = Ctx.CharTy; break;
206 case kCFNumberShortType: T = Ctx.ShortTy; break;
207 case kCFNumberIntType: T = Ctx.IntTy; break;
208 case kCFNumberLongType: T = Ctx.LongTy; break;
209 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
210 case kCFNumberFloatType: T = Ctx.FloatTy; break;
211 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
212 case kCFNumberCFIndexType:
213 case kCFNumberNSIntegerType:
214 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000215 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000216 default:
217 return Optional<uint64_t>();
218 }
Mike Stump11289f42009-09-09 15:08:12 +0000219
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000220 return Ctx.getTypeSize(T);
221}
222
223#if 0
224static const char* GetCFNumberTypeStr(uint64_t i) {
225 static const char* Names[] = {
226 "kCFNumberSInt8Type",
227 "kCFNumberSInt16Type",
228 "kCFNumberSInt32Type",
229 "kCFNumberSInt64Type",
230 "kCFNumberFloat32Type",
231 "kCFNumberFloat64Type",
232 "kCFNumberCharType",
233 "kCFNumberShortType",
234 "kCFNumberIntType",
235 "kCFNumberLongType",
236 "kCFNumberLongLongType",
237 "kCFNumberFloatType",
238 "kCFNumberDoubleType",
239 "kCFNumberCFIndexType",
240 "kCFNumberNSIntegerType",
241 "kCFNumberCGFloatType"
242 };
Mike Stump11289f42009-09-09 15:08:12 +0000243
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000244 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
245}
246#endif
247
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000248void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C,
249 const CallExpr *CE)
250{
Mike Stump11289f42009-09-09 15:08:12 +0000251 const Expr* Callee = CE->getCallee();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000252 const GRState *state = C.getState();
253 SVal CallV = state->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000254 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000255
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000256 if (!FD)
257 return;
258
259 ASTContext &Ctx = C.getASTContext();
260 if (!II)
261 II = &Ctx.Idents.get("CFNumberCreate");
262
263 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
264 return;
Mike Stump11289f42009-09-09 15:08:12 +0000265
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000266 // Get the value of the "theType" argument.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000267 SVal TheTypeVal = state->getSVal(CE->getArg(1));
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.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000271 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
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();
276 Optional<uint64_t> TargetSize = 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.
279 if (!TargetSize.isKnown())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000280 return;
Mike Stump11289f42009-09-09 15:08:12 +0000281
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000282 // Look at the value of the integer being passed by reference. Essentially
283 // we want to catch cases where the value passed in is not equal to the
284 // size of the type being created.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000285 SVal TheValueExpr = state->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000286
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000287 // FIXME: Eventually we should handle arbitrary locations. We can do this
288 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000289 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000290 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000291 return;
Mike Stump11289f42009-09-09 15:08:12 +0000292
Zhongxing Xuf8f3f9d2009-11-10 02:17:20 +0000293 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000294 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000295 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000296
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000297 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000298
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000299 // FIXME: If the pointee isn't an integer type, should we flag a warning?
300 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000301
302 if (!T->isIntegerType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000303 return;
Mike Stump11289f42009-09-09 15:08:12 +0000304
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000305 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000306
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000307 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000308 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000309 return;
Mike Stump11289f42009-09-09 15:08:12 +0000310
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000311 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
312 // otherwise generate a regular node.
313 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000314 // FIXME: We can actually create an abstract "CFNumber" object that has
315 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000316 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000317 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
318 : C.generateNode()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000319 llvm::SmallString<128> sbuf;
320 llvm::raw_svector_ostream os(sbuf);
321
322 os << (SourceSize == 8 ? "An " : "A ")
323 << SourceSize << " bit integer is used to initialize a CFNumber "
324 "object that represents "
325 << (TargetSize == 8 ? "an " : "a ")
326 << TargetSize << " bit integer. ";
327
328 if (SourceSize < TargetSize)
329 os << (TargetSize - SourceSize)
330 << " bits of the CFNumber value will be garbage." ;
331 else
332 os << (SourceSize - TargetSize)
333 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000334
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000335 if (!BT)
336 BT = new APIMisuse("Bad use of CFNumberCreate");
337
338 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
339 report->addRange(CE->getArg(2)->getSourceRange());
340 C.EmitReport(report);
341 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000342}
343
Ted Kremenek1f352db2008-07-22 16:21:24 +0000344//===----------------------------------------------------------------------===//
Jordy Rose40c5c242010-07-06 02:34:42 +0000345// CFRetain/CFRelease checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000346//===----------------------------------------------------------------------===//
347
348namespace {
Jordy Rose40c5c242010-07-06 02:34:42 +0000349class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
Ted Kremenekc057f412009-07-14 00:43:42 +0000350 APIMisuse *BT;
Ted Kremenekc057f412009-07-14 00:43:42 +0000351 IdentifierInfo *Retain, *Release;
Ted Kremenekc057f412009-07-14 00:43:42 +0000352public:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000353 CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
Jordy Rose40c5c242010-07-06 02:34:42 +0000354 static void *getTag() { static int x = 0; return &x; }
Jordy Rose40c5c242010-07-06 02:34:42 +0000355 void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000356};
357} // end anonymous namespace
358
359
Jordy Rose40c5c242010-07-06 02:34:42 +0000360void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
361 const CallExpr* CE) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000362 // If the CallExpr doesn't have exactly 1 argument just give up checking.
363 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000364 return;
Mike Stump11289f42009-09-09 15:08:12 +0000365
Jordy Rose40c5c242010-07-06 02:34:42 +0000366 // Get the function declaration of the callee.
367 const GRState* state = C.getState();
Ted Kremenek57f09892010-02-08 16:18:51 +0000368 SVal X = state->getSVal(CE->getCallee());
Ted Kremenekc057f412009-07-14 00:43:42 +0000369 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000370
Ted Kremenekc057f412009-07-14 00:43:42 +0000371 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000372 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000373
374 if (!BT) {
375 ASTContext &Ctx = C.getASTContext();
376 Retain = &Ctx.Idents.get("CFRetain");
377 Release = &Ctx.Idents.get("CFRelease");
378 BT = new APIMisuse("null passed to CFRetain/CFRelease");
379 }
Mike Stump11289f42009-09-09 15:08:12 +0000380
Jordy Rose40c5c242010-07-06 02:34:42 +0000381 // Check if we called CFRetain/CFRelease.
Mike Stump11289f42009-09-09 15:08:12 +0000382 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000383 if (!(FuncII == Retain || FuncII == Release))
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.
387 // It should probably be refactored and combined with AttrNonNullChecker.
388
389 // Get the argument's value.
390 const Expr *Arg = CE->getArg(0);
391 SVal ArgVal = state->getSVal(Arg);
392 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
393 if (!DefArgVal)
394 return;
395
396 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000397 SValBuilder &svalBuilder = C.getSValBuilder();
398 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
Jordy Rose40c5c242010-07-06 02:34:42 +0000399
400 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000401 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000402
403 // Are they equal?
404 const GRState *stateTrue, *stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000405 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000406
407 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000408 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000409 if (!N)
410 return;
411
Ted Kremenekc057f412009-07-14 00:43:42 +0000412 const char *description = (FuncII == Retain)
413 ? "Null pointer argument in call to CFRetain"
414 : "Null pointer argument in call to CFRelease";
415
Jordy Rose40c5c242010-07-06 02:34:42 +0000416 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
417 report->addRange(Arg->getSourceRange());
418 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
Jordy Rose40c5c242010-07-06 02:34:42 +0000419 C.EmitReport(report);
420 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000421 }
422
Jordy Rose40c5c242010-07-06 02:34:42 +0000423 // From here on, we know the argument is non-null.
424 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000425}
426
427//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000428// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
429//===----------------------------------------------------------------------===//
430
431namespace {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000432class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000433 Selector releaseS;
434 Selector retainS;
435 Selector autoreleaseS;
436 Selector drainS;
437 BugType *BT;
438public:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000439 ClassReleaseChecker()
440 : BT(0) {}
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000441
442 static void *getTag() { static int x = 0; return &x; }
443
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000444 void preVisitObjCMessage(CheckerContext &C, ObjCMessage msg);
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000445};
446}
447
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000448void ClassReleaseChecker::preVisitObjCMessage(CheckerContext &C,
449 ObjCMessage msg) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000450
451 if (!BT) {
452 BT = new APIMisuse("message incorrectly sent to class instead of class "
453 "instance");
454
455 ASTContext &Ctx = C.getASTContext();
456 releaseS = GetNullarySelector("release", Ctx);
457 retainS = GetNullarySelector("retain", Ctx);
458 autoreleaseS = GetNullarySelector("autorelease", Ctx);
459 drainS = GetNullarySelector("drain", Ctx);
460 }
461
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000462 if (msg.isInstanceMessage())
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000463 return;
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000464 const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
465 assert(Class);
Douglas Gregor9a129192010-04-21 00:45:42 +0000466
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000467 Selector S = msg.getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000468 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000469 return;
470
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000471 if (ExplodedNode *N = C.generateNode()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000472 llvm::SmallString<200> buf;
473 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000474
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000475 os << "The '" << S.getAsString() << "' message should be sent to instances "
476 "of class '" << Class->getName()
477 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000478
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000479 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
Argyrios Kyrtzidis37ab7262011-01-25 00:03:53 +0000480 report->addRange(msg.getSourceRange());
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000481 C.EmitReport(report);
482 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000483}
484
485//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000486// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000487//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000488
Ted Kremenek98857c92010-12-23 07:20:52 +0000489void ento::RegisterAppleChecks(ExprEngine& Eng, const Decl &D) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000490 Eng.registerCheck(new NilArgChecker());
491 Eng.registerCheck(new CFNumberCreateChecker());
492 RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D);
Ted Kremenek18c7cee2009-11-03 08:03:59 +0000493 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000494 Eng.registerCheck(new CFRetainReleaseChecker());
495 Eng.registerCheck(new ClassReleaseChecker());
Ted Kremenek1f352db2008-07-22 16:21:24 +0000496}