clang  10.0.0svn
ObjCMissingSuperCallChecker.cpp
Go to the documentation of this file.
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 
17 #include "clang/AST/DeclObjC.h"
18 #include "clang/AST/Expr.h"
19 #include "clang/AST/ExprObjC.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  explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
44 
45  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
46  if (E->getSelector() == Sel)
48  DoesCallSuper = true;
49 
50  // Recurse if we didn't find the super call yet.
51  return !DoesCallSuper;
52  }
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  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  StringRef &SuperclassName) const {
91  for ( ; ID ; ID = ID->getSuperClass())
92  {
93  SuperclassName = ID->getIdentifier()->getName();
94  if (SelectorsForClass.count(SuperclassName))
95  return true;
96  }
97  return false;
98 }
99 
100 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
102  StringRef ClassName) const {
103  llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
104  // Fill the Selectors SmallSet with all selectors we want to check.
105  for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
106  I != E; ++I) {
107  SelectorDescriptor Descriptor = *I;
108  assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
109 
110  // Get the selector.
111  IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
112 
113  Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
114  ClassSelectors.insert(Sel);
115  }
116 }
117 
118 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
119 
120  { // Initialize selectors for: UIViewController
121  const SelectorDescriptor Selectors[] = {
122  { "addChildViewController", 1 },
123  { "viewDidAppear", 1 },
124  { "viewDidDisappear", 1 },
125  { "viewWillAppear", 1 },
126  { "viewWillDisappear", 1 },
127  { "removeFromParentViewController", 0 },
128  { "didReceiveMemoryWarning", 0 },
129  { "viewDidUnload", 0 },
130  { "viewDidLoad", 0 },
131  { "viewWillUnload", 0 },
132  { "updateViewConstraints", 0 },
133  { "encodeRestorableStateWithCoder", 1 },
134  { "restoreStateWithCoder", 1 }};
135 
136  fillSelectors(Ctx, Selectors, "UIViewController");
137  }
138 
139  { // Initialize selectors for: UIResponder
140  const SelectorDescriptor Selectors[] = {
141  { "resignFirstResponder", 0 }};
142 
143  fillSelectors(Ctx, Selectors, "UIResponder");
144  }
145 
146  { // Initialize selectors for: NSResponder
147  const SelectorDescriptor Selectors[] = {
148  { "encodeRestorableStateWithCoder", 1 },
149  { "restoreStateWithCoder", 1 }};
150 
151  fillSelectors(Ctx, Selectors, "NSResponder");
152  }
153 
154  { // Initialize selectors for: NSDocument
155  const SelectorDescriptor Selectors[] = {
156  { "encodeRestorableStateWithCoder", 1 },
157  { "restoreStateWithCoder", 1 }};
158 
159  fillSelectors(Ctx, Selectors, "NSDocument");
160  }
161 
162  IsInitialized = true;
163 }
164 
165 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
166  AnalysisManager &Mgr,
167  BugReporter &BR) const {
168  ASTContext &Ctx = BR.getContext();
169 
170  // We need to initialize the selector table once.
171  if (!IsInitialized)
172  initializeSelectors(Ctx);
173 
174  // Find out whether this class has a superclass that we are supposed to check.
175  StringRef SuperclassName;
176  if (!isCheckableClass(D, SuperclassName))
177  return;
178 
179 
180  // Iterate over all instance methods.
181  for (auto *MD : D->instance_methods()) {
182  Selector S = MD->getSelector();
183  // Find out whether this is a selector that we want to check.
184  if (!SelectorsForClass[SuperclassName].count(S))
185  continue;
186 
187  // Check if the method calls its superclass implementation.
188  if (MD->getBody())
189  {
190  FindSuperCallVisitor Visitor(S);
191  Visitor.TraverseDecl(MD);
192 
193  // It doesn't call super, emit a diagnostic.
194  if (!Visitor.DoesCallSuper) {
195  PathDiagnosticLocation DLoc =
196  PathDiagnosticLocation::createEnd(MD->getBody(),
197  BR.getSourceManager(),
198  Mgr.getAnalysisDeclContext(D));
199 
200  const char *Name = "Missing call to superclass";
201  SmallString<320> Buf;
202  llvm::raw_svector_ostream os(Buf);
203 
204  os << "The '" << S.getAsString()
205  << "' instance method in " << SuperclassName.str() << " subclass '"
206  << *D << "' is missing a [super " << S.getAsString() << "] call";
207 
208  BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
209  os.str(), DLoc);
210  }
211  }
212  }
213 }
214 
215 
216 //===----------------------------------------------------------------------===//
217 // Check registration.
218 //===----------------------------------------------------------------------===//
219 
220 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
221  Mgr.registerChecker<ObjCSuperCallChecker>();
222 }
223 
224 bool ento::shouldRegisterObjCSuperCallChecker(const LangOptions &LO) {
225  return true;
226 }
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 */
The receiver is the instance of the superclass object.
Definition: ExprObjC.h:1107
const char *const CoreFoundationObjectiveC
Smart pointer class that efficiently represents Objective-C method names.
Selector getSelector() const
Definition: ExprObjC.cpp:337
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:269
One of these records is kept for each identifier that is lexed.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:160
instmeth_range instance_methods() const
Definition: DeclObjC.h:1053
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:49
IdentifierTable & Idents
Definition: ASTContext.h:579
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Represents an ObjC class declaration.
Definition: DeclObjC.h:1171
ObjCInterfaceDecl * getSuperClass() const
Definition: DeclObjC.cpp:337
An expression that sends a message to the given Objective-C object or class.
Definition: ExprObjC.h:950
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
Definition: ExprObjC.h:1234
std::string getAsString() const
Derive the full selector name (e.g.
#define false
Definition: stdbool.h:17
SelectorTable & Selectors
Definition: ASTContext.h:580
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
Create a location for the end of the statement.
StringRef getName() const
Return the actual identifier string.
const ObjCInterfaceDecl * getClassInterface() const
Definition: DeclObjC.h:2439
Dataflow Directional Tag Classes.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2551
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV)
Can create any sort of selector.