ObjCUnusedIVarsChecker.cpp revision 55fc873017f10f6f566b182b70f6fc22aefa3464
1e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==// 2395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// 3395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// The LLVM Compiler Infrastructure 4395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// 5395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// This file is distributed under the University of Illinois Open Source 6395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// License. See LICENSE.TXT for details. 7395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// 8395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek//===----------------------------------------------------------------------===// 9395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// 10395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// This file defines a CheckObjCUnusedIvars, a checker that 11395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// analyzes an Objective-C class's interface/implementation to determine if it 12395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// has any ivars that are never accessed. 13395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek// 14395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek//===----------------------------------------------------------------------===// 15395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 167dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis#include "ClangSACheckers.h" 172fa67efeaf66a9332c30a026dc1c21bef6c33a6cBenjamin Kramer#include "clang/AST/Attr.h" 18395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek#include "clang/AST/DeclObjC.h" 192fa67efeaf66a9332c30a026dc1c21bef6c33a6cBenjamin Kramer#include "clang/AST/Expr.h" 202fa67efeaf66a9332c30a026dc1c21bef6c33a6cBenjamin Kramer#include "clang/AST/ExprObjC.h" 21395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek#include "clang/Basic/LangOptions.h" 2258652887b08da3207226ec6d8bdb59ec0e96edfeBenjamin Kramer#include "clang/Basic/SourceManager.h" 2355fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 2455fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 2555fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/Checker.h" 26395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 27395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenekusing namespace clang; 289ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekusing namespace ento; 29395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 30395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenekenum IVarState { Unused, Used }; 310b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenektypedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; 32395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 339c378f705405d37f49795d5e915989de774fe11fTed Kremenekstatic void Scan(IvarUsageMap& M, const Stmt *S) { 34395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek if (!S) 35395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek return; 361eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 3735ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { 3835ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek const ObjCIvarDecl *D = Ex->getDecl(); 39395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek IvarUsageMap::iterator I = M.find(D); 400b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek if (I != M.end()) 410b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek I->second = Used; 42694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek return; 43395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek } 441eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 4535ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek // Blocks can reference an instance variable of a class. 4635ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { 4735ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek Scan(M, BE->getBody()); 4835ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek return; 4935ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek } 5035ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek 515ec351c9507f12d5bede569c51d5257fad167134Anna Zaks if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S)) 525ec351c9507f12d5bede569c51d5257fad167134Anna Zaks for (PseudoObjectExpr::const_semantics_iterator 535ec351c9507f12d5bede569c51d5257fad167134Anna Zaks i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { 545ec351c9507f12d5bede569c51d5257fad167134Anna Zaks const Expr *sub = *i; 555ec351c9507f12d5bede569c51d5257fad167134Anna Zaks if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) 565ec351c9507f12d5bede569c51d5257fad167134Anna Zaks sub = OVE->getSourceExpr(); 575ec351c9507f12d5bede569c51d5257fad167134Anna Zaks Scan(M, sub); 585ec351c9507f12d5bede569c51d5257fad167134Anna Zaks } 595ec351c9507f12d5bede569c51d5257fad167134Anna Zaks 600b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) 61694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek Scan(M, *I); 62694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek} 63694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek 649c378f705405d37f49795d5e915989de774fe11fTed Kremenekstatic void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) { 65694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek if (!D) 66694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek return; 671eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 689c378f705405d37f49795d5e915989de774fe11fTed Kremenek const ObjCIvarDecl *ID = D->getPropertyIvarDecl(); 69694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek 70694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek if (!ID) 71694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek return; 721eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 73694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek IvarUsageMap::iterator I = M.find(ID); 740b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek if (I != M.end()) 750b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek I->second = Used; 76395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek} 77395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 789c378f705405d37f49795d5e915989de774fe11fTed Kremenekstatic void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) { 798fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // Scan the methods for accesses. 808fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), 818fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek E = D->instmeth_end(); I!=E; ++I) 828fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek Scan(M, (*I)->getBody()); 83e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 84e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { 858fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // Scan for @synthesized property methods that act as setters/getters 868fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // to an ivar. 878fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), 888fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek E = ID->propimpl_end(); I!=E; ++I) 89581deb3da481053c4993c7600f97acf7768caac5David Blaikie Scan(M, *I); 90e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 91e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek // Scan the associated categories as well. 92e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek for (const ObjCCategoryDecl *CD = 93e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek ID->getClassInterface()->getCategoryList(); CD ; 94e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek CD = CD->getNextClassCategory()) { 95e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) 96e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek Scan(M, CID); 97e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek } 988fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek } 998fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek} 1008fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek 101b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenekstatic void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, 102b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek SourceManager &SM) { 103b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); 104b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek I!=E; ++I) 105b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { 106b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek SourceLocation L = FD->getLocStart(); 107e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek if (SM.getFileID(L) == FID) 108b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek Scan(M, FD->getBody()); 109b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek } 110b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek} 111b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek 1127dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidisstatic void checkObjCUnusedIvar(const ObjCImplementationDecl *D, 1132376002038c8b904acd20be754aedd1a7471be71Ted Kremenek BugReporter &BR) { 114395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 1159c378f705405d37f49795d5e915989de774fe11fTed Kremenek const ObjCInterfaceDecl *ID = D->getClassInterface(); 116395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek IvarUsageMap M; 117395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 118395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek // Iterate over the ivars. 1190b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), 1200b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek E=ID->ivar_end(); I!=E; ++I) { 1211eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 122581deb3da481053c4993c7600f97acf7768caac5David Blaikie const ObjCIvarDecl *ID = *I; 1231eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 124e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // Ignore ivars that... 125e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // (a) aren't private 126e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // (b) explicitly marked unused 127e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // (c) are iboutlets 128ed50a8a7a8b5fbb5d365b39c81ec389e19e4360eTed Kremenek // (d) are unnamed bitfields 129e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek if (ID->getAccessControl() != ObjCIvarDecl::Private || 130857e918a8a40deb128840308a318bf623d68295fTed Kremenek ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() || 131ed50a8a7a8b5fbb5d365b39c81ec389e19e4360eTed Kremenek ID->getAttr<IBOutletCollectionAttr>() || 132ed50a8a7a8b5fbb5d365b39c81ec389e19e4360eTed Kremenek ID->isUnnamedBitfield()) 133395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek continue; 1341eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 135395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek M[ID] = Unused; 136395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek } 137395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 138395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek if (M.empty()) 139395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek return; 140e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 1418fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // Now scan the implementation declaration. 1428fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek Scan(M, D); 1431eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 144b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // Any potentially unused ivars? 145b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek bool hasUnused = false; 146b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 147b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek if (I->second == Unused) { 148b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek hasUnused = true; 149b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek break; 150b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek } 151e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 152b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek if (!hasUnused) 153b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek return; 154e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 155b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // We found some potentially unused ivars. Scan the entire translation unit 156b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // for functions inside the @implementation that reference these ivars. 157b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // FIXME: In the future hopefully we can just use the lexical DeclContext 158b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // to go from the ObjCImplementationDecl to the lexically "nested" 159b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // C functions. 160b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek SourceManager &SM = BR.getSourceManager(); 161b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); 162b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek 163395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek // Find ivars that are unused. 164395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 165395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek if (I->second == Unused) { 16653ba0b636194dbeaa65a6f85316c9397a0c5298bTed Kremenek std::string sbuf; 16753ba0b636194dbeaa65a6f85316c9397a0c5298bTed Kremenek llvm::raw_string_ostream os(sbuf); 168b8989f27f116ff2400e92a52c067a69846119eb5Benjamin Kramer os << "Instance variable '" << *I->first << "' in class '" << *ID 169694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek << "' is never used by the methods in its @implementation " 170694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek "(although it may be used by category methods)."; 171cc87ba2b950cfef2ef43019627330975a7daf73aTed Kremenek 172590dd8e0959d8df5621827768987c4792b74fc06Anna Zaks PathDiagnosticLocation L = 173590dd8e0959d8df5621827768987c4792b74fc06Anna Zaks PathDiagnosticLocation::create(I->first, BR.getSourceManager()); 17407189521a15d9c088216b943649cb9fe231cbb57Ted Kremenek BR.EmitBasicReport(D, "Unused instance variable", "Optimization", 175590dd8e0959d8df5621827768987c4792b74fc06Anna Zaks os.str(), L); 176395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek } 177395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek} 1787dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis 1797dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis//===----------------------------------------------------------------------===// 1807dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis// ObjCUnusedIvarsChecker 1817dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis//===----------------------------------------------------------------------===// 1827dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis 1837dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidisnamespace { 184ec8605f1d7ec846dbf51047bfd5c56d32d1ff91cArgyrios Kyrtzidisclass ObjCUnusedIvarsChecker : public Checker< 1857dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis check::ASTDecl<ObjCImplementationDecl> > { 1867dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidispublic: 1877dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, 1887dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis BugReporter &BR) const { 1897dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis checkObjCUnusedIvar(D, BR); 1907dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis } 1917dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis}; 1927dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis} 1937dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis 1947dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidisvoid ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) { 1957dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis mgr.registerChecker<ObjCUnusedIvarsChecker>(); 1967dd445ec20e704846cfbdb132e56539280d71311Argyrios Kyrtzidis} 197