clang 20.0.0git
ObjCMissingSuperCallChecker.cpp
Go to the documentation of this file.
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
17#include "clang/AST/DeclObjC.h"
18#include "clang/AST/Expr.h"
19#include "clang/AST/ExprObjC.h"
24#include "llvm/ADT/SmallPtrSet.h"
25#include "llvm/ADT/SmallString.h"
26#include "llvm/Support/raw_ostream.h"
27
28using namespace clang;
29using namespace ento;
30
31namespace {
32struct SelectorDescriptor {
33 const char *SelectorName;
34 unsigned ArgumentCount;
35};
36
37//===----------------------------------------------------------------------===//
38// FindSuperCallVisitor - Identify specific calls to the superclass.
39//===----------------------------------------------------------------------===//
40
41class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
42public:
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
56private:
57 Selector Sel;
58};
59
60//===----------------------------------------------------------------------===//
61// ObjCSuperCallChecker
62//===----------------------------------------------------------------------===//
63
64class ObjCSuperCallChecker : public Checker<
65 check::ASTDecl<ObjCImplementationDecl> > {
66public:
67 ObjCSuperCallChecker() = default;
68
69 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
70 BugReporter &BR) const;
71private:
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::SmallPtrSet<Selector, 16>> SelectorsForClass;
78 mutable bool IsInitialized = false;
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.
88bool 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
100void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
102 StringRef ClassName) const {
103 llvm::SmallPtrSet<Selector, 16> &ClassSelectors =
104 SelectorsForClass[ClassName];
105 // Fill the Selectors SmallSet with all selectors we want to check.
106 for (SelectorDescriptor Descriptor : Sel) {
107 assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
108
109 // Get the selector.
110 const IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
111
112 Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
113 ClassSelectors.insert(Sel);
114 }
115}
116
117void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
118
119 { // Initialize selectors for: UIViewController
120 const SelectorDescriptor Selectors[] = {
121 { "addChildViewController", 1 },
122 { "viewDidAppear", 1 },
123 { "viewDidDisappear", 1 },
124 { "viewWillAppear", 1 },
125 { "viewWillDisappear", 1 },
126 { "removeFromParentViewController", 0 },
127 { "didReceiveMemoryWarning", 0 },
128 { "viewDidUnload", 0 },
129 { "viewDidLoad", 0 },
130 { "viewWillUnload", 0 },
131 { "updateViewConstraints", 0 },
132 { "encodeRestorableStateWithCoder", 1 },
133 { "restoreStateWithCoder", 1 }};
134
135 fillSelectors(Ctx, Selectors, "UIViewController");
136 }
137
138 { // Initialize selectors for: UIResponder
139 const SelectorDescriptor Selectors[] = {
140 { "resignFirstResponder", 0 }};
141
142 fillSelectors(Ctx, Selectors, "UIResponder");
143 }
144
145 { // Initialize selectors for: NSResponder
146 const SelectorDescriptor Selectors[] = {
147 { "encodeRestorableStateWithCoder", 1 },
148 { "restoreStateWithCoder", 1 }};
149
150 fillSelectors(Ctx, Selectors, "NSResponder");
151 }
152
153 { // Initialize selectors for: NSDocument
154 const SelectorDescriptor Selectors[] = {
155 { "encodeRestorableStateWithCoder", 1 },
156 { "restoreStateWithCoder", 1 }};
157
158 fillSelectors(Ctx, Selectors, "NSDocument");
159 }
160
161 IsInitialized = true;
162}
163
164void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
165 AnalysisManager &Mgr,
166 BugReporter &BR) const {
167 ASTContext &Ctx = BR.getContext();
168
169 // We need to initialize the selector table once.
170 if (!IsInitialized)
171 initializeSelectors(Ctx);
172
173 // Find out whether this class has a superclass that we are supposed to check.
174 StringRef SuperclassName;
175 if (!isCheckableClass(D, SuperclassName))
176 return;
177
178
179 // Iterate over all instance methods.
180 for (auto *MD : D->instance_methods()) {
181 Selector S = MD->getSelector();
182 // Find out whether this is a selector that we want to check.
183 if (!SelectorsForClass[SuperclassName].count(S))
184 continue;
185
186 // Check if the method calls its superclass implementation.
187 if (MD->getBody())
188 {
189 FindSuperCallVisitor Visitor(S);
190 Visitor.TraverseDecl(MD);
191
192 // It doesn't call super, emit a diagnostic.
193 if (!Visitor.DoesCallSuper) {
196 BR.getSourceManager(),
198
199 const char *Name = "Missing call to superclass";
201 llvm::raw_svector_ostream os(Buf);
202
203 os << "The '" << S.getAsString()
204 << "' instance method in " << SuperclassName.str() << " subclass '"
205 << *D << "' is missing a [super " << S.getAsString() << "] call";
206
208 os.str(), DLoc);
209 }
210 }
211 }
212}
213
214
215//===----------------------------------------------------------------------===//
216// Check registration.
217//===----------------------------------------------------------------------===//
218
219void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
220 Mgr.registerChecker<ObjCSuperCallChecker>();
221}
222
223bool ento::shouldRegisterObjCSuperCallChecker(const CheckerManager &mgr) {
224 return true;
225}
226
227/*
228 ToDo list for expanding this check in the future, the list is not exhaustive.
229 There are also cases where calling super is suggested but not "mandatory".
230 In addition to be able to check the classes and methods below, architectural
231 improvements like being able to allow for the super-call to be done in a called
232 method would be good too.
233
234UIDocument subclasses
235- finishedHandlingError:recovered: (is multi-arg)
236- finishedHandlingError:recovered: (is multi-arg)
237
238UIViewController subclasses
239- loadView (should *never* call super)
240- transitionFromViewController:toViewController:
241 duration:options:animations:completion: (is multi-arg)
242
243UICollectionViewController subclasses
244- loadView (take care because UIViewController subclasses should NOT call super
245 in loadView, but UICollectionViewController subclasses should)
246
247NSObject subclasses
248- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
249
250UIPopoverBackgroundView subclasses (some of those are class methods)
251- arrowDirection (should *never* call super)
252- arrowOffset (should *never* call super)
253- arrowBase (should *never* call super)
254- arrowHeight (should *never* call super)
255- contentViewInsets (should *never* call super)
256
257UITextSelectionRect subclasses (some of those are properties)
258- rect (should *never* call super)
259- range (should *never* call super)
260- writingDirection (should *never* call super)
261- isVertical (should *never* call super)
262- containsStart (should *never* call super)
263- containsEnd (should *never* call super)
264*/
const Decl * D
Expr * E
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:187
IdentifierTable & Idents
Definition: ASTContext.h:660
SelectorTable & Selectors
Definition: ASTContext.h:661
One of these records is kept for each identifier that is lexed.
IdentifierInfo & get(StringRef Name)
Return the identifier token info for the specified named identifier.
ObjCImplementationDecl - Represents a class definition - this is where method definitions are specifi...
Definition: DeclObjC.h:2596
Represents an ObjC class declaration.
Definition: DeclObjC.h:1153
An expression that sends a message to the given Objective-C object or class.
Definition: ExprObjC.h:945
@ SuperInstance
The receiver is the instance of the superclass object.
Definition: ExprObjC.h:959
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
Selector getSelector(unsigned NumArgs, const IdentifierInfo **IIV)
Can create any sort of selector.
Smart pointer class that efficiently represents Objective-C method names.
AnalysisDeclContext * getAnalysisDeclContext(const Decl *D)
BugReporter is a utility class for generating PathDiagnostics for analysis.
Definition: BugReporter.h:585
const SourceManager & getSourceManager()
Definition: BugReporter.h:623
void EmitBasicReport(const Decl *DeclWithIssue, const CheckerBase *Checker, StringRef BugName, StringRef BugCategory, StringRef BugStr, PathDiagnosticLocation Loc, ArrayRef< SourceRange > Ranges=std::nullopt, ArrayRef< FixItHint > Fixits=std::nullopt)
ASTContext & getContext()
Definition: BugReporter.h:621
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
static PathDiagnosticLocation createEnd(const Stmt *S, const SourceManager &SM, const LocationOrAnalysisDeclContext LAC)
Create a location for the end of the statement.
const char *const CoreFoundationObjectiveC
The JSON file list parser is used to communicate input to InstallAPI.
#define false
Definition: stdbool.h:26