Coverage Report

Created: 2017-10-03 07:32

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