blob: e906e8aa301656dde80217eba60e5b989234531d [file] [log] [blame]
Jordan Rose3cf9a722012-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"
17#include "clang/StaticAnalyzer/Core/Checker.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
20#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21#include "clang/AST/ExprObjC.h"
22#include "clang/AST/Expr.h"
23#include "clang/AST/DeclObjC.h"
24#include "clang/AST/RecursiveASTVisitor.h"
25#include "llvm/ADT/SmallString.h"
26#include "llvm/ADT/SmallSet.h"
27#include "llvm/Support/raw_ostream.h"
28
29using namespace clang;
30using namespace ento;
31
32static bool isUIViewControllerSubclass(ASTContext &Ctx,
33 const ObjCImplementationDecl *D) {
34 IdentifierInfo *ViewControllerII = &Ctx.Idents.get("UIViewController");
35 const ObjCInterfaceDecl *ID = D->getClassInterface();
36
37 for ( ; ID; ID = ID->getSuperClass())
38 if (ID->getIdentifier() == ViewControllerII)
39 return true;
40 return false;
41}
42
43//===----------------------------------------------------------------------===//
44// FindSuperCallVisitor - Identify specific calls to the superclass.
45//===----------------------------------------------------------------------===//
46
47class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
48public:
49 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
50
51 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
52 if (E->getSelector() == Sel)
53 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
54 DoesCallSuper = true;
55
56 // Recurse if we didn't find the super call yet.
57 return !DoesCallSuper;
58 }
59
60 bool DoesCallSuper;
61
62private:
63 Selector Sel;
64};
65
66//===----------------------------------------------------------------------===//
67// ObjCSuperCallChecker
68//===----------------------------------------------------------------------===//
69
70namespace {
71class ObjCSuperCallChecker : public Checker<
72 check::ASTDecl<ObjCImplementationDecl> > {
73public:
74 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
75 BugReporter &BR) const;
76};
77}
78
79void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
80 AnalysisManager &Mgr,
81 BugReporter &BR) const {
82 ASTContext &Ctx = BR.getContext();
83
84 if (!isUIViewControllerSubclass(Ctx, D))
85 return;
86
87 const char *SelectorNames[] =
88 {"addChildViewController", "viewDidAppear", "viewDidDisappear",
89 "viewWillAppear", "viewWillDisappear", "removeFromParentViewController",
90 "didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload",
91 "viewDidLoad"};
92 const unsigned SelectorArgumentCounts[] =
93 {1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
94 const size_t SelectorCount = llvm::array_lengthof(SelectorNames);
95 assert(llvm::array_lengthof(SelectorArgumentCounts) == SelectorCount);
96
97 // Fill the Selectors SmallSet with all selectors we want to check.
98 llvm::SmallSet<Selector, 16> Selectors;
99 for (size_t i = 0; i < SelectorCount; i++) {
100 unsigned ArgumentCount = SelectorArgumentCounts[i];
101 const char *SelectorCString = SelectorNames[i];
102
103 // Get the selector.
104 IdentifierInfo *II = &Ctx.Idents.get(SelectorCString);
105 Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II));
106 }
107
108 // Iterate over all instance methods.
109 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
110 E = D->instmeth_end();
111 I != E; ++I) {
112 Selector S = (*I)->getSelector();
113 // Find out whether this is a selector that we want to check.
114 if (!Selectors.count(S))
115 continue;
116
117 ObjCMethodDecl *MD = *I;
118
119 // Check if the method calls its superclass implementation.
120 if (MD->getBody())
121 {
122 FindSuperCallVisitor Visitor(S);
123 Visitor.TraverseDecl(MD);
124
125 // It doesn't call super, emit a diagnostic.
126 if (!Visitor.DoesCallSuper) {
127 PathDiagnosticLocation DLoc =
128 PathDiagnosticLocation::createEnd(MD->getBody(),
129 BR.getSourceManager(),
130 Mgr.getAnalysisDeclContext(D));
131
132 const char *Name = "Missing call to superclass";
133 SmallString<256> Buf;
134 llvm::raw_svector_ostream os(Buf);
135
136 os << "The '" << S.getAsString()
137 << "' instance method in UIViewController subclass '" << *D
138 << "' is missing a [super " << S.getAsString() << "] call";
139
140 BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
141 os.str(), DLoc);
142 }
143 }
144 }
145}
146
147
148//===----------------------------------------------------------------------===//
149// Check registration.
150//===----------------------------------------------------------------------===//
151
152void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
153 Mgr.registerChecker<ObjCSuperCallChecker>();
154}
155
156
157/*
158 ToDo list for expanding this check in the future, the list is not exhaustive.
159 There are also cases where calling super is suggested but not "mandatory".
160 In addition to be able to check the classes and methods below, architectural
161 improvements like being able to allow for the super-call to be done in a called
162 method would be good too.
163
164*** trivial cases:
165UIResponder subclasses
166- resignFirstResponder
167
168NSResponder subclasses
169- cursorUpdate
170
171*** more difficult cases:
172
173UIDocument subclasses
174- finishedHandlingError:recovered: (is multi-arg)
175- finishedHandlingError:recovered: (is multi-arg)
176
177UIViewController subclasses
178- loadView (should *never* call super)
179- transitionFromViewController:toViewController:
180 duration:options:animations:completion: (is multi-arg)
181
182UICollectionViewController subclasses
183- loadView (take care because UIViewController subclasses should NOT call super
184 in loadView, but UICollectionViewController subclasses should)
185
186NSObject subclasses
187- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
188
189UIPopoverBackgroundView subclasses (some of those are class methods)
190- arrowDirection (should *never* call super)
191- arrowOffset (should *never* call super)
192- arrowBase (should *never* call super)
193- arrowHeight (should *never* call super)
194- contentViewInsets (should *never* call super)
195
196UITextSelectionRect subclasses (some of those are properties)
197- rect (should *never* call super)
198- range (should *never* call super)
199- writingDirection (should *never* call super)
200- isVertical (should *never* call super)
201- containsStart (should *never* call super)
202- containsEnd (should *never* call super)
203*/