blob: 37cfc280b8f1ab6a5cedff5013c721714f5dafbb [file] [log] [blame]
Ted Kremenekb0a2e472008-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 Kremenek7a681942008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenekb0a2e472008-03-27 07:25:52 +000018#include "clang/Analysis/PathSensitive/ExplodedGraph.h"
19#include "clang/Analysis/PathSensitive/GRSimpleAPICheck.h"
20#include "clang/Analysis/PathSensitive/ValueState.h"
Ted Kremenekf00daf02008-04-03 17:57:38 +000021#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000022#include "clang/Analysis/PathDiagnostic.h"
23#include "clang/AST/Expr.h"
Steve Naroff9ed3e772008-05-29 21:12:08 +000024#include "clang/AST/ExprObjC.h"
Ted Kremenekb0a2e472008-03-27 07:25:52 +000025#include "clang/AST/ASTContext.h"
26#include "llvm/Support/Compiler.h"
27
28#include <vector>
Ted Kremenek639247a2008-03-27 22:05:32 +000029#include <sstream>
Ted Kremenekb0a2e472008-03-27 07:25:52 +000030
31using namespace clang;
Ted Kremenek7a681942008-03-27 17:17:22 +000032
Ted Kremenek639247a2008-03-27 22:05:32 +000033static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
34 Expr* Receiver = ME->getReceiver();
35
36 if (!Receiver)
37 return NULL;
38
Ted Kremenek92c78b62008-04-03 21:44:24 +000039 QualType X = Receiver->getType();
Ted Kremenekbfc6cce2008-04-19 19:12:50 +000040
Ted Kremenekd810bf82008-04-30 22:48:21 +000041 if (X->isPointerType()) {
42 Type* TP = X.getTypePtr();
43 const PointerType* T = TP->getAsPointerType();
44 return dyn_cast<ObjCInterfaceType>(T->getPointeeType().getTypePtr());
45 }
46
47 // FIXME: Support ObjCQualifiedIdType?
48 return NULL;
Ted Kremenek639247a2008-03-27 22:05:32 +000049}
50
51static const char* GetReceiverNameType(ObjCMessageExpr* ME) {
52 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
53 return ReceiverType ? ReceiverType->getDecl()->getIdentifier()->getName()
54 : NULL;
55}
Ted Kremenek7a681942008-03-27 17:17:22 +000056
Ted Kremenekf00daf02008-04-03 17:57:38 +000057namespace {
58
Ted Kremeneke3769852008-04-18 20:54:29 +000059class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf00daf02008-04-03 17:57:38 +000060public:
Ted Kremenekf00daf02008-04-03 17:57:38 +000061 virtual ~NilArg() {}
62
63 virtual const char* getName() const {
64 return "nil argument";
65 }
66
Ted Kremenek0e80dea2008-04-09 21:41:14 +000067 class Report : public BugReport {
68 std::string Msg;
69 const char* s;
70 SourceRange R;
71 public:
Ted Kremenekf00daf02008-04-03 17:57:38 +000072
Ted Kremeneke3ef1c72008-04-14 17:39:48 +000073 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
74 ObjCMessageExpr* ME, unsigned Arg)
75 : BugReport(Desc, N) {
Ted Kremenek0e80dea2008-04-09 21:41:14 +000076
77 Expr* E = ME->getArg(Arg);
78 R = E->getSourceRange();
79
80 std::ostringstream os;
81
82 os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
83 << ME->getSelector().getName() << "' cannot be nil.";
84
85 Msg = os.str();
86 s = Msg.c_str();
87 }
88
89 virtual ~Report() {}
90
Ted Kremenek128d65f2008-04-10 16:05:13 +000091 virtual const char* getDescription() const { return s; }
92
Ted Kremenek5c3407a2008-05-01 22:50:36 +000093 virtual void getRanges(BugReporter& BR,
94 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek128d65f2008-04-10 16:05:13 +000095 B = &R;
96 E = B+1;
97 }
Ted Kremenek0e80dea2008-04-09 21:41:14 +000098 };
Ted Kremenekf00daf02008-04-03 17:57:38 +000099};
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000100
Ted Kremenekf00daf02008-04-03 17:57:38 +0000101
102class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000103 NilArg Desc;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000104 ASTContext &Ctx;
105 ValueStateManager* VMgr;
106
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000107 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000108 ErrorsTy Errors;
109
Ted Kremenekf22f8682008-07-10 22:03:41 +0000110 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
Ted Kremenekf00daf02008-04-03 17:57:38 +0000111
112 bool isNSString(ObjCInterfaceType* T, const char* suffix);
113 bool AuditNSString(NodeTy* N, ObjCMessageExpr* ME);
114
115 void Warn(NodeTy* N, Expr* E, const std::string& s);
116 void WarnNilArg(NodeTy* N, Expr* E);
117
118 bool CheckNilArg(NodeTy* N, unsigned Arg);
119
120public:
121 BasicObjCFoundationChecks(ASTContext& ctx, ValueStateManager* vmgr)
122 : Ctx(ctx), VMgr(vmgr) {}
123
124 virtual ~BasicObjCFoundationChecks() {
125 for (ErrorsTy::iterator I = Errors.begin(), E = Errors.end(); I!=E; ++I)
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000126 delete *I;
Ted Kremenekf00daf02008-04-03 17:57:38 +0000127 }
128
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000129 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000130
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000131 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000132
133private:
134
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000135 void AddError(BugReport* R) {
136 Errors.push_back(R);
Ted Kremenekf00daf02008-04-03 17:57:38 +0000137 }
138
139 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000140 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf00daf02008-04-03 17:57:38 +0000141 }
142};
143
144} // end anonymous namespace
145
146
147GRSimpleAPICheck*
148clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx,
149 ValueStateManager* VMgr) {
150
151 return new BasicObjCFoundationChecks(Ctx, VMgr);
152}
153
154
155
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000156bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N,
157 ValueStateManager&) {
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000158
159 ObjCMessageExpr* ME =
160 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000161
Ted Kremenek639247a2008-03-27 22:05:32 +0000162 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000163
164 if (!ReceiverType)
Nuno Lopesf44f0932008-05-20 17:33:56 +0000165 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000166
Ted Kremenek639247a2008-03-27 22:05:32 +0000167 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
168
169 if (!name)
170 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000171
172 if (name[0] != 'N' || name[1] != 'S')
173 return false;
174
175 name += 2;
176
177 // FIXME: Make all of this faster.
178
179 if (isNSString(ReceiverType, name))
180 return AuditNSString(N, ME);
181
Nuno Lopesf44f0932008-05-20 17:33:56 +0000182 return false;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000183}
184
Ted Kremenek583c4382008-03-27 21:15:17 +0000185static inline bool isNil(RVal X) {
186 return isa<lval::ConcreteInt>(X);
187}
188
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000189//===----------------------------------------------------------------------===//
190// Error reporting.
191//===----------------------------------------------------------------------===//
192
193
Ted Kremenek0e80dea2008-04-09 21:41:14 +0000194void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
195
Ted Kremeneke3ef1c72008-04-14 17:39:48 +0000196 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek270ab7d2008-04-18 01:56:37 +0000197 BR.EmitWarning(**I);
Ted Kremenek639247a2008-03-27 22:05:32 +0000198}
199
200bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
201 ObjCMessageExpr* ME =
202 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
203
204 Expr * E = ME->getArg(Arg);
205
206 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf00daf02008-04-03 17:57:38 +0000207 WarnNilArg(N, ME, Arg);
Ted Kremenek639247a2008-03-27 22:05:32 +0000208 return true;
209 }
210
211 return false;
212}
213
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000214//===----------------------------------------------------------------------===//
215// NSString checking.
216//===----------------------------------------------------------------------===//
217
218bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
219 const char* suffix) {
220
221 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
222}
223
224bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
225 ObjCMessageExpr* ME) {
226
227 Selector S = ME->getSelector();
228
229 if (S.isUnarySelector())
230 return false;
231
232 // FIXME: This is going to be really slow doing these checks with
233 // lexical comparisons.
234
235 std::string name = S.getName();
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000236 assert (!name.empty());
237 const char* cstr = &name[0];
238 unsigned len = name.size();
Ted Kremenek639247a2008-03-27 22:05:32 +0000239
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000240 switch (len) {
241 default:
242 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000243 case 8:
Ted Kremenek639247a2008-03-27 22:05:32 +0000244 if (!strcmp(cstr, "compare:"))
245 return CheckNilArg(N, 0);
246
247 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000248
249 case 15:
250 // FIXME: Checking for initWithFormat: will not work in most cases
251 // yet because [NSString alloc] returns id, not NSString*. We will
252 // need support for tracking expected-type information in the analyzer
253 // to find these errors.
254 if (!strcmp(cstr, "initWithFormat:"))
255 return CheckNilArg(N, 0);
256
257 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000258
Ted Kremenek639247a2008-03-27 22:05:32 +0000259 case 16:
260 if (!strcmp(cstr, "compare:options:"))
261 return CheckNilArg(N, 0);
Ted Kremenekb20dccc2008-03-27 21:23:57 +0000262
263 break;
Ted Kremenek639247a2008-03-27 22:05:32 +0000264
265 case 22:
266 if (!strcmp(cstr, "compare:options:range:"))
267 return CheckNilArg(N, 0);
268
269 break;
270
271 case 23:
272
273 if (!strcmp(cstr, "caseInsensitiveCompare:"))
274 return CheckNilArg(N, 0);
275
276 break;
Ted Kremenek44e74042008-03-28 16:09:38 +0000277
Ted Kremenek639247a2008-03-27 22:05:32 +0000278 case 29:
279 if (!strcmp(cstr, "compare:options:range:locale:"))
280 return CheckNilArg(N, 0);
281
282 break;
283
284 case 37:
285 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
286 return CheckNilArg(N, 0);
287
288 break;
Ted Kremenekb0a2e472008-03-27 07:25:52 +0000289 }
290
291 return false;
292}
Ted Kremenekc1290552008-06-26 23:59:48 +0000293
294//===----------------------------------------------------------------------===//
295// Error reporting.
296//===----------------------------------------------------------------------===//
297
298namespace {
299
300class VISIBILITY_HIDDEN BadCFNumberCreate : public BugTypeCacheLocation {
301public:
302 typedef std::vector<BugReport*> AllErrorsTy;
303 AllErrorsTy AllErrors;
304
305 virtual const char* getName() const {
306 return "Bad use of CFNumberCreate";
307 }
308
309 virtual void EmitWarnings(BugReporter& BR) {
310 // FIXME: Refactor this method.
311 for (AllErrorsTy::iterator I=AllErrors.begin(), E=AllErrors.end(); I!=E;++I)
312 BR.EmitWarning(**I);
313 }
314};
315
316 // FIXME: This entire class should be refactored into the common
317 // BugReporter classes.
318class VISIBILITY_HIDDEN StrBugReport : public RangedBugReport {
319 std::string str;
320 const char* cstr;
321public:
322 StrBugReport(BugType& D, ExplodedNode<ValueState>* N, std::string s)
323 : RangedBugReport(D, N), str(s) {
324 cstr = str.c_str();
325 }
326
327 virtual const char* getDescription() const { return cstr; }
328};
329
330
331class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
332 // FIXME: Who should own this?
333 BadCFNumberCreate Desc;
334
335 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
336 // it should always be passed with a call to Audit. The latter
337 // approach makes this class more stateless.
338 ASTContext& Ctx;
339 IdentifierInfo* II;
340 ValueStateManager* VMgr;
341
Ted Kremenekf22f8682008-07-10 22:03:41 +0000342 RVal GetRVal(const ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
343 RVal GetRVal(const ValueState* St, LVal LV) { return VMgr->GetRVal(St, LV); }
Ted Kremenekc1290552008-06-26 23:59:48 +0000344
345public:
346
347 AuditCFNumberCreate(ASTContext& ctx, ValueStateManager* vmgr)
348 : Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), VMgr(vmgr) {}
349
350 virtual ~AuditCFNumberCreate() {}
351
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000352 virtual bool Audit(ExplodedNode<ValueState>* N, ValueStateManager&);
Ted Kremenekc1290552008-06-26 23:59:48 +0000353
354 virtual void EmitWarnings(BugReporter& BR) {
355 Desc.EmitWarnings(BR);
356 }
357
358private:
359
360 void AddError(VarDecl* V, Expr* Ex, ExplodedNode<ValueState> *N,
361 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
362};
363} // end anonymous namespace
364
365enum CFNumberType {
366 kCFNumberSInt8Type = 1,
367 kCFNumberSInt16Type = 2,
368 kCFNumberSInt32Type = 3,
369 kCFNumberSInt64Type = 4,
370 kCFNumberFloat32Type = 5,
371 kCFNumberFloat64Type = 6,
372 kCFNumberCharType = 7,
373 kCFNumberShortType = 8,
374 kCFNumberIntType = 9,
375 kCFNumberLongType = 10,
376 kCFNumberLongLongType = 11,
377 kCFNumberFloatType = 12,
378 kCFNumberDoubleType = 13,
379 kCFNumberCFIndexType = 14,
380 kCFNumberNSIntegerType = 15,
381 kCFNumberCGFloatType = 16
382};
383
384namespace {
385 template<typename T>
386 class Optional {
387 bool IsKnown;
388 T Val;
389 public:
390 Optional() : IsKnown(false), Val(0) {}
391 Optional(const T& val) : IsKnown(true), Val(val) {}
392
393 bool isKnown() const { return IsKnown; }
394
395 const T& getValue() const {
396 assert (isKnown());
397 return Val;
398 }
399
400 operator const T&() const {
401 return getValue();
402 }
403 };
404}
405
406static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
407 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
408
409 if (i < kCFNumberCharType)
410 return FixedSize[i-1];
411
412 QualType T;
413
414 switch (i) {
415 case kCFNumberCharType: T = Ctx.CharTy; break;
416 case kCFNumberShortType: T = Ctx.ShortTy; break;
417 case kCFNumberIntType: T = Ctx.IntTy; break;
418 case kCFNumberLongType: T = Ctx.LongTy; break;
419 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
420 case kCFNumberFloatType: T = Ctx.FloatTy; break;
421 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
422 case kCFNumberCFIndexType:
423 case kCFNumberNSIntegerType:
424 case kCFNumberCGFloatType:
425 // FIXME: We need a way to map from names to Type*.
426 default:
427 return Optional<uint64_t>();
428 }
429
430 return Ctx.getTypeSize(T);
431}
432
433#if 0
434static const char* GetCFNumberTypeStr(uint64_t i) {
435 static const char* Names[] = {
436 "kCFNumberSInt8Type",
437 "kCFNumberSInt16Type",
438 "kCFNumberSInt32Type",
439 "kCFNumberSInt64Type",
440 "kCFNumberFloat32Type",
441 "kCFNumberFloat64Type",
442 "kCFNumberCharType",
443 "kCFNumberShortType",
444 "kCFNumberIntType",
445 "kCFNumberLongType",
446 "kCFNumberLongLongType",
447 "kCFNumberFloatType",
448 "kCFNumberDoubleType",
449 "kCFNumberCFIndexType",
450 "kCFNumberNSIntegerType",
451 "kCFNumberCGFloatType"
452 };
453
454 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
455}
456#endif
457
Ted Kremenekbbafa5b2008-07-22 00:46:16 +0000458bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N,ValueStateManager&){
Ted Kremenekc1290552008-06-26 23:59:48 +0000459 CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
460 Expr* Callee = CE->getCallee();
461 RVal CallV = GetRVal(N->getState(), Callee);
462 lval::FuncVal* FuncV = dyn_cast<lval::FuncVal>(&CallV);
463
464 if (!FuncV || FuncV->getDecl()->getIdentifier() != II || CE->getNumArgs()!=3)
465 return false;
466
467 // Get the value of the "theType" argument.
468 RVal TheTypeVal = GetRVal(N->getState(), CE->getArg(1));
469
470 // FIXME: We really should allow ranges of valid theType values, and
471 // bifurcate the state appropriately.
472 nonlval::ConcreteInt* V = dyn_cast<nonlval::ConcreteInt>(&TheTypeVal);
473
474 if (!V)
475 return false;
476
477 uint64_t NumberKind = V->getValue().getLimitedValue();
478 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
479
480 // FIXME: In some cases we can emit an error.
481 if (!TargetSize.isKnown())
482 return false;
483
484 // Look at the value of the integer being passed by reference. Essentially
485 // we want to catch cases where the value passed in is not equal to the
486 // size of the type being created.
487 RVal TheValueExpr = GetRVal(N->getState(), CE->getArg(2));
488
489 // FIXME: Eventually we should handle arbitrary locations. We can do this
490 // by having an enhanced memory model that does low-level typing.
491 lval::DeclVal* LV = dyn_cast<lval::DeclVal>(&TheValueExpr);
492
493 if (!LV)
494 return false;
495
496 QualType T = LV->getDecl()->getType().getCanonicalType();
497
498 // FIXME: If the pointee isn't an integer type, should we flag a warning?
499 // People can do weird stuff with pointers.
500
501 if (!T->isIntegerType())
502 return false;
503
504 uint64_t SourceSize = Ctx.getTypeSize(T);
505
506 // CHECK: is SourceSize == TargetSize
507
508 if (SourceSize == TargetSize)
509 return false;
510
511 AddError(LV->getDecl(), CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
512
513 // FIXME: We can actually create an abstract "CFNumber" object that has
514 // the bits initialized to the provided values.
515 return SourceSize < TargetSize;
516}
517
518void AuditCFNumberCreate::AddError(VarDecl* V, Expr* Ex,
519 ExplodedNode<ValueState> *N,
520 uint64_t SourceSize, uint64_t TargetSize,
521 uint64_t NumberKind) {
522
523 std::ostringstream os;
524
525 os << (SourceSize == 8 ? "An " : "A ")
526 << SourceSize << " bit integer is used to initialize a CFNumber "
527 "object that represents "
528 << (TargetSize == 8 ? "an " : "a ")
529 << TargetSize << " bit integer. ";
530
531 if (SourceSize < TargetSize)
532 os << (TargetSize - SourceSize)
533 << " bits of the CFNumber value will be garbage." ;
534 else
535 os << (SourceSize - TargetSize)
536 << " bits of the input integer will be lost.";
537
538 StrBugReport* B = new StrBugReport(Desc, N, os.str());
539 B->addRange(Ex->getSourceRange());
540 Desc.AllErrors.push_back(B);
541}
542
543GRSimpleAPICheck*
544clang::CreateAuditCFNumberCreate(ASTContext& Ctx,
545 ValueStateManager* VMgr) {
546
547 return new AuditCFNumberCreate(Ctx, VMgr);
548}
549