/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
Line | Count | Source |
1 | | //==- 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/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" |
28 | | |
29 | | using namespace clang; |
30 | | using namespace ento; |
31 | | |
32 | | namespace { |
33 | | struct SelectorDescriptor { |
34 | | const char *SelectorName; |
35 | | unsigned ArgumentCount; |
36 | | }; |
37 | | |
38 | | //===----------------------------------------------------------------------===// |
39 | | // FindSuperCallVisitor - Identify specific calls to the superclass. |
40 | | //===----------------------------------------------------------------------===// |
41 | | |
42 | | class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { |
43 | | public: |
44 | 30 | explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} |
45 | | |
46 | 17 | bool VisitObjCMessageExpr(ObjCMessageExpr *E) { |
47 | 17 | if (E->getSelector() == Sel) |
48 | 14 | if (14 E->getReceiverKind() == ObjCMessageExpr::SuperInstance14 ) |
49 | 14 | DoesCallSuper = true; |
50 | 17 | |
51 | 17 | // Recurse if we didn't find the super call yet. |
52 | 17 | return !DoesCallSuper; |
53 | 17 | } |
54 | | |
55 | | bool DoesCallSuper; |
56 | | |
57 | | private: |
58 | | Selector Sel; |
59 | | }; |
60 | | |
61 | | //===----------------------------------------------------------------------===// |
62 | | // ObjCSuperCallChecker |
63 | | //===----------------------------------------------------------------------===// |
64 | | |
65 | | class ObjCSuperCallChecker : public Checker< |
66 | | check::ASTDecl<ObjCImplementationDecl> > { |
67 | | public: |
68 | 13 | ObjCSuperCallChecker() : IsInitialized(false) {} |
69 | | |
70 | | void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, |
71 | | BugReporter &BR) const; |
72 | | private: |
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; |
80 | | }; |
81 | | |
82 | | } |
83 | | |
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 | | /// |
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 | 17 | StringRef &SuperclassName) const { |
91 | 17 | const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass(); |
92 | 27 | for ( ; ID27 ; ID = ID->getSuperClass()10 ) |
93 | 18 | { |
94 | 18 | SuperclassName = ID->getIdentifier()->getName(); |
95 | 18 | if (SelectorsForClass.count(SuperclassName)) |
96 | 8 | return true; |
97 | 18 | } |
98 | 9 | return false; |
99 | 17 | } |
100 | | |
101 | | void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, |
102 | | ArrayRef<SelectorDescriptor> Sel, |
103 | 20 | StringRef ClassName) const { |
104 | 20 | llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; |
105 | 20 | // Fill the Selectors SmallSet with all selectors we want to check. |
106 | 20 | for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); |
107 | 110 | I != E110 ; ++I90 ) { |
108 | 90 | SelectorDescriptor Descriptor = *I; |
109 | 90 | assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. |
110 | 90 | |
111 | 90 | // Get the selector. |
112 | 90 | IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); |
113 | 90 | |
114 | 90 | Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); |
115 | 90 | ClassSelectors.insert(Sel); |
116 | 90 | } |
117 | 20 | } |
118 | | |
119 | 5 | void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { |
120 | 5 | |
121 | 5 | { // Initialize selectors for: UIViewController |
122 | 5 | const SelectorDescriptor Selectors[] = { |
123 | 5 | { "addChildViewController", 1 }, |
124 | 5 | { "viewDidAppear", 1 }, |
125 | 5 | { "viewDidDisappear", 1 }, |
126 | 5 | { "viewWillAppear", 1 }, |
127 | 5 | { "viewWillDisappear", 1 }, |
128 | 5 | { "removeFromParentViewController", 0 }, |
129 | 5 | { "didReceiveMemoryWarning", 0 }, |
130 | 5 | { "viewDidUnload", 0 }, |
131 | 5 | { "viewDidLoad", 0 }, |
132 | 5 | { "viewWillUnload", 0 }, |
133 | 5 | { "updateViewConstraints", 0 }, |
134 | 5 | { "encodeRestorableStateWithCoder", 1 }, |
135 | 5 | { "restoreStateWithCoder", 1 }}; |
136 | 5 | |
137 | 5 | fillSelectors(Ctx, Selectors, "UIViewController"); |
138 | 5 | } |
139 | 5 | |
140 | 5 | { // Initialize selectors for: UIResponder |
141 | 5 | const SelectorDescriptor Selectors[] = { |
142 | 5 | { "resignFirstResponder", 0 }}; |
143 | 5 | |
144 | 5 | fillSelectors(Ctx, Selectors, "UIResponder"); |
145 | 5 | } |
146 | 5 | |
147 | 5 | { // Initialize selectors for: NSResponder |
148 | 5 | const SelectorDescriptor Selectors[] = { |
149 | 5 | { "encodeRestorableStateWithCoder", 1 }, |
150 | 5 | { "restoreStateWithCoder", 1 }}; |
151 | 5 | |
152 | 5 | fillSelectors(Ctx, Selectors, "NSResponder"); |
153 | 5 | } |
154 | 5 | |
155 | 5 | { // Initialize selectors for: NSDocument |
156 | 5 | const SelectorDescriptor Selectors[] = { |
157 | 5 | { "encodeRestorableStateWithCoder", 1 }, |
158 | 5 | { "restoreStateWithCoder", 1 }}; |
159 | 5 | |
160 | 5 | fillSelectors(Ctx, Selectors, "NSDocument"); |
161 | 5 | } |
162 | 5 | |
163 | 5 | IsInitialized = true; |
164 | 5 | } |
165 | | |
166 | | void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, |
167 | | AnalysisManager &Mgr, |
168 | 17 | BugReporter &BR) const { |
169 | 17 | ASTContext &Ctx = BR.getContext(); |
170 | 17 | |
171 | 17 | // We need to initialize the selector table once. |
172 | 17 | if (!IsInitialized) |
173 | 5 | initializeSelectors(Ctx); |
174 | 17 | |
175 | 17 | // Find out whether this class has a superclass that we are supposed to check. |
176 | 17 | StringRef SuperclassName; |
177 | 17 | if (!isCheckableClass(D, SuperclassName)) |
178 | 9 | return; |
179 | 8 | |
180 | 8 | |
181 | 8 | // Iterate over all instance methods. |
182 | 8 | for (auto *MD : D->instance_methods()) 8 { |
183 | 33 | Selector S = MD->getSelector(); |
184 | 33 | // Find out whether this is a selector that we want to check. |
185 | 33 | if (!SelectorsForClass[SuperclassName].count(S)) |
186 | 3 | continue; |
187 | 30 | |
188 | 30 | // Check if the method calls its superclass implementation. |
189 | 30 | if (30 MD->getBody()30 ) |
190 | 30 | { |
191 | 30 | FindSuperCallVisitor Visitor(S); |
192 | 30 | Visitor.TraverseDecl(MD); |
193 | 30 | |
194 | 30 | // It doesn't call super, emit a diagnostic. |
195 | 30 | if (!Visitor.DoesCallSuper30 ) { |
196 | 16 | PathDiagnosticLocation DLoc = |
197 | 16 | PathDiagnosticLocation::createEnd(MD->getBody(), |
198 | 16 | BR.getSourceManager(), |
199 | 16 | Mgr.getAnalysisDeclContext(D)); |
200 | 16 | |
201 | 16 | const char *Name = "Missing call to superclass"; |
202 | 16 | SmallString<320> Buf; |
203 | 16 | llvm::raw_svector_ostream os(Buf); |
204 | 16 | |
205 | 16 | os << "The '" << S.getAsString() |
206 | 16 | << "' instance method in " << SuperclassName.str() << " subclass '" |
207 | 16 | << *D << "' is missing a [super " << S.getAsString() << "] call"; |
208 | 16 | |
209 | 16 | BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, |
210 | 16 | os.str(), DLoc); |
211 | 16 | } |
212 | 30 | } |
213 | 33 | } |
214 | 17 | } |
215 | | |
216 | | |
217 | | //===----------------------------------------------------------------------===// |
218 | | // Check registration. |
219 | | //===----------------------------------------------------------------------===// |
220 | | |
221 | 13 | void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { |
222 | 13 | Mgr.registerChecker<ObjCSuperCallChecker>(); |
223 | 13 | } |
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 | | |
233 | | UIDocument subclasses |
234 | | - finishedHandlingError:recovered: (is multi-arg) |
235 | | - finishedHandlingError:recovered: (is multi-arg) |
236 | | |
237 | | UIViewController subclasses |
238 | | - loadView (should *never* call super) |
239 | | - transitionFromViewController:toViewController: |
240 | | duration:options:animations:completion: (is multi-arg) |
241 | | |
242 | | UICollectionViewController subclasses |
243 | | - loadView (take care because UIViewController subclasses should NOT call super |
244 | | in loadView, but UICollectionViewController subclasses should) |
245 | | |
246 | | NSObject subclasses |
247 | | - doesNotRecognizeSelector (it only has to call super if it doesn't throw) |
248 | | |
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) |
255 | | |
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) |
263 | | */ |