ObjCUnusedIVarsChecker.cpp revision ec8605f1d7ec846dbf51047bfd5c56d32d1ff91c
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==// 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// The LLVM Compiler Infrastructure 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file is distributed under the University of Illinois Open Source 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// License. See LICENSE.TXT for details. 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===// 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file defines a CheckObjCUnusedIvars, a checker that 119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch// analyzes an Objective-C class's interface/implementation to determine if it 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// has any ivars that are never accessed. 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===// 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ClangSACheckers.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/StaticAnalyzer/Core/Checker.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/AST/ExprObjC.h" 215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/AST/Expr.h" 225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/AST/DeclObjC.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Basic/LangOptions.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Basic/SourceManager.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using namespace clang; 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using namespace ento; 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum IVarState { Unused, Used }; 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; 31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void Scan(IvarUsageMap& M, const Stmt* S) { 33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (!S) 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) const ObjCIvarDecl *D = Ex->getDecl(); 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) IvarUsageMap::iterator I = M.find(D); 39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles) if (I != M.end()) 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) I->second = Used; 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) // Blocks can reference an instance variable of a class. 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Scan(M, BE->getBody()); 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) } 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) 515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) Scan(M, *I); 525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)} 535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { 555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) if (!D) 565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) return; 575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 58 const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); 59 60 if (!ID) 61 return; 62 63 IvarUsageMap::iterator I = M.find(ID); 64 if (I != M.end()) 65 I->second = Used; 66} 67 68static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { 69 // Scan the methods for accesses. 70 for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), 71 E = D->instmeth_end(); I!=E; ++I) 72 Scan(M, (*I)->getBody()); 73 74 if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { 75 // Scan for @synthesized property methods that act as setters/getters 76 // to an ivar. 77 for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), 78 E = ID->propimpl_end(); I!=E; ++I) 79 Scan(M, *I); 80 81 // Scan the associated categories as well. 82 for (const ObjCCategoryDecl *CD = 83 ID->getClassInterface()->getCategoryList(); CD ; 84 CD = CD->getNextClassCategory()) { 85 if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) 86 Scan(M, CID); 87 } 88 } 89} 90 91static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, 92 SourceManager &SM) { 93 for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); 94 I!=E; ++I) 95 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { 96 SourceLocation L = FD->getLocStart(); 97 if (SM.getFileID(L) == FID) 98 Scan(M, FD->getBody()); 99 } 100} 101 102static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, 103 BugReporter &BR) { 104 105 const ObjCInterfaceDecl* ID = D->getClassInterface(); 106 IvarUsageMap M; 107 108 // Iterate over the ivars. 109 for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), 110 E=ID->ivar_end(); I!=E; ++I) { 111 112 const ObjCIvarDecl* ID = *I; 113 114 // Ignore ivars that... 115 // (a) aren't private 116 // (b) explicitly marked unused 117 // (c) are iboutlets 118 // (d) are unnamed bitfields 119 if (ID->getAccessControl() != ObjCIvarDecl::Private || 120 ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() || 121 ID->getAttr<IBOutletCollectionAttr>() || 122 ID->isUnnamedBitfield()) 123 continue; 124 125 M[ID] = Unused; 126 } 127 128 if (M.empty()) 129 return; 130 131 // Now scan the implementation declaration. 132 Scan(M, D); 133 134 // Any potentially unused ivars? 135 bool hasUnused = false; 136 for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 137 if (I->second == Unused) { 138 hasUnused = true; 139 break; 140 } 141 142 if (!hasUnused) 143 return; 144 145 // We found some potentially unused ivars. Scan the entire translation unit 146 // for functions inside the @implementation that reference these ivars. 147 // FIXME: In the future hopefully we can just use the lexical DeclContext 148 // to go from the ObjCImplementationDecl to the lexically "nested" 149 // C functions. 150 SourceManager &SM = BR.getSourceManager(); 151 Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); 152 153 // Find ivars that are unused. 154 for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 155 if (I->second == Unused) { 156 std::string sbuf; 157 llvm::raw_string_ostream os(sbuf); 158 os << "Instance variable '" << I->first << "' in class '" << ID 159 << "' is never used by the methods in its @implementation " 160 "(although it may be used by category methods)."; 161 162 BR.EmitBasicReport("Unused instance variable", "Optimization", 163 os.str(), I->first->getLocation()); 164 } 165} 166 167//===----------------------------------------------------------------------===// 168// ObjCUnusedIvarsChecker 169//===----------------------------------------------------------------------===// 170 171namespace { 172class ObjCUnusedIvarsChecker : public Checker< 173 check::ASTDecl<ObjCImplementationDecl> > { 174public: 175 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 176 BugReporter &BR) const { 177 checkObjCUnusedIvar(D, BR); 178 } 179}; 180} 181 182void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) { 183 mgr.registerChecker<ObjCUnusedIvarsChecker>(); 184} 185