ObjCUnusedIVarsChecker.cpp revision ec8605f1d7ec846dbf51047bfd5c56d32d1ff91c
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//                     The LLVM Compiler Infrastructure
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// This file is distributed under the University of Illinois Open Source
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// License. See LICENSE.TXT for details.
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===//
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  This file defines a CheckObjCUnusedIvars, a checker that
119ab5563a3196760eb381d102cbb2bc0f7abc6a50Ben Murdoch//  analyzes an Objective-C class's interface/implementation to determine if it
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//  has any ivars that are never accessed.
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//===----------------------------------------------------------------------===//
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "ClangSACheckers.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/StaticAnalyzer/Core/Checker.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/AST/ExprObjC.h"
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/AST/Expr.h"
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/AST/DeclObjC.h"
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Basic/LangOptions.h"
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "clang/Basic/SourceManager.h"
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using namespace clang;
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using namespace ento;
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)enum IVarState { Unused, Used };
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void Scan(IvarUsageMap& M, const Stmt* S) {
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!S)
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const ObjCIvarDecl *D = Ex->getDecl();
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IvarUsageMap::iterator I = M.find(D);
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if (I != M.end())
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      I->second = Used;
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Blocks can reference an instance variable of a class.
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Scan(M, BE->getBody());
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I)
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Scan(M, *I);
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl* D) {
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!D)
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
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      BR.EmitBasicReport("Unused instance variable", "Optimization",
163                         os.str(), I->first->getLocation());
164    }
165}
166
167//===----------------------------------------------------------------------===//
168// ObjCUnusedIvarsChecker
169//===----------------------------------------------------------------------===//
170
171namespace {
172class ObjCUnusedIvarsChecker : public Checker<
173                                      check::ASTDecl<ObjCImplementationDecl> > {
174public:
175  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
176                    BugReporter &BR) const {
177    checkObjCUnusedIvar(D, BR);
178  }
179};
180}
181
182void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
183  mgr.registerChecker<ObjCUnusedIvarsChecker>();
184}
185