ObjCUnusedIVarsChecker.cpp revision d329724745b49f894b768d47275b7c2713106e89
1//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10//  This file defines a CheckObjCUnusedIvars, a checker that
11//  analyzes an Objective-C class's interface/implementation to determine if it
12//  has any ivars that are never accessed.
13//
14//===----------------------------------------------------------------------===//
15
16#include "ClangSACheckers.h"
17#include "clang/AST/Attr.h"
18#include "clang/AST/DeclObjC.h"
19#include "clang/AST/Expr.h"
20#include "clang/AST/ExprObjC.h"
21#include "clang/Basic/LangOptions.h"
22#include "clang/Basic/SourceManager.h"
23#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
24#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
25#include "clang/StaticAnalyzer/Core/Checker.h"
26
27using namespace clang;
28using namespace ento;
29
30enum IVarState { Unused, Used };
31typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
32
33static void Scan(IvarUsageMap& M, const Stmt *S) {
34  if (!S)
35    return;
36
37  if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
38    const ObjCIvarDecl *D = Ex->getDecl();
39    IvarUsageMap::iterator I = M.find(D);
40    if (I != M.end())
41      I->second = Used;
42    return;
43  }
44
45  // Blocks can reference an instance variable of a class.
46  if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
47    Scan(M, BE->getBody());
48    return;
49  }
50
51  if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
52    for (PseudoObjectExpr::const_semantics_iterator
53        i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
54      const Expr *sub = *i;
55      if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
56        sub = OVE->getSourceExpr();
57      Scan(M, sub);
58    }
59
60  for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
61    Scan(M, *I);
62}
63
64static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
65  if (!D)
66    return;
67
68  const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
69
70  if (!ID)
71    return;
72
73  IvarUsageMap::iterator I = M.find(ID);
74  if (I != M.end())
75    I->second = Used;
76}
77
78static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
79  // Scan the methods for accesses.
80  for (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(),
81       E = D->instmeth_end(); I!=E; ++I)
82    Scan(M, (*I)->getBody());
83
84  if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
85    // Scan for @synthesized property methods that act as setters/getters
86    // to an ivar.
87    for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(),
88         E = ID->propimpl_end(); I!=E; ++I)
89      Scan(M, *I);
90
91    // Scan the associated categories as well.
92    for (ObjCInterfaceDecl::visible_categories_iterator
93           Cat = ID->getClassInterface()->visible_categories_begin(),
94           CatEnd = ID->getClassInterface()->visible_categories_end();
95         Cat != CatEnd; ++Cat) {
96      if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
97        Scan(M, CID);
98    }
99  }
100}
101
102static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
103                 SourceManager &SM) {
104  for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end();
105       I!=E; ++I)
106    if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(*I)) {
107      SourceLocation L = FD->getLocStart();
108      if (SM.getFileID(L) == FID)
109        Scan(M, FD->getBody());
110    }
111}
112
113static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
114                                BugReporter &BR) {
115
116  const ObjCInterfaceDecl *ID = D->getClassInterface();
117  IvarUsageMap M;
118
119  // Iterate over the ivars.
120  for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(),
121        E=ID->ivar_end(); I!=E; ++I) {
122
123    const ObjCIvarDecl *ID = *I;
124
125    // Ignore ivars that...
126    // (a) aren't private
127    // (b) explicitly marked unused
128    // (c) are iboutlets
129    // (d) are unnamed bitfields
130    if (ID->getAccessControl() != ObjCIvarDecl::Private ||
131        ID->getAttr<UnusedAttr>() || ID->getAttr<IBOutletAttr>() ||
132        ID->getAttr<IBOutletCollectionAttr>() ||
133        ID->isUnnamedBitfield())
134      continue;
135
136    M[ID] = Unused;
137  }
138
139  if (M.empty())
140    return;
141
142  // Now scan the implementation declaration.
143  Scan(M, D);
144
145  // Any potentially unused ivars?
146  bool hasUnused = false;
147  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
148    if (I->second == Unused) {
149      hasUnused = true;
150      break;
151    }
152
153  if (!hasUnused)
154    return;
155
156  // We found some potentially unused ivars.  Scan the entire translation unit
157  // for functions inside the @implementation that reference these ivars.
158  // FIXME: In the future hopefully we can just use the lexical DeclContext
159  // to go from the ObjCImplementationDecl to the lexically "nested"
160  // C functions.
161  SourceManager &SM = BR.getSourceManager();
162  Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
163
164  // Find ivars that are unused.
165  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
166    if (I->second == Unused) {
167      std::string sbuf;
168      llvm::raw_string_ostream os(sbuf);
169      os << "Instance variable '" << *I->first << "' in class '" << *ID
170         << "' is never used by the methods in its @implementation "
171            "(although it may be used by category methods).";
172
173      PathDiagnosticLocation L =
174        PathDiagnosticLocation::create(I->first, BR.getSourceManager());
175      BR.EmitBasicReport(D, "Unused instance variable", "Optimization",
176                         os.str(), L);
177    }
178}
179
180//===----------------------------------------------------------------------===//
181// ObjCUnusedIvarsChecker
182//===----------------------------------------------------------------------===//
183
184namespace {
185class ObjCUnusedIvarsChecker : public Checker<
186                                      check::ASTDecl<ObjCImplementationDecl> > {
187public:
188  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
189                    BugReporter &BR) const {
190    checkObjCUnusedIvar(D, BR);
191  }
192};
193}
194
195void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
196  mgr.registerChecker<ObjCUnusedIvarsChecker>();
197}
198