clang  10.0.0svn
ObjCUnusedIVarsChecker.cpp
Go to the documentation of this file.
1 //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a CheckObjCUnusedIvars, a checker that
10 // analyzes an Objective-C class's interface/implementation to determine if it
11 // has any ivars that are never accessed.
12 //
13 //===----------------------------------------------------------------------===//
14 
17 #include "clang/AST/Attr.h"
18 #include "clang/AST/DeclObjC.h"
19 #include "clang/AST/Expr.h"
20 #include "clang/AST/ExprObjC.h"
25 
26 using namespace clang;
27 using namespace ento;
28 
29 enum IVarState { Unused, Used };
30 typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
31 
32 static 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))
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 (const Stmt *SubStmt : S->children())
60  Scan(M, SubStmt);
61 }
62 
63 static 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 
77 static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
78  // Scan the methods for accesses.
79  for (const auto *I : D->instance_methods())
80  Scan(M, I->getBody());
81 
82  if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
83  // Scan for @synthesized property methods that act as setters/getters
84  // to an ivar.
85  for (const auto *I : ID->property_impls())
86  Scan(M, I);
87 
88  // Scan the associated categories as well.
89  for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
90  if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
91  Scan(M, CID);
92  }
93  }
94 }
95 
96 static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
97  const SourceManager &SM) {
98  for (const auto *I : C->decls())
99  if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
100  SourceLocation L = FD->getBeginLoc();
101  if (SM.getFileID(L) == FID)
102  Scan(M, FD->getBody());
103  }
104 }
105 
107  BugReporter &BR,
108  const CheckerBase *Checker) {
109 
110  const ObjCInterfaceDecl *ID = D->getClassInterface();
111  IvarUsageMap M;
112 
113  // Iterate over the ivars.
114  for (const auto *Ivar : ID->ivars()) {
115  // Ignore ivars that...
116  // (a) aren't private
117  // (b) explicitly marked unused
118  // (c) are iboutlets
119  // (d) are unnamed bitfields
120  if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
121  Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
122  Ivar->hasAttr<IBOutletCollectionAttr>() ||
123  Ivar->isUnnamedBitfield())
124  continue;
125 
126  M[Ivar] = Unused;
127  }
128 
129  if (M.empty())
130  return;
131 
132  // Now scan the implementation declaration.
133  Scan(M, D);
134 
135  // Any potentially unused ivars?
136  bool hasUnused = false;
137  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
138  if (I->second == Unused) {
139  hasUnused = true;
140  break;
141  }
142 
143  if (!hasUnused)
144  return;
145 
146  // We found some potentially unused ivars. Scan the entire translation unit
147  // for functions inside the @implementation that reference these ivars.
148  // FIXME: In the future hopefully we can just use the lexical DeclContext
149  // to go from the ObjCImplementationDecl to the lexically "nested"
150  // C functions.
151  const SourceManager &SM = BR.getSourceManager();
152  Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
153 
154  // Find ivars that are unused.
155  for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
156  if (I->second == Unused) {
157  std::string sbuf;
158  llvm::raw_string_ostream os(sbuf);
159  os << "Instance variable '" << *I->first << "' in class '" << *ID
160  << "' is never used by the methods in its @implementation "
161  "(although it may be used by category methods).";
162 
163  PathDiagnosticLocation L =
164  PathDiagnosticLocation::create(I->first, BR.getSourceManager());
165  BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
166  os.str(), L);
167  }
168 }
169 
170 //===----------------------------------------------------------------------===//
171 // ObjCUnusedIvarsChecker
172 //===----------------------------------------------------------------------===//
173 
174 namespace {
175 class ObjCUnusedIvarsChecker : public Checker<
176  check::ASTDecl<ObjCImplementationDecl> > {
177 public:
178  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
179  BugReporter &BR) const {
180  checkObjCUnusedIvar(D, BR, this);
181  }
182 };
183 }
184 
185 void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
186  mgr.registerChecker<ObjCUnusedIvarsChecker>();
187 }
188 
189 bool ento::shouldRegisterObjCUnusedIvarsChecker(const LangOptions &LO) {
190  return true;
191 }
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2826
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:1457
decl_range decls() const
decls_begin/decls_end - Iterate over the declarations stored in this context.
Definition: DeclBase.h:2028
instmeth_range instance_methods() const
Definition: DeclObjC.h:1053
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
const Expr *const * const_semantics_iterator
Definition: Expr.h:5740
ObjCContainerDecl - Represents a container for method declarations.
Definition: DeclObjC.h:968
child_range children()
Definition: Stmt.cpp:223
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:1171
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2758
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR, const CheckerBase *Checker)
This represents one expression.
Definition: Expr.h:108
Defines the clang::LangOptions interface.
BlockExpr - Adaptor class for mixing a BlockDecl with expressions.
Definition: Expr.h:5551
DeclContext * getDeclContext()
Definition: DeclBase.h:438
const SourceManager & SM
Definition: Format.cpp:1667
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class...
Definition: Expr.h:1050
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
Definition: Expr.h:5673
Encodes a location in the source.
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2439
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:1271
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:2551
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:546
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1944
ObjCCategoryImplDecl - An object of this class encapsulates a category @implementation declaration...
Definition: DeclObjC.h:2498
This class handles loading and caching of source files into memory.
SourceLocation getLocation() const
Definition: DeclBase.h:429