Coverage Report

Created: 2023-09-30 09:22

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
Line
Count
Source
1
//==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
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 ObjCMissingSuperCallChecker, a checker that
10
//  analyzes a UIViewController implementation to determine if it
11
//  correctly calls super in the methods where this is mandatory.
12
//
13
//===----------------------------------------------------------------------===//
14
15
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16
#include "clang/Analysis/PathDiagnostic.h"
17
#include "clang/AST/DeclObjC.h"
18
#include "clang/AST/Expr.h"
19
#include "clang/AST/ExprObjC.h"
20
#include "clang/AST/RecursiveASTVisitor.h"
21
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
22
#include "clang/StaticAnalyzer/Core/Checker.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
24
#include "llvm/ADT/SmallPtrSet.h"
25
#include "llvm/ADT/SmallString.h"
26
#include "llvm/Support/raw_ostream.h"
27
28
using namespace clang;
29
using namespace ento;
30
31
namespace {
32
struct SelectorDescriptor {
33
  const char *SelectorName;
34
  unsigned ArgumentCount;
35
};
36
37
//===----------------------------------------------------------------------===//
38
// FindSuperCallVisitor - Identify specific calls to the superclass.
39
//===----------------------------------------------------------------------===//
40
41
class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
42
public:
43
30
  explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
44
45
17
  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
46
17
    if (E->getSelector() == Sel)
47
14
      if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
48
14
        DoesCallSuper = true;
49
50
    // Recurse if we didn't find the super call yet.
51
17
    return !DoesCallSuper;
52
17
  }
53
54
  bool DoesCallSuper;
55
56
private:
57
  Selector Sel;
58
};
59
60
//===----------------------------------------------------------------------===//
61
// ObjCSuperCallChecker
62
//===----------------------------------------------------------------------===//
63
64
class ObjCSuperCallChecker : public Checker<
65
                                      check::ASTDecl<ObjCImplementationDecl> > {
66
public:
67
48
  ObjCSuperCallChecker() = default;
68
69
  void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
70
                    BugReporter &BR) const;
71
private:
72
  bool isCheckableClass(const ObjCImplementationDecl *D,
73
                        StringRef &SuperclassName) const;
74
  void initializeSelectors(ASTContext &Ctx) const;
75
  void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
76
                     StringRef ClassName) const;
77
  mutable llvm::StringMap<llvm::SmallPtrSet<Selector, 16>> SelectorsForClass;
78
  mutable bool IsInitialized = false;
79
};
80
81
}
82
83
/// Determine whether the given class has a superclass that we want
84
/// to check. The name of the found superclass is stored in SuperclassName.
85
///
86
/// \param D The declaration to check for superclasses.
87
/// \param[out] SuperclassName On return, the found superclass name.
88
bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
89
18
                                            StringRef &SuperclassName) const {
90
18
  const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass();
91
28
  for ( ; ID ; 
ID = ID->getSuperClass()10
)
92
18
  {
93
18
    SuperclassName = ID->getIdentifier()->getName();
94
18
    if (SelectorsForClass.count(SuperclassName))
95
8
      return true;
96
18
  }
97
10
  return false;
98
18
}
99
100
void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
101
                                         ArrayRef<SelectorDescriptor> Sel,
102
24
                                         StringRef ClassName) const {
103
24
  llvm::SmallPtrSet<Selector, 16> &ClassSelectors =
104
24
      SelectorsForClass[ClassName];
105
  // Fill the Selectors SmallSet with all selectors we want to check.
106
108
  for (SelectorDescriptor Descriptor : Sel) {
107
108
    assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
108
109
    // Get the selector.
110
108
    IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
111
112
108
    Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
113
108
    ClassSelectors.insert(Sel);
114
108
  }
115
24
}
116
117
6
void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
118
119
6
  { // Initialize selectors for: UIViewController
120
6
    const SelectorDescriptor Selectors[] = {
121
6
      { "addChildViewController", 1 },
122
6
      { "viewDidAppear", 1 },
123
6
      { "viewDidDisappear", 1 },
124
6
      { "viewWillAppear", 1 },
125
6
      { "viewWillDisappear", 1 },
126
6
      { "removeFromParentViewController", 0 },
127
6
      { "didReceiveMemoryWarning", 0 },
128
6
      { "viewDidUnload", 0 },
129
6
      { "viewDidLoad", 0 },
130
6
      { "viewWillUnload", 0 },
131
6
      { "updateViewConstraints", 0 },
132
6
      { "encodeRestorableStateWithCoder", 1 },
133
6
      { "restoreStateWithCoder", 1 }};
134
135
6
    fillSelectors(Ctx, Selectors, "UIViewController");
136
6
  }
137
138
6
  { // Initialize selectors for: UIResponder
139
6
    const SelectorDescriptor Selectors[] = {
140
6
      { "resignFirstResponder", 0 }};
141
142
6
    fillSelectors(Ctx, Selectors, "UIResponder");
143
6
  }
144
145
6
  { // Initialize selectors for: NSResponder
146
6
    const SelectorDescriptor Selectors[] = {
147
6
      { "encodeRestorableStateWithCoder", 1 },
148
6
      { "restoreStateWithCoder", 1 }};
149
150
6
    fillSelectors(Ctx, Selectors, "NSResponder");
151
6
  }
152
153
6
  { // Initialize selectors for: NSDocument
154
6
    const SelectorDescriptor Selectors[] = {
155
6
      { "encodeRestorableStateWithCoder", 1 },
156
6
      { "restoreStateWithCoder", 1 }};
157
158
6
    fillSelectors(Ctx, Selectors, "NSDocument");
159
6
  }
160
161
6
  IsInitialized = true;
162
6
}
163
164
void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
165
                                        AnalysisManager &Mgr,
166
18
                                        BugReporter &BR) const {
167
18
  ASTContext &Ctx = BR.getContext();
168
169
  // We need to initialize the selector table once.
170
18
  if (!IsInitialized)
171
6
    initializeSelectors(Ctx);
172
173
  // Find out whether this class has a superclass that we are supposed to check.
174
18
  StringRef SuperclassName;
175
18
  if (!isCheckableClass(D, SuperclassName))
176
10
    return;
177
178
179
  // Iterate over all instance methods.
180
33
  
for (auto *MD : D->instance_methods())8
{
181
33
    Selector S = MD->getSelector();
182
    // Find out whether this is a selector that we want to check.
183
33
    if (!SelectorsForClass[SuperclassName].count(S))
184
3
      continue;
185
186
    // Check if the method calls its superclass implementation.
187
30
    if (MD->getBody())
188
30
    {
189
30
      FindSuperCallVisitor Visitor(S);
190
30
      Visitor.TraverseDecl(MD);
191
192
      // It doesn't call super, emit a diagnostic.
193
30
      if (!Visitor.DoesCallSuper) {
194
16
        PathDiagnosticLocation DLoc =
195
16
          PathDiagnosticLocation::createEnd(MD->getBody(),
196
16
                                            BR.getSourceManager(),
197
16
                                            Mgr.getAnalysisDeclContext(D));
198
199
16
        const char *Name = "Missing call to superclass";
200
16
        SmallString<320> Buf;
201
16
        llvm::raw_svector_ostream os(Buf);
202
203
16
        os << "The '" << S.getAsString()
204
16
           << "' instance method in " << SuperclassName.str() << " subclass '"
205
16
           << *D << "' is missing a [super " << S.getAsString() << "] call";
206
207
16
        BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
208
16
                           os.str(), DLoc);
209
16
      }
210
30
    }
211
30
  }
212
8
}
213
214
215
//===----------------------------------------------------------------------===//
216
// Check registration.
217
//===----------------------------------------------------------------------===//
218
219
48
void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
220
48
  Mgr.registerChecker<ObjCSuperCallChecker>();
221
48
}
222
223
96
bool ento::shouldRegisterObjCSuperCallChecker(const CheckerManager &mgr) {
224
96
  return true;
225
96
}
226
227
/*
228
 ToDo list for expanding this check in the future, the list is not exhaustive.
229
 There are also cases where calling super is suggested but not "mandatory".
230
 In addition to be able to check the classes and methods below, architectural
231
 improvements like being able to allow for the super-call to be done in a called
232
 method would be good too.
233
234
UIDocument subclasses
235
- finishedHandlingError:recovered: (is multi-arg)
236
- finishedHandlingError:recovered: (is multi-arg)
237
238
UIViewController subclasses
239
- loadView (should *never* call super)
240
- transitionFromViewController:toViewController:
241
         duration:options:animations:completion: (is multi-arg)
242
243
UICollectionViewController subclasses
244
- loadView (take care because UIViewController subclasses should NOT call super
245
            in loadView, but UICollectionViewController subclasses should)
246
247
NSObject subclasses
248
- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
249
250
UIPopoverBackgroundView subclasses (some of those are class methods)
251
- arrowDirection (should *never* call super)
252
- arrowOffset (should *never* call super)
253
- arrowBase (should *never* call super)
254
- arrowHeight (should *never* call super)
255
- contentViewInsets (should *never* call super)
256
257
UITextSelectionRect subclasses (some of those are properties)
258
- rect (should *never* call super)
259
- range (should *never* call super)
260
- writingDirection (should *never* call super)
261
- isVertical (should *never* call super)
262
- containsStart (should *never* call super)
263
- containsEnd (should *never* call super)
264
*/