blob: 1667a21494e76d6eb3e9f12c41056e5b96100359 [file] [log] [blame]
Ted Kremenek99c6ad32008-03-27 07:25:52 +00001//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file defines BasicObjCFoundationChecks, a class that encapsulates
11// a set of simple checks to run on Objective-C code using Apple's Foundation
12// classes.
13//
14//===----------------------------------------------------------------------===//
15
Ted Kremenek52755612008-03-27 17:17:22 +000016#include "BasicObjCFoundationChecks.h"
17
Ted Kremenek99c6ad32008-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 Kremenekf1ae7052008-04-03 17:57:38 +000021#include "clang/Analysis/PathSensitive/BugReporter.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000022#include "clang/Analysis/PathDiagnostic.h"
23#include "clang/AST/Expr.h"
Steve Narofff494b572008-05-29 21:12:08 +000024#include "clang/AST/ExprObjC.h"
Ted Kremenek99c6ad32008-03-27 07:25:52 +000025#include "clang/AST/ASTContext.h"
26#include "llvm/Support/Compiler.h"
27
28#include <vector>
Ted Kremenek4ba62832008-03-27 22:05:32 +000029#include <sstream>
Ted Kremenek99c6ad32008-03-27 07:25:52 +000030
31using namespace clang;
Ted Kremenek52755612008-03-27 17:17:22 +000032
Ted Kremenek4ba62832008-03-27 22:05:32 +000033static ObjCInterfaceType* GetReceiverType(ObjCMessageExpr* ME) {
34 Expr* Receiver = ME->getReceiver();
35
36 if (!Receiver)
37 return NULL;
38
Ted Kremenek7956f752008-04-03 21:44:24 +000039 QualType X = Receiver->getType();
Ted Kremenek6bdafbf2008-04-19 19:12:50 +000040
Ted Kremenekc1ff3cd2008-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 Kremenek4ba62832008-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 Kremenek52755612008-03-27 17:17:22 +000056
Ted Kremenekf1ae7052008-04-03 17:57:38 +000057namespace {
58
Ted Kremenek95cc1ba2008-04-18 20:54:29 +000059class VISIBILITY_HIDDEN NilArg : public BugTypeCacheLocation {
Ted Kremenekf1ae7052008-04-03 17:57:38 +000060public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000061 virtual ~NilArg() {}
62
63 virtual const char* getName() const {
64 return "nil argument";
65 }
66
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000067 class Report : public BugReport {
68 std::string Msg;
69 const char* s;
70 SourceRange R;
71 public:
Ted Kremenekf1ae7052008-04-03 17:57:38 +000072
Ted Kremenekd2f642b2008-04-14 17:39:48 +000073 Report(NilArg& Desc, ExplodedNode<ValueState>* N,
74 ObjCMessageExpr* ME, unsigned Arg)
75 : BugReport(Desc, N) {
Ted Kremenek50a6d0c2008-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 Kremenek4d35dac2008-04-10 16:05:13 +000091 virtual const char* getDescription() const { return s; }
92
Ted Kremenekbb77e9b2008-05-01 22:50:36 +000093 virtual void getRanges(BugReporter& BR,
94 const SourceRange*& B, const SourceRange*& E) {
Ted Kremenek4d35dac2008-04-10 16:05:13 +000095 B = &R;
96 E = B+1;
97 }
Ted Kremenek50a6d0c2008-04-09 21:41:14 +000098 };
Ted Kremenekf1ae7052008-04-03 17:57:38 +000099};
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000100
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000101
102class VISIBILITY_HIDDEN BasicObjCFoundationChecks : public GRSimpleAPICheck {
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000103 NilArg Desc;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000104 ASTContext &Ctx;
105 ValueStateManager* VMgr;
106
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000107 typedef std::vector<BugReport*> ErrorsTy;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000108 ErrorsTy Errors;
109
110 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
111
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 Kremenekd2f642b2008-04-14 17:39:48 +0000126 delete *I;
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000127 }
128
129 virtual bool Audit(ExplodedNode<ValueState>* N);
130
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000131 virtual void EmitWarnings(BugReporter& BR);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000132
133private:
134
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000135 void AddError(BugReport* R) {
136 Errors.push_back(R);
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000137 }
138
139 void WarnNilArg(NodeTy* N, ObjCMessageExpr* ME, unsigned Arg) {
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000140 AddError(new NilArg::Report(Desc, N, ME, Arg));
Ted Kremenekf1ae7052008-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 Kremenek99c6ad32008-03-27 07:25:52 +0000156bool BasicObjCFoundationChecks::Audit(ExplodedNode<ValueState>* N) {
157
158 ObjCMessageExpr* ME =
159 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000160
Ted Kremenek4ba62832008-03-27 22:05:32 +0000161 ObjCInterfaceType* ReceiverType = GetReceiverType(ME);
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000162
163 if (!ReceiverType)
Nuno Lopesf7427942008-05-20 17:33:56 +0000164 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000165
Ted Kremenek4ba62832008-03-27 22:05:32 +0000166 const char* name = ReceiverType->getDecl()->getIdentifier()->getName();
167
168 if (!name)
169 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000170
171 if (name[0] != 'N' || name[1] != 'S')
172 return false;
173
174 name += 2;
175
176 // FIXME: Make all of this faster.
177
178 if (isNSString(ReceiverType, name))
179 return AuditNSString(N, ME);
180
Nuno Lopesf7427942008-05-20 17:33:56 +0000181 return false;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000182}
183
Ted Kremeneke5d5c202008-03-27 21:15:17 +0000184static inline bool isNil(RVal X) {
185 return isa<lval::ConcreteInt>(X);
186}
187
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000188//===----------------------------------------------------------------------===//
189// Error reporting.
190//===----------------------------------------------------------------------===//
191
192
Ted Kremenek50a6d0c2008-04-09 21:41:14 +0000193void BasicObjCFoundationChecks::EmitWarnings(BugReporter& BR) {
194
Ted Kremenekd2f642b2008-04-14 17:39:48 +0000195 for (ErrorsTy::iterator I=Errors.begin(), E=Errors.end(); I!=E; ++I)
Ted Kremenek75840e12008-04-18 01:56:37 +0000196 BR.EmitWarning(**I);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000197}
198
199bool BasicObjCFoundationChecks::CheckNilArg(NodeTy* N, unsigned Arg) {
200 ObjCMessageExpr* ME =
201 cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
202
203 Expr * E = ME->getArg(Arg);
204
205 if (isNil(GetRVal(N->getState(), E))) {
Ted Kremenekf1ae7052008-04-03 17:57:38 +0000206 WarnNilArg(N, ME, Arg);
Ted Kremenek4ba62832008-03-27 22:05:32 +0000207 return true;
208 }
209
210 return false;
211}
212
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000213//===----------------------------------------------------------------------===//
214// NSString checking.
215//===----------------------------------------------------------------------===//
216
217bool BasicObjCFoundationChecks::isNSString(ObjCInterfaceType* T,
218 const char* suffix) {
219
220 return !strcmp("String", suffix) || !strcmp("MutableString", suffix);
221}
222
223bool BasicObjCFoundationChecks::AuditNSString(NodeTy* N,
224 ObjCMessageExpr* ME) {
225
226 Selector S = ME->getSelector();
227
228 if (S.isUnarySelector())
229 return false;
230
231 // FIXME: This is going to be really slow doing these checks with
232 // lexical comparisons.
233
234 std::string name = S.getName();
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000235 assert (!name.empty());
236 const char* cstr = &name[0];
237 unsigned len = name.size();
Ted Kremenek4ba62832008-03-27 22:05:32 +0000238
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000239 switch (len) {
240 default:
241 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000242 case 8:
Ted Kremenek4ba62832008-03-27 22:05:32 +0000243 if (!strcmp(cstr, "compare:"))
244 return CheckNilArg(N, 0);
245
246 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000247
248 case 15:
249 // FIXME: Checking for initWithFormat: will not work in most cases
250 // yet because [NSString alloc] returns id, not NSString*. We will
251 // need support for tracking expected-type information in the analyzer
252 // to find these errors.
253 if (!strcmp(cstr, "initWithFormat:"))
254 return CheckNilArg(N, 0);
255
256 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000257
Ted Kremenek4ba62832008-03-27 22:05:32 +0000258 case 16:
259 if (!strcmp(cstr, "compare:options:"))
260 return CheckNilArg(N, 0);
Ted Kremenek9b3fdea2008-03-27 21:23:57 +0000261
262 break;
Ted Kremenek4ba62832008-03-27 22:05:32 +0000263
264 case 22:
265 if (!strcmp(cstr, "compare:options:range:"))
266 return CheckNilArg(N, 0);
267
268 break;
269
270 case 23:
271
272 if (!strcmp(cstr, "caseInsensitiveCompare:"))
273 return CheckNilArg(N, 0);
274
275 break;
Ted Kremenek8730e132008-03-28 16:09:38 +0000276
Ted Kremenek4ba62832008-03-27 22:05:32 +0000277 case 29:
278 if (!strcmp(cstr, "compare:options:range:locale:"))
279 return CheckNilArg(N, 0);
280
281 break;
282
283 case 37:
284 if (!strcmp(cstr, "componentsSeparatedByCharactersInSet:"))
285 return CheckNilArg(N, 0);
286
287 break;
Ted Kremenek99c6ad32008-03-27 07:25:52 +0000288 }
289
290 return false;
291}
Ted Kremenek04bc8762008-06-26 23:59:48 +0000292
293//===----------------------------------------------------------------------===//
294// Error reporting.
295//===----------------------------------------------------------------------===//
296
297namespace {
298
299class VISIBILITY_HIDDEN BadCFNumberCreate : public BugTypeCacheLocation {
300public:
301 typedef std::vector<BugReport*> AllErrorsTy;
302 AllErrorsTy AllErrors;
303
304 virtual const char* getName() const {
305 return "Bad use of CFNumberCreate";
306 }
307
308 virtual void EmitWarnings(BugReporter& BR) {
309 // FIXME: Refactor this method.
310 for (AllErrorsTy::iterator I=AllErrors.begin(), E=AllErrors.end(); I!=E;++I)
311 BR.EmitWarning(**I);
312 }
313};
314
315 // FIXME: This entire class should be refactored into the common
316 // BugReporter classes.
317class VISIBILITY_HIDDEN StrBugReport : public RangedBugReport {
318 std::string str;
319 const char* cstr;
320public:
321 StrBugReport(BugType& D, ExplodedNode<ValueState>* N, std::string s)
322 : RangedBugReport(D, N), str(s) {
323 cstr = str.c_str();
324 }
325
326 virtual const char* getDescription() const { return cstr; }
327};
328
329
330class VISIBILITY_HIDDEN AuditCFNumberCreate : public GRSimpleAPICheck {
331 // FIXME: Who should own this?
332 BadCFNumberCreate Desc;
333
334 // FIXME: Either this should be refactored into GRSimpleAPICheck, or
335 // it should always be passed with a call to Audit. The latter
336 // approach makes this class more stateless.
337 ASTContext& Ctx;
338 IdentifierInfo* II;
339 ValueStateManager* VMgr;
340
341 RVal GetRVal(ValueState* St, Expr* E) { return VMgr->GetRVal(St, E); }
342 RVal GetRVal(ValueState* St, LVal LV) { return VMgr->GetRVal(St, LV); }
343
344public:
345
346 AuditCFNumberCreate(ASTContext& ctx, ValueStateManager* vmgr)
347 : Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), VMgr(vmgr) {}
348
349 virtual ~AuditCFNumberCreate() {}
350
351 virtual bool Audit(ExplodedNode<ValueState>* N);
352
353 virtual void EmitWarnings(BugReporter& BR) {
354 Desc.EmitWarnings(BR);
355 }
356
357private:
358
359 void AddError(VarDecl* V, Expr* Ex, ExplodedNode<ValueState> *N,
360 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
361};
362} // end anonymous namespace
363
364enum CFNumberType {
365 kCFNumberSInt8Type = 1,
366 kCFNumberSInt16Type = 2,
367 kCFNumberSInt32Type = 3,
368 kCFNumberSInt64Type = 4,
369 kCFNumberFloat32Type = 5,
370 kCFNumberFloat64Type = 6,
371 kCFNumberCharType = 7,
372 kCFNumberShortType = 8,
373 kCFNumberIntType = 9,
374 kCFNumberLongType = 10,
375 kCFNumberLongLongType = 11,
376 kCFNumberFloatType = 12,
377 kCFNumberDoubleType = 13,
378 kCFNumberCFIndexType = 14,
379 kCFNumberNSIntegerType = 15,
380 kCFNumberCGFloatType = 16
381};
382
383namespace {
384 template<typename T>
385 class Optional {
386 bool IsKnown;
387 T Val;
388 public:
389 Optional() : IsKnown(false), Val(0) {}
390 Optional(const T& val) : IsKnown(true), Val(val) {}
391
392 bool isKnown() const { return IsKnown; }
393
394 const T& getValue() const {
395 assert (isKnown());
396 return Val;
397 }
398
399 operator const T&() const {
400 return getValue();
401 }
402 };
403}
404
405static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
406 static unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
407
408 if (i < kCFNumberCharType)
409 return FixedSize[i-1];
410
411 QualType T;
412
413 switch (i) {
414 case kCFNumberCharType: T = Ctx.CharTy; break;
415 case kCFNumberShortType: T = Ctx.ShortTy; break;
416 case kCFNumberIntType: T = Ctx.IntTy; break;
417 case kCFNumberLongType: T = Ctx.LongTy; break;
418 case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
419 case kCFNumberFloatType: T = Ctx.FloatTy; break;
420 case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
421 case kCFNumberCFIndexType:
422 case kCFNumberNSIntegerType:
423 case kCFNumberCGFloatType:
424 // FIXME: We need a way to map from names to Type*.
425 default:
426 return Optional<uint64_t>();
427 }
428
429 return Ctx.getTypeSize(T);
430}
431
432#if 0
433static const char* GetCFNumberTypeStr(uint64_t i) {
434 static const char* Names[] = {
435 "kCFNumberSInt8Type",
436 "kCFNumberSInt16Type",
437 "kCFNumberSInt32Type",
438 "kCFNumberSInt64Type",
439 "kCFNumberFloat32Type",
440 "kCFNumberFloat64Type",
441 "kCFNumberCharType",
442 "kCFNumberShortType",
443 "kCFNumberIntType",
444 "kCFNumberLongType",
445 "kCFNumberLongLongType",
446 "kCFNumberFloatType",
447 "kCFNumberDoubleType",
448 "kCFNumberCFIndexType",
449 "kCFNumberNSIntegerType",
450 "kCFNumberCGFloatType"
451 };
452
453 return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
454}
455#endif
456
457bool AuditCFNumberCreate::Audit(ExplodedNode<ValueState>* N) {
458 CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
459 Expr* Callee = CE->getCallee();
460 RVal CallV = GetRVal(N->getState(), Callee);
461 lval::FuncVal* FuncV = dyn_cast<lval::FuncVal>(&CallV);
462
463 if (!FuncV || FuncV->getDecl()->getIdentifier() != II || CE->getNumArgs()!=3)
464 return false;
465
466 // Get the value of the "theType" argument.
467 RVal TheTypeVal = GetRVal(N->getState(), CE->getArg(1));
468
469 // FIXME: We really should allow ranges of valid theType values, and
470 // bifurcate the state appropriately.
471 nonlval::ConcreteInt* V = dyn_cast<nonlval::ConcreteInt>(&TheTypeVal);
472
473 if (!V)
474 return false;
475
476 uint64_t NumberKind = V->getValue().getLimitedValue();
477 Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
478
479 // FIXME: In some cases we can emit an error.
480 if (!TargetSize.isKnown())
481 return false;
482
483 // Look at the value of the integer being passed by reference. Essentially
484 // we want to catch cases where the value passed in is not equal to the
485 // size of the type being created.
486 RVal TheValueExpr = GetRVal(N->getState(), CE->getArg(2));
487
488 // FIXME: Eventually we should handle arbitrary locations. We can do this
489 // by having an enhanced memory model that does low-level typing.
490 lval::DeclVal* LV = dyn_cast<lval::DeclVal>(&TheValueExpr);
491
492 if (!LV)
493 return false;
494
495 QualType T = LV->getDecl()->getType().getCanonicalType();
496
497 // FIXME: If the pointee isn't an integer type, should we flag a warning?
498 // People can do weird stuff with pointers.
499
500 if (!T->isIntegerType())
501 return false;
502
503 uint64_t SourceSize = Ctx.getTypeSize(T);
504
505 // CHECK: is SourceSize == TargetSize
506
507 if (SourceSize == TargetSize)
508 return false;
509
510 AddError(LV->getDecl(), CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
511
512 // FIXME: We can actually create an abstract "CFNumber" object that has
513 // the bits initialized to the provided values.
514 return SourceSize < TargetSize;
515}
516
517void AuditCFNumberCreate::AddError(VarDecl* V, Expr* Ex,
518 ExplodedNode<ValueState> *N,
519 uint64_t SourceSize, uint64_t TargetSize,
520 uint64_t NumberKind) {
521
522 std::ostringstream os;
523
524 os << (SourceSize == 8 ? "An " : "A ")
525 << SourceSize << " bit integer is used to initialize a CFNumber "
526 "object that represents "
527 << (TargetSize == 8 ? "an " : "a ")
528 << TargetSize << " bit integer. ";
529
530 if (SourceSize < TargetSize)
531 os << (TargetSize - SourceSize)
532 << " bits of the CFNumber value will be garbage." ;
533 else
534 os << (SourceSize - TargetSize)
535 << " bits of the input integer will be lost.";
536
537 StrBugReport* B = new StrBugReport(Desc, N, os.str());
538 B->addRange(Ex->getSourceRange());
539 Desc.AllErrors.push_back(B);
540}
541
542GRSimpleAPICheck*
543clang::CreateAuditCFNumberCreate(ASTContext& Ctx,
544 ValueStateManager* VMgr) {
545
546 return new AuditCFNumberCreate(Ctx, VMgr);
547}
548