xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision efef760214b071736eca58b38edaa749a5aa8973)
1 //== 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 
16 #include "ClangSACheckers.h"
17 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18 #include "clang/StaticAnalyzer/Core/Checker.h"
19 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
24 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
25 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
27 #include "clang/AST/DeclObjC.h"
28 #include "clang/AST/Expr.h"
29 #include "clang/AST/ExprObjC.h"
30 #include "clang/AST/StmtObjC.h"
31 #include "clang/AST/ASTContext.h"
32 #include "llvm/ADT/SmallString.h"
33 #include "llvm/ADT/StringMap.h"
34 
35 using namespace clang;
36 using namespace ento;
37 
38 namespace {
39 class APIMisuse : public BugType {
40 public:
41   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
42 };
43 } // end anonymous namespace
44 
45 //===----------------------------------------------------------------------===//
46 // Utility functions.
47 //===----------------------------------------------------------------------===//
48 
49 static const char* GetReceiverNameType(const ObjCMessage &msg) {
50   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
51     return ID->getIdentifier()->getNameStart();
52   return 0;
53 }
54 
55 enum FoundationClass {
56   FC_None,
57   FC_NSArray,
58   FC_NSDictionary,
59   FC_NSEnumerator,
60   FC_NSOrderedSet,
61   FC_NSSet,
62   FC_NSString
63 };
64 
65 static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID) {
66   static llvm::StringMap<FoundationClass> Classes;
67   if (Classes.empty()) {
68     Classes["NSArray"] = FC_NSArray;
69     Classes["NSDictionary"] = FC_NSDictionary;
70     Classes["NSEnumerator"] = FC_NSEnumerator;
71     Classes["NSOrderedSet"] = FC_NSOrderedSet;
72     Classes["NSSet"] = FC_NSSet;
73     Classes["NSString"] = FC_NSString;
74   }
75 
76   // FIXME: Should we cache this at all?
77   FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
78   if (result == FC_None)
79     if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
80       return findKnownClass(Super);
81 
82   return result;
83 }
84 
85 static inline bool isNil(SVal X) {
86   return isa<loc::ConcreteInt>(X);
87 }
88 
89 //===----------------------------------------------------------------------===//
90 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
91 //===----------------------------------------------------------------------===//
92 
93 namespace {
94   class NilArgChecker : public Checker<check::PreObjCMessage> {
95     mutable OwningPtr<APIMisuse> BT;
96 
97     void WarnNilArg(CheckerContext &C,
98                     const ObjCMessage &msg, unsigned Arg) const;
99 
100   public:
101     void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
102   };
103 }
104 
105 void NilArgChecker::WarnNilArg(CheckerContext &C,
106                                const ObjCMessage &msg,
107                                unsigned int Arg) const
108 {
109   if (!BT)
110     BT.reset(new APIMisuse("nil argument"));
111 
112   if (ExplodedNode *N = C.generateSink()) {
113     SmallString<128> sbuf;
114     llvm::raw_svector_ostream os(sbuf);
115     os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
116        << msg.getSelector().getAsString() << "' cannot be nil";
117 
118     BugReport *R = new BugReport(*BT, os.str(), N);
119     R->addRange(msg.getArgSourceRange(Arg));
120     C.EmitReport(R);
121   }
122 }
123 
124 void NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
125                                         CheckerContext &C) const {
126   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
127   if (!ID)
128     return;
129 
130   if (findKnownClass(ID) == FC_NSString) {
131     Selector S = msg.getSelector();
132 
133     if (S.isUnarySelector())
134       return;
135 
136     // FIXME: This is going to be really slow doing these checks with
137     //  lexical comparisons.
138 
139     std::string NameStr = S.getAsString();
140     StringRef Name(NameStr);
141     assert(!Name.empty());
142 
143     // FIXME: Checking for initWithFormat: will not work in most cases
144     //  yet because [NSString alloc] returns id, not NSString*.  We will
145     //  need support for tracking expected-type information in the analyzer
146     //  to find these errors.
147     if (Name == "caseInsensitiveCompare:" ||
148         Name == "compare:" ||
149         Name == "compare:options:" ||
150         Name == "compare:options:range:" ||
151         Name == "compare:options:range:locale:" ||
152         Name == "componentsSeparatedByCharactersInSet:" ||
153         Name == "initWithFormat:") {
154       if (isNil(msg.getArgSVal(0, C.getLocationContext(), C.getState())))
155         WarnNilArg(C, msg, 0);
156     }
157   }
158 }
159 
160 //===----------------------------------------------------------------------===//
161 // Error reporting.
162 //===----------------------------------------------------------------------===//
163 
164 namespace {
165 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
166   mutable OwningPtr<APIMisuse> BT;
167   mutable IdentifierInfo* II;
168 public:
169   CFNumberCreateChecker() : II(0) {}
170 
171   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
172 
173 private:
174   void EmitError(const TypedRegion* R, const Expr *Ex,
175                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
176 };
177 } // end anonymous namespace
178 
179 enum CFNumberType {
180   kCFNumberSInt8Type = 1,
181   kCFNumberSInt16Type = 2,
182   kCFNumberSInt32Type = 3,
183   kCFNumberSInt64Type = 4,
184   kCFNumberFloat32Type = 5,
185   kCFNumberFloat64Type = 6,
186   kCFNumberCharType = 7,
187   kCFNumberShortType = 8,
188   kCFNumberIntType = 9,
189   kCFNumberLongType = 10,
190   kCFNumberLongLongType = 11,
191   kCFNumberFloatType = 12,
192   kCFNumberDoubleType = 13,
193   kCFNumberCFIndexType = 14,
194   kCFNumberNSIntegerType = 15,
195   kCFNumberCGFloatType = 16
196 };
197 
198 namespace {
199   template<typename T>
200   class Optional {
201     bool IsKnown;
202     T Val;
203   public:
204     Optional() : IsKnown(false), Val(0) {}
205     Optional(const T& val) : IsKnown(true), Val(val) {}
206 
207     bool isKnown() const { return IsKnown; }
208 
209     const T& getValue() const {
210       assert (isKnown());
211       return Val;
212     }
213 
214     operator const T&() const {
215       return getValue();
216     }
217   };
218 }
219 
220 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
221   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
222 
223   if (i < kCFNumberCharType)
224     return FixedSize[i-1];
225 
226   QualType T;
227 
228   switch (i) {
229     case kCFNumberCharType:     T = Ctx.CharTy;     break;
230     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
231     case kCFNumberIntType:      T = Ctx.IntTy;      break;
232     case kCFNumberLongType:     T = Ctx.LongTy;     break;
233     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
234     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
235     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
236     case kCFNumberCFIndexType:
237     case kCFNumberNSIntegerType:
238     case kCFNumberCGFloatType:
239       // FIXME: We need a way to map from names to Type*.
240     default:
241       return Optional<uint64_t>();
242   }
243 
244   return Ctx.getTypeSize(T);
245 }
246 
247 #if 0
248 static const char* GetCFNumberTypeStr(uint64_t i) {
249   static const char* Names[] = {
250     "kCFNumberSInt8Type",
251     "kCFNumberSInt16Type",
252     "kCFNumberSInt32Type",
253     "kCFNumberSInt64Type",
254     "kCFNumberFloat32Type",
255     "kCFNumberFloat64Type",
256     "kCFNumberCharType",
257     "kCFNumberShortType",
258     "kCFNumberIntType",
259     "kCFNumberLongType",
260     "kCFNumberLongLongType",
261     "kCFNumberFloatType",
262     "kCFNumberDoubleType",
263     "kCFNumberCFIndexType",
264     "kCFNumberNSIntegerType",
265     "kCFNumberCGFloatType"
266   };
267 
268   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
269 }
270 #endif
271 
272 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
273                                          CheckerContext &C) const {
274   ProgramStateRef state = C.getState();
275   const FunctionDecl *FD = C.getCalleeDecl(CE);
276   if (!FD)
277     return;
278 
279   ASTContext &Ctx = C.getASTContext();
280   if (!II)
281     II = &Ctx.Idents.get("CFNumberCreate");
282 
283   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
284     return;
285 
286   // Get the value of the "theType" argument.
287   const LocationContext *LCtx = C.getLocationContext();
288   SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
289 
290   // FIXME: We really should allow ranges of valid theType values, and
291   //   bifurcate the state appropriately.
292   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
293   if (!V)
294     return;
295 
296   uint64_t NumberKind = V->getValue().getLimitedValue();
297   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
298 
299   // FIXME: In some cases we can emit an error.
300   if (!TargetSize.isKnown())
301     return;
302 
303   // Look at the value of the integer being passed by reference.  Essentially
304   // we want to catch cases where the value passed in is not equal to the
305   // size of the type being created.
306   SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
307 
308   // FIXME: Eventually we should handle arbitrary locations.  We can do this
309   //  by having an enhanced memory model that does low-level typing.
310   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
311   if (!LV)
312     return;
313 
314   const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
315   if (!R)
316     return;
317 
318   QualType T = Ctx.getCanonicalType(R->getValueType());
319 
320   // FIXME: If the pointee isn't an integer type, should we flag a warning?
321   //  People can do weird stuff with pointers.
322 
323   if (!T->isIntegerType())
324     return;
325 
326   uint64_t SourceSize = Ctx.getTypeSize(T);
327 
328   // CHECK: is SourceSize == TargetSize
329   if (SourceSize == TargetSize)
330     return;
331 
332   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
333   // otherwise generate a regular node.
334   //
335   // FIXME: We can actually create an abstract "CFNumber" object that has
336   //  the bits initialized to the provided values.
337   //
338   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
339                                                 : C.addTransition()) {
340     SmallString<128> sbuf;
341     llvm::raw_svector_ostream os(sbuf);
342 
343     os << (SourceSize == 8 ? "An " : "A ")
344        << SourceSize << " bit integer is used to initialize a CFNumber "
345                         "object that represents "
346        << (TargetSize == 8 ? "an " : "a ")
347        << TargetSize << " bit integer. ";
348 
349     if (SourceSize < TargetSize)
350       os << (TargetSize - SourceSize)
351       << " bits of the CFNumber value will be garbage." ;
352     else
353       os << (SourceSize - TargetSize)
354       << " bits of the input integer will be lost.";
355 
356     if (!BT)
357       BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
358 
359     BugReport *report = new BugReport(*BT, os.str(), N);
360     report->addRange(CE->getArg(2)->getSourceRange());
361     C.EmitReport(report);
362   }
363 }
364 
365 //===----------------------------------------------------------------------===//
366 // CFRetain/CFRelease checking for null arguments.
367 //===----------------------------------------------------------------------===//
368 
369 namespace {
370 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
371   mutable OwningPtr<APIMisuse> BT;
372   mutable IdentifierInfo *Retain, *Release;
373 public:
374   CFRetainReleaseChecker(): Retain(0), Release(0) {}
375   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
376 };
377 } // end anonymous namespace
378 
379 
380 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
381                                           CheckerContext &C) const {
382   // If the CallExpr doesn't have exactly 1 argument just give up checking.
383   if (CE->getNumArgs() != 1)
384     return;
385 
386   ProgramStateRef state = C.getState();
387   const FunctionDecl *FD = C.getCalleeDecl(CE);
388   if (!FD)
389     return;
390 
391   if (!BT) {
392     ASTContext &Ctx = C.getASTContext();
393     Retain = &Ctx.Idents.get("CFRetain");
394     Release = &Ctx.Idents.get("CFRelease");
395     BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
396   }
397 
398   // Check if we called CFRetain/CFRelease.
399   const IdentifierInfo *FuncII = FD->getIdentifier();
400   if (!(FuncII == Retain || FuncII == Release))
401     return;
402 
403   // FIXME: The rest of this just checks that the argument is non-null.
404   // It should probably be refactored and combined with AttrNonNullChecker.
405 
406   // Get the argument's value.
407   const Expr *Arg = CE->getArg(0);
408   SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
409   DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
410   if (!DefArgVal)
411     return;
412 
413   // Get a NULL value.
414   SValBuilder &svalBuilder = C.getSValBuilder();
415   DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
416 
417   // Make an expression asserting that they're equal.
418   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
419 
420   // Are they equal?
421   ProgramStateRef stateTrue, stateFalse;
422   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
423 
424   if (stateTrue && !stateFalse) {
425     ExplodedNode *N = C.generateSink(stateTrue);
426     if (!N)
427       return;
428 
429     const char *description = (FuncII == Retain)
430                             ? "Null pointer argument in call to CFRetain"
431                             : "Null pointer argument in call to CFRelease";
432 
433     BugReport *report = new BugReport(*BT, description, N);
434     report->addRange(Arg->getSourceRange());
435     report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg,
436                                                                     report));
437     C.EmitReport(report);
438     return;
439   }
440 
441   // From here on, we know the argument is non-null.
442   C.addTransition(stateFalse);
443 }
444 
445 //===----------------------------------------------------------------------===//
446 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
447 //===----------------------------------------------------------------------===//
448 
449 namespace {
450 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
451   mutable Selector releaseS;
452   mutable Selector retainS;
453   mutable Selector autoreleaseS;
454   mutable Selector drainS;
455   mutable OwningPtr<BugType> BT;
456 
457 public:
458   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
459 };
460 }
461 
462 void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
463                                               CheckerContext &C) const {
464 
465   if (!BT) {
466     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
467                            "instance"));
468 
469     ASTContext &Ctx = C.getASTContext();
470     releaseS = GetNullarySelector("release", Ctx);
471     retainS = GetNullarySelector("retain", Ctx);
472     autoreleaseS = GetNullarySelector("autorelease", Ctx);
473     drainS = GetNullarySelector("drain", Ctx);
474   }
475 
476   if (msg.isInstanceMessage())
477     return;
478   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
479   assert(Class);
480 
481   Selector S = msg.getSelector();
482   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
483     return;
484 
485   if (ExplodedNode *N = C.addTransition()) {
486     SmallString<200> buf;
487     llvm::raw_svector_ostream os(buf);
488 
489     os << "The '" << S.getAsString() << "' message should be sent to instances "
490           "of class '" << Class->getName()
491        << "' and not the class directly";
492 
493     BugReport *report = new BugReport(*BT, os.str(), N);
494     report->addRange(msg.getSourceRange());
495     C.EmitReport(report);
496   }
497 }
498 
499 //===----------------------------------------------------------------------===//
500 // Check for passing non-Objective-C types to variadic methods that expect
501 // only Objective-C types.
502 //===----------------------------------------------------------------------===//
503 
504 namespace {
505 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
506   mutable Selector arrayWithObjectsS;
507   mutable Selector dictionaryWithObjectsAndKeysS;
508   mutable Selector setWithObjectsS;
509   mutable Selector orderedSetWithObjectsS;
510   mutable Selector initWithObjectsS;
511   mutable Selector initWithObjectsAndKeysS;
512   mutable OwningPtr<BugType> BT;
513 
514   bool isVariadicMessage(const ObjCMessage &msg) const;
515 
516 public:
517   void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
518 };
519 }
520 
521 /// isVariadicMessage - Returns whether the given message is a variadic message,
522 /// where all arguments must be Objective-C types.
523 bool
524 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
525   const ObjCMethodDecl *MD = msg.getMethodDecl();
526 
527   if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
528     return false;
529 
530   Selector S = msg.getSelector();
531 
532   if (msg.isInstanceMessage()) {
533     // FIXME: Ideally we'd look at the receiver interface here, but that's not
534     // useful for init, because alloc returns 'id'. In theory, this could lead
535     // to false positives, for example if there existed a class that had an
536     // initWithObjects: implementation that does accept non-Objective-C pointer
537     // types, but the chance of that happening is pretty small compared to the
538     // gains that this analysis gives.
539     const ObjCInterfaceDecl *Class = MD->getClassInterface();
540 
541     switch (findKnownClass(Class)) {
542     case FC_NSArray:
543     case FC_NSOrderedSet:
544     case FC_NSSet:
545       return S == initWithObjectsS;
546     case FC_NSDictionary:
547       return S == initWithObjectsAndKeysS;
548     default:
549       return false;
550     }
551   } else {
552     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
553 
554     switch (findKnownClass(Class)) {
555       case FC_NSArray:
556         return S == arrayWithObjectsS;
557       case FC_NSOrderedSet:
558         return S == orderedSetWithObjectsS;
559       case FC_NSSet:
560         return S == setWithObjectsS;
561       case FC_NSDictionary:
562         return S == dictionaryWithObjectsAndKeysS;
563       default:
564         return false;
565     }
566   }
567 }
568 
569 void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage msg,
570                                                     CheckerContext &C) const {
571   if (!BT) {
572     BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
573                            "Objective-C pointer types"));
574 
575     ASTContext &Ctx = C.getASTContext();
576     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
577     dictionaryWithObjectsAndKeysS =
578       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
579     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
580     orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
581 
582     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
583     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
584   }
585 
586   if (!isVariadicMessage(msg))
587       return;
588 
589   // We are not interested in the selector arguments since they have
590   // well-defined types, so the compiler will issue a warning for them.
591   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
592 
593   // We're not interested in the last argument since it has to be nil or the
594   // compiler would have issued a warning for it elsewhere.
595   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
596 
597   if (variadicArgsEnd <= variadicArgsBegin)
598     return;
599 
600   // Verify that all arguments have Objective-C types.
601   llvm::Optional<ExplodedNode*> errorNode;
602   ProgramStateRef state = C.getState();
603 
604   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
605     QualType ArgTy = msg.getArgType(I);
606     if (ArgTy->isObjCObjectPointerType())
607       continue;
608 
609     // Block pointers are treaded as Objective-C pointers.
610     if (ArgTy->isBlockPointerType())
611       continue;
612 
613     // Ignore pointer constants.
614     if (isa<loc::ConcreteInt>(msg.getArgSVal(I, C.getLocationContext(),
615                                              state)))
616       continue;
617 
618     // Ignore pointer types annotated with 'NSObject' attribute.
619     if (C.getASTContext().isObjCNSObjectType(ArgTy))
620       continue;
621 
622     // Ignore CF references, which can be toll-free bridged.
623     if (coreFoundation::isCFObjectRef(ArgTy))
624       continue;
625 
626     // Generate only one error node to use for all bug reports.
627     if (!errorNode.hasValue()) {
628       errorNode = C.addTransition();
629     }
630 
631     if (!errorNode.getValue())
632       continue;
633 
634     SmallString<128> sbuf;
635     llvm::raw_svector_ostream os(sbuf);
636 
637     if (const char *TypeName = GetReceiverNameType(msg))
638       os << "Argument to '" << TypeName << "' method '";
639     else
640       os << "Argument to method '";
641 
642     os << msg.getSelector().getAsString()
643       << "' should be an Objective-C pointer type, not '"
644       << ArgTy.getAsString() << "'";
645 
646     BugReport *R = new BugReport(*BT, os.str(),
647                                              errorNode.getValue());
648     R->addRange(msg.getArgSourceRange(I));
649     C.EmitReport(R);
650   }
651 }
652 
653 //===----------------------------------------------------------------------===//
654 // Improves the modeling of loops over Cocoa collections.
655 //===----------------------------------------------------------------------===//
656 
657 namespace {
658 class ObjCLoopChecker
659   : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
660 
661 public:
662   void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
663 };
664 }
665 
666 static bool isKnownNonNilCollectionType(QualType T) {
667   const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
668   if (!PT)
669     return false;
670 
671   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
672   if (!ID)
673     return false;
674 
675   switch (findKnownClass(ID)) {
676   case FC_NSArray:
677   case FC_NSDictionary:
678   case FC_NSEnumerator:
679   case FC_NSOrderedSet:
680   case FC_NSSet:
681     return true;
682   default:
683     return false;
684   }
685 }
686 
687 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
688                                     CheckerContext &C) const {
689   ProgramStateRef State = C.getState();
690 
691   // Check if this is the branch for the end of the loop.
692   SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
693   if (CollectionSentinel.isZeroConstant())
694     return;
695 
696   // See if the collection is one where we /know/ the elements are non-nil.
697   const Expr *Collection = FCS->getCollection();
698   if (!isKnownNonNilCollectionType(Collection->getType()))
699     return;
700 
701   // FIXME: Copied from ExprEngineObjC.
702   const Stmt *Element = FCS->getElement();
703   SVal ElementVar;
704   if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
705     const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
706     assert(ElemDecl->getInit() == 0);
707     ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
708   } else {
709     ElementVar = State->getSVal(Element, C.getLocationContext());
710   }
711 
712   if (!isa<Loc>(ElementVar))
713     return;
714 
715   // Go ahead and assume the value is non-nil.
716   SVal Val = State->getSVal(cast<Loc>(ElementVar));
717   State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
718   C.addTransition(State);
719 }
720 
721 
722 //===----------------------------------------------------------------------===//
723 // Check registration.
724 //===----------------------------------------------------------------------===//
725 
726 void ento::registerNilArgChecker(CheckerManager &mgr) {
727   mgr.registerChecker<NilArgChecker>();
728 }
729 
730 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
731   mgr.registerChecker<CFNumberCreateChecker>();
732 }
733 
734 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
735   mgr.registerChecker<CFRetainReleaseChecker>();
736 }
737 
738 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
739   mgr.registerChecker<ClassReleaseChecker>();
740 }
741 
742 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
743   mgr.registerChecker<VariadicMethodTypeChecker>();
744 }
745 
746 void ento::registerObjCLoopChecker(CheckerManager &mgr) {
747   mgr.registerChecker<ObjCLoopChecker>();
748 }
749