BasicObjCFoundationChecks.cpp revision 226633
114895Swollman//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
214895Swollman//
314895Swollman//                     The LLVM Compiler Infrastructure
414895Swollman//
514895Swollman// This file is distributed under the University of Illinois Open Source
614895Swollman// License. See LICENSE.TXT for details.
714895Swollman//
814895Swollman//===----------------------------------------------------------------------===//
914895Swollman//
1014895Swollman//  This file defines BasicObjCFoundationChecks, a class that encapsulates
1114895Swollman//  a set of simple checks to run on Objective-C code using Apple's Foundation
1214895Swollman//  classes.
1314895Swollman//
1414895Swollman//===----------------------------------------------------------------------===//
1514895Swollman
1614895Swollman#include "ClangSACheckers.h"
1714895Swollman#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
1814895Swollman#include "clang/StaticAnalyzer/Core/Checker.h"
1914895Swollman#include "clang/StaticAnalyzer/Core/CheckerManager.h"
2014895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2114895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
2214895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
2314895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ObjCMessage.h"
2414895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
2514895Swollman#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
2614895Swollman#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
2714895Swollman#include "clang/AST/DeclObjC.h"
2814895Swollman#include "clang/AST/Expr.h"
2950476Speter#include "clang/AST/ExprObjC.h"
3014895Swollman#include "clang/AST/ASTContext.h"
3114895Swollman
3214895Swollmanusing namespace clang;
3314895Swollmanusing namespace ento;
3414895Swollman
35253750Savgnamespace {
3614895Swollmanclass APIMisuse : public BugType {
3714895Swollmanpublic:
3814895Swollman  APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
3914895Swollman};
4014895Swollman} // end anonymous namespace
4114895Swollman
4214895Swollman//===----------------------------------------------------------------------===//
4314895Swollman// Utility functions.
4414895Swollman//===----------------------------------------------------------------------===//
4514895Swollman
4614895Swollmanstatic const char* GetReceiverNameType(const ObjCMessage &msg) {
4714895Swollman  if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
4827085Sbde    return ID->getIdentifier()->getNameStart();
4914895Swollman  return 0;
5014895Swollman}
5114895Swollman
5214895Swollmanstatic bool isReceiverClassOrSuperclass(const ObjCInterfaceDecl *ID,
5314895Swollman                                        StringRef ClassName) {
5414895Swollman  if (ID->getIdentifier()->getName() == ClassName)
5527085Sbde    return true;
5614895Swollman
5714895Swollman  if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
5814895Swollman    return isReceiverClassOrSuperclass(Super, ClassName);
5914895Swollman
6014895Swollman  return false;
6114895Swollman}
6214895Swollman
6314895Swollmanstatic inline bool isNil(SVal X) {
6414895Swollman  return isa<loc::ConcreteInt>(X);
6514895Swollman}
6614895Swollman
6727085Sbde//===----------------------------------------------------------------------===//
6814895Swollman// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
6914895Swollman//===----------------------------------------------------------------------===//
7014895Swollman
7127085Sbdenamespace {
7214895Swollman  class NilArgChecker : public Checker<check::PreObjCMessage> {
7314895Swollman    mutable llvm::OwningPtr<APIMisuse> BT;
7414895Swollman
7514895Swollman    void WarnNilArg(CheckerContext &C,
7614895Swollman                    const ObjCMessage &msg, unsigned Arg) const;
7714895Swollman
7814895Swollman  public:
7914895Swollman    void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
8014895Swollman  };
8114895Swollman}
8214895Swollman
8314895Swollmanvoid NilArgChecker::WarnNilArg(CheckerContext &C,
8414895Swollman                               const ObjCMessage &msg,
8514895Swollman                               unsigned int Arg) const
8614895Swollman{
8714895Swollman  if (!BT)
8814895Swollman    BT.reset(new APIMisuse("nil argument"));
8914895Swollman
9014895Swollman  if (ExplodedNode *N = C.generateSink()) {
9114895Swollman    llvm::SmallString<128> sbuf;
9214895Swollman    llvm::raw_svector_ostream os(sbuf);
9314895Swollman    os << "Argument to '" << GetReceiverNameType(msg) << "' method '"
9414895Swollman       << msg.getSelector().getAsString() << "' cannot be nil";
9514895Swollman
9614895Swollman    BugReport *R = new BugReport(*BT, os.str(), N);
9727085Sbde    R->addRange(msg.getArgSourceRange(Arg));
9827085Sbde    C.EmitReport(R);
9927085Sbde  }
10014895Swollman}
10114895Swollman
10214895Swollmanvoid NilArgChecker::checkPreObjCMessage(ObjCMessage msg,
10314895Swollman                                        CheckerContext &C) const {
10414895Swollman  const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
10514895Swollman  if (!ID)
10614895Swollman    return;
10714895Swollman
10814895Swollman  if (isReceiverClassOrSuperclass(ID, "NSString")) {
10914895Swollman    Selector S = msg.getSelector();
11014895Swollman
11114895Swollman    if (S.isUnarySelector())
11214895Swollman      return;
11314895Swollman
11414895Swollman    // FIXME: This is going to be really slow doing these checks with
11514895Swollman    //  lexical comparisons.
11614895Swollman
11714895Swollman    std::string NameStr = S.getAsString();
11814895Swollman    StringRef Name(NameStr);
11914895Swollman    assert(!Name.empty());
12014895Swollman
12114895Swollman    // FIXME: Checking for initWithFormat: will not work in most cases
12214895Swollman    //  yet because [NSString alloc] returns id, not NSString*.  We will
12314895Swollman    //  need support for tracking expected-type information in the analyzer
12414895Swollman    //  to find these errors.
12514895Swollman    if (Name == "caseInsensitiveCompare:" ||
12614895Swollman        Name == "compare:" ||
12714895Swollman        Name == "compare:options:" ||
12814895Swollman        Name == "compare:options:range:" ||
12914895Swollman        Name == "compare:options:range:locale:" ||
13014895Swollman        Name == "componentsSeparatedByCharactersInSet:" ||
13114895Swollman        Name == "initWithFormat:") {
13214895Swollman      if (isNil(msg.getArgSVal(0, C.getState())))
13314895Swollman        WarnNilArg(C, msg, 0);
13414895Swollman    }
13514895Swollman  }
13614895Swollman}
13714895Swollman
13814895Swollman//===----------------------------------------------------------------------===//
13927085Sbde// Error reporting.
14027085Sbde//===----------------------------------------------------------------------===//
14127085Sbde
14227085Sbdenamespace {
14314895Swollmanclass CFNumberCreateChecker : public Checker< check::PreStmt<CallExpr> > {
14414895Swollman  mutable llvm::OwningPtr<APIMisuse> BT;
14514895Swollman  mutable IdentifierInfo* II;
14614895Swollmanpublic:
14714895Swollman  CFNumberCreateChecker() : II(0) {}
14814895Swollman
14914895Swollman  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
15014895Swollman
15114895Swollmanprivate:
15214895Swollman  void EmitError(const TypedRegion* R, const Expr *Ex,
15314895Swollman                uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
15414895Swollman};
15514895Swollman} // end anonymous namespace
15614895Swollman
15714895Swollmanenum CFNumberType {
15814895Swollman  kCFNumberSInt8Type = 1,
15914895Swollman  kCFNumberSInt16Type = 2,
16014895Swollman  kCFNumberSInt32Type = 3,
16114895Swollman  kCFNumberSInt64Type = 4,
16214895Swollman  kCFNumberFloat32Type = 5,
16314895Swollman  kCFNumberFloat64Type = 6,
16414895Swollman  kCFNumberCharType = 7,
16514895Swollman  kCFNumberShortType = 8,
16614895Swollman  kCFNumberIntType = 9,
16714895Swollman  kCFNumberLongType = 10,
16814895Swollman  kCFNumberLongLongType = 11,
16914895Swollman  kCFNumberFloatType = 12,
17014895Swollman  kCFNumberDoubleType = 13,
17114895Swollman  kCFNumberCFIndexType = 14,
17214895Swollman  kCFNumberNSIntegerType = 15,
17314895Swollman  kCFNumberCGFloatType = 16
17414895Swollman};
17514895Swollman
176209455Skevlonamespace {
17714895Swollman  template<typename T>
17814895Swollman  class Optional {
17914895Swollman    bool IsKnown;
180209455Skevlo    T Val;
181209455Skevlo  public:
182209455Skevlo    Optional() : IsKnown(false), Val(0) {}
18314895Swollman    Optional(const T& val) : IsKnown(true), Val(val) {}
18414895Swollman
18514895Swollman    bool isKnown() const { return IsKnown; }
18614895Swollman
18714895Swollman    const T& getValue() const {
18814895Swollman      assert (isKnown());
18927085Sbde      return Val;
19027085Sbde    }
19127085Sbde
19214895Swollman    operator const T&() const {
19314895Swollman      return getValue();
194    }
195  };
196}
197
198static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
199  static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
200
201  if (i < kCFNumberCharType)
202    return FixedSize[i-1];
203
204  QualType T;
205
206  switch (i) {
207    case kCFNumberCharType:     T = Ctx.CharTy;     break;
208    case kCFNumberShortType:    T = Ctx.ShortTy;    break;
209    case kCFNumberIntType:      T = Ctx.IntTy;      break;
210    case kCFNumberLongType:     T = Ctx.LongTy;     break;
211    case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
212    case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
213    case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
214    case kCFNumberCFIndexType:
215    case kCFNumberNSIntegerType:
216    case kCFNumberCGFloatType:
217      // FIXME: We need a way to map from names to Type*.
218    default:
219      return Optional<uint64_t>();
220  }
221
222  return Ctx.getTypeSize(T);
223}
224
225#if 0
226static const char* GetCFNumberTypeStr(uint64_t i) {
227  static const char* Names[] = {
228    "kCFNumberSInt8Type",
229    "kCFNumberSInt16Type",
230    "kCFNumberSInt32Type",
231    "kCFNumberSInt64Type",
232    "kCFNumberFloat32Type",
233    "kCFNumberFloat64Type",
234    "kCFNumberCharType",
235    "kCFNumberShortType",
236    "kCFNumberIntType",
237    "kCFNumberLongType",
238    "kCFNumberLongLongType",
239    "kCFNumberFloatType",
240    "kCFNumberDoubleType",
241    "kCFNumberCFIndexType",
242    "kCFNumberNSIntegerType",
243    "kCFNumberCGFloatType"
244  };
245
246  return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
247}
248#endif
249
250void CFNumberCreateChecker::checkPreStmt(const CallExpr *CE,
251                                         CheckerContext &C) const {
252  const Expr *Callee = CE->getCallee();
253  const ProgramState *state = C.getState();
254  SVal CallV = state->getSVal(Callee);
255  const FunctionDecl *FD = CallV.getAsFunctionDecl();
256
257  if (!FD)
258    return;
259
260  ASTContext &Ctx = C.getASTContext();
261  if (!II)
262    II = &Ctx.Idents.get("CFNumberCreate");
263
264  if (FD->getIdentifier() != II || CE->getNumArgs() != 3)
265    return;
266
267  // Get the value of the "theType" argument.
268  SVal TheTypeVal = state->getSVal(CE->getArg(1));
269
270  // FIXME: We really should allow ranges of valid theType values, and
271  //   bifurcate the state appropriately.
272  nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
273  if (!V)
274    return;
275
276  uint64_t NumberKind = V->getValue().getLimitedValue();
277  Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
278
279  // FIXME: In some cases we can emit an error.
280  if (!TargetSize.isKnown())
281    return;
282
283  // Look at the value of the integer being passed by reference.  Essentially
284  // we want to catch cases where the value passed in is not equal to the
285  // size of the type being created.
286  SVal TheValueExpr = state->getSVal(CE->getArg(2));
287
288  // FIXME: Eventually we should handle arbitrary locations.  We can do this
289  //  by having an enhanced memory model that does low-level typing.
290  loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
291  if (!LV)
292    return;
293
294  const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
295  if (!R)
296    return;
297
298  QualType T = Ctx.getCanonicalType(R->getValueType());
299
300  // FIXME: If the pointee isn't an integer type, should we flag a warning?
301  //  People can do weird stuff with pointers.
302
303  if (!T->isIntegerType())
304    return;
305
306  uint64_t SourceSize = Ctx.getTypeSize(T);
307
308  // CHECK: is SourceSize == TargetSize
309  if (SourceSize == TargetSize)
310    return;
311
312  // Generate an error.  Only generate a sink if 'SourceSize < TargetSize';
313  // otherwise generate a regular node.
314  //
315  // FIXME: We can actually create an abstract "CFNumber" object that has
316  //  the bits initialized to the provided values.
317  //
318  if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink()
319                                                : C.generateNode()) {
320    llvm::SmallString<128> sbuf;
321    llvm::raw_svector_ostream os(sbuf);
322
323    os << (SourceSize == 8 ? "An " : "A ")
324       << SourceSize << " bit integer is used to initialize a CFNumber "
325                        "object that represents "
326       << (TargetSize == 8 ? "an " : "a ")
327       << TargetSize << " bit integer. ";
328
329    if (SourceSize < TargetSize)
330      os << (TargetSize - SourceSize)
331      << " bits of the CFNumber value will be garbage." ;
332    else
333      os << (SourceSize - TargetSize)
334      << " bits of the input integer will be lost.";
335
336    if (!BT)
337      BT.reset(new APIMisuse("Bad use of CFNumberCreate"));
338
339    BugReport *report = new BugReport(*BT, os.str(), N);
340    report->addRange(CE->getArg(2)->getSourceRange());
341    C.EmitReport(report);
342  }
343}
344
345//===----------------------------------------------------------------------===//
346// CFRetain/CFRelease checking for null arguments.
347//===----------------------------------------------------------------------===//
348
349namespace {
350class CFRetainReleaseChecker : public Checker< check::PreStmt<CallExpr> > {
351  mutable llvm::OwningPtr<APIMisuse> BT;
352  mutable IdentifierInfo *Retain, *Release;
353public:
354  CFRetainReleaseChecker(): Retain(0), Release(0) {}
355  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
356};
357} // end anonymous namespace
358
359
360void CFRetainReleaseChecker::checkPreStmt(const CallExpr *CE,
361                                          CheckerContext &C) const {
362  // If the CallExpr doesn't have exactly 1 argument just give up checking.
363  if (CE->getNumArgs() != 1)
364    return;
365
366  // Get the function declaration of the callee.
367  const ProgramState *state = C.getState();
368  SVal X = state->getSVal(CE->getCallee());
369  const FunctionDecl *FD = X.getAsFunctionDecl();
370
371  if (!FD)
372    return;
373
374  if (!BT) {
375    ASTContext &Ctx = C.getASTContext();
376    Retain = &Ctx.Idents.get("CFRetain");
377    Release = &Ctx.Idents.get("CFRelease");
378    BT.reset(new APIMisuse("null passed to CFRetain/CFRelease"));
379  }
380
381  // Check if we called CFRetain/CFRelease.
382  const IdentifierInfo *FuncII = FD->getIdentifier();
383  if (!(FuncII == Retain || FuncII == Release))
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 AttrNonNullChecker.
388
389  // Get the argument's value.
390  const Expr *Arg = CE->getArg(0);
391  SVal ArgVal = state->getSVal(Arg);
392  DefinedSVal *DefArgVal = dyn_cast<DefinedSVal>(&ArgVal);
393  if (!DefArgVal)
394    return;
395
396  // Get a NULL value.
397  SValBuilder &svalBuilder = C.getSValBuilder();
398  DefinedSVal zero = cast<DefinedSVal>(svalBuilder.makeZeroVal(Arg->getType()));
399
400  // Make an expression asserting that they're equal.
401  DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal);
402
403  // Are they equal?
404  const ProgramState *stateTrue, *stateFalse;
405  llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull);
406
407  if (stateTrue && !stateFalse) {
408    ExplodedNode *N = C.generateSink(stateTrue);
409    if (!N)
410      return;
411
412    const char *description = (FuncII == Retain)
413                            ? "Null pointer argument in call to CFRetain"
414                            : "Null pointer argument in call to CFRelease";
415
416    BugReport *report = new BugReport(*BT, description, N);
417    report->addRange(Arg->getSourceRange());
418    report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, Arg));
419    C.EmitReport(report);
420    return;
421  }
422
423  // From here on, we know the argument is non-null.
424  C.addTransition(stateFalse);
425}
426
427//===----------------------------------------------------------------------===//
428// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
429//===----------------------------------------------------------------------===//
430
431namespace {
432class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
433  mutable Selector releaseS;
434  mutable Selector retainS;
435  mutable Selector autoreleaseS;
436  mutable Selector drainS;
437  mutable llvm::OwningPtr<BugType> BT;
438
439public:
440  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
441};
442}
443
444void ClassReleaseChecker::checkPreObjCMessage(ObjCMessage msg,
445                                              CheckerContext &C) const {
446
447  if (!BT) {
448    BT.reset(new APIMisuse("message incorrectly sent to class instead of class "
449                           "instance"));
450
451    ASTContext &Ctx = C.getASTContext();
452    releaseS = GetNullarySelector("release", Ctx);
453    retainS = GetNullarySelector("retain", Ctx);
454    autoreleaseS = GetNullarySelector("autorelease", Ctx);
455    drainS = GetNullarySelector("drain", Ctx);
456  }
457
458  if (msg.isInstanceMessage())
459    return;
460  const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
461  assert(Class);
462
463  Selector S = msg.getSelector();
464  if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
465    return;
466
467  if (ExplodedNode *N = C.generateNode()) {
468    llvm::SmallString<200> buf;
469    llvm::raw_svector_ostream os(buf);
470
471    os << "The '" << S.getAsString() << "' message should be sent to instances "
472          "of class '" << Class->getName()
473       << "' and not the class directly";
474
475    BugReport *report = new BugReport(*BT, os.str(), N);
476    report->addRange(msg.getSourceRange());
477    C.EmitReport(report);
478  }
479}
480
481//===----------------------------------------------------------------------===//
482// Check for passing non-Objective-C types to variadic methods that expect
483// only Objective-C types.
484//===----------------------------------------------------------------------===//
485
486namespace {
487class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
488  mutable Selector arrayWithObjectsS;
489  mutable Selector dictionaryWithObjectsAndKeysS;
490  mutable Selector setWithObjectsS;
491  mutable Selector initWithObjectsS;
492  mutable Selector initWithObjectsAndKeysS;
493  mutable llvm::OwningPtr<BugType> BT;
494
495  bool isVariadicMessage(const ObjCMessage &msg) const;
496
497public:
498  void checkPreObjCMessage(ObjCMessage msg, CheckerContext &C) const;
499};
500}
501
502/// isVariadicMessage - Returns whether the given message is a variadic message,
503/// where all arguments must be Objective-C types.
504bool
505VariadicMethodTypeChecker::isVariadicMessage(const ObjCMessage &msg) const {
506  const ObjCMethodDecl *MD = msg.getMethodDecl();
507
508  if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
509    return false;
510
511  Selector S = msg.getSelector();
512
513  if (msg.isInstanceMessage()) {
514    // FIXME: Ideally we'd look at the receiver interface here, but that's not
515    // useful for init, because alloc returns 'id'. In theory, this could lead
516    // to false positives, for example if there existed a class that had an
517    // initWithObjects: implementation that does accept non-Objective-C pointer
518    // types, but the chance of that happening is pretty small compared to the
519    // gains that this analysis gives.
520    const ObjCInterfaceDecl *Class = MD->getClassInterface();
521
522    // -[NSArray initWithObjects:]
523    if (isReceiverClassOrSuperclass(Class, "NSArray") &&
524        S == initWithObjectsS)
525      return true;
526
527    // -[NSDictionary initWithObjectsAndKeys:]
528    if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
529        S == initWithObjectsAndKeysS)
530      return true;
531
532    // -[NSSet initWithObjects:]
533    if (isReceiverClassOrSuperclass(Class, "NSSet") &&
534        S == initWithObjectsS)
535      return true;
536  } else {
537    const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
538
539    // -[NSArray arrayWithObjects:]
540    if (isReceiverClassOrSuperclass(Class, "NSArray") &&
541        S == arrayWithObjectsS)
542      return true;
543
544    // -[NSDictionary dictionaryWithObjectsAndKeys:]
545    if (isReceiverClassOrSuperclass(Class, "NSDictionary") &&
546        S == dictionaryWithObjectsAndKeysS)
547      return true;
548
549    // -[NSSet setWithObjects:]
550    if (isReceiverClassOrSuperclass(Class, "NSSet") &&
551        S == setWithObjectsS)
552      return true;
553  }
554
555  return false;
556}
557
558void VariadicMethodTypeChecker::checkPreObjCMessage(ObjCMessage 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
570    initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
571    initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
572  }
573
574  if (!isVariadicMessage(msg))
575      return;
576
577  // We are not interested in the selector arguments since they have
578  // well-defined types, so the compiler will issue a warning for them.
579  unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
580
581  // We're not interested in the last argument since it has to be nil or the
582  // compiler would have issued a warning for it elsewhere.
583  unsigned variadicArgsEnd = msg.getNumArgs() - 1;
584
585  if (variadicArgsEnd <= variadicArgsBegin)
586    return;
587
588  // Verify that all arguments have Objective-C types.
589  llvm::Optional<ExplodedNode*> errorNode;
590  const ProgramState *state = C.getState();
591
592  for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
593    QualType ArgTy = msg.getArgType(I);
594    if (ArgTy->isObjCObjectPointerType())
595      continue;
596
597    // Block pointers are treaded as Objective-C pointers.
598    if (ArgTy->isBlockPointerType())
599      continue;
600
601    // Ignore pointer constants.
602    if (isa<loc::ConcreteInt>(msg.getArgSVal(I, state)))
603      continue;
604
605    // Ignore pointer types annotated with 'NSObject' attribute.
606    if (C.getASTContext().isObjCNSObjectType(ArgTy))
607      continue;
608
609    // Ignore CF references, which can be toll-free bridged.
610    if (coreFoundation::isCFObjectRef(ArgTy))
611      continue;
612
613    // Generate only one error node to use for all bug reports.
614    if (!errorNode.hasValue()) {
615      errorNode = C.generateNode();
616    }
617
618    if (!errorNode.getValue())
619      continue;
620
621    llvm::SmallString<128> sbuf;
622    llvm::raw_svector_ostream os(sbuf);
623
624    if (const char *TypeName = GetReceiverNameType(msg))
625      os << "Argument to '" << TypeName << "' method '";
626    else
627      os << "Argument to method '";
628
629    os << msg.getSelector().getAsString()
630      << "' should be an Objective-C pointer type, not '"
631      << ArgTy.getAsString() << "'";
632
633    BugReport *R = new BugReport(*BT, os.str(),
634                                             errorNode.getValue());
635    R->addRange(msg.getArgSourceRange(I));
636    C.EmitReport(R);
637  }
638}
639
640//===----------------------------------------------------------------------===//
641// Check registration.
642//===----------------------------------------------------------------------===//
643
644void ento::registerNilArgChecker(CheckerManager &mgr) {
645  mgr.registerChecker<NilArgChecker>();
646}
647
648void ento::registerCFNumberCreateChecker(CheckerManager &mgr) {
649  mgr.registerChecker<CFNumberCreateChecker>();
650}
651
652void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
653  mgr.registerChecker<CFRetainReleaseChecker>();
654}
655
656void ento::registerClassReleaseChecker(CheckerManager &mgr) {
657  mgr.registerChecker<ClassReleaseChecker>();
658}
659
660void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
661  mgr.registerChecker<VariadicMethodTypeChecker>();
662}
663