]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
Merge ^/head r352587 through r352763.
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / StaticAnalyzer / Checkers / ObjCMissingSuperCallChecker.cpp
1 //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 //  This file defines a ObjCMissingSuperCallChecker, a checker that
10 //  analyzes a UIViewController implementation to determine if it
11 //  correctly calls super in the methods where this is mandatory.
12 //
13 //===----------------------------------------------------------------------===//
14
15 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
16 #include "clang/AST/DeclObjC.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/ExprObjC.h"
19 #include "clang/AST/RecursiveASTVisitor.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
22 #include "clang/StaticAnalyzer/Core/Checker.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
24 #include "llvm/ADT/SmallSet.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/Support/raw_ostream.h"
27
28 using namespace clang;
29 using namespace ento;
30
31 namespace {
32 struct SelectorDescriptor {
33   const char *SelectorName;
34   unsigned ArgumentCount;
35 };
36
37 //===----------------------------------------------------------------------===//
38 // FindSuperCallVisitor - Identify specific calls to the superclass.
39 //===----------------------------------------------------------------------===//
40
41 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
42 public:
43   explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
44
45   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
46     if (E->getSelector() == Sel)
47       if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
48         DoesCallSuper = true;
49
50     // Recurse if we didn't find the super call yet.
51     return !DoesCallSuper;
52   }
53
54   bool DoesCallSuper;
55
56 private:
57   Selector Sel;
58 };
59
60 //===----------------------------------------------------------------------===//
61 // ObjCSuperCallChecker
62 //===----------------------------------------------------------------------===//
63
64 class ObjCSuperCallChecker : public Checker<
65                                       check::ASTDecl<ObjCImplementationDecl> > {
66 public:
67   ObjCSuperCallChecker() : IsInitialized(false) {}
68
69   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
70                     BugReporter &BR) const;
71 private:
72   bool isCheckableClass(const ObjCImplementationDecl *D,
73                         StringRef &SuperclassName) const;
74   void initializeSelectors(ASTContext &Ctx) const;
75   void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
76                      StringRef ClassName) const;
77   mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass;
78   mutable bool IsInitialized;
79 };
80
81 }
82
83 /// Determine whether the given class has a superclass that we want
84 /// to check. The name of the found superclass is stored in SuperclassName.
85 ///
86 /// \param D The declaration to check for superclasses.
87 /// \param[out] SuperclassName On return, the found superclass name.
88 bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
89                                             StringRef &SuperclassName) const {
90   const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass();
91   for ( ; ID ; ID = ID->getSuperClass())
92   {
93     SuperclassName = ID->getIdentifier()->getName();
94     if (SelectorsForClass.count(SuperclassName))
95       return true;
96   }
97   return false;
98 }
99
100 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
101                                          ArrayRef<SelectorDescriptor> Sel,
102                                          StringRef ClassName) const {
103   llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
104   // Fill the Selectors SmallSet with all selectors we want to check.
105   for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
106        I != E; ++I) {
107     SelectorDescriptor Descriptor = *I;
108     assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
109
110     // Get the selector.
111     IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
112
113     Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
114     ClassSelectors.insert(Sel);
115   }
116 }
117
118 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
119
120   { // Initialize selectors for: UIViewController
121     const SelectorDescriptor Selectors[] = {
122       { "addChildViewController", 1 },
123       { "viewDidAppear", 1 },
124       { "viewDidDisappear", 1 },
125       { "viewWillAppear", 1 },
126       { "viewWillDisappear", 1 },
127       { "removeFromParentViewController", 0 },
128       { "didReceiveMemoryWarning", 0 },
129       { "viewDidUnload", 0 },
130       { "viewDidLoad", 0 },
131       { "viewWillUnload", 0 },
132       { "updateViewConstraints", 0 },
133       { "encodeRestorableStateWithCoder", 1 },
134       { "restoreStateWithCoder", 1 }};
135
136     fillSelectors(Ctx, Selectors, "UIViewController");
137   }
138
139   { // Initialize selectors for: UIResponder
140     const SelectorDescriptor Selectors[] = {
141       { "resignFirstResponder", 0 }};
142
143     fillSelectors(Ctx, Selectors, "UIResponder");
144   }
145
146   { // Initialize selectors for: NSResponder
147     const SelectorDescriptor Selectors[] = {
148       { "encodeRestorableStateWithCoder", 1 },
149       { "restoreStateWithCoder", 1 }};
150
151     fillSelectors(Ctx, Selectors, "NSResponder");
152   }
153
154   { // Initialize selectors for: NSDocument
155     const SelectorDescriptor Selectors[] = {
156       { "encodeRestorableStateWithCoder", 1 },
157       { "restoreStateWithCoder", 1 }};
158
159     fillSelectors(Ctx, Selectors, "NSDocument");
160   }
161
162   IsInitialized = true;
163 }
164
165 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
166                                         AnalysisManager &Mgr,
167                                         BugReporter &BR) const {
168   ASTContext &Ctx = BR.getContext();
169
170   // We need to initialize the selector table once.
171   if (!IsInitialized)
172     initializeSelectors(Ctx);
173
174   // Find out whether this class has a superclass that we are supposed to check.
175   StringRef SuperclassName;
176   if (!isCheckableClass(D, SuperclassName))
177     return;
178
179
180   // Iterate over all instance methods.
181   for (auto *MD : D->instance_methods()) {
182     Selector S = MD->getSelector();
183     // Find out whether this is a selector that we want to check.
184     if (!SelectorsForClass[SuperclassName].count(S))
185       continue;
186
187     // Check if the method calls its superclass implementation.
188     if (MD->getBody())
189     {
190       FindSuperCallVisitor Visitor(S);
191       Visitor.TraverseDecl(MD);
192
193       // It doesn't call super, emit a diagnostic.
194       if (!Visitor.DoesCallSuper) {
195         PathDiagnosticLocation DLoc =
196           PathDiagnosticLocation::createEnd(MD->getBody(),
197                                             BR.getSourceManager(),
198                                             Mgr.getAnalysisDeclContext(D));
199
200         const char *Name = "Missing call to superclass";
201         SmallString<320> Buf;
202         llvm::raw_svector_ostream os(Buf);
203
204         os << "The '" << S.getAsString()
205            << "' instance method in " << SuperclassName.str() << " subclass '"
206            << *D << "' is missing a [super " << S.getAsString() << "] call";
207
208         BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
209                            os.str(), DLoc);
210       }
211     }
212   }
213 }
214
215
216 //===----------------------------------------------------------------------===//
217 // Check registration.
218 //===----------------------------------------------------------------------===//
219
220 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
221   Mgr.registerChecker<ObjCSuperCallChecker>();
222 }
223
224 bool ento::shouldRegisterObjCSuperCallChecker(const LangOptions &LO) {
225   return true;
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
235 UIDocument subclasses
236 - finishedHandlingError:recovered: (is multi-arg)
237 - finishedHandlingError:recovered: (is multi-arg)
238
239 UIViewController subclasses
240 - loadView (should *never* call super)
241 - transitionFromViewController:toViewController:
242          duration:options:animations:completion: (is multi-arg)
243
244 UICollectionViewController subclasses
245 - loadView (take care because UIViewController subclasses should NOT call super
246             in loadView, but UICollectionViewController subclasses should)
247
248 NSObject subclasses
249 - doesNotRecognizeSelector (it only has to call super if it doesn't throw)
250
251 UIPopoverBackgroundView 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
258 UITextSelectionRect 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 */