1 //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
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.
14 //===----------------------------------------------------------------------===//
16 #include "ClangSACheckers.h"
17 #include "clang/StaticAnalyzer/Core/Checker.h"
18 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
20 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
21 #include "clang/AST/ExprObjC.h"
22 #include "clang/AST/Expr.h"
23 #include "clang/AST/DeclObjC.h"
24 #include "clang/AST/RecursiveASTVisitor.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/ADT/SmallSet.h"
27 #include "llvm/Support/raw_ostream.h"
29 using namespace clang;
32 static bool isUIViewControllerSubclass(ASTContext &Ctx,
33 const ObjCImplementationDecl *D) {
34 IdentifierInfo *ViewControllerII = &Ctx.Idents.get("UIViewController");
35 const ObjCInterfaceDecl *ID = D->getClassInterface();
37 for ( ; ID; ID = ID->getSuperClass())
38 if (ID->getIdentifier() == ViewControllerII)
43 //===----------------------------------------------------------------------===//
44 // FindSuperCallVisitor - Identify specific calls to the superclass.
45 //===----------------------------------------------------------------------===//
47 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
49 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
51 bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
52 if (E->getSelector() == Sel)
53 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
56 // Recurse if we didn't find the super call yet.
57 return !DoesCallSuper;
66 //===----------------------------------------------------------------------===//
67 // ObjCSuperCallChecker
68 //===----------------------------------------------------------------------===//
71 class ObjCSuperCallChecker : public Checker<
72 check::ASTDecl<ObjCImplementationDecl> > {
74 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
75 BugReporter &BR) const;
79 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
81 BugReporter &BR) const {
82 ASTContext &Ctx = BR.getContext();
84 if (!isUIViewControllerSubclass(Ctx, D))
87 const char *SelectorNames[] =
88 {"addChildViewController", "viewDidAppear", "viewDidDisappear",
89 "viewWillAppear", "viewWillDisappear", "removeFromParentViewController",
90 "didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload",
92 const unsigned SelectorArgumentCounts[] =
93 {1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
94 const size_t SelectorCount = llvm::array_lengthof(SelectorNames);
95 assert(llvm::array_lengthof(SelectorArgumentCounts) == SelectorCount);
97 // Fill the Selectors SmallSet with all selectors we want to check.
98 llvm::SmallSet<Selector, 16> Selectors;
99 for (size_t i = 0; i < SelectorCount; i++) {
100 unsigned ArgumentCount = SelectorArgumentCounts[i];
101 const char *SelectorCString = SelectorNames[i];
104 IdentifierInfo *II = &Ctx.Idents.get(SelectorCString);
105 Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II));
108 // Iterate over all instance methods.
109 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
110 E = D->instmeth_end();
112 Selector S = (*I)->getSelector();
113 // Find out whether this is a selector that we want to check.
114 if (!Selectors.count(S))
117 ObjCMethodDecl *MD = *I;
119 // Check if the method calls its superclass implementation.
122 FindSuperCallVisitor Visitor(S);
123 Visitor.TraverseDecl(MD);
125 // It doesn't call super, emit a diagnostic.
126 if (!Visitor.DoesCallSuper) {
127 PathDiagnosticLocation DLoc =
128 PathDiagnosticLocation::createEnd(MD->getBody(),
129 BR.getSourceManager(),
130 Mgr.getAnalysisDeclContext(D));
132 const char *Name = "Missing call to superclass";
133 SmallString<256> Buf;
134 llvm::raw_svector_ostream os(Buf);
136 os << "The '" << S.getAsString()
137 << "' instance method in UIViewController subclass '" << *D
138 << "' is missing a [super " << S.getAsString() << "] call";
140 BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
148 //===----------------------------------------------------------------------===//
149 // Check registration.
150 //===----------------------------------------------------------------------===//
152 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
153 Mgr.registerChecker<ObjCSuperCallChecker>();
158 ToDo list for expanding this check in the future, the list is not exhaustive.
159 There are also cases where calling super is suggested but not "mandatory".
160 In addition to be able to check the classes and methods below, architectural
161 improvements like being able to allow for the super-call to be done in a called
162 method would be good too.
165 UIResponder subclasses
166 - resignFirstResponder
168 NSResponder subclasses
171 *** more difficult cases:
173 UIDocument subclasses
174 - finishedHandlingError:recovered: (is multi-arg)
175 - finishedHandlingError:recovered: (is multi-arg)
177 UIViewController subclasses
178 - loadView (should *never* call super)
179 - transitionFromViewController:toViewController:
180 duration:options:animations:completion: (is multi-arg)
182 UICollectionViewController subclasses
183 - loadView (take care because UIViewController subclasses should NOT call super
184 in loadView, but UICollectionViewController subclasses should)
187 - doesNotRecognizeSelector (it only has to call super if it doesn't throw)
189 UIPopoverBackgroundView subclasses (some of those are class methods)
190 - arrowDirection (should *never* call super)
191 - arrowOffset (should *never* call super)
192 - arrowBase (should *never* call super)
193 - arrowHeight (should *never* call super)
194 - contentViewInsets (should *never* call super)
196 UITextSelectionRect subclasses (some of those are properties)
197 - rect (should *never* call super)
198 - range (should *never* call super)
199 - writingDirection (should *never* call super)
200 - isVertical (should *never* call super)
201 - containsStart (should *never* call super)
202 - containsEnd (should *never* call super)