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