clang  6.0.0svn
ObjCUnusedIVarsChecker.cpp
Go to the documentation of this file.
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"
26 
27 using namespace clang;
28 using namespace ento;
29 
30 enum IVarState { Unused, Used };
31 typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
32 
33 static 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))
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 (const Stmt *SubStmt : S->children())
61  Scan(M, SubStmt);
62 }
63 
64 static 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 
78 static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
79  // Scan the methods for accesses.
80  for (const auto *I : D->instance_methods())
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 (const auto *I : ID->property_impls())
87  Scan(M, I);
88 
89  // Scan the associated categories as well.
90  for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
91  if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
92  Scan(M, CID);
93  }
94  }
95 }
96 
97 static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
98  SourceManager &SM) {
99  for (const auto *I : C->decls())
100  if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
101  SourceLocation L = FD->getLocStart();
102  if (SM.getFileID(L) == FID)
103  Scan(M, FD->getBody());
104  }
105 }
106 
108  BugReporter &BR,
109  const CheckerBase *Checker) {
110 
111  const ObjCInterfaceDecl *ID = D->getClassInterface();
112  IvarUsageMap M;
113 
114  // Iterate over the ivars.
115  for (const auto *Ivar : ID->ivars()) {
116  // Ignore ivars that...
117  // (a) aren't private
118  // (b) explicitly marked unused
119  // (c) are iboutlets
120  // (d) are unnamed bitfields
121  if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
122  Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
123  Ivar->hasAttr<IBOutletCollectionAttr>() ||
124  Ivar->isUnnamedBitfield())
125  continue;
126 
127  M[Ivar] = Unused;
128  }
129 
130  if (M.empty())
131  return;
132 
133  // Now scan the implementation declaration.
134  Scan(M, D);
135 
136  // Any potentially unused ivars?
137  bool hasUnused = false;
138  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
139  if (I->second == Unused) {
140  hasUnused = true;
141  break;
142  }
143 
144  if (!hasUnused)
145  return;
146 
147  // We found some potentially unused ivars. Scan the entire translation unit
148  // for functions inside the @implementation that reference these ivars.
149  // FIXME: In the future hopefully we can just use the lexical DeclContext
150  // to go from the ObjCImplementationDecl to the lexically "nested"
151  // C functions.
153  Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
154 
155  // Find ivars that are unused.
156  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
157  if (I->second == Unused) {
158  std::string sbuf;
159  llvm::raw_string_ostream os(sbuf);
160  os << "Instance variable '" << *I->first << "' in class '" << *ID
161  << "' is never used by the methods in its @implementation "
162  "(although it may be used by category methods).";
163 
166  BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
167  os.str(), L);
168  }
169 }
170 
171 //===----------------------------------------------------------------------===//
172 // ObjCUnusedIvarsChecker
173 //===----------------------------------------------------------------------===//
174 
175 namespace {
176 class ObjCUnusedIvarsChecker : public Checker<
177  check::ASTDecl<ObjCImplementationDecl> > {
178 public:
179  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
180  BugReporter &BR) const {
181  checkObjCUnusedIvar(D, BR, this);
182  }
183 };
184 }
185 
186 void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
187  mgr.registerChecker<ObjCUnusedIvarsChecker>();
188 }
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2846
Stmt - This represents one statement.
Definition: Stmt.h:66
Defines the SourceManager interface.
static void Scan(IvarUsageMap &M, const Stmt *S)
ivar_range ivars() const
Definition: DeclObjC.h:1477
decl_range decls() const
decls_begin/decls_end - Iterate over the declarations stored in this context.
Definition: DeclBase.h:1576
instmeth_range instance_methods() const
Definition: DeclObjC.h:1072
const Expr *const * const_semantics_iterator
Definition: Expr.h:5036
ObjCContainerDecl - Represents a container for method declarations.
Definition: DeclObjC.h:986
child_range children()
Definition: Stmt.cpp:226
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
Represents an ObjC class declaration.
Definition: DeclObjC.h:1191
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2778
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR, const CheckerBase *Checker)
Expr - This represents one expression.
Definition: Expr.h:106
Defines the clang::LangOptions interface.
BlockExpr - Adaptor class for mixing a BlockDecl with expressions.
Definition: Expr.h:4851
DeclContext * getDeclContext()
Definition: DeclBase.h:425
const SourceManager & SM
Definition: Format.cpp:1337
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:403
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class...
Definition: Expr.h:868
CHECKER * registerChecker()
Used to register checkers.
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
Definition: Expr.h:4969
Encodes a location in the source.
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=None)
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2459
Dataflow Directional Tag Classes.
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1252
llvm::DenseMap< const ObjCIvarDecl *, IVarState > IvarUsageMap
FileID getFileID(SourceLocation SpellingLoc) const
Return the FileID for a SourceLocation.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2571
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:513
SourceManager & getSourceManager()
Definition: BugReporter.h:463
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1964
ObjCCategoryImplDecl - An object of this class encapsulates a category @implementation declaration...
Definition: DeclObjC.h:2518
This class handles loading and caching of source files into memory.
SourceLocation getLocation() const
Definition: DeclBase.h:416