Coverage Report

Created: 2019-07-24 05:18

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