ObjCUnusedIVarsChecker.cpp revision 9b663716449b618ba0390b1dbebc54fa8e971124
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 1621142581d55918beed544a757e4af3bb865b1812Ted Kremenek#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" 179b663716449b618ba0390b1dbebc54fa8e971124Ted Kremenek#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 189b663716449b618ba0390b1dbebc54fa8e971124Ted Kremenek#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek#include "clang/AST/ExprObjC.h" 20395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek#include "clang/AST/Expr.h" 21395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek#include "clang/AST/DeclObjC.h" 22395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek#include "clang/Basic/LangOptions.h" 2358652887b08da3207226ec6d8bdb59ec0e96edfeBenjamin Kramer#include "clang/Basic/SourceManager.h" 24395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 25395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenekusing namespace clang; 269ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekusing namespace ento; 27395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 28395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenekenum IVarState { Unused, Used }; 290b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenektypedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; 30395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 310b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenekstatic void Scan(IvarUsageMap& M, const Stmt* S) { 32395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek if (!S) 33395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek return; 341eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 3535ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { 3635ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek const ObjCIvarDecl *D = Ex->getDecl(); 37395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek IvarUsageMap::iterator I = M.find(D); 380b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek if (I != M.end()) 390b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek I->second = Used; 40694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek return; 41395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek } 421eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 4335ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek // Blocks can reference an instance variable of a class. 4435ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { 4535ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek Scan(M, BE->getBody()); 4635ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek return; 4735ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek } 4835ffcf3c2a054ee124fe8d47152c5d1bcdf86261Ted Kremenek 490b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) 50694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek Scan(M, *I); 51694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek} 52694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek 530b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenekstatic void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) { 54694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek if (!D) 55694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek return; 561eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 570b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek const ObjCIvarDecl* ID = D->getPropertyIvarDecl(); 58694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek 59694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek if (!ID) 60694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek return; 611eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 62694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek IvarUsageMap::iterator I = M.find(ID); 630b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek if (I != M.end()) 640b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek I->second = Used; 65395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek} 66395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 678fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenekstatic void Scan(IvarUsageMap& M, const ObjCContainerDecl* D) { 688fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // Scan the methods for accesses. 698fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), 708fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek E = D->instmeth_end(); I!=E; ++I) 718fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek Scan(M, (*I)->getBody()); 72e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 73e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { 748fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // Scan for @synthesized property methods that act as setters/getters 758fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // to an ivar. 768fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), 778fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek E = ID->propimpl_end(); I!=E; ++I) 788fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek Scan(M, *I); 79e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 80e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek // Scan the associated categories as well. 81e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek for (const ObjCCategoryDecl *CD = 82e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek ID->getClassInterface()->getCategoryList(); CD ; 83e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek CD = CD->getNextClassCategory()) { 84e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) 85e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek Scan(M, CID); 86e8ec699167a7c3a2872feefd03e0ea2fabb980e0Ted Kremenek } 878fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek } 888fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek} 898fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek 90b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenekstatic void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, 91b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek SourceManager &SM) { 92b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); 93b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek I!=E; ++I) 94b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) { 95b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek SourceLocation L = FD->getLocStart(); 96e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek if (SM.getFileID(L) == FID) 97b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek Scan(M, FD->getBody()); 98b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek } 99b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek} 100b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek 1019ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekvoid ento::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, 1022376002038c8b904acd20be754aedd1a7471be71Ted Kremenek BugReporter &BR) { 103395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 1040b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek const ObjCInterfaceDecl* ID = D->getClassInterface(); 105395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek IvarUsageMap M; 106395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 107395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek // Iterate over the ivars. 1080b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), 1090b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek E=ID->ivar_end(); I!=E; ++I) { 1101eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 1110b2dd776318e612c682d32e1f4ca70f7b223c05eTed Kremenek const ObjCIvarDecl* ID = *I; 1121eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 113e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // Ignore ivars that... 114e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // (a) aren't private 115e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // (b) explicitly marked unused 116e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek // (c) are iboutlets 117ed50a8a7a8b5fbb5d365b39c81ec389e19e4360eTed Kremenek // (d) are unnamed bitfields 118e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek if (ID->getAccessControl() != ObjCIvarDecl::Private || 119857e918a8a40deb128840308a318bf623d68295fTed Kremenek ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() || 120ed50a8a7a8b5fbb5d365b39c81ec389e19e4360eTed Kremenek ID->getAttr<IBOutletCollectionAttr>() || 121ed50a8a7a8b5fbb5d365b39c81ec389e19e4360eTed Kremenek ID->isUnnamedBitfield()) 122395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek continue; 1231eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 124395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek M[ID] = Unused; 125395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek } 126395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek 127395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek if (M.empty()) 128395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek return; 129e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 1308fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek // Now scan the implementation declaration. 1318fa3a1a6732dda5e4bd8815cc64ab516da5c19fcTed Kremenek Scan(M, D); 1321eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump 133b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // Any potentially unused ivars? 134b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek bool hasUnused = false; 135b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 136b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek if (I->second == Unused) { 137b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek hasUnused = true; 138b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek break; 139b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek } 140e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 141b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek if (!hasUnused) 142b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek return; 143e3972a902d4a6f61fb21df092da2cace2b16cb3eTed Kremenek 144b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // We found some potentially unused ivars. Scan the entire translation unit 145b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // for functions inside the @implementation that reference these ivars. 146b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // FIXME: In the future hopefully we can just use the lexical DeclContext 147b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // to go from the ObjCImplementationDecl to the lexically "nested" 148b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek // C functions. 149b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek SourceManager &SM = BR.getSourceManager(); 150b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); 151b221e4fb46f6e35b0721399ed2734daadbcc1f00Ted Kremenek 152395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek // Find ivars that are unused. 153395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) 154395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek if (I->second == Unused) { 15553ba0b636194dbeaa65a6f85316c9397a0c5298bTed Kremenek std::string sbuf; 15653ba0b636194dbeaa65a6f85316c9397a0c5298bTed Kremenek llvm::raw_string_ostream os(sbuf); 157900fc6388e803868a34b9483510c345e9b49d7ebBenjamin Kramer os << "Instance variable '" << I->first << "' in class '" << ID 158694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek << "' is never used by the methods in its @implementation " 159694eefb7452dd650bae16e1026571ab5a43a74fcTed Kremenek "(although it may be used by category methods)."; 160cc87ba2b950cfef2ef43019627330975a7daf73aTed Kremenek 16113493ea1583f39d62a66e2b2a0802f08d8ec32caTed Kremenek BR.EmitBasicReport("Unused instance variable", "Optimization", 162f0171732efb4647772ad2a45c0f31978b0e34f71Benjamin Kramer os.str(), I->first->getLocation()); 163395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek } 164395aaf20d6e1ab04741562dc6b7d47164bcbd87eTed Kremenek} 165