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