zcov: / lib/Checker/CheckObjCDealloc.cpp


Files: 1 Branches Taken: 74.2% 89 / 120
Generated: 2010-02-10 01:31 Branches Executed: 96.7% 116 / 120
Line Coverage: 84.7% 94 / 111


Programs: 1 Runs 2897


       1                 : //==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- 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 a CheckObjCDealloc, a checker that
      11                 : //  analyzes an Objective-C class's implementation to determine if it
      12                 : //  correctly implements -dealloc.
      13                 : //
      14                 : //===----------------------------------------------------------------------===//
      15                 : 
      16                 : #include "clang/Checker/Checkers/LocalCheckers.h"
      17                 : #include "clang/Checker/BugReporter/PathDiagnostic.h"
      18                 : #include "clang/Checker/BugReporter/BugReporter.h"
      19                 : #include "clang/AST/ExprObjC.h"
      20                 : #include "clang/AST/Expr.h"
      21                 : #include "clang/AST/DeclObjC.h"
      22                 : #include "clang/Basic/LangOptions.h"
      23                 : #include "llvm/Support/raw_ostream.h"
      24                 : 
      25                 : using namespace clang;
      26                 : 
      27               43: static bool scan_dealloc(Stmt* S, Selector Dealloc) {
      28                 : 
                        8: branch 1 taken
                       35: branch 2 taken
      29               43:   if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
                        3: branch 2 taken
                        5: branch 3 taken
      30                8:     if (ME->getSelector() == Dealloc)
                        3: branch 1 taken
                        0: branch 2 not taken
      31                3:       if (ME->getReceiver())
                        3: branch 2 taken
                        0: branch 3 not taken
      32                3:         if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
      33                3:           return isa<ObjCSuperExpr>(Receiver);
      34                 : 
      35                 :   // Recurse to children.
      36                 : 
                       40: branch 4 taken
                       37: branch 5 taken
      37               77:   for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I)
                       40: branch 1 taken
                        0: branch 2 not taken
                        3: branch 5 taken
                       37: branch 6 taken
                        3: branch 7 taken
                       37: branch 8 taken
      38               40:     if (*I && scan_dealloc(*I, Dealloc))
      39                3:       return true;
      40                 : 
      41               37:   return false;
      42                 : }
      43                 : 
      44                 : static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID,
      45                 :                               const ObjCPropertyDecl* PD,
      46                 :                               Selector Release,
      47                 :                               IdentifierInfo* SelfII,
      48              141:                               ASTContext& Ctx) {
      49                 : 
      50                 :   // [mMyIvar release]
                       30: branch 1 taken
                      111: branch 2 taken
      51              141:   if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
                       18: branch 2 taken
                       12: branch 3 taken
      52               30:     if (ME->getSelector() == Release)
                       18: branch 1 taken
                        0: branch 2 not taken
      53               18:       if (ME->getReceiver())
                       18: branch 2 taken
                        0: branch 3 not taken
      54               18:         if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
                       18: branch 1 taken
                        0: branch 2 not taken
      55               18:           if (ObjCIvarRefExpr* E = dyn_cast<ObjCIvarRefExpr>(Receiver))
                        2: branch 1 taken
                       16: branch 2 taken
      56               18:             if (E->getDecl() == ID)
      57                2:               return true;
      58                 : 
      59                 :   // [self setMyIvar:nil];
                       28: branch 1 taken
                      111: branch 2 taken
      60              139:   if (ObjCMessageExpr* ME = dyn_cast<ObjCMessageExpr>(S))
                       28: branch 1 taken
                        0: branch 2 not taken
      61               28:     if (ME->getReceiver())
                       28: branch 2 taken
                        0: branch 3 not taken
      62               28:       if (Expr* Receiver = ME->getReceiver()->IgnoreParenCasts())
                        7: branch 1 taken
                       21: branch 2 taken
      63               28:         if (DeclRefExpr* E = dyn_cast<DeclRefExpr>(Receiver))
                        7: branch 2 taken
                        0: branch 3 not taken
      64                7:           if (E->getDecl()->getIdentifier() == SelfII)
                        2: branch 2 taken
                        5: branch 3 taken
                        2: branch 5 taken
                        0: branch 6 not taken
                        1: branch 9 taken
                        1: branch 10 taken
                        1: branch 11 taken
                        6: branch 12 taken
      65                7:             if (ME->getMethodDecl() == PD->getSetterMethodDecl() &&
      66                 :                 ME->getNumArgs() == 1 &&
      67                 :                 ME->getArg(0)->isNullPointerConstant(Ctx, 
      68                 :                                               Expr::NPC_ValueDependentIsNull))
      69                1:               return true;
      70                 : 
      71                 :   // self.myIvar = nil;
                       10: branch 1 taken
                      128: branch 2 taken
      72              138:   if (BinaryOperator* BO = dyn_cast<BinaryOperator>(S))
                       10: branch 1 taken
                        0: branch 2 not taken
      73               10:     if (BO->isAssignmentOp())
                        7: branch 0 taken
                        3: branch 1 taken
      74               10:       if (ObjCPropertyRefExpr* PRE =
      75               10:          dyn_cast<ObjCPropertyRefExpr>(BO->getLHS()->IgnoreParenCasts()))
                        3: branch 1 taken
                        4: branch 2 taken
      76                7:           if (PRE->getProperty() == PD)
                        3: branch 2 taken
                        0: branch 3 not taken
      77                3:             if (BO->getRHS()->isNullPointerConstant(Ctx, 
      78                 :                                             Expr::NPC_ValueDependentIsNull)) {
      79                 :               // This is only a 'release' if the property kind is not
      80                 :               // 'assign'.
      81                3:               return PD->getSetterKind() != ObjCPropertyDecl::Assign;;
      82                 :             }
      83                 : 
      84                 :   // Recurse to children.
                      132: branch 4 taken
                      131: branch 5 taken
      85              263:   for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I)
                      132: branch 1 taken
                        0: branch 2 not taken
                        4: branch 5 taken
                      128: branch 6 taken
                        4: branch 7 taken
                      128: branch 8 taken
      86              132:     if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx))
      87                4:       return true;
      88                 : 
      89              131:   return false;
      90                 : }
      91                 : 
      92                 : void clang::CheckObjCDealloc(const ObjCImplementationDecl* D,
      93                6:                              const LangOptions& LOpts, BugReporter& BR) {
      94                 : 
                        0: branch 1 not taken
                        6: branch 2 taken
      95                6:   assert (LOpts.getGCMode() != LangOptions::GCOnly);
      96                 : 
      97                6:   ASTContext& Ctx = BR.getContext();
      98                6:   const ObjCInterfaceDecl* ID = D->getClassInterface();
      99                 : 
     100                 :   // Does the class contain any ivars that are pointers (or id<...>)?
     101                 :   // If not, skip the check entirely.
     102                 :   // NOTE: This is motivated by PR 2517:
     103                 :   //        http://llvm.org/bugs/show_bug.cgi?id=2517
     104                 : 
     105                6:   bool containsPointerIvar = false;
     106                 : 
                        7: branch 2 taken
                        2: branch 3 taken
     107                9:   for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end();
     108                 :        I!=E; ++I) {
     109                 : 
     110                7:     ObjCIvarDecl* ID = *I;
     111                7:     QualType T = ID->getType();
     112                 : 
                        5: branch 2 taken
                        2: branch 3 taken
                        1: branch 5 taken
                        4: branch 6 taken
                        4: branch 7 taken
                        3: branch 8 taken
     113                7:     if (!T->isObjCObjectPointerType() ||
     114                 :         ID->getAttr<IBOutletAttr>()) // Skip IBOutlets.
     115                3:       continue;
     116                 : 
     117                4:     containsPointerIvar = true;
     118                4:     break;
     119                 :   }
     120                 : 
                        2: branch 0 taken
                        4: branch 1 taken
     121                6:   if (!containsPointerIvar)
     122                2:     return;
     123                 : 
     124                 :   // Determine if the class subclasses NSObject.
     125                4:   IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
     126                4:   IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase");
     127                 : 
     128                 : 
                        8: branch 1 taken
                        0: branch 2 not taken
     129                8:   for ( ; ID ; ID = ID->getSuperClass()) {
     130                8:     IdentifierInfo *II = ID->getIdentifier();
     131                 : 
                        3: branch 0 taken
                        5: branch 1 taken
     132                8:     if (II == NSObjectII)
     133                3:       break;
     134                 : 
     135                 :     // FIXME: For now, ignore classes that subclass SenTestCase, as these don't
     136                 :     // need to implement -dealloc.  They implement tear down in another way,
     137                 :     // which we should try and catch later.
     138                 :     //  http://llvm.org/bugs/show_bug.cgi?id=3187
                        1: branch 0 taken
                        4: branch 1 taken
     139                5:     if (II == SenTestCaseII)
     140                1:       return;
     141                 :   }
     142                 : 
                        0: branch 0 not taken
                        3: branch 1 taken
     143                3:   if (!ID)
     144                0:     return;
     145                 : 
     146                 :   // Get the "dealloc" selector.
     147                3:   IdentifierInfo* II = &Ctx.Idents.get("dealloc");
     148                3:   Selector S = Ctx.Selectors.getSelector(0, &II);
     149                3:   ObjCMethodDecl* MD = 0;
     150                 : 
     151                 :   // Scan the instance methods for "dealloc".
                        5: branch 3 taken
                        0: branch 4 not taken
     152                8:   for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
     153                3:        E = D->instmeth_end(); I!=E; ++I) {
     154                 : 
                        3: branch 3 taken
                        2: branch 4 taken
     155                5:     if ((*I)->getSelector() == S) {
     156                3:       MD = *I;
     157                3:       break;
     158                 :     }
     159                 :   }
     160                 : 
                        0: branch 0 not taken
                        3: branch 1 taken
     161                3:   if (!MD) { // No dealloc found.
     162                 : 
     163                 :     const char* name = LOpts.getGCMode() == LangOptions::NonGC
     164                 :                        ? "missing -dealloc"
                        0: branch 1 not taken
                        0: branch 2 not taken
     165                0:                        : "missing -dealloc (Hybrid MM, non-GC)";
     166                 : 
     167                0:     std::string buf;
     168                0:     llvm::raw_string_ostream os(buf);
     169                 :     os << "Objective-C class '" << D->getNameAsString()
     170                0:        << "' lacks a 'dealloc' instance method";
     171                 : 
     172                0:     BR.EmitBasicReport(name, os.str(), D->getLocStart());
     173                0:     return;
     174                 :   }
     175                 : 
     176                 :   // dealloc found.  Scan for missing [super dealloc].
                        3: branch 1 taken
                        0: branch 2 not taken
                        0: branch 5 not taken
                        3: branch 6 taken
                        0: branch 7 not taken
                        3: branch 8 taken
     177                3:   if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) {
     178                 : 
     179                 :     const char* name = LOpts.getGCMode() == LangOptions::NonGC
     180                 :                        ? "missing [super dealloc]"
                        0: branch 1 not taken
                        0: branch 2 not taken
     181                0:                        : "missing [super dealloc] (Hybrid MM, non-GC)";
     182                 : 
     183                0:     std::string buf;
     184                0:     llvm::raw_string_ostream os(buf);
     185                 :     os << "The 'dealloc' instance method in Objective-C class '"
     186                 :        << D->getNameAsString()
     187                 :        << "' does not send a 'dealloc' message to its super class"
     188                0:            " (missing [super dealloc])";
     189                 : 
     190                0:     BR.EmitBasicReport(name, os.str(), D->getLocStart());
     191                0:     return;
     192                 :   }
     193                 : 
     194                 :   // Get the "release" selector.
     195                3:   IdentifierInfo* RII = &Ctx.Idents.get("release");
     196                3:   Selector RS = Ctx.Selectors.getSelector(0, &RII);
     197                 : 
     198                 :   // Get the "self" identifier
     199                3:   IdentifierInfo* SelfII = &Ctx.Idents.get("self");
     200                 : 
     201                 :   // Scan for missing and extra releases of ivars used by implementations
     202                 :   // of synthesized properties
                       10: branch 3 taken
                        3: branch 4 taken
     203               16:   for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(),
     204                3:        E = D->propimpl_end(); I!=E; ++I) {
     205                 : 
     206                 :     // We can only check the synthesized properties
                        0: branch 2 not taken
                       10: branch 3 taken
     207               10:     if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
     208                0:       continue;
     209                 : 
     210               10:     ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl();
                        0: branch 0 not taken
                       10: branch 1 taken
     211               10:     if (!ID)
     212                0:       continue;
     213                 : 
     214               10:     QualType T = ID->getType();
                        0: branch 2 not taken
                       10: branch 3 taken
     215               10:     if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars
     216                0:       continue;
     217                 : 
     218               10:     const ObjCPropertyDecl* PD = (*I)->getPropertyDecl();
                        0: branch 0 not taken
                       10: branch 1 taken
     219               10:     if (!PD)
     220                0:       continue;
     221                 : 
     222                 :     // ivars cannot be set via read-only properties, so we'll skip them
                        1: branch 1 taken
                        9: branch 2 taken
     223               10:     if (PD->isReadOnly())
     224                1:        continue;
     225                 : 
     226                 :     // ivar must be released if and only if the kind of setter was not 'assign'
     227                9:     bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign;
                        3: branch 2 taken
                        6: branch 3 taken
     228                9:     if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx)
     229                 :        != requiresRelease) {
     230                 :       const char *name;
     231                3:       const char* category = "Memory (Core Foundation/Objective-C)";
     232                 : 
     233                3:       std::string buf;
     234                3:       llvm::raw_string_ostream os(buf);
     235                 : 
                        2: branch 0 taken
                        1: branch 1 taken
     236                3:       if (requiresRelease) {
     237                 :         name = LOpts.getGCMode() == LangOptions::NonGC
     238                 :                ? "missing ivar release (leak)"
                        2: branch 1 taken
                        0: branch 2 not taken
     239                2:                : "missing ivar release (Hybrid MM, non-GC)";
     240                 : 
     241                 :         os << "The '" << ID->getNameAsString()
     242                 :            << "' instance variable was retained by a synthesized property but "
     243                2:               "wasn't released in 'dealloc'";
     244                 :       } else {
     245                 :         name = LOpts.getGCMode() == LangOptions::NonGC
     246                 :                ? "extra ivar release (use-after-release)"
                        1: branch 1 taken
                        0: branch 2 not taken
     247                1:                : "extra ivar release (Hybrid MM, non-GC)";
     248                 : 
     249                 :         os << "The '" << ID->getNameAsString()
     250                 :            << "' instance variable was not retained by a synthesized property "
     251                1:               "but was released in 'dealloc'";
     252                 :       }
     253                 : 
     254                3:       BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation());
     255                 :     }
     256                 :   }
     257                 : }
     258                 : 

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