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