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