zcov: / lib/Checker/BasicObjCFoundationChecks.cpp


Files: 1 Branches Taken: 71.9% 92 / 128
Generated: 2010-02-10 01:31 Branches Executed: 92.2% 118 / 128
Line Coverage: 91.8% 169 / 184


Programs: 1 Runs 2897


       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 "BasicObjCFoundationChecks.h"
      17                 : 
      18                 : #include "clang/Checker/PathSensitive/ExplodedGraph.h"
      19                 : #include "clang/Checker/PathSensitive/GRSimpleAPICheck.h"
      20                 : #include "clang/Checker/PathSensitive/GRExprEngine.h"
      21                 : #include "clang/Checker/PathSensitive/GRState.h"
      22                 : #include "clang/Checker/BugReporter/BugReporter.h"
      23                 : #include "clang/Checker/PathSensitive/MemRegion.h"
      24                 : #include "clang/Checker/BugReporter/PathDiagnostic.h"
      25                 : #include "clang/Checker/PathSensitive/CheckerVisitor.h"
      26                 : #include "clang/Checker/Checkers/LocalCheckers.h"
      27                 : #include "clang/AST/DeclObjC.h"
      28                 : #include "clang/AST/Expr.h"
      29                 : #include "clang/AST/ExprObjC.h"
      30                 : #include "clang/AST/ASTContext.h"
      31                 : 
      32                 : using namespace clang;
      33                 : 
      34             1226: static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) {
      35             1226:   const Expr* Receiver = ME->getReceiver();
      36                 : 
                      349: branch 0 taken
                      877: branch 1 taken
      37             1226:   if (!Receiver)
      38              349:     return NULL;
      39                 : 
                      875: branch 0 taken
                        2: branch 1 taken
      40              877:   if (const ObjCObjectPointerType *PT =
      41              877:       Receiver->getType()->getAs<ObjCObjectPointerType>())
      42              875:     return PT->getInterfaceType();
      43                 : 
      44                2:   return NULL;
      45                 : }
      46                 : 
      47               24: static const char* GetReceiverNameType(const ObjCMessageExpr* ME) {
                       24: branch 1 taken
                        0: branch 2 not taken
      48               24:   if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME))
      49               24:     return ReceiverType->getDecl()->getIdentifier()->getNameStart();
      50                0:   return NULL;
      51                 : }
      52                 : 
      53                 : namespace {
      54                 : 
                       40: branch 1 taken
                        0: branch 2 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
      55               40: class APIMisuse : public BugType {
      56                 : public:
      57               40:   APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {}
      58                 : };
      59                 : 
                     2138: branch 1 taken
                        0: branch 2 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
      60             2138: class BasicObjCFoundationChecks : public GRSimpleAPICheck {
      61                 :   APIMisuse *BT;
      62                 :   BugReporter& BR;
      63                 :   ASTContext &Ctx;
      64                 : 
      65                 :   bool isNSString(const ObjCInterfaceType *T, llvm::StringRef suffix);
      66                 :   bool AuditNSString(ExplodedNode* N, const ObjCMessageExpr* ME);
      67                 : 
      68                 :   void Warn(ExplodedNode* N, const Expr* E, const std::string& s);
      69                 :   void WarnNilArg(ExplodedNode* N, const Expr* E);
      70                 : 
      71                 :   bool CheckNilArg(ExplodedNode* N, unsigned Arg);
      72                 : 
      73                 : public:
      74             2138:   BasicObjCFoundationChecks(ASTContext& ctx, BugReporter& br)
      75             2138:     : BT(0), BR(br), Ctx(ctx) {}
      76                 : 
      77                 :   bool Audit(ExplodedNode* N, GRStateManager&);
      78                 : 
      79                 : private:
      80               24:   void WarnNilArg(ExplodedNode* N, const ObjCMessageExpr* ME, unsigned Arg) {
      81               24:     std::string sbuf;
      82               24:     llvm::raw_string_ostream os(sbuf);
      83                 :     os << "Argument to '" << GetReceiverNameType(ME) << "' method '"
      84               24:        << ME->getSelector().getAsString() << "' cannot be nil.";
      85                 : 
      86                 :     // Lazily create the BugType object for NilArg.  This will be owned
      87                 :     // by the BugReporter object 'BR' once we call BR.EmitWarning.
                       24: branch 0 taken
                        0: branch 1 not taken
      88               24:     if (!BT) BT = new APIMisuse("nil argument");
      89                 : 
      90               24:     RangedBugReport *R = new RangedBugReport(*BT, os.str(), N);
      91               24:     R->addRange(ME->getArg(Arg)->getSourceRange());
      92               24:     BR.EmitReport(R);
      93               24:   }
      94                 : };
      95                 : 
      96                 : } // end anonymous namespace
      97                 : 
      98                 : 
      99                 : GRSimpleAPICheck*
     100             2138: clang::CreateBasicObjCFoundationChecks(ASTContext& Ctx, BugReporter& BR) {
     101             2138:   return new BasicObjCFoundationChecks(Ctx, BR);
     102                 : }
     103                 : 
     104                 : 
     105                 : 
     106                 : bool BasicObjCFoundationChecks::Audit(ExplodedNode* N,
     107             1202:                                       GRStateManager&) {
     108                 : 
     109                 :   const ObjCMessageExpr* ME =
     110             1202:     cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
     111                 : 
     112             1202:   const ObjCInterfaceType *ReceiverType = GetReceiverType(ME);
     113                 : 
                      772: branch 0 taken
                      430: branch 1 taken
     114             1202:   if (!ReceiverType)
     115              772:     return false;
     116                 : 
                      153: branch 4 taken
                      277: branch 5 taken
     117              430:   if (isNSString(ReceiverType,
     118                 :                  ReceiverType->getDecl()->getIdentifier()->getName()))
     119              153:     return AuditNSString(N, ME);
     120                 : 
     121              277:   return false;
     122                 : }
     123                 : 
     124               24: static inline bool isNil(SVal X) {
     125               24:   return isa<loc::ConcreteInt>(X);
     126                 : }
     127                 : 
     128                 : //===----------------------------------------------------------------------===//
     129                 : // Error reporting.
     130                 : //===----------------------------------------------------------------------===//
     131                 : 
     132               24: bool BasicObjCFoundationChecks::CheckNilArg(ExplodedNode* N, unsigned Arg) {
     133                 :   const ObjCMessageExpr* ME =
     134               24:     cast<ObjCMessageExpr>(cast<PostStmt>(N->getLocation()).getStmt());
     135                 : 
     136               24:   const Expr * E = ME->getArg(Arg);
     137                 : 
                       24: branch 4 taken
                        0: branch 5 not taken
     138               24:   if (isNil(N->getState()->getSVal(E))) {
     139               24:     WarnNilArg(N, ME, Arg);
     140               24:     return true;
     141                 :   }
     142                 : 
     143                0:   return false;
     144                 : }
     145                 : 
     146                 : //===----------------------------------------------------------------------===//
     147                 : // NSString checking.
     148                 : //===----------------------------------------------------------------------===//
     149                 : 
     150                 : bool BasicObjCFoundationChecks::isNSString(const ObjCInterfaceType *T,
     151              430:                                            llvm::StringRef ClassName) {
                      277: branch 2 taken
                      153: branch 3 taken
                        0: branch 6 not taken
                      277: branch 7 taken
     152              430:   return ClassName == "NSString" || ClassName == "NSMutableString";
     153                 : }
     154                 : 
     155                 : bool BasicObjCFoundationChecks::AuditNSString(ExplodedNode* N,
     156              153:                                               const ObjCMessageExpr* ME) {
     157                 : 
     158              153:   Selector S = ME->getSelector();
     159                 : 
                      128: branch 1 taken
                       25: branch 2 taken
     160              153:   if (S.isUnarySelector())
     161              128:     return false;
     162                 : 
     163                 :   // FIXME: This is going to be really slow doing these checks with
     164                 :   //  lexical comparisons.
     165                 : 
     166               25:   std::string NameStr = S.getAsString();
     167               25:   llvm::StringRef Name(NameStr);
                        0: branch 1 not taken
                       25: branch 2 taken
     168               25:   assert(!Name.empty());
     169                 : 
     170                 :   // FIXME: Checking for initWithFormat: will not work in most cases
     171                 :   //  yet because [NSString alloc] returns id, not NSString*.  We will
     172                 :   //  need support for tracking expected-type information in the analyzer
     173                 :   //  to find these errors.
                       21: branch 2 taken
                        4: branch 3 taken
                       17: branch 6 taken
                        4: branch 7 taken
                       13: branch 10 taken
                        4: branch 11 taken
                        9: branch 14 taken
                        4: branch 15 taken
                        5: branch 18 taken
                        4: branch 19 taken
                        1: branch 22 taken
                        4: branch 23 taken
                        0: branch 26 not taken
                        1: branch 27 taken
                       24: branch 28 taken
                        1: branch 29 taken
     174               25:   if (Name == "caseInsensitiveCompare:" ||
     175                 :       Name == "compare:" ||
     176                 :       Name == "compare:options:" ||
     177                 :       Name == "compare:options:range:" ||
     178                 :       Name == "compare:options:range:locale:" ||
     179                 :       Name == "componentsSeparatedByCharactersInSet:" ||
     180                 :       Name == "initWithFormat:")
     181               24:     return CheckNilArg(N, 0);
     182                 : 
     183                1:   return false;
     184                 : }
     185                 : 
     186                 : //===----------------------------------------------------------------------===//
     187                 : // Error reporting.
     188                 : //===----------------------------------------------------------------------===//
     189                 : 
     190                 : namespace {
     191                 : 
     192                 : class AuditCFNumberCreate : public GRSimpleAPICheck {
     193                 :   APIMisuse* BT;
     194                 : 
     195                 :   // FIXME: Either this should be refactored into GRSimpleAPICheck, or
     196                 :   //   it should always be passed with a call to Audit.  The latter
     197                 :   //   approach makes this class more stateless.
     198                 :   ASTContext& Ctx;
     199                 :   IdentifierInfo* II;
     200                 :   BugReporter& BR;
     201                 : 
     202                 : public:
     203             2138:   AuditCFNumberCreate(ASTContext& ctx, BugReporter& br)
     204             2138:   : BT(0), Ctx(ctx), II(&Ctx.Idents.get("CFNumberCreate")), BR(br){}
     205                 : 
                     2138: branch 1 taken
                        0: branch 2 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
     206             2138:   ~AuditCFNumberCreate() {}
     207                 : 
     208                 :   bool Audit(ExplodedNode* N, GRStateManager&);
     209                 : 
     210                 : private:
     211                 :   void AddError(const TypedRegion* R, const Expr* Ex, ExplodedNode *N,
     212                 :                 uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
     213                 : };
     214                 : } // end anonymous namespace
     215                 : 
     216                 : enum CFNumberType {
     217                 :   kCFNumberSInt8Type = 1,
     218                 :   kCFNumberSInt16Type = 2,
     219                 :   kCFNumberSInt32Type = 3,
     220                 :   kCFNumberSInt64Type = 4,
     221                 :   kCFNumberFloat32Type = 5,
     222                 :   kCFNumberFloat64Type = 6,
     223                 :   kCFNumberCharType = 7,
     224                 :   kCFNumberShortType = 8,
     225                 :   kCFNumberIntType = 9,
     226                 :   kCFNumberLongType = 10,
     227                 :   kCFNumberLongLongType = 11,
     228                 :   kCFNumberFloatType = 12,
     229                 :   kCFNumberDoubleType = 13,
     230                 :   kCFNumberCFIndexType = 14,
     231                 :   kCFNumberNSIntegerType = 15,
     232                 :   kCFNumberCGFloatType = 16
     233                 : };
     234                 : 
     235                 : namespace {
     236                 :   template<typename T>
     237                 :   class Optional {
     238                 :     bool IsKnown;
     239                 :     T Val;
     240                 :   public:
     241                0:     Optional() : IsKnown(false), Val(0) {}
     242               28:     Optional(const T& val) : IsKnown(true), Val(val) {}
     243                 : 
     244               80:     bool isKnown() const { return IsKnown; }
     245                 : 
     246               52:     const T& getValue() const {
                        0: branch 1 not taken
                       52: branch 2 taken
     247               52:       assert (isKnown());
     248               52:       return Val;
     249                 :     }
     250                 : 
     251               52:     operator const T&() const {
     252               52:       return getValue();
     253                 :     }
     254                 :   };
     255                 : }
     256                 : 
     257               28: static Optional<uint64_t> GetCFNumberSize(ASTContext& Ctx, uint64_t i) {
     258                 :   static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
     259                 : 
                       20: branch 0 taken
                        8: branch 1 taken
     260               28:   if (i < kCFNumberCharType)
     261               20:     return FixedSize[i-1];
     262                 : 
     263                8:   QualType T;
     264                 : 
                        0: branch 0 not taken
                        0: branch 1 not taken
                        4: branch 2 taken
                        4: branch 3 taken
                        0: branch 4 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
                        0: branch 7 not taken
     265                8:   switch (i) {
     266                0:     case kCFNumberCharType:     T = Ctx.CharTy;     break;
     267                0:     case kCFNumberShortType:    T = Ctx.ShortTy;    break;
     268                4:     case kCFNumberIntType:      T = Ctx.IntTy;      break;
     269                4:     case kCFNumberLongType:     T = Ctx.LongTy;     break;
     270                0:     case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
     271                0:     case kCFNumberFloatType:    T = Ctx.FloatTy;    break;
     272                0:     case kCFNumberDoubleType:   T = Ctx.DoubleTy;   break;
     273                 :     case kCFNumberCFIndexType:
     274                 :     case kCFNumberNSIntegerType:
     275                 :     case kCFNumberCGFloatType:
     276                 :       // FIXME: We need a way to map from names to Type*.
     277                 :     default:
     278                0:       return Optional<uint64_t>();
     279                 :   }
     280                 : 
     281                8:   return Ctx.getTypeSize(T);
     282                 : }
     283                 : 
     284                 : #if 0
     285                 : static const char* GetCFNumberTypeStr(uint64_t i) {
     286                 :   static const char* Names[] = {
     287                 :     "kCFNumberSInt8Type",
     288                 :     "kCFNumberSInt16Type",
     289                 :     "kCFNumberSInt32Type",
     290                 :     "kCFNumberSInt64Type",
     291                 :     "kCFNumberFloat32Type",
     292                 :     "kCFNumberFloat64Type",
     293                 :     "kCFNumberCharType",
     294                 :     "kCFNumberShortType",
     295                 :     "kCFNumberIntType",
     296                 :     "kCFNumberLongType",
     297                 :     "kCFNumberLongLongType",
     298                 :     "kCFNumberFloatType",
     299                 :     "kCFNumberDoubleType",
     300                 :     "kCFNumberCFIndexType",
     301                 :     "kCFNumberNSIntegerType",
     302                 :     "kCFNumberCGFloatType"
     303                 :   };
     304                 : 
     305                 :   return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
     306                 : }
     307                 : #endif
     308                 : 
     309             1575: bool AuditCFNumberCreate::Audit(ExplodedNode* N,GRStateManager&){
     310                 :   const CallExpr* CE =
     311             1575:     cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
     312             1575:   const Expr* Callee = CE->getCallee();
     313             1575:   SVal CallV = N->getState()->getSVal(Callee);
     314             1575:   const FunctionDecl* FD = CallV.getAsFunctionDecl();
     315                 : 
                     1439: branch 0 taken
                      136: branch 1 taken
                       28: branch 3 taken
                     1411: branch 4 taken
                        0: branch 6 not taken
                       28: branch 7 taken
                     1547: branch 8 taken
                       28: branch 9 taken
     316             1575:   if (!FD || FD->getIdentifier() != II || CE->getNumArgs()!=3)
     317             1547:     return false;
     318                 : 
     319                 :   // Get the value of the "theType" argument.
     320               28:   SVal TheTypeVal = N->getState()->getSVal(CE->getArg(1));
     321                 : 
     322                 :     // FIXME: We really should allow ranges of valid theType values, and
     323                 :     //   bifurcate the state appropriately.
     324               28:   nonloc::ConcreteInt* V = dyn_cast<nonloc::ConcreteInt>(&TheTypeVal);
     325                 : 
                        0: branch 0 not taken
                       28: branch 1 taken
     326               28:   if (!V)
     327                0:     return false;
     328                 : 
     329               28:   uint64_t NumberKind = V->getValue().getLimitedValue();
     330               28:   Optional<uint64_t> TargetSize = GetCFNumberSize(Ctx, NumberKind);
     331                 : 
     332                 :   // FIXME: In some cases we can emit an error.
                        0: branch 1 not taken
                       28: branch 2 taken
     333               28:   if (!TargetSize.isKnown())
     334                0:     return false;
     335                 : 
     336                 :   // Look at the value of the integer being passed by reference.  Essentially
     337                 :   // we want to catch cases where the value passed in is not equal to the
     338                 :   // size of the type being created.
     339               28:   SVal TheValueExpr = N->getState()->getSVal(CE->getArg(2));
     340                 : 
     341                 :   // FIXME: Eventually we should handle arbitrary locations.  We can do this
     342                 :   //  by having an enhanced memory model that does low-level typing.
     343               28:   loc::MemRegionVal* LV = dyn_cast<loc::MemRegionVal>(&TheValueExpr);
     344                 : 
                        0: branch 0 not taken
                       28: branch 1 taken
     345               28:   if (!LV)
     346                0:     return false;
     347                 : 
     348               28:   const TypedRegion* R = dyn_cast<TypedRegion>(LV->StripCasts());
     349                 : 
                        0: branch 0 not taken
                       28: branch 1 taken
     350               28:   if (!R)
     351                0:     return false;
     352                 : 
     353               28:   QualType T = Ctx.getCanonicalType(R->getValueType(Ctx));
     354                 : 
     355                 :   // FIXME: If the pointee isn't an integer type, should we flag a warning?
     356                 :   //  People can do weird stuff with pointers.
     357                 : 
                        0: branch 2 not taken
                       28: branch 3 taken
     358               28:   if (!T->isIntegerType())
     359                0:     return false;
     360                 : 
     361               28:   uint64_t SourceSize = Ctx.getTypeSize(T);
     362                 : 
     363                 :   // CHECK: is SourceSize == TargetSize
     364                 : 
                       16: branch 1 taken
                       12: branch 2 taken
     365               28:   if (SourceSize == TargetSize)
     366               16:     return false;
     367                 : 
     368               12:   AddError(R, CE->getArg(2), N, SourceSize, TargetSize, NumberKind);
     369                 : 
     370                 :   // FIXME: We can actually create an abstract "CFNumber" object that has
     371                 :   //  the bits initialized to the provided values.
     372               12:   return SourceSize < TargetSize;
     373                 : }
     374                 : 
     375                 : void AuditCFNumberCreate::AddError(const TypedRegion* R, const Expr* Ex,
     376                 :                                    ExplodedNode *N,
     377                 :                                    uint64_t SourceSize, uint64_t TargetSize,
     378               12:                                    uint64_t NumberKind) {
     379                 : 
     380               12:   std::string sbuf;
     381               12:   llvm::raw_string_ostream os(sbuf);
     382                 : 
     383                 :   os << (SourceSize == 8 ? "An " : "A ")
     384                 :      << SourceSize << " bit integer is used to initialize a CFNumber "
     385                 :         "object that represents "
     386                 :      << (TargetSize == 8 ? "an " : "a ")
                        4: branch 0 taken
                        8: branch 1 taken
                        4: branch 2 taken
                        8: branch 3 taken
     387               12:      << TargetSize << " bit integer. ";
     388                 : 
                        8: branch 0 taken
                        4: branch 1 taken
     389               12:   if (SourceSize < TargetSize)
     390                 :     os << (TargetSize - SourceSize)
     391                8:        << " bits of the CFNumber value will be garbage." ;
     392                 :   else
     393                 :     os << (SourceSize - TargetSize)
     394                4:        << " bits of the input integer will be lost.";
     395                 : 
     396                 :   // Lazily create the BugType object.  This will be owned
     397                 :   // by the BugReporter object 'BR' once we call BR.EmitWarning.
                       12: branch 0 taken
                        0: branch 1 not taken
     398               12:   if (!BT) BT = new APIMisuse("Bad use of CFNumberCreate");
     399               12:   RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
     400               12:   report->addRange(Ex->getSourceRange());
     401               12:   BR.EmitReport(report);
     402               12: }
     403                 : 
     404                 : GRSimpleAPICheck*
     405             2138: clang::CreateAuditCFNumberCreate(ASTContext& Ctx, BugReporter& BR) {
     406             2138:   return new AuditCFNumberCreate(Ctx, BR);
     407                 : }
     408                 : 
     409                 : //===----------------------------------------------------------------------===//
     410                 : // CFRetain/CFRelease auditing for null arguments.
     411                 : //===----------------------------------------------------------------------===//
     412                 : 
     413                 : namespace {
     414                 : class AuditCFRetainRelease : public GRSimpleAPICheck {
     415                 :   APIMisuse *BT;
     416                 : 
     417                 :   // FIXME: Either this should be refactored into GRSimpleAPICheck, or
     418                 :   //   it should always be passed with a call to Audit.  The latter
     419                 :   //   approach makes this class more stateless.
     420                 :   ASTContext& Ctx;
     421                 :   IdentifierInfo *Retain, *Release;
     422                 :   BugReporter& BR;
     423                 : 
     424                 : public:
     425             2138:   AuditCFRetainRelease(ASTContext& ctx, BugReporter& br)
     426                 :   : BT(0), Ctx(ctx),
     427                 :     Retain(&Ctx.Idents.get("CFRetain")), Release(&Ctx.Idents.get("CFRelease")),
     428             2138:     BR(br){}
     429                 : 
                     2138: branch 1 taken
                        0: branch 2 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
     430             2138:   ~AuditCFRetainRelease() {}
     431                 : 
     432                 :   bool Audit(ExplodedNode* N, GRStateManager&);
     433                 : };
     434                 : } // end anonymous namespace
     435                 : 
     436                 : 
     437             1575: bool AuditCFRetainRelease::Audit(ExplodedNode* N, GRStateManager&) {
     438             1575:   const CallExpr* CE = cast<CallExpr>(cast<PostStmt>(N->getLocation()).getStmt());
     439                 : 
     440                 :   // If the CallExpr doesn't have exactly 1 argument just give up checking.
                      820: branch 1 taken
                      755: branch 2 taken
     441             1575:   if (CE->getNumArgs() != 1)
     442              820:     return false;
     443                 : 
     444                 :   // Check if we called CFRetain/CFRelease.
     445              755:   const GRState* state = N->getState();
     446              755:   SVal X = state->getSVal(CE->getCallee());
     447              755:   const FunctionDecl* FD = X.getAsFunctionDecl();
     448                 : 
                       60: branch 0 taken
                      695: branch 1 taken
     449              755:   if (!FD)
     450               60:     return false;
     451                 : 
     452              695:   const IdentifierInfo *FuncII = FD->getIdentifier();
                      633: branch 0 taken
                       62: branch 1 taken
                      551: branch 2 taken
                       82: branch 3 taken
     453              695:   if (!(FuncII == Retain || FuncII == Release))
     454              551:     return false;
     455                 : 
     456                 :   // Finally, check if the argument is NULL.
     457                 :   // FIXME: We should be able to bifurcate the state here, as a successful
     458                 :   // check will result in the value not being NULL afterwards.
     459                 :   // FIXME: Need a way to register vistors for the BugReporter.  Would like
     460                 :   // to benefit from the same diagnostics that regular null dereference
     461                 :   // reporting has.
                        4: branch 3 taken
                      140: branch 4 taken
     462              144:   if (state->getStateManager().isEqual(state, CE->getArg(0), 0)) {
                        2: branch 0 taken
                        2: branch 1 taken
     463                4:     if (!BT)
     464                2:       BT = new APIMisuse("null passed to CFRetain/CFRelease");
     465                 : 
     466                 :     const char *description = (FuncII == Retain)
     467                 :                             ? "Null pointer argument in call to CFRetain"
                        2: branch 0 taken
                        2: branch 1 taken
     468                4:                             : "Null pointer argument in call to CFRelease";
     469                 : 
     470                4:     RangedBugReport *report = new RangedBugReport(*BT, description, N);
     471                4:     report->addRange(CE->getArg(0)->getSourceRange());
     472                4:     BR.EmitReport(report);
     473                4:     return true;
     474                 :   }
     475                 : 
     476              140:   return false;
     477                 : }
     478                 : 
     479                 : 
     480                 : GRSimpleAPICheck*
     481             2138: clang::CreateAuditCFRetainRelease(ASTContext& Ctx, BugReporter& BR) {
     482             2138:   return new AuditCFRetainRelease(Ctx, BR);
     483                 : }
     484                 : 
     485                 : //===----------------------------------------------------------------------===//
     486                 : // Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
     487                 : //===----------------------------------------------------------------------===//
     488                 : 
     489                 : namespace {
     490                 : class ClassReleaseChecker :
                     2138: branch 1 taken
                        0: branch 2 not taken
                        0: branch 5 not taken
                        0: branch 6 not taken
     491             2138:     public CheckerVisitor<ClassReleaseChecker> {
     492                 :   Selector releaseS;
     493                 :   Selector retainS;
     494                 :   Selector autoreleaseS;
     495                 :   Selector drainS;
     496                 :   BugType *BT;
     497                 : public:
     498             2138:   ClassReleaseChecker(ASTContext &Ctx)
     499                 :     : releaseS(GetNullarySelector("release", Ctx)),
     500                 :       retainS(GetNullarySelector("retain", Ctx)),
     501                 :       autoreleaseS(GetNullarySelector("autorelease", Ctx)),
     502                 :       drainS(GetNullarySelector("drain", Ctx)),
     503             2138:       BT(0) {}
     504                 : 
     505             2138:   static void *getTag() { static int x = 0; return &x; }
     506                 :       
     507                 :   void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME);    
     508                 : };
     509                 : }
     510                 : 
     511                 : void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C,
     512             1255:                                                   const ObjCMessageExpr *ME) {
     513                 :   
     514             1255:   const IdentifierInfo *ClsName = ME->getClassName();
                      890: branch 0 taken
                      365: branch 1 taken
     515             1255:   if (!ClsName)
     516              890:     return;
     517                 :   
     518              365:   Selector S = ME->getSelector();
                      363: branch 1 taken
                        2: branch 2 taken
                      361: branch 4 taken
                        2: branch 5 taken
                      359: branch 7 taken
                        2: branch 8 taken
                      357: branch 10 taken
                        2: branch 11 taken
                      357: branch 12 taken
                        8: branch 13 taken
     519              365:   if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
     520              357:     return;
     521                 :   
                        2: branch 0 taken
                        6: branch 1 taken
     522                8:   if (!BT)
     523                 :     BT = new APIMisuse("message incorrectly sent to class instead of class "
     524                2:                        "instance");
     525                 :   
     526                8:   ExplodedNode *N = C.GenerateNode();
     527                 : 
                        0: branch 0 not taken
                        8: branch 1 taken
     528                8:   if (!N)
     529                0:     return;
     530                 :   
     531                8:   llvm::SmallString<200> buf;
     532                8:   llvm::raw_svector_ostream os(buf);
     533                 : 
     534                 :   os << "The '" << S.getAsString() << "' message should be sent to instances "
     535                 :         "of class '" << ClsName->getName()
     536                8:      << "' and not the class directly";
     537                 :   
     538                8:   RangedBugReport *report = new RangedBugReport(*BT, os.str(), N);
     539                8:   report->addRange(ME->getSourceRange());
     540                8:   C.EmitReport(report);
     541                 : }
     542                 : 
     543                 : //===----------------------------------------------------------------------===//
     544                 : // Check registration.
     545                 : //===----------------------------------------------------------------------===//
     546                 : 
     547             2138: void clang::RegisterAppleChecks(GRExprEngine& Eng, const Decl &D) {
     548             2138:   ASTContext& Ctx = Eng.getContext();
     549             2138:   BugReporter &BR = Eng.getBugReporter();
     550                 : 
     551                 :   Eng.AddCheck(CreateBasicObjCFoundationChecks(Ctx, BR),
     552             2138:                Stmt::ObjCMessageExprClass);
     553             2138:   Eng.AddCheck(CreateAuditCFNumberCreate(Ctx, BR), Stmt::CallExprClass);
     554             2138:   Eng.AddCheck(CreateAuditCFRetainRelease(Ctx, BR), Stmt::CallExprClass);
     555                 : 
     556             2138:   RegisterNSErrorChecks(BR, Eng, D);
     557             2138:   RegisterNSAutoreleasePoolChecks(Eng);
     558             2138:   Eng.registerCheck(new ClassReleaseChecker(Ctx));
     559             2138: }

Generated: 2010-02-10 01:31 by zcov