xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg //  This file defines a ObjCMissingSuperCallChecker, a checker that
107330f729Sjoerg //  analyzes a UIViewController implementation to determine if it
117330f729Sjoerg //  correctly calls super in the methods where this is mandatory.
127330f729Sjoerg //
137330f729Sjoerg //===----------------------------------------------------------------------===//
147330f729Sjoerg 
157330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
167330f729Sjoerg #include "clang/Analysis/PathDiagnostic.h"
177330f729Sjoerg #include "clang/AST/DeclObjC.h"
187330f729Sjoerg #include "clang/AST/Expr.h"
197330f729Sjoerg #include "clang/AST/ExprObjC.h"
207330f729Sjoerg #include "clang/AST/RecursiveASTVisitor.h"
217330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
227330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
237330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
24*e038c9c4Sjoerg #include "llvm/ADT/SmallPtrSet.h"
257330f729Sjoerg #include "llvm/ADT/SmallString.h"
267330f729Sjoerg #include "llvm/Support/raw_ostream.h"
277330f729Sjoerg 
287330f729Sjoerg using namespace clang;
297330f729Sjoerg using namespace ento;
307330f729Sjoerg 
317330f729Sjoerg namespace {
327330f729Sjoerg struct SelectorDescriptor {
337330f729Sjoerg   const char *SelectorName;
347330f729Sjoerg   unsigned ArgumentCount;
357330f729Sjoerg };
367330f729Sjoerg 
377330f729Sjoerg //===----------------------------------------------------------------------===//
387330f729Sjoerg // FindSuperCallVisitor - Identify specific calls to the superclass.
397330f729Sjoerg //===----------------------------------------------------------------------===//
407330f729Sjoerg 
417330f729Sjoerg class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
427330f729Sjoerg public:
FindSuperCallVisitor(Selector S)437330f729Sjoerg   explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
447330f729Sjoerg 
VisitObjCMessageExpr(ObjCMessageExpr * E)457330f729Sjoerg   bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
467330f729Sjoerg     if (E->getSelector() == Sel)
477330f729Sjoerg       if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
487330f729Sjoerg         DoesCallSuper = true;
497330f729Sjoerg 
507330f729Sjoerg     // Recurse if we didn't find the super call yet.
517330f729Sjoerg     return !DoesCallSuper;
527330f729Sjoerg   }
537330f729Sjoerg 
547330f729Sjoerg   bool DoesCallSuper;
557330f729Sjoerg 
567330f729Sjoerg private:
577330f729Sjoerg   Selector Sel;
587330f729Sjoerg };
597330f729Sjoerg 
607330f729Sjoerg //===----------------------------------------------------------------------===//
617330f729Sjoerg // ObjCSuperCallChecker
627330f729Sjoerg //===----------------------------------------------------------------------===//
637330f729Sjoerg 
647330f729Sjoerg class ObjCSuperCallChecker : public Checker<
657330f729Sjoerg                                       check::ASTDecl<ObjCImplementationDecl> > {
667330f729Sjoerg public:
ObjCSuperCallChecker()677330f729Sjoerg   ObjCSuperCallChecker() : IsInitialized(false) {}
687330f729Sjoerg 
697330f729Sjoerg   void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
707330f729Sjoerg                     BugReporter &BR) const;
717330f729Sjoerg private:
727330f729Sjoerg   bool isCheckableClass(const ObjCImplementationDecl *D,
737330f729Sjoerg                         StringRef &SuperclassName) const;
747330f729Sjoerg   void initializeSelectors(ASTContext &Ctx) const;
757330f729Sjoerg   void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
767330f729Sjoerg                      StringRef ClassName) const;
77*e038c9c4Sjoerg   mutable llvm::StringMap<llvm::SmallPtrSet<Selector, 16>> SelectorsForClass;
787330f729Sjoerg   mutable bool IsInitialized;
797330f729Sjoerg };
807330f729Sjoerg 
817330f729Sjoerg }
827330f729Sjoerg 
837330f729Sjoerg /// Determine whether the given class has a superclass that we want
847330f729Sjoerg /// to check. The name of the found superclass is stored in SuperclassName.
857330f729Sjoerg ///
867330f729Sjoerg /// \param D The declaration to check for superclasses.
877330f729Sjoerg /// \param[out] SuperclassName On return, the found superclass name.
isCheckableClass(const ObjCImplementationDecl * D,StringRef & SuperclassName) const887330f729Sjoerg bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
897330f729Sjoerg                                             StringRef &SuperclassName) const {
907330f729Sjoerg   const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass();
917330f729Sjoerg   for ( ; ID ; ID = ID->getSuperClass())
927330f729Sjoerg   {
937330f729Sjoerg     SuperclassName = ID->getIdentifier()->getName();
947330f729Sjoerg     if (SelectorsForClass.count(SuperclassName))
957330f729Sjoerg       return true;
967330f729Sjoerg   }
977330f729Sjoerg   return false;
987330f729Sjoerg }
997330f729Sjoerg 
fillSelectors(ASTContext & Ctx,ArrayRef<SelectorDescriptor> Sel,StringRef ClassName) const1007330f729Sjoerg void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
1017330f729Sjoerg                                          ArrayRef<SelectorDescriptor> Sel,
1027330f729Sjoerg                                          StringRef ClassName) const {
103*e038c9c4Sjoerg   llvm::SmallPtrSet<Selector, 16> &ClassSelectors =
104*e038c9c4Sjoerg       SelectorsForClass[ClassName];
1057330f729Sjoerg   // Fill the Selectors SmallSet with all selectors we want to check.
1067330f729Sjoerg   for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
1077330f729Sjoerg        I != E; ++I) {
1087330f729Sjoerg     SelectorDescriptor Descriptor = *I;
1097330f729Sjoerg     assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
1107330f729Sjoerg 
1117330f729Sjoerg     // Get the selector.
1127330f729Sjoerg     IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
1137330f729Sjoerg 
1147330f729Sjoerg     Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
1157330f729Sjoerg     ClassSelectors.insert(Sel);
1167330f729Sjoerg   }
1177330f729Sjoerg }
1187330f729Sjoerg 
initializeSelectors(ASTContext & Ctx) const1197330f729Sjoerg void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
1207330f729Sjoerg 
1217330f729Sjoerg   { // Initialize selectors for: UIViewController
1227330f729Sjoerg     const SelectorDescriptor Selectors[] = {
1237330f729Sjoerg       { "addChildViewController", 1 },
1247330f729Sjoerg       { "viewDidAppear", 1 },
1257330f729Sjoerg       { "viewDidDisappear", 1 },
1267330f729Sjoerg       { "viewWillAppear", 1 },
1277330f729Sjoerg       { "viewWillDisappear", 1 },
1287330f729Sjoerg       { "removeFromParentViewController", 0 },
1297330f729Sjoerg       { "didReceiveMemoryWarning", 0 },
1307330f729Sjoerg       { "viewDidUnload", 0 },
1317330f729Sjoerg       { "viewDidLoad", 0 },
1327330f729Sjoerg       { "viewWillUnload", 0 },
1337330f729Sjoerg       { "updateViewConstraints", 0 },
1347330f729Sjoerg       { "encodeRestorableStateWithCoder", 1 },
1357330f729Sjoerg       { "restoreStateWithCoder", 1 }};
1367330f729Sjoerg 
1377330f729Sjoerg     fillSelectors(Ctx, Selectors, "UIViewController");
1387330f729Sjoerg   }
1397330f729Sjoerg 
1407330f729Sjoerg   { // Initialize selectors for: UIResponder
1417330f729Sjoerg     const SelectorDescriptor Selectors[] = {
1427330f729Sjoerg       { "resignFirstResponder", 0 }};
1437330f729Sjoerg 
1447330f729Sjoerg     fillSelectors(Ctx, Selectors, "UIResponder");
1457330f729Sjoerg   }
1467330f729Sjoerg 
1477330f729Sjoerg   { // Initialize selectors for: NSResponder
1487330f729Sjoerg     const SelectorDescriptor Selectors[] = {
1497330f729Sjoerg       { "encodeRestorableStateWithCoder", 1 },
1507330f729Sjoerg       { "restoreStateWithCoder", 1 }};
1517330f729Sjoerg 
1527330f729Sjoerg     fillSelectors(Ctx, Selectors, "NSResponder");
1537330f729Sjoerg   }
1547330f729Sjoerg 
1557330f729Sjoerg   { // Initialize selectors for: NSDocument
1567330f729Sjoerg     const SelectorDescriptor Selectors[] = {
1577330f729Sjoerg       { "encodeRestorableStateWithCoder", 1 },
1587330f729Sjoerg       { "restoreStateWithCoder", 1 }};
1597330f729Sjoerg 
1607330f729Sjoerg     fillSelectors(Ctx, Selectors, "NSDocument");
1617330f729Sjoerg   }
1627330f729Sjoerg 
1637330f729Sjoerg   IsInitialized = true;
1647330f729Sjoerg }
1657330f729Sjoerg 
checkASTDecl(const ObjCImplementationDecl * D,AnalysisManager & Mgr,BugReporter & BR) const1667330f729Sjoerg void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
1677330f729Sjoerg                                         AnalysisManager &Mgr,
1687330f729Sjoerg                                         BugReporter &BR) const {
1697330f729Sjoerg   ASTContext &Ctx = BR.getContext();
1707330f729Sjoerg 
1717330f729Sjoerg   // We need to initialize the selector table once.
1727330f729Sjoerg   if (!IsInitialized)
1737330f729Sjoerg     initializeSelectors(Ctx);
1747330f729Sjoerg 
1757330f729Sjoerg   // Find out whether this class has a superclass that we are supposed to check.
1767330f729Sjoerg   StringRef SuperclassName;
1777330f729Sjoerg   if (!isCheckableClass(D, SuperclassName))
1787330f729Sjoerg     return;
1797330f729Sjoerg 
1807330f729Sjoerg 
1817330f729Sjoerg   // Iterate over all instance methods.
1827330f729Sjoerg   for (auto *MD : D->instance_methods()) {
1837330f729Sjoerg     Selector S = MD->getSelector();
1847330f729Sjoerg     // Find out whether this is a selector that we want to check.
1857330f729Sjoerg     if (!SelectorsForClass[SuperclassName].count(S))
1867330f729Sjoerg       continue;
1877330f729Sjoerg 
1887330f729Sjoerg     // Check if the method calls its superclass implementation.
1897330f729Sjoerg     if (MD->getBody())
1907330f729Sjoerg     {
1917330f729Sjoerg       FindSuperCallVisitor Visitor(S);
1927330f729Sjoerg       Visitor.TraverseDecl(MD);
1937330f729Sjoerg 
1947330f729Sjoerg       // It doesn't call super, emit a diagnostic.
1957330f729Sjoerg       if (!Visitor.DoesCallSuper) {
1967330f729Sjoerg         PathDiagnosticLocation DLoc =
1977330f729Sjoerg           PathDiagnosticLocation::createEnd(MD->getBody(),
1987330f729Sjoerg                                             BR.getSourceManager(),
1997330f729Sjoerg                                             Mgr.getAnalysisDeclContext(D));
2007330f729Sjoerg 
2017330f729Sjoerg         const char *Name = "Missing call to superclass";
2027330f729Sjoerg         SmallString<320> Buf;
2037330f729Sjoerg         llvm::raw_svector_ostream os(Buf);
2047330f729Sjoerg 
2057330f729Sjoerg         os << "The '" << S.getAsString()
2067330f729Sjoerg            << "' instance method in " << SuperclassName.str() << " subclass '"
2077330f729Sjoerg            << *D << "' is missing a [super " << S.getAsString() << "] call";
2087330f729Sjoerg 
2097330f729Sjoerg         BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
2107330f729Sjoerg                            os.str(), DLoc);
2117330f729Sjoerg       }
2127330f729Sjoerg     }
2137330f729Sjoerg   }
2147330f729Sjoerg }
2157330f729Sjoerg 
2167330f729Sjoerg 
2177330f729Sjoerg //===----------------------------------------------------------------------===//
2187330f729Sjoerg // Check registration.
2197330f729Sjoerg //===----------------------------------------------------------------------===//
2207330f729Sjoerg 
registerObjCSuperCallChecker(CheckerManager & Mgr)2217330f729Sjoerg void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
2227330f729Sjoerg   Mgr.registerChecker<ObjCSuperCallChecker>();
2237330f729Sjoerg }
2247330f729Sjoerg 
shouldRegisterObjCSuperCallChecker(const CheckerManager & mgr)225*e038c9c4Sjoerg bool ento::shouldRegisterObjCSuperCallChecker(const CheckerManager &mgr) {
2267330f729Sjoerg   return true;
2277330f729Sjoerg }
2287330f729Sjoerg 
2297330f729Sjoerg /*
2307330f729Sjoerg  ToDo list for expanding this check in the future, the list is not exhaustive.
2317330f729Sjoerg  There are also cases where calling super is suggested but not "mandatory".
2327330f729Sjoerg  In addition to be able to check the classes and methods below, architectural
2337330f729Sjoerg  improvements like being able to allow for the super-call to be done in a called
2347330f729Sjoerg  method would be good too.
2357330f729Sjoerg 
2367330f729Sjoerg UIDocument subclasses
2377330f729Sjoerg - finishedHandlingError:recovered: (is multi-arg)
2387330f729Sjoerg - finishedHandlingError:recovered: (is multi-arg)
2397330f729Sjoerg 
2407330f729Sjoerg UIViewController subclasses
2417330f729Sjoerg - loadView (should *never* call super)
2427330f729Sjoerg - transitionFromViewController:toViewController:
2437330f729Sjoerg          duration:options:animations:completion: (is multi-arg)
2447330f729Sjoerg 
2457330f729Sjoerg UICollectionViewController subclasses
2467330f729Sjoerg - loadView (take care because UIViewController subclasses should NOT call super
2477330f729Sjoerg             in loadView, but UICollectionViewController subclasses should)
2487330f729Sjoerg 
2497330f729Sjoerg NSObject subclasses
2507330f729Sjoerg - doesNotRecognizeSelector (it only has to call super if it doesn't throw)
2517330f729Sjoerg 
2527330f729Sjoerg UIPopoverBackgroundView subclasses (some of those are class methods)
2537330f729Sjoerg - arrowDirection (should *never* call super)
2547330f729Sjoerg - arrowOffset (should *never* call super)
2557330f729Sjoerg - arrowBase (should *never* call super)
2567330f729Sjoerg - arrowHeight (should *never* call super)
2577330f729Sjoerg - contentViewInsets (should *never* call super)
2587330f729Sjoerg 
2597330f729Sjoerg UITextSelectionRect subclasses (some of those are properties)
2607330f729Sjoerg - rect (should *never* call super)
2617330f729Sjoerg - range (should *never* call super)
2627330f729Sjoerg - writingDirection (should *never* call super)
2637330f729Sjoerg - isVertical (should *never* call super)
2647330f729Sjoerg - containsStart (should *never* call super)
2657330f729Sjoerg - containsEnd (should *never* call super)
2667330f729Sjoerg */
267