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 "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"
29 using namespace clang;
33 struct SelectorDescriptor {
34 const char *SelectorName;
35 unsigned ArgumentCount;
39 //===----------------------------------------------------------------------===//
40 // FindSuperCallVisitor - Identify specific calls to the superclass.
41 //===----------------------------------------------------------------------===//
43 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
45 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
47 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
48 if (E->getSelector() == Sel)
49 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
52 // Recurse if we didn't find the super call yet.
53 return !DoesCallSuper;
62 //===----------------------------------------------------------------------===//
63 // ObjCSuperCallChecker
64 //===----------------------------------------------------------------------===//
67 class ObjCSuperCallChecker : public Checker<
68 check::ASTDecl<ObjCImplementationDecl> > {
70 ObjCSuperCallChecker() : IsInitialized(false) {}
72 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
73 BugReporter &BR) const;
75 bool isCheckableClass(const ObjCImplementationDecl *D,
76 StringRef &SuperclassName) const;
77 void initializeSelectors(ASTContext &Ctx) const;
78 void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
79 StringRef ClassName) const;
80 mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass;
81 mutable bool IsInitialized;
86 /// \brief Determine whether the given class has a superclass that we want
87 /// to check. The name of the found superclass is stored in SuperclassName.
89 /// \param D The declaration to check for superclasses.
90 /// \param[out] SuperclassName On return, the found superclass name.
91 bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
92 StringRef &SuperclassName) const {
93 const ObjCInterfaceDecl *ID = D->getClassInterface();
94 for ( ; ID ; ID = ID->getSuperClass())
96 SuperclassName = ID->getIdentifier()->getName();
97 if (SelectorsForClass.count(SuperclassName))
103 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
104 ArrayRef<SelectorDescriptor> Sel,
105 StringRef ClassName) const {
106 llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
107 // Fill the Selectors SmallSet with all selectors we want to check.
108 for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
110 SelectorDescriptor Descriptor = *I;
111 assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
114 IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
116 Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
117 ClassSelectors.insert(Sel);
121 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
123 { // Initialize selectors for: UIViewController
124 const SelectorDescriptor Selectors[] = {
125 { "addChildViewController", 1 },
126 { "viewDidAppear", 1 },
127 { "viewDidDisappear", 1 },
128 { "viewWillAppear", 1 },
129 { "viewWillDisappear", 1 },
130 { "removeFromParentViewController", 0 },
131 { "didReceiveMemoryWarning", 0 },
132 { "viewDidUnload", 0 },
133 { "viewDidLoad", 0 },
134 { "viewWillUnload", 0 },
135 { "updateViewConstraints", 0 },
136 { "encodeRestorableStateWithCoder", 1 },
137 { "restoreStateWithCoder", 1 }};
139 fillSelectors(Ctx, Selectors, "UIViewController");
142 { // Initialize selectors for: UIResponder
143 const SelectorDescriptor Selectors[] = {
144 { "resignFirstResponder", 0 }};
146 fillSelectors(Ctx, Selectors, "UIResponder");
149 { // Initialize selectors for: NSResponder
150 const SelectorDescriptor Selectors[] = {
151 { "encodeRestorableStateWithCoder", 1 },
152 { "restoreStateWithCoder", 1 }};
154 fillSelectors(Ctx, Selectors, "NSResponder");
157 { // Initialize selectors for: NSDocument
158 const SelectorDescriptor Selectors[] = {
159 { "encodeRestorableStateWithCoder", 1 },
160 { "restoreStateWithCoder", 1 }};
162 fillSelectors(Ctx, Selectors, "NSDocument");
165 IsInitialized = true;
168 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
169 AnalysisManager &Mgr,
170 BugReporter &BR) const {
171 ASTContext &Ctx = BR.getContext();
173 // We need to initialize the selector table once.
175 initializeSelectors(Ctx);
177 // Find out whether this class has a superclass that we are supposed to check.
178 StringRef SuperclassName;
179 if (!isCheckableClass(D, SuperclassName))
183 // Iterate over all instance methods.
184 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
185 E = D->instmeth_end();
187 Selector S = (*I)->getSelector();
188 // Find out whether this is a selector that we want to check.
189 if (!SelectorsForClass[SuperclassName].count(S))
192 ObjCMethodDecl *MD = *I;
194 // Check if the method calls its superclass implementation.
197 FindSuperCallVisitor Visitor(S);
198 Visitor.TraverseDecl(MD);
200 // It doesn't call super, emit a diagnostic.
201 if (!Visitor.DoesCallSuper) {
202 PathDiagnosticLocation DLoc =
203 PathDiagnosticLocation::createEnd(MD->getBody(),
204 BR.getSourceManager(),
205 Mgr.getAnalysisDeclContext(D));
207 const char *Name = "Missing call to superclass";
208 SmallString<320> Buf;
209 llvm::raw_svector_ostream os(Buf);
211 os << "The '" << S.getAsString()
212 << "' instance method in " << SuperclassName.str() << " subclass '"
213 << *D << "' is missing a [super " << S.getAsString() << "] call";
215 BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
223 //===----------------------------------------------------------------------===//
224 // Check registration.
225 //===----------------------------------------------------------------------===//
227 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
228 Mgr.registerChecker<ObjCSuperCallChecker>();
233 ToDo list for expanding this check in the future, the list is not exhaustive.
234 There are also cases where calling super is suggested but not "mandatory".
235 In addition to be able to check the classes and methods below, architectural
236 improvements like being able to allow for the super-call to be done in a called
237 method would be good too.
239 UIDocument subclasses
240 - finishedHandlingError:recovered: (is multi-arg)
241 - finishedHandlingError:recovered: (is multi-arg)
243 UIViewController subclasses
244 - loadView (should *never* call super)
245 - transitionFromViewController:toViewController:
246 duration:options:animations:completion: (is multi-arg)
248 UICollectionViewController subclasses
249 - loadView (take care because UIViewController subclasses should NOT call super
250 in loadView, but UICollectionViewController subclasses should)
253 - doesNotRecognizeSelector (it only has to call super if it doesn't throw)
255 UIPopoverBackgroundView subclasses (some of those are class methods)
256 - arrowDirection (should *never* call super)
257 - arrowOffset (should *never* call super)
258 - arrowBase (should *never* call super)
259 - arrowHeight (should *never* call super)
260 - contentViewInsets (should *never* call super)
262 UITextSelectionRect subclasses (some of those are properties)
263 - rect (should *never* call super)
264 - range (should *never* call super)
265 - writingDirection (should *never* call super)
266 - isVertical (should *never* call super)
267 - containsStart (should *never* call super)
268 - containsEnd (should *never* call super)