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