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