xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp (revision 4aca9b1cd852fcf4e11fa7ff26b73df6fbef8a4c)
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/AST/ASTContext.h"
18 #include "clang/AST/DeclObjC.h"
19 #include "clang/AST/Expr.h"
20 #include "clang/AST/ExprObjC.h"
21 #include "clang/AST/StmtObjC.h"
22 #include "clang/Analysis/DomainSpecific/CocoaConventions.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
25 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
26 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
27 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
28 #include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
29 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
30 #include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
31 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
32 #include "llvm/ADT/SmallString.h"
33 #include "llvm/ADT/StringMap.h"
34 #include "llvm/Support/raw_ostream.h"
35 
36 using namespace clang;
37 using namespace ento;
38 
39 namespace {
40 class APIMisuse : public BugType {
41 public:
42   APIMisuse(const CheckerBase *checker, const char *name)
43       : BugType(checker, name, "API Misuse (Apple)") {}
44 };
45 } // end anonymous namespace
46 
47 //===----------------------------------------------------------------------===//
48 // Utility functions.
49 //===----------------------------------------------------------------------===//
50 
51 static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
52   if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
53     return ID->getIdentifier()->getName();
54   return StringRef();
55 }
56 
57 enum FoundationClass {
58   FC_None,
59   FC_NSArray,
60   FC_NSDictionary,
61   FC_NSEnumerator,
62   FC_NSNull,
63   FC_NSOrderedSet,
64   FC_NSSet,
65   FC_NSString
66 };
67 
68 static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
69                                       bool IncludeSuperclasses = true) {
70   static llvm::StringMap<FoundationClass> Classes;
71   if (Classes.empty()) {
72     Classes["NSArray"] = FC_NSArray;
73     Classes["NSDictionary"] = FC_NSDictionary;
74     Classes["NSEnumerator"] = FC_NSEnumerator;
75     Classes["NSNull"] = FC_NSNull;
76     Classes["NSOrderedSet"] = FC_NSOrderedSet;
77     Classes["NSSet"] = FC_NSSet;
78     Classes["NSString"] = FC_NSString;
79   }
80 
81   // FIXME: Should we cache this at all?
82   FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
83   if (result == FC_None && IncludeSuperclasses)
84     if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
85       return findKnownClass(Super);
86 
87   return result;
88 }
89 
90 //===----------------------------------------------------------------------===//
91 // NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
92 //===----------------------------------------------------------------------===//
93 
94 namespace {
95   class NilArgChecker : public Checker<check::PreObjCMessage,
96                                        check::PostStmt<ObjCDictionaryLiteral>,
97                                        check::PostStmt<ObjCArrayLiteral> > {
98     mutable OwningPtr<APIMisuse> BT;
99 
100     void warnIfNilExpr(const Expr *E,
101                        const char *Msg,
102                        CheckerContext &C) const;
103 
104     void warnIfNilArg(CheckerContext &C,
105                       const ObjCMethodCall &msg, unsigned Arg,
106                       FoundationClass Class,
107                       bool CanBeSubscript = false) const;
108 
109     void generateBugReport(ExplodedNode *N,
110                            StringRef Msg,
111                            SourceRange Range,
112                            const Expr *Expr,
113                            CheckerContext &C) const;
114 
115   public:
116     void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
117     void checkPostStmt(const ObjCDictionaryLiteral *DL,
118                        CheckerContext &C) const;
119     void checkPostStmt(const ObjCArrayLiteral *AL,
120                        CheckerContext &C) const;
121   };
122 }
123 
124 void NilArgChecker::warnIfNilExpr(const Expr *E,
125                                   const char *Msg,
126                                   CheckerContext &C) const {
127   ProgramStateRef State = C.getState();
128   if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
129 
130     if (ExplodedNode *N = C.generateSink()) {
131       generateBugReport(N, Msg, E->getSourceRange(), E, C);
132     }
133 
134   }
135 }
136 
137 void NilArgChecker::warnIfNilArg(CheckerContext &C,
138                                  const ObjCMethodCall &msg,
139                                  unsigned int Arg,
140                                  FoundationClass Class,
141                                  bool CanBeSubscript) const {
142   // Check if the argument is nil.
143   ProgramStateRef State = C.getState();
144   if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
145       return;
146 
147   if (ExplodedNode *N = C.generateSink()) {
148     SmallString<128> sbuf;
149     llvm::raw_svector_ostream os(sbuf);
150 
151     if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
152 
153       if (Class == FC_NSArray) {
154         os << "Array element cannot be nil";
155       } else if (Class == FC_NSDictionary) {
156         if (Arg == 0) {
157           os << "Value stored into '";
158           os << GetReceiverInterfaceName(msg) << "' cannot be nil";
159         } else {
160           assert(Arg == 1);
161           os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
162         }
163       } else
164         llvm_unreachable("Missing foundation class for the subscript expr");
165 
166     } else {
167       if (Class == FC_NSDictionary) {
168         if (Arg == 0)
169           os << "Value argument ";
170         else {
171           assert(Arg == 1);
172           os << "Key argument ";
173         }
174         os << "to '";
175         msg.getSelector().print(os);
176         os << "' cannot be nil";
177       } else {
178         os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
179         msg.getSelector().print(os);
180         os << "' cannot be nil";
181       }
182     }
183 
184     generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
185                       msg.getArgExpr(Arg), C);
186   }
187 }
188 
189 void NilArgChecker::generateBugReport(ExplodedNode *N,
190                                       StringRef Msg,
191                                       SourceRange Range,
192                                       const Expr *E,
193                                       CheckerContext &C) const {
194   if (!BT)
195     BT.reset(new APIMisuse(this, "nil argument"));
196 
197   BugReport *R = new BugReport(*BT, Msg, N);
198   R->addRange(Range);
199   bugreporter::trackNullOrUndefValue(N, E, *R);
200   C.emitReport(R);
201 }
202 
203 void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
204                                         CheckerContext &C) const {
205   const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
206   if (!ID)
207     return;
208 
209   FoundationClass Class = findKnownClass(ID);
210 
211   static const unsigned InvalidArgIndex = UINT_MAX;
212   unsigned Arg = InvalidArgIndex;
213   bool CanBeSubscript = false;
214 
215   if (Class == FC_NSString) {
216     Selector S = msg.getSelector();
217 
218     if (S.isUnarySelector())
219       return;
220 
221     // FIXME: This is going to be really slow doing these checks with
222     //  lexical comparisons.
223 
224     std::string NameStr = S.getAsString();
225     StringRef Name(NameStr);
226     assert(!Name.empty());
227 
228     // FIXME: Checking for initWithFormat: will not work in most cases
229     //  yet because [NSString alloc] returns id, not NSString*.  We will
230     //  need support for tracking expected-type information in the analyzer
231     //  to find these errors.
232     if (Name == "caseInsensitiveCompare:" ||
233         Name == "compare:" ||
234         Name == "compare:options:" ||
235         Name == "compare:options:range:" ||
236         Name == "compare:options:range:locale:" ||
237         Name == "componentsSeparatedByCharactersInSet:" ||
238         Name == "initWithFormat:") {
239       Arg = 0;
240     }
241   } else if (Class == FC_NSArray) {
242     Selector S = msg.getSelector();
243 
244     if (S.isUnarySelector())
245       return;
246 
247     if (S.getNameForSlot(0).equals("addObject")) {
248       Arg = 0;
249     } else if (S.getNameForSlot(0).equals("insertObject") &&
250                S.getNameForSlot(1).equals("atIndex")) {
251       Arg = 0;
252     } else if (S.getNameForSlot(0).equals("replaceObjectAtIndex") &&
253                S.getNameForSlot(1).equals("withObject")) {
254       Arg = 1;
255     } else if (S.getNameForSlot(0).equals("setObject") &&
256                S.getNameForSlot(1).equals("atIndexedSubscript")) {
257       Arg = 0;
258       CanBeSubscript = true;
259     } else if (S.getNameForSlot(0).equals("arrayByAddingObject")) {
260       Arg = 0;
261     }
262   } else if (Class == FC_NSDictionary) {
263     Selector S = msg.getSelector();
264 
265     if (S.isUnarySelector())
266       return;
267 
268     if (S.getNameForSlot(0).equals("dictionaryWithObject") &&
269         S.getNameForSlot(1).equals("forKey")) {
270       Arg = 0;
271       warnIfNilArg(C, msg, /* Arg */1, Class);
272     } else if (S.getNameForSlot(0).equals("setObject") &&
273                S.getNameForSlot(1).equals("forKey")) {
274       Arg = 0;
275       warnIfNilArg(C, msg, /* Arg */1, Class);
276     } else if (S.getNameForSlot(0).equals("setObject") &&
277                S.getNameForSlot(1).equals("forKeyedSubscript")) {
278       CanBeSubscript = true;
279       Arg = 0;
280       warnIfNilArg(C, msg, /* Arg */1, Class, CanBeSubscript);
281     } else if (S.getNameForSlot(0).equals("removeObjectForKey")) {
282       Arg = 0;
283     }
284   }
285 
286   // If argument is '0', report a warning.
287   if ((Arg != InvalidArgIndex))
288     warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
289 
290 }
291 
292 void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
293                                   CheckerContext &C) const {
294   unsigned NumOfElements = AL->getNumElements();
295   for (unsigned i = 0; i < NumOfElements; ++i) {
296     warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
297   }
298 }
299 
300 void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
301                                   CheckerContext &C) const {
302   unsigned NumOfElements = DL->getNumElements();
303   for (unsigned i = 0; i < NumOfElements; ++i) {
304     ObjCDictionaryElement Element = DL->getKeyValueElement(i);
305     warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
306     warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
307   }
308 }
309 
310 //===----------------------------------------------------------------------===//
311 // Error reporting.
312 //===----------------------------------------------------------------------===//
313 
314 namespace {
315 class CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
316   mutable OwningPtr<APIMisuse> BT;
317   mutable IdentifierInfo* II;
318 public:
319   CFNumberCreateChecker() : II(0) {}
320 
321   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
322 
323 private:
324   void EmitError(const TypedRegion* R, const Expr *Ex,
325                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
326 };
327 } // end anonymous namespace
328 
329 enum CFNumberType {
330   kCFNumberSInt8Type = 1,
331   kCFNumberSInt16Type = 2,
332   kCFNumberSInt32Type = 3,
333   kCFNumberSInt64Type = 4,
334   kCFNumberFloat32Type = 5,
335   kCFNumberFloat64Type = 6,
336   kCFNumberCharType = 7,
337   kCFNumberShortType = 8,
338   kCFNumberIntType = 9,
339   kCFNumberLongType = 10,
340   kCFNumberLongLongType = 11,
341   kCFNumberFloatType = 12,
342   kCFNumberDoubleType = 13,
343   kCFNumberCFIndexType = 14,
344   kCFNumberNSIntegerType = 15,
345   kCFNumberCGFloatType = 16
346 };
347 
348 static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
349   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
350 
351   if (i < kCFNumberCharType)
352     return FixedSize[i-1];
353 
354   QualType T;
355 
356   switch (i) {
357     case kCFNumberCharType:     T = Ctx.CharTy;     break;
358     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
359     case kCFNumberIntType:      T = Ctx.IntTy;      break;
360     case kCFNumberLongType:     T = Ctx.LongTy;     break;
361     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
362     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
363     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
364     case kCFNumberCFIndexType:
365     case kCFNumberNSIntegerType:
366     case kCFNumberCGFloatType:
367       // FIXME: We need a way to map from names to Type*.
368     default:
369       return None;
370   }
371 
372   return Ctx.getTypeSize(T);
373 }
374 
375 #if 0
376 static const char* GetCFNumberTypeStr(uint64_t i) {
377   static const char* Names[] = {
378     "kCFNumberSInt8Type",
379     "kCFNumberSInt16Type",
380     "kCFNumberSInt32Type",
381     "kCFNumberSInt64Type",
382     "kCFNumberFloat32Type",
383     "kCFNumberFloat64Type",
384     "kCFNumberCharType",
385     "kCFNumberShortType",
386     "kCFNumberIntType",
387     "kCFNumberLongType",
388     "kCFNumberLongLongType",
389     "kCFNumberFloatType",
390     "kCFNumberDoubleType",
391     "kCFNumberCFIndexType",
392     "kCFNumberNSIntegerType",
393     "kCFNumberCGFloatType"
394   };
395 
396   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
397 }
398 #endif
399 
400 void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
401                                          CheckerContext &C) const {
402   ProgramStateRef state = C.getState();
403   const FunctionDecl *FD = C.getCalleeDecl(CE);
404   if (!FD)
405     return;
406 
407   ASTContext &Ctx = C.getASTContext();
408   if (!II)
409     II = &Ctx.Idents.get("CFNumberCreate");
410 
411   if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
412     return;
413 
414   // Get the value of the "theType" argument.
415   const LocationContext *LCtx = C.getLocationContext();
416   SVal TheTypeVal = state->getSVal(CE->getArg(1), LCtx);
417 
418   // FIXME: We really should allow ranges of valid theType values, and
419   //   bifurcate the state appropriately.
420   Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
421   if (!V)
422     return;
423 
424   uint64_t NumberKind = V->getValue().getLimitedValue();
425   Optional<uint64_t> OptTargetSize = GetCFNumberSize(Ctx, NumberKind);
426 
427   // FIXME: In some cases we can emit an error.
428   if (!OptTargetSize)
429     return;
430 
431   uint64_t TargetSize = *OptTargetSize;
432 
433   // Look at the value of the integer being passed by reference.  Essentially
434   // we want to catch cases where the value passed in is not equal to the
435   // size of the type being created.
436   SVal TheValueExpr = state->getSVal(CE->getArg(2), LCtx);
437 
438   // FIXME: Eventually we should handle arbitrary locations.  We can do this
439   //  by having an enhanced memory model that does low-level typing.
440   Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
441   if (!LV)
442     return;
443 
444   const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
445   if (!R)
446     return;
447 
448   QualType T = Ctx.getCanonicalType(R->getValueType());
449 
450   // FIXME: If the pointee isn't an integer type, should we flag a warning?
451   //  People can do weird stuff with pointers.
452 
453   if (!T->isIntegralOrEnumerationType())
454     return;
455 
456   uint64_t SourceSize = Ctx.getTypeSize(T);
457 
458   // CHECK: is SourceSize == TargetSize
459   if (SourceSize == TargetSize)
460     return;
461 
462   // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
463   // otherwise generate a regular node.
464   //
465   // FIXME: We can actually create an abstract "CFNumber" object that has
466   //  the bits initialized to the provided values.
467   //
468   if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
469                                                 : C.addTransition()) {
470     SmallString<128> sbuf;
471     llvm::raw_svector_ostream os(sbuf);
472 
473     os << (SourceSize == 8 ? "An " : "A ")
474        << SourceSize << " bit integer is used to initialize a CFNumber "
475                         "object that represents "
476        << (TargetSize == 8 ? "an " : "a ")
477        << TargetSize << " bit integer. ";
478 
479     if (SourceSize < TargetSize)
480       os << (TargetSize - SourceSize)
481       << " bits of the CFNumber value will be garbage." ;
482     else
483       os << (SourceSize - TargetSize)
484       << " bits of the input integer will be lost.";
485 
486     if (!BT)
487       BT.reset(new APIMisuse(this, "Bad use of CFNumberCreate"));
488 
489     BugReport *report = new BugReport(*BT, os.str(), N);
490     report->addRange(CE->getArg(2)->getSourceRange());
491     C.emitReport(report);
492   }
493 }
494 
495 //===----------------------------------------------------------------------===//
496 // CFRetain/CFRelease/CFMakeCollectable checking for null arguments.
497 //===----------------------------------------------------------------------===//
498 
499 namespace {
500 class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
501   mutable OwningPtr<APIMisuse> BT;
502   mutable IdentifierInfo *Retain, *Release, *MakeCollectable;
503 public:
504   CFRetainReleaseChecker(): Retain(0), Release(0), MakeCollectable(0) {}
505   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
506 };
507 } // end anonymous namespace
508 
509 
510 void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
511                                           CheckerContext &C) const {
512   // If the CallExpr doesn't have exactly 1 argument just give up checking.
513   if (CE->getNumArgs() != 1)
514     return;
515 
516   ProgramStateRef state = C.getState();
517   const FunctionDecl *FD = C.getCalleeDecl(CE);
518   if (!FD)
519     return;
520 
521   if (!BT) {
522     ASTContext &Ctx = C.getASTContext();
523     Retain = &Ctx.Idents.get("CFRetain");
524     Release = &Ctx.Idents.get("CFRelease");
525     MakeCollectable = &Ctx.Idents.get("CFMakeCollectable");
526     BT.reset(new APIMisuse(
527         this, "null passed to CFRetain/CFRelease/CFMakeCollectable"));
528   }
529 
530   // Check if we called CFRetain/CFRelease/CFMakeCollectable.
531   const IdentifierInfo *FuncII = FD->getIdentifier();
532   if (!(FuncII == Retain || FuncII == Release || FuncII == MakeCollectable))
533     return;
534 
535   // FIXME: The rest of this just checks that the argument is non-null.
536   // It should probably be refactored and combined with NonNullParamChecker.
537 
538   // Get the argument's value.
539   const Expr *Arg = CE->getArg(0);
540   SVal ArgVal = state->getSVal(Arg, C.getLocationContext());
541   Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
542   if (!DefArgVal)
543     return;
544 
545   // Get a NULL value.
546   SValBuilder &svalBuilder = C.getSValBuilder();
547   DefinedSVal zero =
548       svalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
549 
550   // Make an expression asserting that they're equal.
551   DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
552 
553   // Are they equal?
554   ProgramStateRef stateTrue, stateFalse;
555   llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
556 
557   if (stateTrue && !stateFalse) {
558     ExplodedNode *N = C.generateSink(stateTrue);
559     if (!N)
560       return;
561 
562     const char *description;
563     if (FuncII == Retain)
564       description = "Null pointer argument in call to CFRetain";
565     else if (FuncII == Release)
566       description = "Null pointer argument in call to CFRelease";
567     else if (FuncII == MakeCollectable)
568       description = "Null pointer argument in call to CFMakeCollectable";
569     else
570       llvm_unreachable("impossible case");
571 
572     BugReport *report = new BugReport(*BT, description, N);
573     report->addRange(Arg->getSourceRange());
574     bugreporter::trackNullOrUndefValue(N, Arg, *report);
575     C.emitReport(report);
576     return;
577   }
578 
579   // From here on, we know the argument is non-null.
580   C.addTransition(stateFalse);
581 }
582 
583 //===----------------------------------------------------------------------===//
584 // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
585 //===----------------------------------------------------------------------===//
586 
587 namespace {
588 class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
589   mutable Selector releaseS;
590   mutable Selector retainS;
591   mutable Selector autoreleaseS;
592   mutable Selector drainS;
593   mutable OwningPtr<BugType> BT;
594 
595 public:
596   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
597 };
598 }
599 
600 void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
601                                               CheckerContext &C) const {
602 
603   if (!BT) {
604     BT.reset(new APIMisuse(
605         this, "message incorrectly sent to class instead of class instance"));
606 
607     ASTContext &Ctx = C.getASTContext();
608     releaseS = GetNullarySelector("release", Ctx);
609     retainS = GetNullarySelector("retain", Ctx);
610     autoreleaseS = GetNullarySelector("autorelease", Ctx);
611     drainS = GetNullarySelector("drain", Ctx);
612   }
613 
614   if (msg.isInstanceMessage())
615     return;
616   const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
617   assert(Class);
618 
619   Selector S = msg.getSelector();
620   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
621     return;
622 
623   if (ExplodedNode *N = C.addTransition()) {
624     SmallString<200> buf;
625     llvm::raw_svector_ostream os(buf);
626 
627     os << "The '";
628     S.print(os);
629     os << "' message should be sent to instances "
630           "of class '" << Class->getName()
631        << "' and not the class directly";
632 
633     BugReport *report = new BugReport(*BT, os.str(), N);
634     report->addRange(msg.getSourceRange());
635     C.emitReport(report);
636   }
637 }
638 
639 //===----------------------------------------------------------------------===//
640 // Check for passing non-Objective-C types to variadic methods that expect
641 // only Objective-C types.
642 //===----------------------------------------------------------------------===//
643 
644 namespace {
645 class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
646   mutable Selector arrayWithObjectsS;
647   mutable Selector dictionaryWithObjectsAndKeysS;
648   mutable Selector setWithObjectsS;
649   mutable Selector orderedSetWithObjectsS;
650   mutable Selector initWithObjectsS;
651   mutable Selector initWithObjectsAndKeysS;
652   mutable OwningPtr<BugType> BT;
653 
654   bool isVariadicMessage(const ObjCMethodCall &msg) const;
655 
656 public:
657   void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
658 };
659 }
660 
661 /// isVariadicMessage - Returns whether the given message is a variadic message,
662 /// where all arguments must be Objective-C types.
663 bool
664 VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
665   const ObjCMethodDecl *MD = msg.getDecl();
666 
667   if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
668     return false;
669 
670   Selector S = msg.getSelector();
671 
672   if (msg.isInstanceMessage()) {
673     // FIXME: Ideally we'd look at the receiver interface here, but that's not
674     // useful for init, because alloc returns 'id'. In theory, this could lead
675     // to false positives, for example if there existed a class that had an
676     // initWithObjects: implementation that does accept non-Objective-C pointer
677     // types, but the chance of that happening is pretty small compared to the
678     // gains that this analysis gives.
679     const ObjCInterfaceDecl *Class = MD->getClassInterface();
680 
681     switch (findKnownClass(Class)) {
682     case FC_NSArray:
683     case FC_NSOrderedSet:
684     case FC_NSSet:
685       return S == initWithObjectsS;
686     case FC_NSDictionary:
687       return S == initWithObjectsAndKeysS;
688     default:
689       return false;
690     }
691   } else {
692     const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
693 
694     switch (findKnownClass(Class)) {
695       case FC_NSArray:
696         return S == arrayWithObjectsS;
697       case FC_NSOrderedSet:
698         return S == orderedSetWithObjectsS;
699       case FC_NSSet:
700         return S == setWithObjectsS;
701       case FC_NSDictionary:
702         return S == dictionaryWithObjectsAndKeysS;
703       default:
704         return false;
705     }
706   }
707 }
708 
709 void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
710                                                     CheckerContext &C) const {
711   if (!BT) {
712     BT.reset(new APIMisuse(this,
713                            "Arguments passed to variadic method aren't all "
714                            "Objective-C pointer types"));
715 
716     ASTContext &Ctx = C.getASTContext();
717     arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
718     dictionaryWithObjectsAndKeysS =
719       GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
720     setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
721     orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
722 
723     initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
724     initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
725   }
726 
727   if (!isVariadicMessage(msg))
728       return;
729 
730   // We are not interested in the selector arguments since they have
731   // well-defined types, so the compiler will issue a warning for them.
732   unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
733 
734   // We're not interested in the last argument since it has to be nil or the
735   // compiler would have issued a warning for it elsewhere.
736   unsigned variadicArgsEnd = msg.getNumArgs() - 1;
737 
738   if (variadicArgsEnd <= variadicArgsBegin)
739     return;
740 
741   // Verify that all arguments have Objective-C types.
742   Optional<ExplodedNode*> errorNode;
743 
744   for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
745     QualType ArgTy = msg.getArgExpr(I)->getType();
746     if (ArgTy->isObjCObjectPointerType())
747       continue;
748 
749     // Block pointers are treaded as Objective-C pointers.
750     if (ArgTy->isBlockPointerType())
751       continue;
752 
753     // Ignore pointer constants.
754     if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
755       continue;
756 
757     // Ignore pointer types annotated with 'NSObject' attribute.
758     if (C.getASTContext().isObjCNSObjectType(ArgTy))
759       continue;
760 
761     // Ignore CF references, which can be toll-free bridged.
762     if (coreFoundation::isCFObjectRef(ArgTy))
763       continue;
764 
765     // Generate only one error node to use for all bug reports.
766     if (!errorNode.hasValue())
767       errorNode = C.addTransition();
768 
769     if (!errorNode.getValue())
770       continue;
771 
772     SmallString<128> sbuf;
773     llvm::raw_svector_ostream os(sbuf);
774 
775     StringRef TypeName = GetReceiverInterfaceName(msg);
776     if (!TypeName.empty())
777       os << "Argument to '" << TypeName << "' method '";
778     else
779       os << "Argument to method '";
780 
781     msg.getSelector().print(os);
782     os << "' should be an Objective-C pointer type, not '";
783     ArgTy.print(os, C.getLangOpts());
784     os << "'";
785 
786     BugReport *R = new BugReport(*BT, os.str(), errorNode.getValue());
787     R->addRange(msg.getArgSourceRange(I));
788     C.emitReport(R);
789   }
790 }
791 
792 //===----------------------------------------------------------------------===//
793 // Improves the modeling of loops over Cocoa collections.
794 //===----------------------------------------------------------------------===//
795 
796 // The map from container symbol to the container count symbol.
797 // We currently will remember the last countainer count symbol encountered.
798 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
799 REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
800 
801 namespace {
802 class ObjCLoopChecker
803   : public Checker<check::PostStmt<ObjCForCollectionStmt>,
804                    check::PostObjCMessage,
805                    check::DeadSymbols,
806                    check::PointerEscape > {
807   mutable IdentifierInfo *CountSelectorII;
808 
809   bool isCollectionCountMethod(const ObjCMethodCall &M,
810                                CheckerContext &C) const;
811 
812 public:
813   ObjCLoopChecker() : CountSelectorII(0) {}
814   void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
815   void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
816   void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
817   ProgramStateRef checkPointerEscape(ProgramStateRef State,
818                                      const InvalidatedSymbols &Escaped,
819                                      const CallEvent *Call,
820                                      PointerEscapeKind Kind) const;
821 };
822 }
823 
824 static bool isKnownNonNilCollectionType(QualType T) {
825   const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
826   if (!PT)
827     return false;
828 
829   const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
830   if (!ID)
831     return false;
832 
833   switch (findKnownClass(ID)) {
834   case FC_NSArray:
835   case FC_NSDictionary:
836   case FC_NSEnumerator:
837   case FC_NSOrderedSet:
838   case FC_NSSet:
839     return true;
840   default:
841     return false;
842   }
843 }
844 
845 /// Assumes that the collection is non-nil.
846 ///
847 /// If the collection is known to be nil, returns NULL to indicate an infeasible
848 /// path.
849 static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
850                                              ProgramStateRef State,
851                                              const ObjCForCollectionStmt *FCS) {
852   if (!State)
853     return NULL;
854 
855   SVal CollectionVal = C.getSVal(FCS->getCollection());
856   Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
857   if (!KnownCollection)
858     return State;
859 
860   ProgramStateRef StNonNil, StNil;
861   llvm::tie(StNonNil, StNil) = State->assume(*KnownCollection);
862   if (StNil && !StNonNil) {
863     // The collection is nil. This path is infeasible.
864     return NULL;
865   }
866 
867   return StNonNil;
868 }
869 
870 /// Assumes that the collection elements are non-nil.
871 ///
872 /// This only applies if the collection is one of those known not to contain
873 /// nil values.
874 static ProgramStateRef checkElementNonNil(CheckerContext &C,
875                                           ProgramStateRef State,
876                                           const ObjCForCollectionStmt *FCS) {
877   if (!State)
878     return NULL;
879 
880   // See if the collection is one where we /know/ the elements are non-nil.
881   if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
882     return State;
883 
884   const LocationContext *LCtx = C.getLocationContext();
885   const Stmt *Element = FCS->getElement();
886 
887   // FIXME: Copied from ExprEngineObjC.
888   Optional<Loc> ElementLoc;
889   if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
890     const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
891     assert(ElemDecl->getInit() == 0);
892     ElementLoc = State->getLValue(ElemDecl, LCtx);
893   } else {
894     ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
895   }
896 
897   if (!ElementLoc)
898     return State;
899 
900   // Go ahead and assume the value is non-nil.
901   SVal Val = State->getSVal(*ElementLoc);
902   return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
903 }
904 
905 /// Returns NULL state if the collection is known to contain elements
906 /// (or is known not to contain elements if the Assumption parameter is false.)
907 static ProgramStateRef
908 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
909                          SymbolRef CollectionS, bool Assumption) {
910   if (!State || !CollectionS)
911     return State;
912 
913   const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
914   if (!CountS) {
915     const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
916     if (!KnownNonEmpty)
917       return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
918     return (Assumption == *KnownNonEmpty) ? State : NULL;
919   }
920 
921   SValBuilder &SvalBuilder = C.getSValBuilder();
922   SVal CountGreaterThanZeroVal =
923     SvalBuilder.evalBinOp(State, BO_GT,
924                           nonloc::SymbolVal(*CountS),
925                           SvalBuilder.makeIntVal(0, (*CountS)->getType()),
926                           SvalBuilder.getConditionType());
927   Optional<DefinedSVal> CountGreaterThanZero =
928     CountGreaterThanZeroVal.getAs<DefinedSVal>();
929   if (!CountGreaterThanZero) {
930     // The SValBuilder cannot construct a valid SVal for this condition.
931     // This means we cannot properly reason about it.
932     return State;
933   }
934 
935   return State->assume(*CountGreaterThanZero, Assumption);
936 }
937 
938 static ProgramStateRef
939 assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
940                          const ObjCForCollectionStmt *FCS,
941                          bool Assumption) {
942   if (!State)
943     return NULL;
944 
945   SymbolRef CollectionS =
946     State->getSVal(FCS->getCollection(), C.getLocationContext()).getAsSymbol();
947   return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
948 }
949 
950 
951 /// If the fist block edge is a back edge, we are reentering the loop.
952 static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
953                                              const ObjCForCollectionStmt *FCS) {
954   if (!N)
955     return false;
956 
957   ProgramPoint P = N->getLocation();
958   if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
959     if (BE->getSrc()->getLoopTarget() == FCS)
960       return true;
961     return false;
962   }
963 
964   // Keep looking for a block edge.
965   for (ExplodedNode::const_pred_iterator I = N->pred_begin(),
966                                          E = N->pred_end(); I != E; ++I) {
967     if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS))
968       return true;
969   }
970 
971   return false;
972 }
973 
974 void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
975                                     CheckerContext &C) const {
976   ProgramStateRef State = C.getState();
977 
978   // Check if this is the branch for the end of the loop.
979   SVal CollectionSentinel = C.getSVal(FCS);
980   if (CollectionSentinel.isZeroConstant()) {
981     if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
982       State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
983 
984   // Otherwise, this is a branch that goes through the loop body.
985   } else {
986     State = checkCollectionNonNil(C, State, FCS);
987     State = checkElementNonNil(C, State, FCS);
988     State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
989   }
990 
991   if (!State)
992     C.generateSink();
993   else if (State != C.getState())
994     C.addTransition(State);
995 }
996 
997 bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
998                                               CheckerContext &C) const {
999   Selector S = M.getSelector();
1000   // Initialize the identifiers on first use.
1001   if (!CountSelectorII)
1002     CountSelectorII = &C.getASTContext().Idents.get("count");
1003 
1004   // If the method returns collection count, record the value.
1005   if (S.isUnarySelector() &&
1006       (S.getIdentifierInfoForSlot(0) == CountSelectorII))
1007     return true;
1008 
1009   return false;
1010 }
1011 
1012 void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1013                                            CheckerContext &C) const {
1014   if (!M.isInstanceMessage())
1015     return;
1016 
1017   const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
1018   if (!ClassID)
1019     return;
1020 
1021   FoundationClass Class = findKnownClass(ClassID);
1022   if (Class != FC_NSDictionary &&
1023       Class != FC_NSArray &&
1024       Class != FC_NSSet &&
1025       Class != FC_NSOrderedSet)
1026     return;
1027 
1028   SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
1029   if (!ContainerS)
1030     return;
1031 
1032   // If we are processing a call to "count", get the symbolic value returned by
1033   // a call to "count" and add it to the map.
1034   if (!isCollectionCountMethod(M, C))
1035     return;
1036 
1037   const Expr *MsgExpr = M.getOriginExpr();
1038   SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
1039   if (CountS) {
1040     ProgramStateRef State = C.getState();
1041 
1042     C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
1043     State = State->set<ContainerCountMap>(ContainerS, CountS);
1044 
1045     if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
1046       State = State->remove<ContainerNonEmptyMap>(ContainerS);
1047       State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
1048     }
1049 
1050     C.addTransition(State);
1051   }
1052   return;
1053 }
1054 
1055 static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
1056   const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
1057   if (!Message)
1058     return 0;
1059 
1060   const ObjCMethodDecl *MD = Message->getDecl();
1061   if (!MD)
1062     return 0;
1063 
1064   const ObjCInterfaceDecl *StaticClass;
1065   if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
1066     // We can't find out where the method was declared without doing more work.
1067     // Instead, see if the receiver is statically typed as a known immutable
1068     // collection.
1069     StaticClass = Message->getOriginExpr()->getReceiverInterface();
1070   } else {
1071     StaticClass = MD->getClassInterface();
1072   }
1073 
1074   if (!StaticClass)
1075     return 0;
1076 
1077   switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
1078   case FC_None:
1079     return 0;
1080   case FC_NSArray:
1081   case FC_NSDictionary:
1082   case FC_NSEnumerator:
1083   case FC_NSNull:
1084   case FC_NSOrderedSet:
1085   case FC_NSSet:
1086   case FC_NSString:
1087     break;
1088   }
1089 
1090   return Message->getReceiverSVal().getAsSymbol();
1091 }
1092 
1093 ProgramStateRef
1094 ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
1095                                     const InvalidatedSymbols &Escaped,
1096                                     const CallEvent *Call,
1097                                     PointerEscapeKind Kind) const {
1098   SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
1099 
1100   // Remove the invalidated symbols form the collection count map.
1101   for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
1102        E = Escaped.end();
1103        I != E; ++I) {
1104     SymbolRef Sym = *I;
1105 
1106     // Don't invalidate this symbol's count if we know the method being called
1107     // is declared on an immutable class. This isn't completely correct if the
1108     // receiver is also passed as an argument, but in most uses of NSArray,
1109     // NSDictionary, etc. this isn't likely to happen in a dangerous way.
1110     if (Sym == ImmutableReceiver)
1111       continue;
1112 
1113     // The symbol escaped. Pessimistically, assume that the count could have
1114     // changed.
1115     State = State->remove<ContainerCountMap>(Sym);
1116     State = State->remove<ContainerNonEmptyMap>(Sym);
1117   }
1118   return State;
1119 }
1120 
1121 void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
1122                                        CheckerContext &C) const {
1123   ProgramStateRef State = C.getState();
1124 
1125   // Remove the dead symbols from the collection count map.
1126   ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
1127   for (ContainerCountMapTy::iterator I = Tracked.begin(),
1128                                      E = Tracked.end(); I != E; ++I) {
1129     SymbolRef Sym = I->first;
1130     if (SymReaper.isDead(Sym)) {
1131       State = State->remove<ContainerCountMap>(Sym);
1132       State = State->remove<ContainerNonEmptyMap>(Sym);
1133     }
1134   }
1135 
1136   C.addTransition(State);
1137 }
1138 
1139 namespace {
1140 /// \class ObjCNonNilReturnValueChecker
1141 /// \brief The checker restricts the return values of APIs known to
1142 /// never (or almost never) return 'nil'.
1143 class ObjCNonNilReturnValueChecker
1144   : public Checker<check::PostObjCMessage,
1145                    check::PostStmt<ObjCArrayLiteral>,
1146                    check::PostStmt<ObjCDictionaryLiteral>,
1147                    check::PostStmt<ObjCBoxedExpr> > {
1148     mutable bool Initialized;
1149     mutable Selector ObjectAtIndex;
1150     mutable Selector ObjectAtIndexedSubscript;
1151     mutable Selector NullSelector;
1152 
1153 public:
1154   ObjCNonNilReturnValueChecker() : Initialized(false) {}
1155 
1156   ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
1157                                       ProgramStateRef State,
1158                                       CheckerContext &C) const;
1159   void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
1160     C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
1161   }
1162 
1163   void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
1164     assumeExprIsNonNull(E, C);
1165   }
1166   void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
1167     assumeExprIsNonNull(E, C);
1168   }
1169   void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
1170     assumeExprIsNonNull(E, C);
1171   }
1172 
1173   void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
1174 };
1175 }
1176 
1177 ProgramStateRef
1178 ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
1179                                                   ProgramStateRef State,
1180                                                   CheckerContext &C) const {
1181   SVal Val = State->getSVal(NonNullExpr, C.getLocationContext());
1182   if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
1183     return State->assume(*DV, true);
1184   return State;
1185 }
1186 
1187 void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
1188                                                         CheckerContext &C)
1189                                                         const {
1190   ProgramStateRef State = C.getState();
1191 
1192   if (!Initialized) {
1193     ASTContext &Ctx = C.getASTContext();
1194     ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
1195     ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
1196     NullSelector = GetNullarySelector("null", Ctx);
1197   }
1198 
1199   // Check the receiver type.
1200   if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
1201 
1202     // Assume that object returned from '[self init]' or '[super init]' is not
1203     // 'nil' if we are processing an inlined function/method.
1204     //
1205     // A defensive callee will (and should) check if the object returned by
1206     // '[super init]' is 'nil' before doing it's own initialization. However,
1207     // since 'nil' is rarely returned in practice, we should not warn when the
1208     // caller to the defensive constructor uses the object in contexts where
1209     // 'nil' is not accepted.
1210     if (!C.inTopFrame() && M.getDecl() &&
1211         M.getDecl()->getMethodFamily() == OMF_init &&
1212         M.isReceiverSelfOrSuper()) {
1213       State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1214     }
1215 
1216     FoundationClass Cl = findKnownClass(Interface);
1217 
1218     // Objects returned from
1219     // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
1220     // are never 'nil'.
1221     if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
1222       Selector Sel = M.getSelector();
1223       if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
1224         // Go ahead and assume the value is non-nil.
1225         State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1226       }
1227     }
1228 
1229     // Objects returned from [NSNull null] are not nil.
1230     if (Cl == FC_NSNull) {
1231       if (M.getSelector() == NullSelector) {
1232         // Go ahead and assume the value is non-nil.
1233         State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
1234       }
1235     }
1236   }
1237   C.addTransition(State);
1238 }
1239 
1240 //===----------------------------------------------------------------------===//
1241 // Check registration.
1242 //===----------------------------------------------------------------------===//
1243 
1244 void ento::registerNilArgChecker(CheckerManager &mgr) {
1245   mgr.registerChecker<NilArgChecker>();
1246 }
1247 
1248 void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
1249   mgr.registerChecker<CFNumberCreateChecker>();
1250 }
1251 
1252 void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
1253   mgr.registerChecker<CFRetainReleaseChecker>();
1254 }
1255 
1256 void ento::registerClassReleaseChecker(CheckerManager &mgr) {
1257   mgr.registerChecker<ClassReleaseChecker>();
1258 }
1259 
1260 void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
1261   mgr.registerChecker<VariadicMethodTypeChecker>();
1262 }
1263 
1264 void ento::registerObjCLoopChecker(CheckerManager &mgr) {
1265   mgr.registerChecker<ObjCLoopChecker>();
1266 }
1267 
1268 void
1269 ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
1270   mgr.registerChecker<ObjCNonNilReturnValueChecker>();
1271 }
1272