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