1218887Sdim//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim//  This file defines a CheckObjCUnusedIvars, a checker that
11218887Sdim//  analyzes an Objective-C class's interface/implementation to determine if it
12218887Sdim//  has any ivars that are never accessed.
13218887Sdim//
14218887Sdim//===----------------------------------------------------------------------===//
15218887Sdim
16218887Sdim#include "ClangSACheckers.h"
17249423Sdim#include "clang/AST/Attr.h"
18249423Sdim#include "clang/AST/DeclObjC.h"
19249423Sdim#include "clang/AST/Expr.h"
20218887Sdim#include "clang/AST/ExprObjC.h"
21218887Sdim#include "clang/Basic/LangOptions.h"
22218887Sdim#include "clang/Basic/SourceManager.h"
23249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
24249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
25249423Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
26218887Sdim
27218887Sdimusing namespace clang;
28218887Sdimusing namespace ento;
29218887Sdim
30218887Sdimenum IVarState { Unused, Used };
31218887Sdimtypedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
32218887Sdim
33226633Sdimstatic void Scan(IvarUsageMap& M, const Stmt *S) {
34218887Sdim  if (!S)
35218887Sdim    return;
36218887Sdim
37218887Sdim  if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
38218887Sdim    const ObjCIvarDecl *D = Ex->getDecl();
39218887Sdim    IvarUsageMap::iterator I = M.find(D);
40218887Sdim    if (I != M.end())
41218887Sdim      I->second = Used;
42218887Sdim    return;
43218887Sdim  }
44218887Sdim
45218887Sdim  // Blocks can reference an instance variable of a class.
46218887Sdim  if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
47218887Sdim    Scan(M, BE->getBody());
48218887Sdim    return;
49218887Sdim  }
50218887Sdim
51239462Sdim  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
52239462Sdim    for (PseudoObjectExpr::const_semantics_iterator
53239462Sdim        i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
54239462Sdim      const Expr *sub = *i;
55239462Sdim      if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
56239462Sdim        sub = OVE->getSourceExpr();
57239462Sdim      Scan(M, sub);
58239462Sdim    }
59239462Sdim
60218887Sdim  for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
61218887Sdim    Scan(M, *I);
62218887Sdim}
63218887Sdim
64226633Sdimstatic void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
65218887Sdim  if (!D)
66218887Sdim    return;
67218887Sdim
68226633Sdim  const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
69218887Sdim
70218887Sdim  if (!ID)
71218887Sdim    return;
72218887Sdim
73218887Sdim  IvarUsageMap::iterator I = M.find(ID);
74218887Sdim  if (I != M.end())
75218887Sdim    I->second = Used;
76218887Sdim}
77218887Sdim
78226633Sdimstatic void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
79218887Sdim  // Scan the methods for accesses.
80218887Sdim  for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(),
81218887Sdim       E = D->instmeth_end(); I!=E; ++I)
82218887Sdim    Scan(M, (*I)->getBody());
83218887Sdim
84218887Sdim  if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
85218887Sdim    // Scan for @synthesized property methods that act as setters/getters
86218887Sdim    // to an ivar.
87218887Sdim    for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(),
88218887Sdim         E = ID->propimpl_end(); I!=E; ++I)
89218887Sdim      Scan(M, *I);
90218887Sdim
91218887Sdim    // Scan the associated categories as well.
92249423Sdim    for (ObjCInterfaceDecl::visible_categories_iterator
93249423Sdim           Cat = ID->getClassInterface()->visible_categories_begin(),
94249423Sdim           CatEnd = ID->getClassInterface()->visible_categories_end();
95249423Sdim         Cat != CatEnd; ++Cat) {
96249423Sdim      if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
97218887Sdim        Scan(M, CID);
98218887Sdim    }
99218887Sdim  }
100218887Sdim}
101218887Sdim
102218887Sdimstatic void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
103218887Sdim                 SourceManager &SM) {
104218887Sdim  for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end();
105218887Sdim       I!=E; ++I)
106218887Sdim    if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) {
107218887Sdim      SourceLocation L = FD->getLocStart();
108218887Sdim      if (SM.getFileID(L) == FID)
109218887Sdim        Scan(M, FD->getBody());
110218887Sdim    }
111218887Sdim}
112218887Sdim
113218887Sdimstatic void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
114218887Sdim                                BugReporter &BR) {
115218887Sdim
116226633Sdim  const ObjCInterfaceDecl *ID = D->getClassInterface();
117218887Sdim  IvarUsageMap M;
118218887Sdim
119218887Sdim  // Iterate over the ivars.
120218887Sdim  for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(),
121218887Sdim        E=ID->ivar_end(); I!=E; ++I) {
122218887Sdim
123226633Sdim    const ObjCIvarDecl *ID = *I;
124218887Sdim
125218887Sdim    // Ignore ivars that...
126218887Sdim    // (a) aren't private
127218887Sdim    // (b) explicitly marked unused
128218887Sdim    // (c) are iboutlets
129218887Sdim    // (d) are unnamed bitfields
130218887Sdim    if (ID->getAccessControl() != ObjCIvarDecl::Private ||
131218887Sdim        ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() ||
132218887Sdim        ID->getAttr<IBOutletCollectionAttr>() ||
133218887Sdim        ID->isUnnamedBitfield())
134218887Sdim      continue;
135218887Sdim
136218887Sdim    M[ID] = Unused;
137218887Sdim  }
138218887Sdim
139218887Sdim  if (M.empty())
140218887Sdim    return;
141218887Sdim
142218887Sdim  // Now scan the implementation declaration.
143218887Sdim  Scan(M, D);
144218887Sdim
145218887Sdim  // Any potentially unused ivars?
146218887Sdim  bool hasUnused = false;
147218887Sdim  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
148218887Sdim    if (I->second == Unused) {
149218887Sdim      hasUnused = true;
150218887Sdim      break;
151218887Sdim    }
152218887Sdim
153218887Sdim  if (!hasUnused)
154218887Sdim    return;
155218887Sdim
156218887Sdim  // We found some potentially unused ivars.  Scan the entire translation unit
157218887Sdim  // for functions inside the @implementation that reference these ivars.
158218887Sdim  // FIXME: In the future hopefully we can just use the lexical DeclContext
159218887Sdim  // to go from the ObjCImplementationDecl to the lexically "nested"
160218887Sdim  // C functions.
161218887Sdim  SourceManager &SM = BR.getSourceManager();
162218887Sdim  Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
163218887Sdim
164218887Sdim  // Find ivars that are unused.
165218887Sdim  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
166218887Sdim    if (I->second == Unused) {
167218887Sdim      std::string sbuf;
168218887Sdim      llvm::raw_string_ostream os(sbuf);
169226633Sdim      os << "Instance variable '" << *I->first << "' in class '" << *ID
170218887Sdim         << "' is never used by the methods in its @implementation "
171218887Sdim            "(although it may be used by category methods).";
172218887Sdim
173226633Sdim      PathDiagnosticLocation L =
174226633Sdim        PathDiagnosticLocation::create(I->first, BR.getSourceManager());
175234353Sdim      BR.EmitBasicReport(D, "Unused instance variable", "Optimization",
176226633Sdim                         os.str(), L);
177218887Sdim    }
178218887Sdim}
179218887Sdim
180218887Sdim//===----------------------------------------------------------------------===//
181218887Sdim// ObjCUnusedIvarsChecker
182218887Sdim//===----------------------------------------------------------------------===//
183218887Sdim
184218887Sdimnamespace {
185221345Sdimclass ObjCUnusedIvarsChecker : public Checker<
186218887Sdim                                      check::ASTDecl<ObjCImplementationDecl> > {
187218887Sdimpublic:
188218887Sdim  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
189218887Sdim                    BugReporter &BR) const {
190218887Sdim    checkObjCUnusedIvar(D, BR);
191218887Sdim  }
192218887Sdim};
193218887Sdim}
194218887Sdim
195218887Sdimvoid ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
196218887Sdim  mgr.registerChecker<ObjCUnusedIvarsChecker>();
197218887Sdim}
198