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