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