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