1 //==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
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
7 //===----------------------------------------------------------------------===//
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.
13 //===----------------------------------------------------------------------===//
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/Analysis/PathDiagnostic.h"
17 #include "clang/AST/Attr.h"
18 #include "clang/AST/DeclObjC.h"
19 #include "clang/AST/Expr.h"
20 #include "clang/AST/ExprObjC.h"
21 #include "clang/Basic/LangOptions.h"
22 #include "clang/Basic/SourceManager.h"
23 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
24 #include "clang/StaticAnalyzer/Core/Checker.h"
26 using namespace clang;
29 enum IVarState { Unused, Used };
30 typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
32 static void Scan(IvarUsageMap& M, const Stmt *S) {
36 if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
37 const ObjCIvarDecl *D = Ex->getDecl();
38 IvarUsageMap::iterator I = M.find(D);
44 // Blocks can reference an instance variable of a class.
45 if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
46 Scan(M, BE->getBody());
50 if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
51 for (PseudoObjectExpr::const_semantics_iterator
52 i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
54 if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
55 sub = OVE->getSourceExpr();
59 for (const Stmt *SubStmt : S->children())
63 static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
67 const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
72 IvarUsageMap::iterator I = M.find(ID);
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());
82 if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
83 // Scan for @synthesized property methods that act as setters/getters
85 for (const auto *I : ID->property_impls())
88 // Scan the associated categories as well.
89 for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
90 if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
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());
106 static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
108 const CheckerBase *Checker) {
110 const ObjCInterfaceDecl *ID = D->getClassInterface();
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
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())
132 // Now scan the implementation declaration.
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) {
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"
151 const SourceManager &SM = BR.getSourceManager();
152 Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
154 // Find ivars that are unused.
155 for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
156 if (I->second == Unused) {
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).";
163 PathDiagnosticLocation L =
164 PathDiagnosticLocation::create(I->first, BR.getSourceManager());
165 BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
170 //===----------------------------------------------------------------------===//
171 // ObjCUnusedIvarsChecker
172 //===----------------------------------------------------------------------===//
175 class ObjCUnusedIvarsChecker : public Checker<
176 check::ASTDecl<ObjCImplementationDecl> > {
178 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
179 BugReporter &BR) const {
180 checkObjCUnusedIvar(D, BR, this);
185 void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
186 mgr.registerChecker<ObjCUnusedIvarsChecker>();
189 bool ento::shouldRegisterObjCUnusedIvarsChecker(const CheckerManager &mgr) {