clang  14.0.0git
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 CheckerManager &mgr) {
190  return true;
191 }
clang::ObjCInterfaceDecl
Represents an ObjC class declaration.
Definition: DeclObjC.h:1151
clang::ObjCPropertyImplDecl::getPropertyIvarDecl
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2824
string
string(SUBSTRING ${CMAKE_CURRENT_BINARY_DIR} 0 ${PATH_LIB_START} PATH_HEAD) string(SUBSTRING $
Definition: CMakeLists.txt:22
clang::DeclContext
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1347
clang::ObjCImplementationDecl
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2546
Used
@ Used
Definition: ObjCUnusedIVarsChecker.cpp:29
clang::SourceLocation
Encodes a location in the source.
Definition: SourceLocation.h:88
Attr.h
clang::ObjCContainerDecl::instance_methods
instmeth_range instance_methods() const
Definition: DeclObjC.h:1033
clang::ObjCPropertyImplDecl
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2751
SourceManager.h
clang::ObjCImplDecl::getClassInterface
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2434
clang::SourceManager
This class handles loading and caching of source files into memory.
Definition: SourceManager.h:626
clang::OpaqueValueExpr
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class.
Definition: Expr.h:1129
DeclObjC.h
BuiltinCheckerRegistration.h
clang::ento::PathDiagnosticLocation::create
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
Definition: PathDiagnostic.h:250
BugReporter.h
LangOptions.h
clang::PseudoObjectExpr::const_semantics_iterator
const typedef Expr *const * const_semantics_iterator
Definition: Expr.h:6162
clang::PseudoObjectExpr
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
Definition: Expr.h:6095
Expr.h
clang::ObjCIvarDecl::Private
@ Private
Definition: DeclObjC.h:1929
clang::ObjCCategoryImplDecl
ObjCCategoryImplDecl - An object of this class encapsulates a category @implementation declaration.
Definition: DeclObjC.h:2493
PathDiagnostic.h
ExprObjC.h
IvarUsageMap
llvm::DenseMap< const ObjCIvarDecl *, IVarState > IvarUsageMap
Definition: ObjCUnusedIVarsChecker.cpp:30
clang::BlockExpr
BlockExpr - Adaptor class for mixing a BlockDecl with expressions.
Definition: Expr.h:5965
Unused
@ Unused
Definition: ObjCUnusedIVarsChecker.cpp:29
IVarState
IVarState
Definition: ObjCUnusedIVarsChecker.cpp:29
Checker.h
clang::Builtin::ID
ID
Definition: Builtins.h:48
clang
Definition: CalledOnceCheck.h:17
checkObjCUnusedIvar
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR, const CheckerBase *Checker)
Definition: ObjCUnusedIVarsChecker.cpp:106
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:69
clang::ObjCIvarDecl
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1924
clang::FileID
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Definition: SourceLocation.h:40
Scan
static void Scan(IvarUsageMap &M, const Stmt *S)
Definition: ObjCUnusedIVarsChecker.cpp:32
clang::ObjCContainerDecl
ObjCContainerDecl - Represents a container for method declarations.
Definition: DeclObjC.h:948
clang::Expr
This represents one expression.
Definition: Expr.h:109
SM
#define SM(sm)
Definition: Cuda.cpp:78
clang::Decl::getLocation
SourceLocation getLocation() const
Definition: DeclBase.h:430
clang::ObjCIvarRefExpr
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:548
clang::Decl::getDeclContext
DeclContext * getDeclContext()
Definition: DeclBase.h:439