clang API Documentation

ObjCUnusedIVarsChecker.cpp
Go to the documentation of this file.
00001 //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 //  This file defines a CheckObjCUnusedIvars, a checker that
00011 //  analyzes an Objective-C class's interface/implementation to determine if it
00012 //  has any ivars that are never accessed.
00013 //
00014 //===----------------------------------------------------------------------===//
00015 
00016 #include "ClangSACheckers.h"
00017 #include "clang/StaticAnalyzer/Core/Checker.h"
00018 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
00019 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
00020 #include "clang/AST/ExprObjC.h"
00021 #include "clang/AST/Expr.h"
00022 #include "clang/AST/DeclObjC.h"
00023 #include "clang/Basic/LangOptions.h"
00024 #include "clang/Basic/SourceManager.h"
00025 
00026 using namespace clang;
00027 using namespace ento;
00028 
00029 enum IVarState { Unused, Used };
00030 typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
00031 
00032 static void Scan(IvarUsageMap& M, const Stmt *S) {
00033   if (!S)
00034     return;
00035 
00036   if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
00037     const ObjCIvarDecl *D = Ex->getDecl();
00038     IvarUsageMap::iterator I = M.find(D);
00039     if (I != M.end())
00040       I->second = Used;
00041     return;
00042   }
00043 
00044   // Blocks can reference an instance variable of a class.
00045   if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
00046     Scan(M, BE->getBody());
00047     return;
00048   }
00049 
00050   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
00051     for (PseudoObjectExpr::const_semantics_iterator
00052         i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
00053       const Expr *sub = *i;
00054       if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
00055         sub = OVE->getSourceExpr();
00056       Scan(M, sub);
00057     }
00058 
00059   for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
00060     Scan(M, *I);
00061 }
00062 
00063 static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
00064   if (!D)
00065     return;
00066 
00067   const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
00068 
00069   if (!ID)
00070     return;
00071 
00072   IvarUsageMap::iterator I = M.find(ID);
00073   if (I != M.end())
00074     I->second = Used;
00075 }
00076 
00077 static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
00078   // Scan the methods for accesses.
00079   for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(),
00080        E = D->instmeth_end(); I!=E; ++I)
00081     Scan(M, (*I)->getBody());
00082 
00083   if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
00084     // Scan for @synthesized property methods that act as setters/getters
00085     // to an ivar.
00086     for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(),
00087          E = ID->propimpl_end(); I!=E; ++I)
00088       Scan(M, &*I);
00089 
00090     // Scan the associated categories as well.
00091     for (const ObjCCategoryDecl *CD =
00092           ID->getClassInterface()->getCategoryList(); CD ;
00093           CD = CD->getNextClassCategory()) {
00094       if (const ObjCCategoryImplDecl *CID = CD->getImplementation())
00095         Scan(M, CID);
00096     }
00097   }
00098 }
00099 
00100 static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
00101                  SourceManager &SM) {
00102   for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end();
00103        I!=E; ++I)
00104     if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) {
00105       SourceLocation L = FD->getLocStart();
00106       if (SM.getFileID(L) == FID)
00107         Scan(M, FD->getBody());
00108     }
00109 }
00110 
00111 static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
00112                                 BugReporter &BR) {
00113 
00114   const ObjCInterfaceDecl *ID = D->getClassInterface();
00115   IvarUsageMap M;
00116 
00117   // Iterate over the ivars.
00118   for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(),
00119         E=ID->ivar_end(); I!=E; ++I) {
00120 
00121     const ObjCIvarDecl *ID = &*I;
00122 
00123     // Ignore ivars that...
00124     // (a) aren't private
00125     // (b) explicitly marked unused
00126     // (c) are iboutlets
00127     // (d) are unnamed bitfields
00128     if (ID->getAccessControl() != ObjCIvarDecl::Private ||
00129         ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() ||
00130         ID->getAttr<IBOutletCollectionAttr>() ||
00131         ID->isUnnamedBitfield())
00132       continue;
00133 
00134     M[ID] = Unused;
00135   }
00136 
00137   if (M.empty())
00138     return;
00139 
00140   // Now scan the implementation declaration.
00141   Scan(M, D);
00142 
00143   // Any potentially unused ivars?
00144   bool hasUnused = false;
00145   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
00146     if (I->second == Unused) {
00147       hasUnused = true;
00148       break;
00149     }
00150 
00151   if (!hasUnused)
00152     return;
00153 
00154   // We found some potentially unused ivars.  Scan the entire translation unit
00155   // for functions inside the @implementation that reference these ivars.
00156   // FIXME: In the future hopefully we can just use the lexical DeclContext
00157   // to go from the ObjCImplementationDecl to the lexically "nested"
00158   // C functions.
00159   SourceManager &SM = BR.getSourceManager();
00160   Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
00161 
00162   // Find ivars that are unused.
00163   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
00164     if (I->second == Unused) {
00165       std::string sbuf;
00166       llvm::raw_string_ostream os(sbuf);
00167       os << "Instance variable '" << *I->first << "' in class '" << *ID
00168          << "' is never used by the methods in its @implementation "
00169             "(although it may be used by category methods).";
00170 
00171       PathDiagnosticLocation L =
00172         PathDiagnosticLocation::create(I->first, BR.getSourceManager());
00173       BR.EmitBasicReport(D, "Unused instance variable", "Optimization",
00174                          os.str(), L);
00175     }
00176 }
00177 
00178 //===----------------------------------------------------------------------===//
00179 // ObjCUnusedIvarsChecker
00180 //===----------------------------------------------------------------------===//
00181 
00182 namespace {
00183 class ObjCUnusedIvarsChecker : public Checker<
00184                                       check::ASTDecl<ObjCImplementationDecl> > {
00185 public:
00186   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
00187                     BugReporter &BR) const {
00188     checkObjCUnusedIvar(D, BR);
00189   }
00190 };
00191 }
00192 
00193 void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
00194   mgr.registerChecker<ObjCUnusedIvarsChecker>();
00195 }