Coverage Report

Created: 2022-01-18 06:27

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
Line
Count
Source (jump to first uncovered line)
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
47
  ObjCSuperCallChecker() : IsInitialized(false) {}
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;
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
24
  for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
107
132
       I != E; 
++I108
) {
108
108
    SelectorDescriptor Descriptor = *I;
109
108
    assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
110
111
    // Get the selector.
112
0
    IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
113
114
108
    Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
115
108
    ClassSelectors.insert(Sel);
116
108
  }
117
24
}
118
119
6
void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
120
121
6
  { // Initialize selectors for: UIViewController
122
6
    const SelectorDescriptor Selectors[] = {
123
6
      { "addChildViewController", 1 },
124
6
      { "viewDidAppear", 1 },
125
6
      { "viewDidDisappear", 1 },
126
6
      { "viewWillAppear", 1 },
127
6
      { "viewWillDisappear", 1 },
128
6
      { "removeFromParentViewController", 0 },
129
6
      { "didReceiveMemoryWarning", 0 },
130
6
      { "viewDidUnload", 0 },
131
6
      { "viewDidLoad", 0 },
132
6
      { "viewWillUnload", 0 },
133
6
      { "updateViewConstraints", 0 },
134
6
      { "encodeRestorableStateWithCoder", 1 },
135
6
      { "restoreStateWithCoder", 1 }};
136
137
6
    fillSelectors(Ctx, Selectors, "UIViewController");
138
6
  }
139
140
6
  { // Initialize selectors for: UIResponder
141
6
    const SelectorDescriptor Selectors[] = {
142
6
      { "resignFirstResponder", 0 }};
143
144
6
    fillSelectors(Ctx, Selectors, "UIResponder");
145
6
  }
146
147
6
  { // Initialize selectors for: NSResponder
148
6
    const SelectorDescriptor Selectors[] = {
149
6
      { "encodeRestorableStateWithCoder", 1 },
150
6
      { "restoreStateWithCoder", 1 }};
151
152
6
    fillSelectors(Ctx, Selectors, "NSResponder");
153
6
  }
154
155
6
  { // Initialize selectors for: NSDocument
156
6
    const SelectorDescriptor Selectors[] = {
157
6
      { "encodeRestorableStateWithCoder", 1 },
158
6
      { "restoreStateWithCoder", 1 }};
159
160
6
    fillSelectors(Ctx, Selectors, "NSDocument");
161
6
  }
162
163
6
  IsInitialized = true;
164
6
}
165
166
void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
167
                                        AnalysisManager &Mgr,
168
18
                                        BugReporter &BR) const {
169
18
  ASTContext &Ctx = BR.getContext();
170
171
  // We need to initialize the selector table once.
172
18
  if (!IsInitialized)
173
6
    initializeSelectors(Ctx);
174
175
  // Find out whether this class has a superclass that we are supposed to check.
176
18
  StringRef SuperclassName;
177
18
  if (!isCheckableClass(D, SuperclassName))
178
10
    return;
179
180
181
  // Iterate over all instance methods.
182
33
  
for (auto *MD : D->instance_methods())8
{
183
33
    Selector S = MD->getSelector();
184
    // Find out whether this is a selector that we want to check.
185
33
    if (!SelectorsForClass[SuperclassName].count(S))
186
3
      continue;
187
188
    // Check if the method calls its superclass implementation.
189
30
    if (MD->getBody())
190
30
    {
191
30
      FindSuperCallVisitor Visitor(S);
192
30
      Visitor.TraverseDecl(MD);
193
194
      // It doesn't call super, emit a diagnostic.
195
30
      if (!Visitor.DoesCallSuper) {
196
16
        PathDiagnosticLocation DLoc =
197
16
          PathDiagnosticLocation::createEnd(MD->getBody(),
198
16
                                            BR.getSourceManager(),
199
16
                                            Mgr.getAnalysisDeclContext(D));
200
201
16
        const char *Name = "Missing call to superclass";
202
16
        SmallString<320> Buf;
203
16
        llvm::raw_svector_ostream os(Buf);
204
205
16
        os << "The '" << S.getAsString()
206
16
           << "' instance method in " << SuperclassName.str() << " subclass '"
207
16
           << *D << "' is missing a [super " << S.getAsString() << "] call";
208
209
16
        BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
210
16
                           os.str(), DLoc);
211
16
      }
212
30
    }
213
30
  }
214
8
}
215
216
217
//===----------------------------------------------------------------------===//
218
// Check registration.
219
//===----------------------------------------------------------------------===//
220
221
47
void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
222
47
  Mgr.registerChecker<ObjCSuperCallChecker>();
223
47
}
224
225
94
bool ento::shouldRegisterObjCSuperCallChecker(const CheckerManager &mgr) {
226
94
  return true;
227
94
}
228
229
/*
230
 ToDo list for expanding this check in the future, the list is not exhaustive.
231
 There are also cases where calling super is suggested but not "mandatory".
232
 In addition to be able to check the classes and methods below, architectural
233
 improvements like being able to allow for the super-call to be done in a called
234
 method would be good too.
235
236
UIDocument subclasses
237
- finishedHandlingError:recovered: (is multi-arg)
238
- finishedHandlingError:recovered: (is multi-arg)
239
240
UIViewController subclasses
241
- loadView (should *never* call super)
242
- transitionFromViewController:toViewController:
243
         duration:options:animations:completion: (is multi-arg)
244
245
UICollectionViewController subclasses
246
- loadView (take care because UIViewController subclasses should NOT call super
247
            in loadView, but UICollectionViewController subclasses should)
248
249
NSObject subclasses
250
- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
251
252
UIPopoverBackgroundView subclasses (some of those are class methods)
253
- arrowDirection (should *never* call super)
254
- arrowOffset (should *never* call super)
255
- arrowBase (should *never* call super)
256
- arrowHeight (should *never* call super)
257
- contentViewInsets (should *never* call super)
258
259
UITextSelectionRect subclasses (some of those are properties)
260
- rect (should *never* call super)
261
- range (should *never* call super)
262
- writingDirection (should *never* call super)
263
- isVertical (should *never* call super)
264
- containsStart (should *never* call super)
265
- containsEnd (should *never* call super)
266
*/