1218887Sdim//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==// 2218887Sdim// 3218887Sdim// The LLVM Compiler Infrastructure 4218887Sdim// 5218887Sdim// This file is distributed under the University of Illinois Open Source 6218887Sdim// License. See LICENSE.TXT for details. 7218887Sdim// 8218887Sdim//===----------------------------------------------------------------------===// 9218887Sdim// 10218887Sdim// This file defines a CheckObjCUnusedIvars, a checker that 11218887Sdim// analyzes an Objective-C class's interface/implementation to determine if it 12218887Sdim// has any ivars that are never accessed. 13218887Sdim// 14218887Sdim//===----------------------------------------------------------------------===// 15218887Sdim 16218887Sdim#include "ClangSACheckers.h" 17249423Sdim#include "clang/AST/Attr.h" 18249423Sdim#include "clang/AST/DeclObjC.h" 19249423Sdim#include "clang/AST/Expr.h" 20218887Sdim#include "clang/AST/ExprObjC.h" 21218887Sdim#include "clang/Basic/LangOptions.h" 22218887Sdim#include "clang/Basic/SourceManager.h" 23249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 24249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 25249423Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 26218887Sdim 27218887Sdimusing namespace clang; 28218887Sdimusing namespace ento; 29218887Sdim 30218887Sdimenum IVarState { Unused, Used }; 31218887Sdimtypedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; 32218887Sdim 33226633Sdimstatic void Scan(IvarUsageMap& M, const Stmt *S) { 34218887Sdim if (!S) 35218887Sdim return; 36218887Sdim 37218887Sdim if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { 38218887Sdim const ObjCIvarDecl *D = Ex->getDecl(); 39218887Sdim IvarUsageMap::iterator I = M.find(D); 40218887Sdim if (I != M.end()) 41218887Sdim I->second = Used; 42218887Sdim return; 43218887Sdim } 44218887Sdim 45218887Sdim // Blocks can reference an instance variable of a class. 46218887Sdim if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { 47218887Sdim Scan(M, BE->getBody()); 48218887Sdim return; 49218887Sdim } 50218887Sdim 51239462Sdim if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S)) 52239462Sdim for (PseudoObjectExpr::const_semantics_iterator 53239462Sdim i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { 54239462Sdim const Expr *sub = *i; 55239462Sdim if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) 56239462Sdim sub = OVE->getSourceExpr(); 57239462Sdim Scan(M, sub); 58239462Sdim } 59239462Sdim 60218887Sdim for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) 61218887Sdim Scan(M, *I); 62218887Sdim} 63218887Sdim 64226633Sdimstatic void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) { 65218887Sdim if (!D) 66218887Sdim return; 67218887Sdim 68226633Sdim const ObjCIvarDecl *ID = D->getPropertyIvarDecl(); 69218887Sdim 70218887Sdim if (!ID) 71218887Sdim return; 72218887Sdim 73218887Sdim IvarUsageMap::iterator I = M.find(ID); 74218887Sdim if (I != M.end()) 75218887Sdim I->second = Used; 76218887Sdim} 77218887Sdim 78226633Sdimstatic void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) { 79218887Sdim // Scan the methods for accesses. 80218887Sdim for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), 81218887Sdim E = D->instmeth_end(); I!=E; ++I) 82218887Sdim Scan(M, (*I)->getBody()); 83218887Sdim 84218887Sdim if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { 85218887Sdim // Scan for @synthesized property methods that act as setters/getters 86218887Sdim // to an ivar. 87218887Sdim for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), 88218887Sdim E = ID->propimpl_end(); I!=E; ++I) 89218887Sdim Scan(M, *I); 90218887Sdim 91218887Sdim // Scan the associated categories as well. 92249423Sdim for (ObjCInterfaceDecl::visible_categories_iterator 93249423Sdim Cat = ID->getClassInterface()->visible_categories_begin(), 94249423Sdim CatEnd = ID->getClassInterface()->visible_categories_end(); 95249423Sdim Cat != CatEnd; ++Cat) { 96249423Sdim if (const ObjCCategoryImplDecl *CID = Cat->getImplementation()) 97218887Sdim Scan(M, CID); 98218887Sdim } 99218887Sdim } 100218887Sdim} 101218887Sdim 102218887Sdimstatic void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, 103218887Sdim SourceManager &SM) { 104218887Sdim for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); 105218887Sdim I!=E; ++I) 106218887Sdim if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { 107218887Sdim SourceLocation L = FD->getLocStart(); 108218887Sdim if (SM.getFileID(L) == FID) 109218887Sdim Scan(M, FD->getBody()); 110218887Sdim } 111218887Sdim} 112218887Sdim 113218887Sdimstatic void checkObjCUnusedIvar(const ObjCImplementationDecl *D, 114218887Sdim BugReporter &BR) { 115218887Sdim 116226633Sdim const ObjCInterfaceDecl *ID = D->getClassInterface(); 117218887Sdim IvarUsageMap M; 118218887Sdim 119218887Sdim // Iterate over the ivars. 120218887Sdim for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), 121218887Sdim E=ID->ivar_end(); I!=E; ++I) { 122218887Sdim 123226633Sdim const ObjCIvarDecl *ID = *I; 124218887Sdim 125218887Sdim // Ignore ivars that... 126218887Sdim // (a) aren't private 127218887Sdim // (b) explicitly marked unused 128218887Sdim // (c) are iboutlets 129218887Sdim // (d) are unnamed bitfields 130218887Sdim if (ID->getAccessControl() != ObjCIvarDecl::Private || 131218887Sdim ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() || 132218887Sdim ID->getAttr<IBOutletCollectionAttr>() || 133218887Sdim ID->isUnnamedBitfield()) 134218887Sdim continue; 135218887Sdim 136218887Sdim M[ID] = Unused; 137218887Sdim } 138218887Sdim 139218887Sdim if (M.empty()) 140218887Sdim return; 141218887Sdim 142218887Sdim // Now scan the implementation declaration. 143218887Sdim Scan(M, D); 144218887Sdim 145218887Sdim // Any potentially unused ivars? 146218887Sdim bool hasUnused = false; 147218887Sdim for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 148218887Sdim if (I->second == Unused) { 149218887Sdim hasUnused = true; 150218887Sdim break; 151218887Sdim } 152218887Sdim 153218887Sdim if (!hasUnused) 154218887Sdim return; 155218887Sdim 156218887Sdim // We found some potentially unused ivars. Scan the entire translation unit 157218887Sdim // for functions inside the @implementation that reference these ivars. 158218887Sdim // FIXME: In the future hopefully we can just use the lexical DeclContext 159218887Sdim // to go from the ObjCImplementationDecl to the lexically "nested" 160218887Sdim // C functions. 161218887Sdim SourceManager &SM = BR.getSourceManager(); 162218887Sdim Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); 163218887Sdim 164218887Sdim // Find ivars that are unused. 165218887Sdim for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 166218887Sdim if (I->second == Unused) { 167218887Sdim std::string sbuf; 168218887Sdim llvm::raw_string_ostream os(sbuf); 169226633Sdim os << "Instance variable '" << *I->first << "' in class '" << *ID 170218887Sdim << "' is never used by the methods in its @implementation " 171218887Sdim "(although it may be used by category methods)."; 172218887Sdim 173226633Sdim PathDiagnosticLocation L = 174226633Sdim PathDiagnosticLocation::create(I->first, BR.getSourceManager()); 175234353Sdim BR.EmitBasicReport(D, "Unused instance variable", "Optimization", 176226633Sdim os.str(), L); 177218887Sdim } 178218887Sdim} 179218887Sdim 180218887Sdim//===----------------------------------------------------------------------===// 181218887Sdim// ObjCUnusedIvarsChecker 182218887Sdim//===----------------------------------------------------------------------===// 183218887Sdim 184218887Sdimnamespace { 185221345Sdimclass ObjCUnusedIvarsChecker : public Checker< 186218887Sdim check::ASTDecl<ObjCImplementationDecl> > { 187218887Sdimpublic: 188218887Sdim void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 189218887Sdim BugReporter &BR) const { 190218887Sdim checkObjCUnusedIvar(D, BR); 191218887Sdim } 192218887Sdim}; 193218887Sdim} 194218887Sdim 195218887Sdimvoid ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) { 196218887Sdim mgr.registerChecker<ObjCUnusedIvarsChecker>(); 197218887Sdim} 198