Coverage Report

Created: 2023-09-21 18:56

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