xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision 9159e16f2123e3cf9d35146ad307047f30db526f)
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/CallEvent.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.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 StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
50   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
51     return ID->getIdentifier()->getName();
52   return StringRef();
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 ObjCMethodCall &msg, unsigned Arg) const;
99 
100   public:
101     void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
102   };
103 }
104 
105 void NilArgChecker::WarnNilArg(CheckerContext &C,
106                                const ObjCMethodCall &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 '" << GetReceiverInterfaceName(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(const ObjCMethodCall &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)))
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     bugreporter::addTrackNullOrUndefValueVisitor(N, Arg, report);
436     C.EmitReport(report);
437     return;
438   }
439 
440   // From here on, we know the argument is non-null.
441   C.addTransition(stateFalse);
442 }
443 
444 //===----------------------------------------------------------------------===//
445 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
446 //===----------------------------------------------------------------------===//
447 
448 namespace {
449 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
450   mutable Selector releaseS;
451   mutable Selector retainS;
452   mutable Selector autoreleaseS;
453   mutable Selector drainS;
454   mutable OwningPtr<BugType> BT;
455 
456 public:
457   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
458 };
459 }
460 
461 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
462                                               CheckerContext &C) const {
463 
464   if (!BT) {
465     BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
466                            "instance"));
467 
468     ASTContext &Ctx = C.getASTContext();
469     releaseS = GetNullarySelector("release", Ctx);
470     retainS = GetNullarySelector("retain", Ctx);
471     autoreleaseS = GetNullarySelector("autorelease", Ctx);
472     drainS = GetNullarySelector("drain", Ctx);
473   }
474 
475   if (msg.isInstanceMessage())
476     return;
477   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
478   assert(Class);
479 
480   Selector S = msg.getSelector();
481   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
482     return;
483 
484   if (ExplodedNode *N = C.addTransition()) {
485     SmallString<200> buf;
486     llvm::raw_svector_ostream os(buf);
487 
488     os << "The '" << S.getAsString() << "' message should be sent to instances "
489           "of class '" << Class->getName()
490        << "' and not the class directly";
491 
492     BugReport *report = new BugReport(*BT, os.str(), N);
493     report->addRange(msg.getSourceRange());
494     C.EmitReport(report);
495   }
496 }
497 
498 //===----------------------------------------------------------------------===//
499 // Check for passing non-Objective-C types to variadic methods that expect
500 // only Objective-C types.
501 //===----------------------------------------------------------------------===//
502 
503 namespace {
504 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
505   mutable Selector arrayWithObjectsS;
506   mutable Selector dictionaryWithObjectsAndKeysS;
507   mutable Selector setWithObjectsS;
508   mutable Selector orderedSetWithObjectsS;
509   mutable Selector initWithObjectsS;
510   mutable Selector initWithObjectsAndKeysS;
511   mutable OwningPtr<BugType> BT;
512 
513   bool isVariadicMessage(const ObjCMethodCall &msg) const;
514 
515 public:
516   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
517 };
518 }
519 
520 /// isVariadicMessage - Returns whether the given message is a variadic message,
521 /// where all arguments must be Objective-C types.
522 bool
523 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
524   const ObjCMethodDecl *MD = msg.getDecl();
525 
526   if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
527     return false;
528 
529   Selector S = msg.getSelector();
530 
531   if (msg.isInstanceMessage()) {
532     // FIXME: Ideally we'd look at the receiver interface here, but that's not
533     // useful for init, because alloc returns 'id'. In theory, this could lead
534     // to false positives, for example if there existed a class that had an
535     // initWithObjects: implementation that does accept non-Objective-C pointer
536     // types, but the chance of that happening is pretty small compared to the
537     // gains that this analysis gives.
538     const ObjCInterfaceDecl *Class = MD->getClassInterface();
539 
540     switch (findKnownClass(Class)) {
541     case FC_NSArray:
542     case FC_NSOrderedSet:
543     case FC_NSSet:
544       return S == initWithObjectsS;
545     case FC_NSDictionary:
546       return S == initWithObjectsAndKeysS;
547     default:
548       return false;
549     }
550   } else {
551     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
552 
553     switch (findKnownClass(Class)) {
554       case FC_NSArray:
555         return S == arrayWithObjectsS;
556       case FC_NSOrderedSet:
557         return S == orderedSetWithObjectsS;
558       case FC_NSSet:
559         return S == setWithObjectsS;
560       case FC_NSDictionary:
561         return S == dictionaryWithObjectsAndKeysS;
562       default:
563         return false;
564     }
565   }
566 }
567 
568 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
569                                                     CheckerContext &C) const {
570   if (!BT) {
571     BT.reset(new APIMisuse("Arguments passed to variadic method aren't all "
572                            "Objective-C pointer types"));
573 
574     ASTContext &Ctx = C.getASTContext();
575     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
576     dictionaryWithObjectsAndKeysS =
577       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
578     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
579     orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
580 
581     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
582     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
583   }
584 
585   if (!isVariadicMessage(msg))
586       return;
587 
588   // We are not interested in the selector arguments since they have
589   // well-defined types, so the compiler will issue a warning for them.
590   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
591 
592   // We're not interested in the last argument since it has to be nil or the
593   // compiler would have issued a warning for it elsewhere.
594   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
595 
596   if (variadicArgsEnd <= variadicArgsBegin)
597     return;
598 
599   // Verify that all arguments have Objective-C types.
600   llvm::Optional<ExplodedNode*> errorNode;
601   ProgramStateRef state = C.getState();
602 
603   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
604     QualType ArgTy = msg.getArgExpr(I)->getType();
605     if (ArgTy->isObjCObjectPointerType())
606       continue;
607 
608     // Block pointers are treaded as Objective-C pointers.
609     if (ArgTy->isBlockPointerType())
610       continue;
611 
612     // Ignore pointer constants.
613     if (isa<loc::ConcreteInt>(msg.getArgSVal(I)))
614       continue;
615 
616     // Ignore pointer types annotated with 'NSObject' attribute.
617     if (C.getASTContext().isObjCNSObjectType(ArgTy))
618       continue;
619 
620     // Ignore CF references, which can be toll-free bridged.
621     if (coreFoundation::isCFObjectRef(ArgTy))
622       continue;
623 
624     // Generate only one error node to use for all bug reports.
625     if (!errorNode.hasValue())
626       errorNode = C.addTransition();
627 
628     if (!errorNode.getValue())
629       continue;
630 
631     SmallString<128> sbuf;
632     llvm::raw_svector_ostream os(sbuf);
633 
634     StringRef TypeName = GetReceiverInterfaceName(msg);
635     if (!TypeName.empty())
636       os << "Argument to '" << TypeName << "' method '";
637     else
638       os << "Argument to method '";
639 
640     os << msg.getSelector().getAsString()
641        << "' should be an Objective-C pointer type, not '";
642     ArgTy.print(os, C.getLangOpts());
643     os << "'";
644 
645     BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
646     R->addRange(msg.getArgSourceRange(I));
647     C.EmitReport(R);
648   }
649 }
650 
651 //===----------------------------------------------------------------------===//
652 // Improves the modeling of loops over Cocoa collections.
653 //===----------------------------------------------------------------------===//
654 
655 namespace {
656 class ObjCLoopChecker
657   : public Checker<check::PostStmt<ObjCForCollectionStmt> > {
658 
659 public:
660   void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
661 };
662 }
663 
664 static bool isKnownNonNilCollectionType(QualType T) {
665   const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
666   if (!PT)
667     return false;
668 
669   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
670   if (!ID)
671     return false;
672 
673   switch (findKnownClass(ID)) {
674   case FC_NSArray:
675   case FC_NSDictionary:
676   case FC_NSEnumerator:
677   case FC_NSOrderedSet:
678   case FC_NSSet:
679     return true;
680   default:
681     return false;
682   }
683 }
684 
685 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
686                                     CheckerContext &C) const {
687   ProgramStateRef State = C.getState();
688 
689   // Check if this is the branch for the end of the loop.
690   SVal CollectionSentinel = State->getSVal(FCS, C.getLocationContext());
691   if (CollectionSentinel.isZeroConstant())
692     return;
693 
694   // See if the collection is one where we /know/ the elements are non-nil.
695   const Expr *Collection = FCS->getCollection();
696   if (!isKnownNonNilCollectionType(Collection->getType()))
697     return;
698 
699   // FIXME: Copied from ExprEngineObjC.
700   const Stmt *Element = FCS->getElement();
701   SVal ElementVar;
702   if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
703     const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
704     assert(ElemDecl->getInit() == 0);
705     ElementVar = State->getLValue(ElemDecl, C.getLocationContext());
706   } else {
707     ElementVar = State->getSVal(Element, C.getLocationContext());
708   }
709 
710   if (!isa<Loc>(ElementVar))
711     return;
712 
713   // Go ahead and assume the value is non-nil.
714   SVal Val = State->getSVal(cast<Loc>(ElementVar));
715   State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
716   C.addTransition(State);
717 }
718 
719 namespace {
720 /// \class ObjCNonNilReturnValueChecker
721 /// \brief The checker restricts the return values of APIs known to never
722 /// return nil.
723 class ObjCNonNilReturnValueChecker
724   : public Checker<check::PostObjCMessage> {
725     mutable bool Initialized;
726     mutable Selector ObjectAtIndex;
727     mutable Selector ObjectAtIndexedSubscript;
728 
729 public:
730   ObjCNonNilReturnValueChecker() : Initialized(false) {}
731   void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
732 };
733 }
734 
735 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
736                                                         CheckerContext &C)
737                                                          const {
738   ProgramStateRef State = C.getState();
739 
740   if (!Initialized) {
741     ASTContext &Ctx = C.getASTContext();
742     ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
743     ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
744   }
745 
746   // Check the receiver type.
747   if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
748     FoundationClass Cl = findKnownClass(Interface);
749     if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
750       Selector Sel = M.getSelector();
751       if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
752         // Go ahead and assume the value is non-nil.
753         SVal Val = State->getSVal(M.getOriginExpr(), C.getLocationContext());
754         if (!isa<DefinedOrUnknownSVal>(Val))
755           return;
756         State = State->assume(cast<DefinedOrUnknownSVal>(Val), true);
757         C.addTransition(State);
758       }
759     }
760   }
761 }
762 
763 //===----------------------------------------------------------------------===//
764 // Check registration.
765 //===----------------------------------------------------------------------===//
766 
767 void ento::registerNilArgChecker(CheckerManager &mgr) {
768   mgr.registerChecker<NilArgChecker>();
769 }
770 
771 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
772   mgr.registerChecker<CFNumberCreateChecker>();
773 }
774 
775 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
776   mgr.registerChecker<CFRetainReleaseChecker>();
777 }
778 
779 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
780   mgr.registerChecker<ClassReleaseChecker>();
781 }
782 
783 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
784   mgr.registerChecker<VariadicMethodTypeChecker>();
785 }
786 
787 void ento::registerObjCLoopChecker(CheckerManager &mgr) {
788   mgr.registerChecker<ObjCLoopChecker>();
789 }
790 
791 void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
792   mgr.registerChecker<ObjCNonNilReturnValueChecker>();
793 }
794