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