Coverage Report

Created: 2019-07-24 05:18

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