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