clang  6.0.0svn
ObjCMissingSuperCallChecker.cpp
Go to the documentation of this file.
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"
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  explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
45 
46  bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
47  if (E->getSelector() == Sel)
49  DoesCallSuper = true;
50 
51  // Recurse if we didn't find the super call yet.
52  return !DoesCallSuper;
53  }
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  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  StringRef &SuperclassName) const {
92  for ( ; ID ; ID = ID->getSuperClass())
93  {
94  SuperclassName = ID->getIdentifier()->getName();
95  if (SelectorsForClass.count(SuperclassName))
96  return true;
97  }
98  return false;
99 }
100 
101 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
103  StringRef ClassName) const {
104  llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
105  // Fill the Selectors SmallSet with all selectors we want to check.
106  for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
107  I != E; ++I) {
108  SelectorDescriptor Descriptor = *I;
109  assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
110 
111  // Get the selector.
112  IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
113 
114  Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
115  ClassSelectors.insert(Sel);
116  }
117 }
118 
119 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
120 
121  { // Initialize selectors for: UIViewController
122  const SelectorDescriptor Selectors[] = {
123  { "addChildViewController", 1 },
124  { "viewDidAppear", 1 },
125  { "viewDidDisappear", 1 },
126  { "viewWillAppear", 1 },
127  { "viewWillDisappear", 1 },
128  { "removeFromParentViewController", 0 },
129  { "didReceiveMemoryWarning", 0 },
130  { "viewDidUnload", 0 },
131  { "viewDidLoad", 0 },
132  { "viewWillUnload", 0 },
133  { "updateViewConstraints", 0 },
134  { "encodeRestorableStateWithCoder", 1 },
135  { "restoreStateWithCoder", 1 }};
136 
137  fillSelectors(Ctx, Selectors, "UIViewController");
138  }
139 
140  { // Initialize selectors for: UIResponder
141  const SelectorDescriptor Selectors[] = {
142  { "resignFirstResponder", 0 }};
143 
144  fillSelectors(Ctx, Selectors, "UIResponder");
145  }
146 
147  { // Initialize selectors for: NSResponder
148  const SelectorDescriptor Selectors[] = {
149  { "encodeRestorableStateWithCoder", 1 },
150  { "restoreStateWithCoder", 1 }};
151 
152  fillSelectors(Ctx, Selectors, "NSResponder");
153  }
154 
155  { // Initialize selectors for: NSDocument
156  const SelectorDescriptor Selectors[] = {
157  { "encodeRestorableStateWithCoder", 1 },
158  { "restoreStateWithCoder", 1 }};
159 
160  fillSelectors(Ctx, Selectors, "NSDocument");
161  }
162 
163  IsInitialized = true;
164 }
165 
166 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
167  AnalysisManager &Mgr,
168  BugReporter &BR) const {
169  ASTContext &Ctx = BR.getContext();
170 
171  // We need to initialize the selector table once.
172  if (!IsInitialized)
173  initializeSelectors(Ctx);
174 
175  // Find out whether this class has a superclass that we are supposed to check.
176  StringRef SuperclassName;
177  if (!isCheckableClass(D, SuperclassName))
178  return;
179 
180 
181  // Iterate over all instance methods.
182  for (auto *MD : D->instance_methods()) {
183  Selector S = MD->getSelector();
184  // Find out whether this is a selector that we want to check.
185  if (!SelectorsForClass[SuperclassName].count(S))
186  continue;
187 
188  // Check if the method calls its superclass implementation.
189  if (MD->getBody())
190  {
191  FindSuperCallVisitor Visitor(S);
192  Visitor.TraverseDecl(MD);
193 
194  // It doesn't call super, emit a diagnostic.
195  if (!Visitor.DoesCallSuper) {
197  PathDiagnosticLocation::createEnd(MD->getBody(),
198  BR.getSourceManager(),
199  Mgr.getAnalysisDeclContext(D));
200 
201  const char *Name = "Missing call to superclass";
202  SmallString<320> Buf;
203  llvm::raw_svector_ostream os(Buf);
204 
205  os << "The '" << S.getAsString()
206  << "' instance method in " << SuperclassName.str() << " subclass '"
207  << *D << "' is missing a [super " << S.getAsString() << "] call";
208 
210  os.str(), DLoc);
211  }
212  }
213  }
214 }
215 
216 
217 //===----------------------------------------------------------------------===//
218 // Check registration.
219 //===----------------------------------------------------------------------===//
220 
221 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
222  Mgr.registerChecker<ObjCSuperCallChecker>();
223 }
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 */
The receiver is the instance of the superclass object.
Definition: ExprObjC.h:1060
const char *const CoreFoundationObjectiveC
Smart pointer class that efficiently represents Objective-C method names.
Selector getSelector() const
Definition: ExprObjC.cpp:312
IdentifierInfo * getIdentifier() const
getIdentifier - Get the identifier that names this declaration, if there is one.
Definition: Decl.h:265
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:149
instmeth_range instance_methods() const
Definition: DeclObjC.h:1072
IdentifierTable & Idents
Definition: ASTContext.h:537
ASTContext & getContext()
Definition: BugReporter.h:461
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:1191
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
ObjCInterfaceDecl * getSuperClass() const
Definition: DeclObjC.cpp:330
An expression that sends a message to the given Objective-C object or class.
Definition: ExprObjC.h:903
ReceiverKind getReceiverKind() const
Determine the kind of receiver that this message is being sent to.
Definition: ExprObjC.h:1187
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:403
std::string getAsString() const
Derive the full selector name (e.g.
#define false
Definition: stdbool.h:33
SelectorTable & Selectors
Definition: ASTContext.h:538
CHECKER * registerChecker()
Used to register checkers.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=None)
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:2459
Dataflow Directional Tag Classes.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2571
Selector getSelector(unsigned NumArgs, IdentifierInfo **IIV)
Can create any sort of selector.
SourceManager & getSourceManager()
Definition: BugReporter.h:463