blob: 32a1adb587bfd2a152937d75d2547e5fc85eccea [file] [log] [blame]
Jordan Rose3c2f65a2012-10-30 01:21:35 +00001//==- 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"
Chandler Carruth3a022472012-12-04 09:13:33 +000017#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"
Jordan Rose3c2f65a2012-10-30 01:21:35 +000023#include "clang/StaticAnalyzer/Core/Checker.h"
24#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
Jordan Rose3c2f65a2012-10-30 01:21:35 +000025#include "llvm/ADT/SmallSet.h"
Chandler Carruth3a022472012-12-04 09:13:33 +000026#include "llvm/ADT/SmallString.h"
Jordan Rose3c2f65a2012-10-30 01:21:35 +000027#include "llvm/Support/raw_ostream.h"
28
29using namespace clang;
30using namespace ento;
31
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000032namespace {
33struct SelectorDescriptor {
34 const char *SelectorName;
35 unsigned ArgumentCount;
36};
Jordan Rose3c2f65a2012-10-30 01:21:35 +000037
38//===----------------------------------------------------------------------===//
39// FindSuperCallVisitor - Identify specific calls to the superclass.
40//===----------------------------------------------------------------------===//
41
42class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
43public:
44 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
45
46 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
47 if (E->getSelector() == Sel)
48 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
49 DoesCallSuper = true;
50
51 // Recurse if we didn't find the super call yet.
Ted Kremenek3a0678e2015-09-08 03:50:52 +000052 return !DoesCallSuper;
Jordan Rose3c2f65a2012-10-30 01:21:35 +000053 }
54
55 bool DoesCallSuper;
56
57private:
58 Selector Sel;
59};
60
61//===----------------------------------------------------------------------===//
Ted Kremenek3a0678e2015-09-08 03:50:52 +000062// ObjCSuperCallChecker
Jordan Rose3c2f65a2012-10-30 01:21:35 +000063//===----------------------------------------------------------------------===//
64
Jordan Rose3c2f65a2012-10-30 01:21:35 +000065class ObjCSuperCallChecker : public Checker<
66 check::ASTDecl<ObjCImplementationDecl> > {
67public:
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000068 ObjCSuperCallChecker() : IsInitialized(false) {}
69
Jordan Rose3c2f65a2012-10-30 01:21:35 +000070 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
71 BugReporter &BR) const;
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000072private:
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;
Jordan Rose3c2f65a2012-10-30 01:21:35 +000080};
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000081
Alexander Kornienkoab9db512015-06-22 23:07:51 +000082}
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000083
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///
Jordan Rose2806a542012-12-13 18:26:05 +000087/// \param D The declaration to check for superclasses.
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000088/// \param[out] SuperclassName On return, the found superclass name.
89bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
90 StringRef &SuperclassName) const {
Devin Coughlinde2cc01c2015-08-08 01:31:51 +000091 const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass();
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000092 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
101void 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();
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
119void 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;
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000164}
165
166void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
167 AnalysisManager &Mgr,
168 BugReporter &BR) const {
169 ASTContext &Ctx = BR.getContext();
170
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000171 // 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))
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000178 return;
179
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000180
181 // Iterate over all instance methods.
Aaron Ballmanf26acce2014-03-13 19:50:17 +0000182 for (auto *MD : D->instance_methods()) {
183 Selector S = MD->getSelector();
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000184 // Find out whether this is a selector that we want to check.
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000185 if (!SelectorsForClass[SuperclassName].count(S))
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000186 continue;
187
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000188 // 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) {
196 PathDiagnosticLocation DLoc =
197 PathDiagnosticLocation::createEnd(MD->getBody(),
198 BR.getSourceManager(),
199 Mgr.getAnalysisDeclContext(D));
200
201 const char *Name = "Missing call to superclass";
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000202 SmallString<320> Buf;
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000203 llvm::raw_svector_ostream os(Buf);
204
Ted Kremenek3a0678e2015-09-08 03:50:52 +0000205 os << "The '" << S.getAsString()
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000206 << "' instance method in " << SuperclassName.str() << " subclass '"
207 << *D << "' is missing a [super " << S.getAsString() << "] call";
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000208
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000209 BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000210 os.str(), DLoc);
211 }
212 }
213 }
214}
215
216
217//===----------------------------------------------------------------------===//
218// Check registration.
219//===----------------------------------------------------------------------===//
220
221void 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
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000233UIDocument subclasses
234- finishedHandlingError:recovered: (is multi-arg)
235- finishedHandlingError:recovered: (is multi-arg)
236
237UIViewController subclasses
238- loadView (should *never* call super)
239- transitionFromViewController:toViewController:
240 duration:options:animations:completion: (is multi-arg)
241
242UICollectionViewController subclasses
243- loadView (take care because UIViewController subclasses should NOT call super
244 in loadView, but UICollectionViewController subclasses should)
245
246NSObject subclasses
247- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
248
249UIPopoverBackgroundView 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
256UITextSelectionRect 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*/