blob: b4e2959150f1ab536c36a871482288707926ee87 [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
Steve Naroff7cae42b2009-07-10 23:34:53 +000045static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
Douglas Gregor9a129192010-04-21 00:45:42 +000046 QualType T;
47 switch (ME->getReceiverKind()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +000048 case ObjCMessageExpr::Instance:
49 T = ME->getInstanceReceiver()->getType();
50 break;
51
52 case ObjCMessageExpr::SuperInstance:
53 T = ME->getSuperType();
54 break;
55
56 case ObjCMessageExpr::Class:
57 case ObjCMessageExpr::SuperClass:
58 return 0;
Douglas Gregor9a129192010-04-21 00:45:42 +000059 }
Ted Kremenekbd2c8002010-10-20 23:38:56 +000060
Douglas Gregor9a129192010-04-21 00:45:42 +000061 if (const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>())
Steve Naroff7cae42b2009-07-10 23:34:53 +000062 return PT->getInterfaceType();
Ted Kremenekbd2c8002010-10-20 23:38:56 +000063
Ted Kremenekf20e2282008-04-30 22:48:21 +000064 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000065}
66
Steve Naroff7cae42b2009-07-10 23:34:53 +000067static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
Daniel Dunbar2c422dc92009-10-18 20:26:12 +000068 if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
69 return ReceiverType->getDecl()->getIdentifier()->getNameStart();
70 return NULL;
Ted Kremenek276278e2008-03-27 22:05:32 +000071}
Ted Kremeneka4d60b62008-03-27 17:17:22 +000072
Ted Kremenekbd2c8002010-10-20 23:38:56 +000073static bool isNSString(llvm::StringRef ClassName) {
74 return ClassName == "NSString" || ClassName == "NSMutableString";
Ted Kremenekc0414922008-03-27 07:25:52 +000075}
76
Zhongxing Xu27f17422008-10-17 05:57:07 +000077static inline bool isNil(SVal X) {
Mike Stump11289f42009-09-09 15:08:12 +000078 return isa<loc::ConcreteInt>(X);
Ted Kremenek27156c82008-03-27 21:15:17 +000079}
80
Ted Kremenekc0414922008-03-27 07:25:52 +000081//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +000082// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
Ted Kremenekc0414922008-03-27 07:25:52 +000083//===----------------------------------------------------------------------===//
84
Benjamin Kramer2fc373e2010-10-22 16:33:16 +000085namespace {
86 class NilArgChecker : public CheckerVisitor<NilArgChecker> {
87 APIMisuse *BT;
88 void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg);
89 public:
90 NilArgChecker() : BT(0) {}
91 static void *getTag() { static int x = 0; return &x; }
92 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
93 };
94}
Mike Stump11289f42009-09-09 15:08:12 +000095
Ted Kremenekbd2c8002010-10-20 23:38:56 +000096void NilArgChecker::WarnNilArg(CheckerContext &C,
97 const clang::ObjCMessageExpr *ME,
98 unsigned int Arg)
99{
100 if (!BT)
101 BT = new APIMisuse("nil argument");
102
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000103 if (ExplodedNode *N = C.generateSink()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000104 llvm::SmallString<128> sbuf;
105 llvm::raw_svector_ostream os(sbuf);
106 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
107 << ME->getSelector().getAsString() << "' cannot be nil";
Mike Stump11289f42009-09-09 15:08:12 +0000108
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000109 RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
110 R->addRange(ME->getArg(Arg)->getSourceRange());
111 C.EmitReport(R);
Ted Kremenek276278e2008-03-27 22:05:32 +0000112 }
Ted Kremenek276278e2008-03-27 22:05:32 +0000113}
114
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000115void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C,
116 const ObjCMessageExpr *ME)
117{
118 const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
119 if (!ReceiverType)
120 return;
121
122 if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) {
123 Selector S = ME->getSelector();
124
125 if (S.isUnarySelector())
126 return;
127
128 // FIXME: This is going to be really slow doing these checks with
129 // lexical comparisons.
130
131 std::string NameStr = S.getAsString();
132 llvm::StringRef Name(NameStr);
133 assert(!Name.empty());
134
135 // FIXME: Checking for initWithFormat: will not work in most cases
136 // yet because [NSString alloc] returns id, not NSString*. We will
137 // need support for tracking expected-type information in the analyzer
138 // to find these errors.
139 if (Name == "caseInsensitiveCompare:" ||
140 Name == "compare:" ||
141 Name == "compare:options:" ||
142 Name == "compare:options:range:" ||
143 Name == "compare:options:range:locale:" ||
144 Name == "componentsSeparatedByCharactersInSet:" ||
145 Name == "initWithFormat:") {
146 if (isNil(C.getState()->getSVal(ME->getArg(0))))
147 WarnNilArg(C, ME, 0);
148 }
149 }
Ted Kremenekc0414922008-03-27 07:25:52 +0000150}
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000151
152//===----------------------------------------------------------------------===//
153// Error reporting.
154//===----------------------------------------------------------------------===//
155
156namespace {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000157class CFNumberCreateChecker : public CheckerVisitor<CFNumberCreateChecker> {
Ted Kremenekfc5d0672009-02-04 23:49:09 +0000158 APIMisuse* BT;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000159 IdentifierInfo* II;
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000160public:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000161 CFNumberCreateChecker() : BT(0), II(0) {}
162 ~CFNumberCreateChecker() {}
163 static void *getTag() { static int x = 0; return &x; }
164 void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000165private:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000166 void EmitError(const TypedRegion* R, const Expr* Ex,
Mike Stump11289f42009-09-09 15:08:12 +0000167 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000168};
169} // end anonymous namespace
170
171enum CFNumberType {
172 kCFNumberSInt8Type = 1,
173 kCFNumberSInt16Type = 2,
174 kCFNumberSInt32Type = 3,
175 kCFNumberSInt64Type = 4,
176 kCFNumberFloat32Type = 5,
177 kCFNumberFloat64Type = 6,
178 kCFNumberCharType = 7,
179 kCFNumberShortType = 8,
180 kCFNumberIntType = 9,
181 kCFNumberLongType = 10,
182 kCFNumberLongLongType = 11,
183 kCFNumberFloatType = 12,
184 kCFNumberDoubleType = 13,
185 kCFNumberCFIndexType = 14,
186 kCFNumberNSIntegerType = 15,
187 kCFNumberCGFloatType = 16
188};
189
190namespace {
191 template<typename T>
192 class Optional {
193 bool IsKnown;
194 T Val;
195 public:
196 Optional() : IsKnown(false), Val(0) {}
197 Optional(const T& val) : IsKnown(true), Val(val) {}
Mike Stump11289f42009-09-09 15:08:12 +0000198
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000199 bool isKnown() const { return IsKnown; }
200
201 const T& getValue() const {
202 assert (isKnown());
203 return Val;
204 }
205
206 operator const T&() const {
207 return getValue();
208 }
209 };
210}
211
212static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
Nuno Lopescfca1f02009-12-23 17:49:57 +0000213 static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
Mike Stump11289f42009-09-09 15:08:12 +0000214
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000215 if (i < kCFNumberCharType)
216 return FixedSize[i-1];
Mike Stump11289f42009-09-09 15:08:12 +0000217
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000218 QualType T;
Mike Stump11289f42009-09-09 15:08:12 +0000219
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000220 switch (i) {
221 case kCFNumberCharType: T = Ctx.CharTy; break;
222 case kCFNumberShortType: T = Ctx.ShortTy; break;
223 case kCFNumberIntType: T = Ctx.IntTy; break;
224 case kCFNumberLongType: T = Ctx.LongTy; break;
225 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
226 case kCFNumberFloatType: T = Ctx.FloatTy; break;
227 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
228 case kCFNumberCFIndexType:
229 case kCFNumberNSIntegerType:
230 case kCFNumberCGFloatType:
Mike Stump11289f42009-09-09 15:08:12 +0000231 // FIXME: We need a way to map from names to Type*.
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000232 default:
233 return Optional<uint64_t>();
234 }
Mike Stump11289f42009-09-09 15:08:12 +0000235
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000236 return Ctx.getTypeSize(T);
237}
238
239#if 0
240static const char* GetCFNumberTypeStr(uint64_t i) {
241 static const char* Names[] = {
242 "kCFNumberSInt8Type",
243 "kCFNumberSInt16Type",
244 "kCFNumberSInt32Type",
245 "kCFNumberSInt64Type",
246 "kCFNumberFloat32Type",
247 "kCFNumberFloat64Type",
248 "kCFNumberCharType",
249 "kCFNumberShortType",
250 "kCFNumberIntType",
251 "kCFNumberLongType",
252 "kCFNumberLongLongType",
253 "kCFNumberFloatType",
254 "kCFNumberDoubleType",
255 "kCFNumberCFIndexType",
256 "kCFNumberNSIntegerType",
257 "kCFNumberCGFloatType"
258 };
Mike Stump11289f42009-09-09 15:08:12 +0000259
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000260 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
261}
262#endif
263
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000264void CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C,
265 const CallExpr *CE)
266{
Mike Stump11289f42009-09-09 15:08:12 +0000267 const Expr* Callee = CE->getCallee();
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000268 const GRState *state = C.getState();
269 SVal CallV = state->getSVal(Callee);
Zhongxing Xuac129432009-04-20 05:24:46 +0000270 const FunctionDecl* FD = CallV.getAsFunctionDecl();
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000271
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000272 if (!FD)
273 return;
274
275 ASTContext &Ctx = C.getASTContext();
276 if (!II)
277 II = &Ctx.Idents.get("CFNumberCreate");
278
279 if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
280 return;
Mike Stump11289f42009-09-09 15:08:12 +0000281
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000282 // Get the value of the "theType" argument.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000283 SVal TheTypeVal = state->getSVal(CE->getArg(1));
Mike Stump11289f42009-09-09 15:08:12 +0000284
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000285 // FIXME: We really should allow ranges of valid theType values, and
286 // bifurcate the state appropriately.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000287 nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000288 if (!V)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000289 return;
Mike Stump11289f42009-09-09 15:08:12 +0000290
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000291 uint64_t NumberKind = V->getValue().getLimitedValue();
292 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
Mike Stump11289f42009-09-09 15:08:12 +0000293
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000294 // FIXME: In some cases we can emit an error.
295 if (!TargetSize.isKnown())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000296 return;
Mike Stump11289f42009-09-09 15:08:12 +0000297
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000298 // Look at the value of the integer being passed by reference. Essentially
299 // we want to catch cases where the value passed in is not equal to the
300 // size of the type being created.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000301 SVal TheValueExpr = state->getSVal(CE->getArg(2));
Mike Stump11289f42009-09-09 15:08:12 +0000302
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000303 // FIXME: Eventually we should handle arbitrary locations. We can do this
304 // by having an enhanced memory model that does low-level typing.
Zhongxing Xu27f17422008-10-17 05:57:07 +0000305 loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000306 if (!LV)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000307 return;
Mike Stump11289f42009-09-09 15:08:12 +0000308
Zhongxing Xuf8f3f9d2009-11-10 02:17:20 +0000309 const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
Ted Kremenek87a7a452009-07-29 18:17:40 +0000310 if (!R)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000311 return;
Ted Kremenek87a7a452009-07-29 18:17:40 +0000312
Zhongxing Xu8de0a3d2010-08-11 06:10:55 +0000313 QualType T = Ctx.getCanonicalType(R->getValueType());
Mike Stump11289f42009-09-09 15:08:12 +0000314
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000315 // FIXME: If the pointee isn't an integer type, should we flag a warning?
316 // People can do weird stuff with pointers.
Mike Stump11289f42009-09-09 15:08:12 +0000317
318 if (!T->isIntegerType())
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000319 return;
Mike Stump11289f42009-09-09 15:08:12 +0000320
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000321 uint64_t SourceSize = Ctx.getTypeSize(T);
Mike Stump11289f42009-09-09 15:08:12 +0000322
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000323 // CHECK: is SourceSize == TargetSize
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000324 if (SourceSize == TargetSize)
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000325 return;
Mike Stump11289f42009-09-09 15:08:12 +0000326
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000327 // Generate an error. Only generate a sink if 'SourceSize < TargetSize';
328 // otherwise generate a regular node.
329 //
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000330 // FIXME: We can actually create an abstract "CFNumber" object that has
331 // the bits initialized to the provided values.
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000332 //
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000333 if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
334 : C.generateNode()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000335 llvm::SmallString<128> sbuf;
336 llvm::raw_svector_ostream os(sbuf);
337
338 os << (SourceSize == 8 ? "An " : "A ")
339 << SourceSize << " bit integer is used to initialize a CFNumber "
340 "object that represents "
341 << (TargetSize == 8 ? "an " : "a ")
342 << TargetSize << " bit integer. ";
343
344 if (SourceSize < TargetSize)
345 os << (TargetSize - SourceSize)
346 << " bits of the CFNumber value will be garbage." ;
347 else
348 os << (SourceSize - TargetSize)
349 << " bits of the input integer will be lost.";
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000350
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000351 if (!BT)
352 BT = new APIMisuse("Bad use of CFNumberCreate");
353
354 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
355 report->addRange(CE->getArg(2)->getSourceRange());
356 C.EmitReport(report);
357 }
Ted Kremenekcf1ab192008-06-26 23:59:48 +0000358}
359
Ted Kremenek1f352db2008-07-22 16:21:24 +0000360//===----------------------------------------------------------------------===//
Jordy Rose40c5c242010-07-06 02:34:42 +0000361// CFRetain/CFRelease checking for null arguments.
Ted Kremenekc057f412009-07-14 00:43:42 +0000362//===----------------------------------------------------------------------===//
363
364namespace {
Jordy Rose40c5c242010-07-06 02:34:42 +0000365class CFRetainReleaseChecker : public CheckerVisitor<CFRetainReleaseChecker> {
Ted Kremenekc057f412009-07-14 00:43:42 +0000366 APIMisuse *BT;
Ted Kremenekc057f412009-07-14 00:43:42 +0000367 IdentifierInfo *Retain, *Release;
Ted Kremenekc057f412009-07-14 00:43:42 +0000368public:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000369 CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {}
Jordy Rose40c5c242010-07-06 02:34:42 +0000370 static void *getTag() { static int x = 0; return &x; }
Jordy Rose40c5c242010-07-06 02:34:42 +0000371 void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE);
Ted Kremenekc057f412009-07-14 00:43:42 +0000372};
373} // end anonymous namespace
374
375
Jordy Rose40c5c242010-07-06 02:34:42 +0000376void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C,
377 const CallExpr* CE) {
Ted Kremenekc057f412009-07-14 00:43:42 +0000378 // If the CallExpr doesn't have exactly 1 argument just give up checking.
379 if (CE->getNumArgs() != 1)
Jordy Rose40c5c242010-07-06 02:34:42 +0000380 return;
Mike Stump11289f42009-09-09 15:08:12 +0000381
Jordy Rose40c5c242010-07-06 02:34:42 +0000382 // Get the function declaration of the callee.
383 const GRState* state = C.getState();
Ted Kremenek57f09892010-02-08 16:18:51 +0000384 SVal X = state->getSVal(CE->getCallee());
Ted Kremenekc057f412009-07-14 00:43:42 +0000385 const FunctionDecl* FD = X.getAsFunctionDecl();
Mike Stump11289f42009-09-09 15:08:12 +0000386
Ted Kremenekc057f412009-07-14 00:43:42 +0000387 if (!FD)
Jordy Rose40c5c242010-07-06 02:34:42 +0000388 return;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000389
390 if (!BT) {
391 ASTContext &Ctx = C.getASTContext();
392 Retain = &Ctx.Idents.get("CFRetain");
393 Release = &Ctx.Idents.get("CFRelease");
394 BT = new APIMisuse("null passed to CFRetain/CFRelease");
395 }
Mike Stump11289f42009-09-09 15:08:12 +0000396
Jordy Rose40c5c242010-07-06 02:34:42 +0000397 // Check if we called CFRetain/CFRelease.
Mike Stump11289f42009-09-09 15:08:12 +0000398 const IdentifierInfo *FuncII = FD->getIdentifier();
Ted Kremenekc057f412009-07-14 00:43:42 +0000399 if (!(FuncII == Retain || FuncII == Release))
Jordy Rose40c5c242010-07-06 02:34:42 +0000400 return;
Mike Stump11289f42009-09-09 15:08:12 +0000401
Jordy Rose40c5c242010-07-06 02:34:42 +0000402 // FIXME: The rest of this just checks that the argument is non-null.
403 // It should probably be refactored and combined with AttrNonNullChecker.
404
405 // Get the argument's value.
406 const Expr *Arg = CE->getArg(0);
407 SVal ArgVal = state->getSVal(Arg);
408 DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
409 if (!DefArgVal)
410 return;
411
412 // Get a NULL value.
Ted Kremenek90af9092010-12-02 07:49:45 +0000413 SValBuilder &svalBuilder = C.getSValBuilder();
414 DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
Jordy Rose40c5c242010-07-06 02:34:42 +0000415
416 // Make an expression asserting that they're equal.
Ted Kremenek90af9092010-12-02 07:49:45 +0000417 DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
Jordy Rose40c5c242010-07-06 02:34:42 +0000418
419 // Are they equal?
420 const GRState *stateTrue, *stateFalse;
Ted Kremenekc5bea1e2010-12-01 22:16:56 +0000421 llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
Jordy Rose40c5c242010-07-06 02:34:42 +0000422
423 if (stateTrue && !stateFalse) {
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000424 ExplodedNode *N = C.generateSink(stateTrue);
Jordy Rose40c5c242010-07-06 02:34:42 +0000425 if (!N)
426 return;
427
Ted Kremenekc057f412009-07-14 00:43:42 +0000428 const char *description = (FuncII == Retain)
429 ? "Null pointer argument in call to CFRetain"
430 : "Null pointer argument in call to CFRelease";
431
Jordy Rose40c5c242010-07-06 02:34:42 +0000432 EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N);
433 report->addRange(Arg->getSourceRange());
434 report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg);
Jordy Rose40c5c242010-07-06 02:34:42 +0000435 C.EmitReport(report);
436 return;
Ted Kremenekc057f412009-07-14 00:43:42 +0000437 }
438
Jordy Rose40c5c242010-07-06 02:34:42 +0000439 // From here on, we know the argument is non-null.
440 C.addTransition(stateFalse);
Ted Kremenekc057f412009-07-14 00:43:42 +0000441}
442
443//===----------------------------------------------------------------------===//
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000444// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
445//===----------------------------------------------------------------------===//
446
447namespace {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000448class ClassReleaseChecker : public CheckerVisitor<ClassReleaseChecker> {
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000449 Selector releaseS;
450 Selector retainS;
451 Selector autoreleaseS;
452 Selector drainS;
453 BugType *BT;
454public:
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000455 ClassReleaseChecker()
456 : BT(0) {}
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000457
458 static void *getTag() { static int x = 0; return &x; }
459
460 void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);
461};
462}
463
464void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
465 const ObjCMessageExpr *ME) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000466
467 if (!BT) {
468 BT = new APIMisuse("message incorrectly sent to class instead of class "
469 "instance");
470
471 ASTContext &Ctx = C.getASTContext();
472 releaseS = GetNullarySelector("release", Ctx);
473 retainS = GetNullarySelector("retain", Ctx);
474 autoreleaseS = GetNullarySelector("autorelease", Ctx);
475 drainS = GetNullarySelector("drain", Ctx);
476 }
477
Douglas Gregor9a129192010-04-21 00:45:42 +0000478 ObjCInterfaceDecl *Class = 0;
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000479
Douglas Gregor9a129192010-04-21 00:45:42 +0000480 switch (ME->getReceiverKind()) {
481 case ObjCMessageExpr::Class:
John McCall96fa4842010-05-17 21:00:27 +0000482 Class = ME->getClassReceiver()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor9a129192010-04-21 00:45:42 +0000483 break;
Douglas Gregor9a129192010-04-21 00:45:42 +0000484 case ObjCMessageExpr::SuperClass:
John McCall96fa4842010-05-17 21:00:27 +0000485 Class = ME->getSuperType()->getAs<ObjCObjectType>()->getInterface();
Douglas Gregor9a129192010-04-21 00:45:42 +0000486 break;
Douglas Gregor9a129192010-04-21 00:45:42 +0000487 case ObjCMessageExpr::Instance:
488 case ObjCMessageExpr::SuperInstance:
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000489 return;
Douglas Gregor9a129192010-04-21 00:45:42 +0000490 }
491
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000492 Selector S = ME->getSelector();
Benjamin Kramer7d875c72009-11-20 10:03:00 +0000493 if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000494 return;
495
Ted Kremenek750b7ac2010-12-20 21:19:09 +0000496 if (ExplodedNode *N = C.generateNode()) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000497 llvm::SmallString<200> buf;
498 llvm::raw_svector_ostream os(buf);
Ted Kremenekf5735152009-11-23 22:22:01 +0000499
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000500 os << "The '" << S.getAsString() << "' message should be sent to instances "
501 "of class '" << Class->getName()
502 << "' and not the class directly";
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000503
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000504 RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
505 report->addRange(ME->getSourceRange());
506 C.EmitReport(report);
507 }
Ted Kremeneka4f7c182009-11-20 05:27:05 +0000508}
509
510//===----------------------------------------------------------------------===//
Ted Kremenek1f352db2008-07-22 16:21:24 +0000511// Check registration.
Ted Kremenekc057f412009-07-14 00:43:42 +0000512//===----------------------------------------------------------------------===//
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000513
Ted Kremenek98857c92010-12-23 07:20:52 +0000514void ento::RegisterAppleChecks(ExprEngine& Eng, const Decl &D) {
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000515 Eng.registerCheck(new NilArgChecker());
516 Eng.registerCheck(new CFNumberCreateChecker());
517 RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D);
Ted Kremenek18c7cee2009-11-03 08:03:59 +0000518 RegisterNSAutoreleasePoolChecks(Eng);
Ted Kremenekbd2c8002010-10-20 23:38:56 +0000519 Eng.registerCheck(new CFRetainReleaseChecker());
520 Eng.registerCheck(new ClassReleaseChecker());
Ted Kremenek1f352db2008-07-22 16:21:24 +0000521}