xref: /aosp_15_r20/external/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp (revision 67e74705e28f6214e480b399dd47ea732279e315)
1*67e74705SXin Li //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
2*67e74705SXin Li //
3*67e74705SXin Li //                     The LLVM Compiler Infrastructure
4*67e74705SXin Li //
5*67e74705SXin Li // This file is distributed under the University of Illinois Open Source
6*67e74705SXin Li // License. See LICENSE.TXT for details.
7*67e74705SXin Li //
8*67e74705SXin Li //===----------------------------------------------------------------------===//
9*67e74705SXin Li //
10*67e74705SXin Li //  This file defines a CheckObjCUnusedIvars, a checker that
11*67e74705SXin Li //  analyzes an Objective-C class's interface/implementation to determine if it
12*67e74705SXin Li //  has any ivars that are never accessed.
13*67e74705SXin Li //
14*67e74705SXin Li //===----------------------------------------------------------------------===//
15*67e74705SXin Li 
16*67e74705SXin Li #include "ClangSACheckers.h"
17*67e74705SXin Li #include "clang/AST/Attr.h"
18*67e74705SXin Li #include "clang/AST/DeclObjC.h"
19*67e74705SXin Li #include "clang/AST/Expr.h"
20*67e74705SXin Li #include "clang/AST/ExprObjC.h"
21*67e74705SXin Li #include "clang/Basic/LangOptions.h"
22*67e74705SXin Li #include "clang/Basic/SourceManager.h"
23*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
24*67e74705SXin Li #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
25*67e74705SXin Li #include "clang/StaticAnalyzer/Core/Checker.h"
26*67e74705SXin Li 
27*67e74705SXin Li using namespace clang;
28*67e74705SXin Li using namespace ento;
29*67e74705SXin Li 
30*67e74705SXin Li enum IVarState { Unused, Used };
31*67e74705SXin Li typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
32*67e74705SXin Li 
Scan(IvarUsageMap & M,const Stmt * S)33*67e74705SXin Li static void Scan(IvarUsageMap& M, const Stmt *S) {
34*67e74705SXin Li   if (!S)
35*67e74705SXin Li     return;
36*67e74705SXin Li 
37*67e74705SXin Li   if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
38*67e74705SXin Li     const ObjCIvarDecl *D = Ex->getDecl();
39*67e74705SXin Li     IvarUsageMap::iterator I = M.find(D);
40*67e74705SXin Li     if (I != M.end())
41*67e74705SXin Li       I->second = Used;
42*67e74705SXin Li     return;
43*67e74705SXin Li   }
44*67e74705SXin Li 
45*67e74705SXin Li   // Blocks can reference an instance variable of a class.
46*67e74705SXin Li   if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
47*67e74705SXin Li     Scan(M, BE->getBody());
48*67e74705SXin Li     return;
49*67e74705SXin Li   }
50*67e74705SXin Li 
51*67e74705SXin Li   if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
52*67e74705SXin Li     for (PseudoObjectExpr::const_semantics_iterator
53*67e74705SXin Li         i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
54*67e74705SXin Li       const Expr *sub = *i;
55*67e74705SXin Li       if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
56*67e74705SXin Li         sub = OVE->getSourceExpr();
57*67e74705SXin Li       Scan(M, sub);
58*67e74705SXin Li     }
59*67e74705SXin Li 
60*67e74705SXin Li   for (const Stmt *SubStmt : S->children())
61*67e74705SXin Li     Scan(M, SubStmt);
62*67e74705SXin Li }
63*67e74705SXin Li 
Scan(IvarUsageMap & M,const ObjCPropertyImplDecl * D)64*67e74705SXin Li static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
65*67e74705SXin Li   if (!D)
66*67e74705SXin Li     return;
67*67e74705SXin Li 
68*67e74705SXin Li   const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
69*67e74705SXin Li 
70*67e74705SXin Li   if (!ID)
71*67e74705SXin Li     return;
72*67e74705SXin Li 
73*67e74705SXin Li   IvarUsageMap::iterator I = M.find(ID);
74*67e74705SXin Li   if (I != M.end())
75*67e74705SXin Li     I->second = Used;
76*67e74705SXin Li }
77*67e74705SXin Li 
Scan(IvarUsageMap & M,const ObjCContainerDecl * D)78*67e74705SXin Li static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
79*67e74705SXin Li   // Scan the methods for accesses.
80*67e74705SXin Li   for (const auto *I : D->instance_methods())
81*67e74705SXin Li     Scan(M, I->getBody());
82*67e74705SXin Li 
83*67e74705SXin Li   if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
84*67e74705SXin Li     // Scan for @synthesized property methods that act as setters/getters
85*67e74705SXin Li     // to an ivar.
86*67e74705SXin Li     for (const auto *I : ID->property_impls())
87*67e74705SXin Li       Scan(M, I);
88*67e74705SXin Li 
89*67e74705SXin Li     // Scan the associated categories as well.
90*67e74705SXin Li     for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
91*67e74705SXin Li       if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
92*67e74705SXin Li         Scan(M, CID);
93*67e74705SXin Li     }
94*67e74705SXin Li   }
95*67e74705SXin Li }
96*67e74705SXin Li 
Scan(IvarUsageMap & M,const DeclContext * C,const FileID FID,SourceManager & SM)97*67e74705SXin Li static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
98*67e74705SXin Li                  SourceManager &SM) {
99*67e74705SXin Li   for (const auto *I : C->decls())
100*67e74705SXin Li     if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
101*67e74705SXin Li       SourceLocation L = FD->getLocStart();
102*67e74705SXin Li       if (SM.getFileID(L) == FID)
103*67e74705SXin Li         Scan(M, FD->getBody());
104*67e74705SXin Li     }
105*67e74705SXin Li }
106*67e74705SXin Li 
checkObjCUnusedIvar(const ObjCImplementationDecl * D,BugReporter & BR,const CheckerBase * Checker)107*67e74705SXin Li static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
108*67e74705SXin Li                                 BugReporter &BR,
109*67e74705SXin Li                                 const CheckerBase *Checker) {
110*67e74705SXin Li 
111*67e74705SXin Li   const ObjCInterfaceDecl *ID = D->getClassInterface();
112*67e74705SXin Li   IvarUsageMap M;
113*67e74705SXin Li 
114*67e74705SXin Li   // Iterate over the ivars.
115*67e74705SXin Li   for (const auto *Ivar : ID->ivars()) {
116*67e74705SXin Li     // Ignore ivars that...
117*67e74705SXin Li     // (a) aren't private
118*67e74705SXin Li     // (b) explicitly marked unused
119*67e74705SXin Li     // (c) are iboutlets
120*67e74705SXin Li     // (d) are unnamed bitfields
121*67e74705SXin Li     if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
122*67e74705SXin Li         Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
123*67e74705SXin Li         Ivar->hasAttr<IBOutletCollectionAttr>() ||
124*67e74705SXin Li         Ivar->isUnnamedBitfield())
125*67e74705SXin Li       continue;
126*67e74705SXin Li 
127*67e74705SXin Li     M[Ivar] = Unused;
128*67e74705SXin Li   }
129*67e74705SXin Li 
130*67e74705SXin Li   if (M.empty())
131*67e74705SXin Li     return;
132*67e74705SXin Li 
133*67e74705SXin Li   // Now scan the implementation declaration.
134*67e74705SXin Li   Scan(M, D);
135*67e74705SXin Li 
136*67e74705SXin Li   // Any potentially unused ivars?
137*67e74705SXin Li   bool hasUnused = false;
138*67e74705SXin Li   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
139*67e74705SXin Li     if (I->second == Unused) {
140*67e74705SXin Li       hasUnused = true;
141*67e74705SXin Li       break;
142*67e74705SXin Li     }
143*67e74705SXin Li 
144*67e74705SXin Li   if (!hasUnused)
145*67e74705SXin Li     return;
146*67e74705SXin Li 
147*67e74705SXin Li   // We found some potentially unused ivars.  Scan the entire translation unit
148*67e74705SXin Li   // for functions inside the @implementation that reference these ivars.
149*67e74705SXin Li   // FIXME: In the future hopefully we can just use the lexical DeclContext
150*67e74705SXin Li   // to go from the ObjCImplementationDecl to the lexically "nested"
151*67e74705SXin Li   // C functions.
152*67e74705SXin Li   SourceManager &SM = BR.getSourceManager();
153*67e74705SXin Li   Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
154*67e74705SXin Li 
155*67e74705SXin Li   // Find ivars that are unused.
156*67e74705SXin Li   for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
157*67e74705SXin Li     if (I->second == Unused) {
158*67e74705SXin Li       std::string sbuf;
159*67e74705SXin Li       llvm::raw_string_ostream os(sbuf);
160*67e74705SXin Li       os << "Instance variable '" << *I->first << "' in class '" << *ID
161*67e74705SXin Li          << "' is never used by the methods in its @implementation "
162*67e74705SXin Li             "(although it may be used by category methods).";
163*67e74705SXin Li 
164*67e74705SXin Li       PathDiagnosticLocation L =
165*67e74705SXin Li         PathDiagnosticLocation::create(I->first, BR.getSourceManager());
166*67e74705SXin Li       BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
167*67e74705SXin Li                          os.str(), L);
168*67e74705SXin Li     }
169*67e74705SXin Li }
170*67e74705SXin Li 
171*67e74705SXin Li //===----------------------------------------------------------------------===//
172*67e74705SXin Li // ObjCUnusedIvarsChecker
173*67e74705SXin Li //===----------------------------------------------------------------------===//
174*67e74705SXin Li 
175*67e74705SXin Li namespace {
176*67e74705SXin Li class ObjCUnusedIvarsChecker : public Checker<
177*67e74705SXin Li                                       check::ASTDecl<ObjCImplementationDecl> > {
178*67e74705SXin Li public:
checkASTDecl(const ObjCImplementationDecl * D,AnalysisManager & mgr,BugReporter & BR) const179*67e74705SXin Li   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
180*67e74705SXin Li                     BugReporter &BR) const {
181*67e74705SXin Li     checkObjCUnusedIvar(D, BR, this);
182*67e74705SXin Li   }
183*67e74705SXin Li };
184*67e74705SXin Li }
185*67e74705SXin Li 
registerObjCUnusedIvarsChecker(CheckerManager & mgr)186*67e74705SXin Li void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
187*67e74705SXin Li   mgr.registerChecker<ObjCUnusedIvarsChecker>();
188*67e74705SXin Li }
189