|  | //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | //  This file defines a CheckObjCUnusedIvars, a checker that | 
|  | //  analyzes an Objective-C class's interface/implementation to determine if it | 
|  | //  has any ivars that are never accessed. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "clang/Checker/Checkers/LocalCheckers.h" | 
|  | #include "clang/Checker/BugReporter/PathDiagnostic.h" | 
|  | #include "clang/Checker/BugReporter/BugReporter.h" | 
|  | #include "clang/AST/ExprObjC.h" | 
|  | #include "clang/AST/Expr.h" | 
|  | #include "clang/AST/DeclObjC.h" | 
|  | #include "clang/Basic/LangOptions.h" | 
|  | #include "clang/Basic/SourceManager.h" | 
|  |  | 
|  | using namespace clang; | 
|  |  | 
|  | enum IVarState { Unused, Used }; | 
|  | typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; | 
|  |  | 
|  | static void Scan(IvarUsageMap& M, const Stmt* S) { | 
|  | if (!S) | 
|  | return; | 
|  |  | 
|  | if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { | 
|  | const ObjCIvarDecl *D = Ex->getDecl(); | 
|  | IvarUsageMap::iterator I = M.find(D); | 
|  | if (I != M.end()) | 
|  | I->second = Used; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Blocks can reference an instance variable of a class. | 
|  | if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { | 
|  | Scan(M, BE->getBody()); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) | 
|  | Scan(M, *I); | 
|  | } | 
|  |  | 
|  | static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { | 
|  | if (!D) | 
|  | return; | 
|  |  | 
|  | const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); | 
|  |  | 
|  | if (!ID) | 
|  | return; | 
|  |  | 
|  | IvarUsageMap::iterator I = M.find(ID); | 
|  | if (I != M.end()) | 
|  | I->second = Used; | 
|  | } | 
|  |  | 
|  | static void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { | 
|  | // Scan the methods for accesses. | 
|  | for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), | 
|  | E = D->instmeth_end(); I!=E; ++I) | 
|  | Scan(M, (*I)->getBody()); | 
|  |  | 
|  | if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { | 
|  | // Scan for @synthesized property methods that act as setters/getters | 
|  | // to an ivar. | 
|  | for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), | 
|  | E = ID->propimpl_end(); I!=E; ++I) | 
|  | Scan(M, *I); | 
|  |  | 
|  | // Scan the associated categories as well. | 
|  | for (const ObjCCategoryDecl *CD = | 
|  | ID->getClassInterface()->getCategoryList(); CD ; | 
|  | CD = CD->getNextClassCategory()) { | 
|  | if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) | 
|  | Scan(M, CID); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, | 
|  | SourceManager &SM) { | 
|  | for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); | 
|  | I!=E; ++I) | 
|  | if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { | 
|  | SourceLocation L = FD->getLocStart(); | 
|  | if (SM.getFileID(L) == FID) | 
|  | Scan(M, FD->getBody()); | 
|  | } | 
|  | } | 
|  |  | 
|  | void clang::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, | 
|  | BugReporter &BR) { | 
|  |  | 
|  | const ObjCInterfaceDecl* ID = D->getClassInterface(); | 
|  | IvarUsageMap M; | 
|  |  | 
|  | // Iterate over the ivars. | 
|  | for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), | 
|  | E=ID->ivar_end(); I!=E; ++I) { | 
|  |  | 
|  | const ObjCIvarDecl* ID = *I; | 
|  |  | 
|  | // Ignore ivars that... | 
|  | // (a) aren't private | 
|  | // (b) explicitly marked unused | 
|  | // (c) are iboutlets | 
|  | if (ID->getAccessControl() != ObjCIvarDecl::Private || | 
|  | ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>()) | 
|  | continue; | 
|  |  | 
|  | M[ID] = Unused; | 
|  | } | 
|  |  | 
|  | if (M.empty()) | 
|  | return; | 
|  |  | 
|  | // Now scan the implementation declaration. | 
|  | Scan(M, D); | 
|  |  | 
|  | // Any potentially unused ivars? | 
|  | bool hasUnused = false; | 
|  | for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) | 
|  | if (I->second == Unused) { | 
|  | hasUnused = true; | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (!hasUnused) | 
|  | return; | 
|  |  | 
|  | // We found some potentially unused ivars.  Scan the entire translation unit | 
|  | // for functions inside the @implementation that reference these ivars. | 
|  | // FIXME: In the future hopefully we can just use the lexical DeclContext | 
|  | // to go from the ObjCImplementationDecl to the lexically "nested" | 
|  | // C functions. | 
|  | SourceManager &SM = BR.getSourceManager(); | 
|  | Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); | 
|  |  | 
|  | // Find ivars that are unused. | 
|  | for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) | 
|  | if (I->second == Unused) { | 
|  | std::string sbuf; | 
|  | llvm::raw_string_ostream os(sbuf); | 
|  | os << "Instance variable '" << I->first << "' in class '" << ID | 
|  | << "' is never used by the methods in its @implementation " | 
|  | "(although it may be used by category methods)."; | 
|  |  | 
|  | BR.EmitBasicReport("Unused instance variable", "Optimization", | 
|  | os.str(), I->first->getLocation()); | 
|  | } | 
|  | } |