Home | History | Annotate | Download | only in Checkers
      1 //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- 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 CheckObjCUnusedIvars, a checker that
     11 //  analyzes an Objective-C class's interface/implementation to determine if it
     12 //  has any ivars that are never accessed.
     13 //
     14 //===----------------------------------------------------------------------===//
     15 
     16 #include "ClangSACheckers.h"
     17 #include "clang/AST/Attr.h"
     18 #include "clang/AST/DeclObjC.h"
     19 #include "clang/AST/Expr.h"
     20 #include "clang/AST/ExprObjC.h"
     21 #include "clang/Basic/LangOptions.h"
     22 #include "clang/Basic/SourceManager.h"
     23 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
     24 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
     25 #include "clang/StaticAnalyzer/Core/Checker.h"
     26 
     27 using namespace clang;
     28 using namespace ento;
     29 
     30 enum IVarState { Unused, Used };
     31 typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
     32 
     33 static void Scan(IvarUsageMap& M, const Stmt *S) {
     34   if (!S)
     35     return;
     36 
     37   if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
     38     const ObjCIvarDecl *D = Ex->getDecl();
     39     IvarUsageMap::iterator I = M.find(D);
     40     if (I != M.end())
     41       I->second = Used;
     42     return;
     43   }
     44 
     45   // Blocks can reference an instance variable of a class.
     46   if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
     47     Scan(M, BE->getBody());
     48     return;
     49   }
     50 
     51   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
     52     for (PseudoObjectExpr::const_semantics_iterator
     53         i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
     54       const Expr *sub = *i;
     55       if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
     56         sub = OVE->getSourceExpr();
     57       Scan(M, sub);
     58     }
     59 
     60   for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
     61     Scan(M, *I);
     62 }
     63 
     64 static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
     65   if (!D)
     66     return;
     67 
     68   const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
     69 
     70   if (!ID)
     71     return;
     72 
     73   IvarUsageMap::iterator I = M.find(ID);
     74   if (I != M.end())
     75     I->second = Used;
     76 }
     77 
     78 static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
     79   // Scan the methods for accesses.
     80   for (const auto *I : D->instance_methods())
     81     Scan(M, I->getBody());
     82 
     83   if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
     84     // Scan for @synthesized property methods that act as setters/getters
     85     // to an ivar.
     86     for (const auto *I : ID->property_impls())
     87       Scan(M, I);
     88 
     89     // Scan the associated categories as well.
     90     for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
     91       if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
     92         Scan(M, CID);
     93     }
     94   }
     95 }
     96 
     97 static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
     98                  SourceManager &SM) {
     99   for (const auto *I : C->decls())
    100     if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
    101       SourceLocation L = FD->getLocStart();
    102       if (SM.getFileID(L) == FID)
    103         Scan(M, FD->getBody());
    104     }
    105 }
    106 
    107 static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
    108                                 BugReporter &BR,
    109                                 const CheckerBase *Checker) {
    110 
    111   const ObjCInterfaceDecl *ID = D->getClassInterface();
    112   IvarUsageMap M;
    113 
    114   // Iterate over the ivars.
    115   for (const auto *Ivar : ID->ivars()) {
    116     // Ignore ivars that...
    117     // (a) aren't private
    118     // (b) explicitly marked unused
    119     // (c) are iboutlets
    120     // (d) are unnamed bitfields
    121     if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
    122         Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
    123         Ivar->hasAttr<IBOutletCollectionAttr>() ||
    124         Ivar->isUnnamedBitfield())
    125       continue;
    126 
    127     M[Ivar] = Unused;
    128   }
    129 
    130   if (M.empty())
    131     return;
    132 
    133   // Now scan the implementation declaration.
    134   Scan(M, D);
    135 
    136   // Any potentially unused ivars?
    137   bool hasUnused = false;
    138   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
    139     if (I->second == Unused) {
    140       hasUnused = true;
    141       break;
    142     }
    143 
    144   if (!hasUnused)
    145     return;
    146 
    147   // We found some potentially unused ivars.  Scan the entire translation unit
    148   // for functions inside the @implementation that reference these ivars.
    149   // FIXME: In the future hopefully we can just use the lexical DeclContext
    150   // to go from the ObjCImplementationDecl to the lexically "nested"
    151   // C functions.
    152   SourceManager &SM = BR.getSourceManager();
    153   Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
    154 
    155   // Find ivars that are unused.
    156   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
    157     if (I->second == Unused) {
    158       std::string sbuf;
    159       llvm::raw_string_ostream os(sbuf);
    160       os << "Instance variable '" << *I->first << "' in class '" << *ID
    161          << "' is never used by the methods in its @implementation "
    162             "(although it may be used by category methods).";
    163 
    164       PathDiagnosticLocation L =
    165         PathDiagnosticLocation::create(I->first, BR.getSourceManager());
    166       BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
    167                          os.str(), L);
    168     }
    169 }
    170 
    171 //===----------------------------------------------------------------------===//
    172 // ObjCUnusedIvarsChecker
    173 //===----------------------------------------------------------------------===//
    174 
    175 namespace {
    176 class ObjCUnusedIvarsChecker : public Checker<
    177                                       check::ASTDecl<ObjCImplementationDecl> > {
    178 public:
    179   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
    180                     BugReporter &BR) const {
    181     checkObjCUnusedIvar(D, BR, this);
    182   }
    183 };
    184 }
    185 
    186 void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
    187   mgr.registerChecker<ObjCUnusedIvarsChecker>();
    188 }
    189