1243791Sdim//==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==// 2243791Sdim// 3243791Sdim// The LLVM Compiler Infrastructure 4243791Sdim// 5243791Sdim// This file is distributed under the University of Illinois Open Source 6243791Sdim// License. See LICENSE.TXT for details. 7243791Sdim// 8243791Sdim//===----------------------------------------------------------------------===// 9243791Sdim// 10243791Sdim// This file defines a ObjCMissingSuperCallChecker, a checker that 11243791Sdim// analyzes a UIViewController implementation to determine if it 12243791Sdim// correctly calls super in the methods where this is mandatory. 13243791Sdim// 14243791Sdim//===----------------------------------------------------------------------===// 15243791Sdim 16243791Sdim#include "ClangSACheckers.h" 17249423Sdim#include "clang/AST/DeclObjC.h" 18249423Sdim#include "clang/AST/Expr.h" 19249423Sdim#include "clang/AST/ExprObjC.h" 20249423Sdim#include "clang/AST/RecursiveASTVisitor.h" 21249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 22249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 23243791Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 24243791Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 25249423Sdim#include "llvm/ADT/SmallSet.h" 26243791Sdim#include "llvm/ADT/SmallString.h" 27243791Sdim#include "llvm/Support/raw_ostream.h" 28243791Sdim 29243791Sdimusing namespace clang; 30243791Sdimusing namespace ento; 31243791Sdim 32249423Sdimnamespace { 33249423Sdimstruct SelectorDescriptor { 34249423Sdim const char *SelectorName; 35249423Sdim unsigned ArgumentCount; 36249423Sdim}; 37243791Sdim 38243791Sdim//===----------------------------------------------------------------------===// 39243791Sdim// FindSuperCallVisitor - Identify specific calls to the superclass. 40243791Sdim//===----------------------------------------------------------------------===// 41243791Sdim 42243791Sdimclass FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 43243791Sdimpublic: 44243791Sdim explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} 45243791Sdim 46243791Sdim bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 47243791Sdim if (E->getSelector() == Sel) 48243791Sdim if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) 49243791Sdim DoesCallSuper = true; 50243791Sdim 51243791Sdim // Recurse if we didn't find the super call yet. 52296417Sdim return !DoesCallSuper; 53243791Sdim } 54243791Sdim 55243791Sdim bool DoesCallSuper; 56243791Sdim 57243791Sdimprivate: 58243791Sdim Selector Sel; 59243791Sdim}; 60243791Sdim 61243791Sdim//===----------------------------------------------------------------------===// 62296417Sdim// ObjCSuperCallChecker 63243791Sdim//===----------------------------------------------------------------------===// 64243791Sdim 65243791Sdimclass ObjCSuperCallChecker : public Checker< 66243791Sdim check::ASTDecl<ObjCImplementationDecl> > { 67243791Sdimpublic: 68249423Sdim ObjCSuperCallChecker() : IsInitialized(false) {} 69249423Sdim 70243791Sdim void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 71243791Sdim BugReporter &BR) const; 72249423Sdimprivate: 73249423Sdim bool isCheckableClass(const ObjCImplementationDecl *D, 74249423Sdim StringRef &SuperclassName) const; 75249423Sdim void initializeSelectors(ASTContext &Ctx) const; 76249423Sdim void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, 77249423Sdim StringRef ClassName) const; 78249423Sdim mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; 79249423Sdim mutable bool IsInitialized; 80243791Sdim}; 81249423Sdim 82243791Sdim} 83243791Sdim 84249423Sdim/// \brief Determine whether the given class has a superclass that we want 85249423Sdim/// to check. The name of the found superclass is stored in SuperclassName. 86249423Sdim/// 87249423Sdim/// \param D The declaration to check for superclasses. 88249423Sdim/// \param[out] SuperclassName On return, the found superclass name. 89249423Sdimbool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, 90249423Sdim StringRef &SuperclassName) const { 91296417Sdim const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass(); 92249423Sdim for ( ; ID ; ID = ID->getSuperClass()) 93249423Sdim { 94249423Sdim SuperclassName = ID->getIdentifier()->getName(); 95249423Sdim if (SelectorsForClass.count(SuperclassName)) 96249423Sdim return true; 97249423Sdim } 98249423Sdim return false; 99249423Sdim} 100249423Sdim 101249423Sdimvoid ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, 102249423Sdim ArrayRef<SelectorDescriptor> Sel, 103249423Sdim StringRef ClassName) const { 104249423Sdim llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; 105249423Sdim // Fill the Selectors SmallSet with all selectors we want to check. 106249423Sdim for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); 107249423Sdim I != E; ++I) { 108249423Sdim SelectorDescriptor Descriptor = *I; 109249423Sdim assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. 110249423Sdim 111249423Sdim // Get the selector. 112249423Sdim IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); 113249423Sdim 114249423Sdim Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); 115249423Sdim ClassSelectors.insert(Sel); 116249423Sdim } 117249423Sdim} 118249423Sdim 119249423Sdimvoid ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { 120249423Sdim 121249423Sdim { // Initialize selectors for: UIViewController 122249423Sdim const SelectorDescriptor Selectors[] = { 123249423Sdim { "addChildViewController", 1 }, 124249423Sdim { "viewDidAppear", 1 }, 125249423Sdim { "viewDidDisappear", 1 }, 126249423Sdim { "viewWillAppear", 1 }, 127249423Sdim { "viewWillDisappear", 1 }, 128249423Sdim { "removeFromParentViewController", 0 }, 129249423Sdim { "didReceiveMemoryWarning", 0 }, 130249423Sdim { "viewDidUnload", 0 }, 131249423Sdim { "viewDidLoad", 0 }, 132249423Sdim { "viewWillUnload", 0 }, 133249423Sdim { "updateViewConstraints", 0 }, 134249423Sdim { "encodeRestorableStateWithCoder", 1 }, 135249423Sdim { "restoreStateWithCoder", 1 }}; 136249423Sdim 137249423Sdim fillSelectors(Ctx, Selectors, "UIViewController"); 138249423Sdim } 139249423Sdim 140249423Sdim { // Initialize selectors for: UIResponder 141249423Sdim const SelectorDescriptor Selectors[] = { 142249423Sdim { "resignFirstResponder", 0 }}; 143249423Sdim 144249423Sdim fillSelectors(Ctx, Selectors, "UIResponder"); 145249423Sdim } 146249423Sdim 147249423Sdim { // Initialize selectors for: NSResponder 148249423Sdim const SelectorDescriptor Selectors[] = { 149249423Sdim { "encodeRestorableStateWithCoder", 1 }, 150249423Sdim { "restoreStateWithCoder", 1 }}; 151249423Sdim 152249423Sdim fillSelectors(Ctx, Selectors, "NSResponder"); 153249423Sdim } 154249423Sdim 155249423Sdim { // Initialize selectors for: NSDocument 156249423Sdim const SelectorDescriptor Selectors[] = { 157249423Sdim { "encodeRestorableStateWithCoder", 1 }, 158249423Sdim { "restoreStateWithCoder", 1 }}; 159249423Sdim 160249423Sdim fillSelectors(Ctx, Selectors, "NSDocument"); 161249423Sdim } 162249423Sdim 163249423Sdim IsInitialized = true; 164249423Sdim} 165249423Sdim 166243791Sdimvoid ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 167243791Sdim AnalysisManager &Mgr, 168243791Sdim BugReporter &BR) const { 169243791Sdim ASTContext &Ctx = BR.getContext(); 170243791Sdim 171249423Sdim // We need to initialize the selector table once. 172249423Sdim if (!IsInitialized) 173249423Sdim initializeSelectors(Ctx); 174249423Sdim 175249423Sdim // Find out whether this class has a superclass that we are supposed to check. 176249423Sdim StringRef SuperclassName; 177249423Sdim if (!isCheckableClass(D, SuperclassName)) 178243791Sdim return; 179243791Sdim 180243791Sdim 181243791Sdim // Iterate over all instance methods. 182276479Sdim for (auto *MD : D->instance_methods()) { 183276479Sdim Selector S = MD->getSelector(); 184243791Sdim // Find out whether this is a selector that we want to check. 185249423Sdim if (!SelectorsForClass[SuperclassName].count(S)) 186243791Sdim continue; 187243791Sdim 188243791Sdim // Check if the method calls its superclass implementation. 189243791Sdim if (MD->getBody()) 190243791Sdim { 191243791Sdim FindSuperCallVisitor Visitor(S); 192243791Sdim Visitor.TraverseDecl(MD); 193243791Sdim 194243791Sdim // It doesn't call super, emit a diagnostic. 195243791Sdim if (!Visitor.DoesCallSuper) { 196243791Sdim PathDiagnosticLocation DLoc = 197243791Sdim PathDiagnosticLocation::createEnd(MD->getBody(), 198243791Sdim BR.getSourceManager(), 199243791Sdim Mgr.getAnalysisDeclContext(D)); 200243791Sdim 201243791Sdim const char *Name = "Missing call to superclass"; 202249423Sdim SmallString<320> Buf; 203243791Sdim llvm::raw_svector_ostream os(Buf); 204243791Sdim 205296417Sdim os << "The '" << S.getAsString() 206249423Sdim << "' instance method in " << SuperclassName.str() << " subclass '" 207249423Sdim << *D << "' is missing a [super " << S.getAsString() << "] call"; 208243791Sdim 209276479Sdim BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, 210243791Sdim os.str(), DLoc); 211243791Sdim } 212243791Sdim } 213243791Sdim } 214243791Sdim} 215243791Sdim 216243791Sdim 217243791Sdim//===----------------------------------------------------------------------===// 218243791Sdim// Check registration. 219243791Sdim//===----------------------------------------------------------------------===// 220243791Sdim 221243791Sdimvoid ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { 222243791Sdim Mgr.registerChecker<ObjCSuperCallChecker>(); 223243791Sdim} 224243791Sdim 225243791Sdim 226243791Sdim/* 227243791Sdim ToDo list for expanding this check in the future, the list is not exhaustive. 228243791Sdim There are also cases where calling super is suggested but not "mandatory". 229243791Sdim In addition to be able to check the classes and methods below, architectural 230243791Sdim improvements like being able to allow for the super-call to be done in a called 231243791Sdim method would be good too. 232243791Sdim 233243791SdimUIDocument subclasses 234243791Sdim- finishedHandlingError:recovered: (is multi-arg) 235243791Sdim- finishedHandlingError:recovered: (is multi-arg) 236243791Sdim 237243791SdimUIViewController subclasses 238243791Sdim- loadView (should *never* call super) 239243791Sdim- transitionFromViewController:toViewController: 240243791Sdim duration:options:animations:completion: (is multi-arg) 241243791Sdim 242243791SdimUICollectionViewController subclasses 243243791Sdim- loadView (take care because UIViewController subclasses should NOT call super 244243791Sdim in loadView, but UICollectionViewController subclasses should) 245243791Sdim 246243791SdimNSObject subclasses 247243791Sdim- doesNotRecognizeSelector (it only has to call super if it doesn't throw) 248243791Sdim 249243791SdimUIPopoverBackgroundView subclasses (some of those are class methods) 250243791Sdim- arrowDirection (should *never* call super) 251243791Sdim- arrowOffset (should *never* call super) 252243791Sdim- arrowBase (should *never* call super) 253243791Sdim- arrowHeight (should *never* call super) 254243791Sdim- contentViewInsets (should *never* call super) 255243791Sdim 256243791SdimUITextSelectionRect subclasses (some of those are properties) 257243791Sdim- rect (should *never* call super) 258243791Sdim- range (should *never* call super) 259243791Sdim- writingDirection (should *never* call super) 260243791Sdim- isVertical (should *never* call super) 261243791Sdim- containsStart (should *never* call super) 262243791Sdim- containsEnd (should *never* call super) 263243791Sdim*/ 264