clang 17.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
26using namespace clang;
27using namespace ento;
28
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 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
63static 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
77static 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
96static 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
165 BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
166 os.str(), L);
167 }
168}
169
170//===----------------------------------------------------------------------===//
171// ObjCUnusedIvarsChecker
172//===----------------------------------------------------------------------===//
173
174namespace {
175class ObjCUnusedIvarsChecker : public Checker<
176 check::ASTDecl<ObjCImplementationDecl> > {
177public:
178 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
179 BugReporter &BR) const {
180 checkObjCUnusedIvar(D, BR, this);
181 }
182};
183}
184
185void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
186 mgr.registerChecker<ObjCUnusedIvarsChecker>();
187}
188
189bool ento::shouldRegisterObjCUnusedIvarsChecker(const CheckerManager &mgr) {
190 return true;
191}
#define SM(sm)
Definition: Cuda.cpp:80
Defines the clang::LangOptions interface.
static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter &BR, const CheckerBase *Checker)
llvm::DenseMap< const ObjCIvarDecl *, IVarState > IvarUsageMap
static void Scan(IvarUsageMap &M, const Stmt *S)
Defines the SourceManager interface.
BlockExpr - Adaptor class for mixing a BlockDecl with expressions.
Definition: Expr.h:6025
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1402
SourceLocation getLocation() const
Definition: DeclBase.h:432
DeclContext * getDeclContext()
Definition: DeclBase.h:441
This represents one expression.
Definition: Expr.h:110
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
ObjCCategoryImplDecl - An object of this class encapsulates a category @implementation declaration.
Definition: DeclObjC.h:2531
ObjCContainerDecl - Represents a container for method declarations.
Definition: DeclObjC.h:941
instmeth_range instance_methods() const
Definition: DeclObjC.h:1026
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2472
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2584
Represents an ObjC class declaration.
Definition: DeclObjC.h:1147
ObjCIvarDecl - Represents an ObjC instance variable.
Definition: DeclObjC.h:1939
ObjCIvarRefExpr - A reference to an ObjC instance variable.
Definition: ExprObjC.h:548
ObjCPropertyImplDecl - Represents implementation declaration of a property in a class or category imp...
Definition: DeclObjC.h:2789
ObjCIvarDecl * getPropertyIvarDecl() const
Definition: DeclObjC.h:2862
OpaqueValueExpr - An expression referring to an opaque object of a fixed type and value class.
Definition: Expr.h:1145
PseudoObjectExpr - An expression which accesses a pseudo-object l-value.
Definition: Expr.h:6157
const Expr *const * const_semantics_iterator
Definition: Expr.h:6222
Encodes a location in the source.
This class handles loading and caching of source files into memory.
Stmt - This represents one statement.
Definition: Stmt.h:72
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:616
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
static PathDiagnosticLocation create(const Decl *D, const SourceManager &SM)
Create a location corresponding to the given declaration.
@ C
Languages that the frontend can parse and compile.