1 //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 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. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "ClangSACheckers.h" 17 #include "clang/AST/DeclObjC.h" 18 #include "clang/AST/Expr.h" 19 #include "clang/AST/ExprObjC.h" 20 #include "clang/AST/RecursiveASTVisitor.h" 21 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 22 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 23 #include "clang/StaticAnalyzer/Core/Checker.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 25 #include "llvm/ADT/SmallSet.h" 26 #include "llvm/ADT/SmallString.h" 27 #include "llvm/Support/raw_ostream.h" 28 29 using namespace clang; 30 using namespace ento; 31 32 static bool isUIViewControllerSubclass(ASTContext &Ctx, 33 const ObjCImplementationDecl *D) { 34 IdentifierInfo *ViewControllerII = &Ctx.Idents.get("UIViewController"); 35 const ObjCInterfaceDecl *ID = D->getClassInterface(); 36 37 for ( ; ID; ID = ID->getSuperClass()) 38 if (ID->getIdentifier() == ViewControllerII) 39 return true; 40 return false; 41 } 42 43 //===----------------------------------------------------------------------===// 44 // FindSuperCallVisitor - Identify specific calls to the superclass. 45 //===----------------------------------------------------------------------===// 46 47 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 48 public: 49 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} 50 51 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 52 if (E->getSelector() == Sel) 53 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) 54 DoesCallSuper = true; 55 56 // Recurse if we didn't find the super call yet. 57 return !DoesCallSuper; 58 } 59 60 bool DoesCallSuper; 61 62 private: 63 Selector Sel; 64 }; 65 66 //===----------------------------------------------------------------------===// 67 // ObjCSuperCallChecker 68 //===----------------------------------------------------------------------===// 69 70 namespace { 71 class ObjCSuperCallChecker : public Checker< 72 check::ASTDecl<ObjCImplementationDecl> > { 73 public: 74 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 75 BugReporter &BR) const; 76 }; 77 } 78 79 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 80 AnalysisManager &Mgr, 81 BugReporter &BR) const { 82 ASTContext &Ctx = BR.getContext(); 83 84 if (!isUIViewControllerSubclass(Ctx, D)) 85 return; 86 87 const char *SelectorNames[] = 88 {"addChildViewController", "viewDidAppear", "viewDidDisappear", 89 "viewWillAppear", "viewWillDisappear", "removeFromParentViewController", 90 "didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload", 91 "viewDidLoad"}; 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); 96 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]; 102 103 // Get the selector. 104 IdentifierInfo *II = &Ctx.Idents.get(SelectorCString); 105 Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II)); 106 } 107 108 // Iterate over all instance methods. 109 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), 110 E = D->instmeth_end(); 111 I != E; ++I) { 112 Selector S = (*I)->getSelector(); 113 // Find out whether this is a selector that we want to check. 114 if (!Selectors.count(S)) 115 continue; 116 117 ObjCMethodDecl *MD = *I; 118 119 // Check if the method calls its superclass implementation. 120 if (MD->getBody()) 121 { 122 FindSuperCallVisitor Visitor(S); 123 Visitor.TraverseDecl(MD); 124 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)); 131 132 const char *Name = "Missing call to superclass"; 133 SmallString<256> Buf; 134 llvm::raw_svector_ostream os(Buf); 135 136 os << "The '" << S.getAsString() 137 << "' instance method in UIViewController subclass '" << *D 138 << "' is missing a [super " << S.getAsString() << "] call"; 139 140 BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, 141 os.str(), DLoc); 142 } 143 } 144 } 145 } 146 147 148 //===----------------------------------------------------------------------===// 149 // Check registration. 150 //===----------------------------------------------------------------------===// 151 152 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { 153 Mgr.registerChecker<ObjCSuperCallChecker>(); 154 } 155 156 157 /* 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. 163 164 *** trivial cases: 165 UIResponder subclasses 166 - resignFirstResponder 167 168 NSResponder subclasses 169 - cursorUpdate 170 171 *** more difficult cases: 172 173 UIDocument subclasses 174 - finishedHandlingError:recovered: (is multi-arg) 175 - finishedHandlingError:recovered: (is multi-arg) 176 177 UIViewController subclasses 178 - loadView (should *never* call super) 179 - transitionFromViewController:toViewController: 180 duration:options:animations:completion: (is multi-arg) 181 182 UICollectionViewController subclasses 183 - loadView (take care because UIViewController subclasses should NOT call super 184 in loadView, but UICollectionViewController subclasses should) 185 186 NSObject subclasses 187 - doesNotRecognizeSelector (it only has to call super if it doesn't throw) 188 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) 195 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) 203 */ 204