1 //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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.
14 //===----------------------------------------------------------------------===//
16 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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"
29 using namespace clang;
33 struct SelectorDescriptor {
34 const char *SelectorName;
35 unsigned ArgumentCount;
38 //===----------------------------------------------------------------------===//
39 // FindSuperCallVisitor - Identify specific calls to the superclass.
40 //===----------------------------------------------------------------------===//
42 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
44 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
46 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
47 if (E->getSelector() == Sel)
48 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
51 // Recurse if we didn't find the super call yet.
52 return !DoesCallSuper;
61 //===----------------------------------------------------------------------===//
62 // ObjCSuperCallChecker
63 //===----------------------------------------------------------------------===//
65 class ObjCSuperCallChecker : public Checker<
66 check::ASTDecl<ObjCImplementationDecl> > {
68 ObjCSuperCallChecker() : IsInitialized(false) {}
70 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
71 BugReporter &BR) const;
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;
84 /// Determine whether the given class has a superclass that we want
85 /// to check. The name of the found superclass is stored in SuperclassName.
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 {
91 const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass();
92 for ( ; ID ; ID = ID->getSuperClass())
94 SuperclassName = ID->getIdentifier()->getName();
95 if (SelectorsForClass.count(SuperclassName))
101 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
102 ArrayRef<SelectorDescriptor> Sel,
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();
108 SelectorDescriptor Descriptor = *I;
109 assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
112 IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
114 Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
115 ClassSelectors.insert(Sel);
119 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
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 }};
137 fillSelectors(Ctx, Selectors, "UIViewController");
140 { // Initialize selectors for: UIResponder
141 const SelectorDescriptor Selectors[] = {
142 { "resignFirstResponder", 0 }};
144 fillSelectors(Ctx, Selectors, "UIResponder");
147 { // Initialize selectors for: NSResponder
148 const SelectorDescriptor Selectors[] = {
149 { "encodeRestorableStateWithCoder", 1 },
150 { "restoreStateWithCoder", 1 }};
152 fillSelectors(Ctx, Selectors, "NSResponder");
155 { // Initialize selectors for: NSDocument
156 const SelectorDescriptor Selectors[] = {
157 { "encodeRestorableStateWithCoder", 1 },
158 { "restoreStateWithCoder", 1 }};
160 fillSelectors(Ctx, Selectors, "NSDocument");
163 IsInitialized = true;
166 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
167 AnalysisManager &Mgr,
168 BugReporter &BR) const {
169 ASTContext &Ctx = BR.getContext();
171 // We need to initialize the selector table once.
173 initializeSelectors(Ctx);
175 // Find out whether this class has a superclass that we are supposed to check.
176 StringRef SuperclassName;
177 if (!isCheckableClass(D, SuperclassName))
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))
188 // Check if the method calls its superclass implementation.
191 FindSuperCallVisitor Visitor(S);
192 Visitor.TraverseDecl(MD);
194 // It doesn't call super, emit a diagnostic.
195 if (!Visitor.DoesCallSuper) {
196 PathDiagnosticLocation DLoc =
197 PathDiagnosticLocation::createEnd(MD->getBody(),
198 BR.getSourceManager(),
199 Mgr.getAnalysisDeclContext(D));
201 const char *Name = "Missing call to superclass";
202 SmallString<320> Buf;
203 llvm::raw_svector_ostream os(Buf);
205 os << "The '" << S.getAsString()
206 << "' instance method in " << SuperclassName.str() << " subclass '"
207 << *D << "' is missing a [super " << S.getAsString() << "] call";
209 BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
217 //===----------------------------------------------------------------------===//
218 // Check registration.
219 //===----------------------------------------------------------------------===//
221 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
222 Mgr.registerChecker<ObjCSuperCallChecker>();
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.
233 UIDocument subclasses
234 - finishedHandlingError:recovered: (is multi-arg)
235 - finishedHandlingError:recovered: (is multi-arg)
237 UIViewController subclasses
238 - loadView (should *never* call super)
239 - transitionFromViewController:toViewController:
240 duration:options:animations:completion: (is multi-arg)
242 UICollectionViewController subclasses
243 - loadView (take care because UIViewController subclasses should NOT call super
244 in loadView, but UICollectionViewController subclasses should)
247 - doesNotRecognizeSelector (it only has to call super if it doesn't throw)
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)
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)