blob: a2cf8e10d09b6db40105446ab906a410559a2721 [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//===----------------------------------------------------------------------===//
40// FindSuperCallVisitor - Identify specific calls to the superclass.
41//===----------------------------------------------------------------------===//
42
43class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
44public:
45 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
46
47 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
48 if (E->getSelector() == Sel)
49 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
50 DoesCallSuper = true;
51
52 // Recurse if we didn't find the super call yet.
53 return !DoesCallSuper;
54 }
55
56 bool DoesCallSuper;
57
58private:
59 Selector Sel;
60};
61
62//===----------------------------------------------------------------------===//
63// ObjCSuperCallChecker
64//===----------------------------------------------------------------------===//
65
66namespace {
67class ObjCSuperCallChecker : public Checker<
68 check::ASTDecl<ObjCImplementationDecl> > {
69public:
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000070 ObjCSuperCallChecker() : IsInitialized(false) {}
71
Jordan Rose3c2f65a2012-10-30 01:21:35 +000072 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
73 BugReporter &BR) const;
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000074private:
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;
Jordan Rose3c2f65a2012-10-30 01:21:35 +000082};
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000083
84}
85
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.
88///
Jordan Rose2806a542012-12-13 18:26:05 +000089/// \param D The declaration to check for superclasses.
Jordan Roseb0fe7fd2012-12-13 03:06:45 +000090/// \param[out] SuperclassName On return, the found superclass name.
91bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
92 StringRef &SuperclassName) const {
93 const ObjCInterfaceDecl *ID = D->getClassInterface();
94 for ( ; ID ; ID = ID->getSuperClass())
95 {
96 SuperclassName = ID->getIdentifier()->getName();
97 if (SelectorsForClass.count(SuperclassName))
98 return true;
99 }
100 return false;
101}
102
103void 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();
109 I != E; ++I) {
110 SelectorDescriptor Descriptor = *I;
111 assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
112
113 // Get the selector.
114 IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
115
116 Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
117 ClassSelectors.insert(Sel);
118 }
119}
120
121void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
122
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 }};
138
139 fillSelectors(Ctx, Selectors, "UIViewController");
140 }
141
142 { // Initialize selectors for: UIResponder
143 const SelectorDescriptor Selectors[] = {
144 { "resignFirstResponder", 0 }};
145
146 fillSelectors(Ctx, Selectors, "UIResponder");
147 }
148
149 { // Initialize selectors for: NSResponder
150 const SelectorDescriptor Selectors[] = {
151 { "encodeRestorableStateWithCoder", 1 },
152 { "restoreStateWithCoder", 1 }};
153
154 fillSelectors(Ctx, Selectors, "NSResponder");
155 }
156
157 { // Initialize selectors for: NSDocument
158 const SelectorDescriptor Selectors[] = {
159 { "encodeRestorableStateWithCoder", 1 },
160 { "restoreStateWithCoder", 1 }};
161
162 fillSelectors(Ctx, Selectors, "NSDocument");
163 }
164
165 IsInitialized = true;
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000166}
167
168void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
169 AnalysisManager &Mgr,
170 BugReporter &BR) const {
171 ASTContext &Ctx = BR.getContext();
172
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000173 // We need to initialize the selector table once.
174 if (!IsInitialized)
175 initializeSelectors(Ctx);
176
177 // Find out whether this class has a superclass that we are supposed to check.
178 StringRef SuperclassName;
179 if (!isCheckableClass(D, SuperclassName))
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000180 return;
181
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000182
183 // Iterate over all instance methods.
Aaron Ballmanf26acce2014-03-13 19:50:17 +0000184 for (auto *MD : D->instance_methods()) {
185 Selector S = MD->getSelector();
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000186 // Find out whether this is a selector that we want to check.
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000187 if (!SelectorsForClass[SuperclassName].count(S))
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000188 continue;
189
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000190 // Check if the method calls its superclass implementation.
191 if (MD->getBody())
192 {
193 FindSuperCallVisitor Visitor(S);
194 Visitor.TraverseDecl(MD);
195
196 // It doesn't call super, emit a diagnostic.
197 if (!Visitor.DoesCallSuper) {
198 PathDiagnosticLocation DLoc =
199 PathDiagnosticLocation::createEnd(MD->getBody(),
200 BR.getSourceManager(),
201 Mgr.getAnalysisDeclContext(D));
202
203 const char *Name = "Missing call to superclass";
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000204 SmallString<320> Buf;
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000205 llvm::raw_svector_ostream os(Buf);
206
207 os << "The '" << S.getAsString()
Jordan Roseb0fe7fd2012-12-13 03:06:45 +0000208 << "' instance method in " << SuperclassName.str() << " subclass '"
209 << *D << "' is missing a [super " << S.getAsString() << "] call";
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000210
Alexander Kornienko4aca9b12014-02-11 21:49:21 +0000211 BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000212 os.str(), DLoc);
213 }
214 }
215 }
216}
217
218
219//===----------------------------------------------------------------------===//
220// Check registration.
221//===----------------------------------------------------------------------===//
222
223void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
224 Mgr.registerChecker<ObjCSuperCallChecker>();
225}
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
Jordan Rose3c2f65a2012-10-30 01:21:35 +0000235UIDocument subclasses
236- finishedHandlingError:recovered: (is multi-arg)
237- finishedHandlingError:recovered: (is multi-arg)
238
239UIViewController subclasses
240- loadView (should *never* call super)
241- transitionFromViewController:toViewController:
242 duration:options:animations:completion: (is multi-arg)
243
244UICollectionViewController subclasses
245- loadView (take care because UIViewController subclasses should NOT call super
246 in loadView, but UICollectionViewController subclasses should)
247
248NSObject subclasses
249- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
250
251UIPopoverBackgroundView 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
258UITextSelectionRect 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*/