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/AST/Attr.h"
00018 #include "clang/AST/DeclObjC.h"
00019 #include "clang/AST/Expr.h"
00020 #include "clang/AST/ExprObjC.h"
00021 #include "clang/Basic/LangOptions.h"
00022 #include "clang/Basic/SourceManager.h"
00023 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
00024 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
00025 #include "clang/StaticAnalyzer/Core/Checker.h"
00026 
00027 using namespace clang;
00028 using namespace ento;
00029 
00030 enum IVarState { Unused, Used };
00031 typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
00032 
00033 static void Scan(IvarUsageMap& M, const Stmt *S) {
00034   if (!S)
00035     return;
00036 
00037   if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
00038     const ObjCIvarDecl *D = Ex->getDecl();
00039     IvarUsageMap::iterator I = M.find(D);
00040     if (I != M.end())
00041       I->second = Used;
00042     return;
00043   }
00044 
00045   // Blocks can reference an instance variable of a class.
00046   if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
00047     Scan(M, BE->getBody());
00048     return;
00049   }
00050 
00051   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
00052     for (PseudoObjectExpr::const_semantics_iterator
00053         i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
00054       const Expr *sub = *i;
00055       if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
00056         sub = OVE->getSourceExpr();
00057       Scan(M, sub);
00058     }
00059 
00060   for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
00061     Scan(M, *I);
00062 }
00063 
00064 static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
00065   if (!D)
00066     return;
00067 
00068   const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
00069 
00070   if (!ID)
00071     return;
00072 
00073   IvarUsageMap::iterator I = M.find(ID);
00074   if (I != M.end())
00075     I->second = Used;
00076 }
00077 
00078 static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
00079   // Scan the methods for accesses.
00080   for (const auto *I : D->instance_methods())
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 (const auto *I : ID->property_impls())
00087       Scan(M, I);
00088 
00089     // Scan the associated categories as well.
00090     for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
00091       if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
00092         Scan(M, CID);
00093     }
00094   }
00095 }
00096 
00097 static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
00098                  SourceManager &SM) {
00099   for (const auto *I : C->decls())
00100     if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
00101       SourceLocation L = FD->getLocStart();
00102       if (SM.getFileID(L) == FID)
00103         Scan(M, FD->getBody());
00104     }
00105 }
00106 
00107 static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
00108                                 BugReporter &BR,
00109                                 const CheckerBase *Checker) {
00110 
00111   const ObjCInterfaceDecl *ID = D->getClassInterface();
00112   IvarUsageMap M;
00113 
00114   // Iterate over the ivars.
00115   for (const auto *Ivar : ID->ivars()) {
00116     // Ignore ivars that...
00117     // (a) aren't private
00118     // (b) explicitly marked unused
00119     // (c) are iboutlets
00120     // (d) are unnamed bitfields
00121     if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
00122         Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
00123         Ivar->hasAttr<IBOutletCollectionAttr>() ||
00124         Ivar->isUnnamedBitfield())
00125       continue;
00126 
00127     M[Ivar] = Unused;
00128   }
00129 
00130   if (M.empty())
00131     return;
00132 
00133   // Now scan the implementation declaration.
00134   Scan(M, D);
00135 
00136   // Any potentially unused ivars?
00137   bool hasUnused = false;
00138   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
00139     if (I->second == Unused) {
00140       hasUnused = true;
00141       break;
00142     }
00143 
00144   if (!hasUnused)
00145     return;
00146 
00147   // We found some potentially unused ivars.  Scan the entire translation unit
00148   // for functions inside the @implementation that reference these ivars.
00149   // FIXME: In the future hopefully we can just use the lexical DeclContext
00150   // to go from the ObjCImplementationDecl to the lexically "nested"
00151   // C functions.
00152   SourceManager &SM = BR.getSourceManager();
00153   Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
00154 
00155   // Find ivars that are unused.
00156   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
00157     if (I->second == Unused) {
00158       std::string sbuf;
00159       llvm::raw_string_ostream os(sbuf);
00160       os << "Instance variable '" << *I->first << "' in class '" << *ID
00161          << "' is never used by the methods in its @implementation "
00162             "(although it may be used by category methods).";
00163 
00164       PathDiagnosticLocation L =
00165         PathDiagnosticLocation::create(I->first, BR.getSourceManager());
00166       BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
00167                          os.str(), L);
00168     }
00169 }
00170 
00171 //===----------------------------------------------------------------------===//
00172 // ObjCUnusedIvarsChecker
00173 //===----------------------------------------------------------------------===//
00174 
00175 namespace {
00176 class ObjCUnusedIvarsChecker : public Checker<
00177                                       check::ASTDecl<ObjCImplementationDecl> > {
00178 public:
00179   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
00180                     BugReporter &BR) const {
00181     checkObjCUnusedIvar(D, BR, this);
00182   }
00183 };
00184 }
00185 
00186 void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
00187   mgr.registerChecker<ObjCUnusedIvarsChecker>();
00188 }