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//===----------------------------------------------------------------------===// 40243791Sdim// FindSuperCallVisitor - Identify specific calls to the superclass. 41243791Sdim//===----------------------------------------------------------------------===// 42243791Sdim 43243791Sdimclass FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 44243791Sdimpublic: 45243791Sdim explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} 46243791Sdim 47243791Sdim bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 48243791Sdim if (E->getSelector() == Sel) 49243791Sdim if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) 50243791Sdim DoesCallSuper = true; 51243791Sdim 52243791Sdim // Recurse if we didn't find the super call yet. 53243791Sdim return !DoesCallSuper; 54243791Sdim } 55243791Sdim 56243791Sdim bool DoesCallSuper; 57243791Sdim 58243791Sdimprivate: 59243791Sdim Selector Sel; 60243791Sdim}; 61243791Sdim 62243791Sdim//===----------------------------------------------------------------------===// 63243791Sdim// ObjCSuperCallChecker 64243791Sdim//===----------------------------------------------------------------------===// 65243791Sdim 66243791Sdimnamespace { 67243791Sdimclass ObjCSuperCallChecker : public Checker< 68243791Sdim check::ASTDecl<ObjCImplementationDecl> > { 69243791Sdimpublic: 70249423Sdim ObjCSuperCallChecker() : IsInitialized(false) {} 71249423Sdim 72243791Sdim void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 73243791Sdim BugReporter &BR) const; 74249423Sdimprivate: 75249423Sdim bool isCheckableClass(const ObjCImplementationDecl *D, 76249423Sdim StringRef &SuperclassName) const; 77249423Sdim void initializeSelectors(ASTContext &Ctx) const; 78249423Sdim void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, 79249423Sdim StringRef ClassName) const; 80249423Sdim mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; 81249423Sdim mutable bool IsInitialized; 82243791Sdim}; 83249423Sdim 84243791Sdim} 85243791Sdim 86249423Sdim/// \brief Determine whether the given class has a superclass that we want 87249423Sdim/// to check. The name of the found superclass is stored in SuperclassName. 88249423Sdim/// 89249423Sdim/// \param D The declaration to check for superclasses. 90249423Sdim/// \param[out] SuperclassName On return, the found superclass name. 91249423Sdimbool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, 92249423Sdim StringRef &SuperclassName) const { 93249423Sdim const ObjCInterfaceDecl *ID = D->getClassInterface(); 94249423Sdim for ( ; ID ; ID = ID->getSuperClass()) 95249423Sdim { 96249423Sdim SuperclassName = ID->getIdentifier()->getName(); 97249423Sdim if (SelectorsForClass.count(SuperclassName)) 98249423Sdim return true; 99249423Sdim } 100249423Sdim return false; 101249423Sdim} 102249423Sdim 103249423Sdimvoid ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, 104249423Sdim ArrayRef<SelectorDescriptor> Sel, 105249423Sdim StringRef ClassName) const { 106249423Sdim llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; 107249423Sdim // Fill the Selectors SmallSet with all selectors we want to check. 108249423Sdim for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); 109249423Sdim I != E; ++I) { 110249423Sdim SelectorDescriptor Descriptor = *I; 111249423Sdim assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. 112249423Sdim 113249423Sdim // Get the selector. 114249423Sdim IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); 115249423Sdim 116249423Sdim Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); 117249423Sdim ClassSelectors.insert(Sel); 118249423Sdim } 119249423Sdim} 120249423Sdim 121249423Sdimvoid ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { 122249423Sdim 123249423Sdim { // Initialize selectors for: UIViewController 124249423Sdim const SelectorDescriptor Selectors[] = { 125249423Sdim { "addChildViewController", 1 }, 126249423Sdim { "viewDidAppear", 1 }, 127249423Sdim { "viewDidDisappear", 1 }, 128249423Sdim { "viewWillAppear", 1 }, 129249423Sdim { "viewWillDisappear", 1 }, 130249423Sdim { "removeFromParentViewController", 0 }, 131249423Sdim { "didReceiveMemoryWarning", 0 }, 132249423Sdim { "viewDidUnload", 0 }, 133249423Sdim { "viewDidLoad", 0 }, 134249423Sdim { "viewWillUnload", 0 }, 135249423Sdim { "updateViewConstraints", 0 }, 136249423Sdim { "encodeRestorableStateWithCoder", 1 }, 137249423Sdim { "restoreStateWithCoder", 1 }}; 138249423Sdim 139249423Sdim fillSelectors(Ctx, Selectors, "UIViewController"); 140249423Sdim } 141249423Sdim 142249423Sdim { // Initialize selectors for: UIResponder 143249423Sdim const SelectorDescriptor Selectors[] = { 144249423Sdim { "resignFirstResponder", 0 }}; 145249423Sdim 146249423Sdim fillSelectors(Ctx, Selectors, "UIResponder"); 147249423Sdim } 148249423Sdim 149249423Sdim { // Initialize selectors for: NSResponder 150249423Sdim const SelectorDescriptor Selectors[] = { 151249423Sdim { "encodeRestorableStateWithCoder", 1 }, 152249423Sdim { "restoreStateWithCoder", 1 }}; 153249423Sdim 154249423Sdim fillSelectors(Ctx, Selectors, "NSResponder"); 155249423Sdim } 156249423Sdim 157249423Sdim { // Initialize selectors for: NSDocument 158249423Sdim const SelectorDescriptor Selectors[] = { 159249423Sdim { "encodeRestorableStateWithCoder", 1 }, 160249423Sdim { "restoreStateWithCoder", 1 }}; 161249423Sdim 162249423Sdim fillSelectors(Ctx, Selectors, "NSDocument"); 163249423Sdim } 164249423Sdim 165249423Sdim IsInitialized = true; 166249423Sdim} 167249423Sdim 168243791Sdimvoid ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 169243791Sdim AnalysisManager &Mgr, 170243791Sdim BugReporter &BR) const { 171243791Sdim ASTContext &Ctx = BR.getContext(); 172243791Sdim 173249423Sdim // We need to initialize the selector table once. 174249423Sdim if (!IsInitialized) 175249423Sdim initializeSelectors(Ctx); 176249423Sdim 177249423Sdim // Find out whether this class has a superclass that we are supposed to check. 178249423Sdim StringRef SuperclassName; 179249423Sdim if (!isCheckableClass(D, SuperclassName)) 180243791Sdim return; 181243791Sdim 182243791Sdim 183243791Sdim // Iterate over all instance methods. 184243791Sdim for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), 185243791Sdim E = D->instmeth_end(); 186243791Sdim I != E; ++I) { 187243791Sdim Selector S = (*I)->getSelector(); 188243791Sdim // Find out whether this is a selector that we want to check. 189249423Sdim if (!SelectorsForClass[SuperclassName].count(S)) 190243791Sdim continue; 191243791Sdim 192243791Sdim ObjCMethodDecl *MD = *I; 193243791Sdim 194243791Sdim // Check if the method calls its superclass implementation. 195243791Sdim if (MD->getBody()) 196243791Sdim { 197243791Sdim FindSuperCallVisitor Visitor(S); 198243791Sdim Visitor.TraverseDecl(MD); 199243791Sdim 200243791Sdim // It doesn't call super, emit a diagnostic. 201243791Sdim if (!Visitor.DoesCallSuper) { 202243791Sdim PathDiagnosticLocation DLoc = 203243791Sdim PathDiagnosticLocation::createEnd(MD->getBody(), 204243791Sdim BR.getSourceManager(), 205243791Sdim Mgr.getAnalysisDeclContext(D)); 206243791Sdim 207243791Sdim const char *Name = "Missing call to superclass"; 208249423Sdim SmallString<320> Buf; 209243791Sdim llvm::raw_svector_ostream os(Buf); 210243791Sdim 211243791Sdim os << "The '" << S.getAsString() 212249423Sdim << "' instance method in " << SuperclassName.str() << " subclass '" 213249423Sdim << *D << "' is missing a [super " << S.getAsString() << "] call"; 214243791Sdim 215243791Sdim BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, 216243791Sdim os.str(), DLoc); 217243791Sdim } 218243791Sdim } 219243791Sdim } 220243791Sdim} 221243791Sdim 222243791Sdim 223243791Sdim//===----------------------------------------------------------------------===// 224243791Sdim// Check registration. 225243791Sdim//===----------------------------------------------------------------------===// 226243791Sdim 227243791Sdimvoid ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { 228243791Sdim Mgr.registerChecker<ObjCSuperCallChecker>(); 229243791Sdim} 230243791Sdim 231243791Sdim 232243791Sdim/* 233243791Sdim ToDo list for expanding this check in the future, the list is not exhaustive. 234243791Sdim There are also cases where calling super is suggested but not "mandatory". 235243791Sdim In addition to be able to check the classes and methods below, architectural 236243791Sdim improvements like being able to allow for the super-call to be done in a called 237243791Sdim method would be good too. 238243791Sdim 239243791SdimUIDocument subclasses 240243791Sdim- finishedHandlingError:recovered: (is multi-arg) 241243791Sdim- finishedHandlingError:recovered: (is multi-arg) 242243791Sdim 243243791SdimUIViewController subclasses 244243791Sdim- loadView (should *never* call super) 245243791Sdim- transitionFromViewController:toViewController: 246243791Sdim duration:options:animations:completion: (is multi-arg) 247243791Sdim 248243791SdimUICollectionViewController subclasses 249243791Sdim- loadView (take care because UIViewController subclasses should NOT call super 250243791Sdim in loadView, but UICollectionViewController subclasses should) 251243791Sdim 252243791SdimNSObject subclasses 253243791Sdim- doesNotRecognizeSelector (it only has to call super if it doesn't throw) 254243791Sdim 255243791SdimUIPopoverBackgroundView subclasses (some of those are class methods) 256243791Sdim- arrowDirection (should *never* call super) 257243791Sdim- arrowOffset (should *never* call super) 258243791Sdim- arrowBase (should *never* call super) 259243791Sdim- arrowHeight (should *never* call super) 260243791Sdim- contentViewInsets (should *never* call super) 261243791Sdim 262243791SdimUITextSelectionRect subclasses (some of those are properties) 263243791Sdim- rect (should *never* call super) 264243791Sdim- range (should *never* call super) 265243791Sdim- writingDirection (should *never* call super) 266243791Sdim- isVertical (should *never* call super) 267243791Sdim- containsStart (should *never* call super) 268243791Sdim- containsEnd (should *never* call super) 269243791Sdim*/ 270