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 namespace { 33 struct SelectorDescriptor { 34 const char *SelectorName; 35 unsigned ArgumentCount; 36 }; 37 } 38 39 //===----------------------------------------------------------------------===// 40 // FindSuperCallVisitor - Identify specific calls to the superclass. 41 //===----------------------------------------------------------------------===// 42 43 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 44 public: 45 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} 46 47 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 48 if (E->getSelector() == Sel) 49 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) 50 DoesCallSuper = true; 51 52 // Recurse if we didn't find the super call yet. 53 return !DoesCallSuper; 54 } 55 56 bool DoesCallSuper; 57 58 private: 59 Selector Sel; 60 }; 61 62 //===----------------------------------------------------------------------===// 63 // ObjCSuperCallChecker 64 //===----------------------------------------------------------------------===// 65 66 namespace { 67 class ObjCSuperCallChecker : public Checker< 68 check::ASTDecl<ObjCImplementationDecl> > { 69 public: 70 ObjCSuperCallChecker() : IsInitialized(false) {} 71 72 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 73 BugReporter &BR) const; 74 private: 75 bool isCheckableClass(const ObjCImplementationDecl *D, 76 StringRef &SuperclassName) const; 77 void initializeSelectors(ASTContext &Ctx) const; 78 void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, 79 StringRef ClassName) const; 80 mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; 81 mutable bool IsInitialized; 82 }; 83 84 } 85 86 /// \brief Determine whether the given class has a superclass that we want 87 /// to check. The name of the found superclass is stored in SuperclassName. 88 /// 89 /// \param D The declaration to check for superclasses. 90 /// \param[out] SuperclassName On return, the found superclass name. 91 bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, 92 StringRef &SuperclassName) const { 93 const ObjCInterfaceDecl *ID = D->getClassInterface(); 94 for ( ; ID ; ID = ID->getSuperClass()) 95 { 96 SuperclassName = ID->getIdentifier()->getName(); 97 if (SelectorsForClass.count(SuperclassName)) 98 return true; 99 } 100 return false; 101 } 102 103 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, 104 ArrayRef<SelectorDescriptor> Sel, 105 StringRef ClassName) const { 106 llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; 107 // Fill the Selectors SmallSet with all selectors we want to check. 108 for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); 109 I != E; ++I) { 110 SelectorDescriptor Descriptor = *I; 111 assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. 112 113 // Get the selector. 114 IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); 115 116 Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); 117 ClassSelectors.insert(Sel); 118 } 119 } 120 121 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { 122 123 { // Initialize selectors for: UIViewController 124 const SelectorDescriptor Selectors[] = { 125 { "addChildViewController", 1 }, 126 { "viewDidAppear", 1 }, 127 { "viewDidDisappear", 1 }, 128 { "viewWillAppear", 1 }, 129 { "viewWillDisappear", 1 }, 130 { "removeFromParentViewController", 0 }, 131 { "didReceiveMemoryWarning", 0 }, 132 { "viewDidUnload", 0 }, 133 { "viewDidLoad", 0 }, 134 { "viewWillUnload", 0 }, 135 { "updateViewConstraints", 0 }, 136 { "encodeRestorableStateWithCoder", 1 }, 137 { "restoreStateWithCoder", 1 }}; 138 139 fillSelectors(Ctx, Selectors, "UIViewController"); 140 } 141 142 { // Initialize selectors for: UIResponder 143 const SelectorDescriptor Selectors[] = { 144 { "resignFirstResponder", 0 }}; 145 146 fillSelectors(Ctx, Selectors, "UIResponder"); 147 } 148 149 { // Initialize selectors for: NSResponder 150 const SelectorDescriptor Selectors[] = { 151 { "encodeRestorableStateWithCoder", 1 }, 152 { "restoreStateWithCoder", 1 }}; 153 154 fillSelectors(Ctx, Selectors, "NSResponder"); 155 } 156 157 { // Initialize selectors for: NSDocument 158 const SelectorDescriptor Selectors[] = { 159 { "encodeRestorableStateWithCoder", 1 }, 160 { "restoreStateWithCoder", 1 }}; 161 162 fillSelectors(Ctx, Selectors, "NSDocument"); 163 } 164 165 IsInitialized = true; 166 } 167 168 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 169 AnalysisManager &Mgr, 170 BugReporter &BR) const { 171 ASTContext &Ctx = BR.getContext(); 172 173 // We need to initialize the selector table once. 174 if (!IsInitialized) 175 initializeSelectors(Ctx); 176 177 // Find out whether this class has a superclass that we are supposed to check. 178 StringRef SuperclassName; 179 if (!isCheckableClass(D, SuperclassName)) 180 return; 181 182 183 // Iterate over all instance methods. 184 for (auto *MD : D->instance_methods()) { 185 Selector S = MD->getSelector(); 186 // Find out whether this is a selector that we want to check. 187 if (!SelectorsForClass[SuperclassName].count(S)) 188 continue; 189 190 // Check if the method calls its superclass implementation. 191 if (MD->getBody()) 192 { 193 FindSuperCallVisitor Visitor(S); 194 Visitor.TraverseDecl(MD); 195 196 // It doesn't call super, emit a diagnostic. 197 if (!Visitor.DoesCallSuper) { 198 PathDiagnosticLocation DLoc = 199 PathDiagnosticLocation::createEnd(MD->getBody(), 200 BR.getSourceManager(), 201 Mgr.getAnalysisDeclContext(D)); 202 203 const char *Name = "Missing call to superclass"; 204 SmallString<320> Buf; 205 llvm::raw_svector_ostream os(Buf); 206 207 os << "The '" << S.getAsString() 208 << "' instance method in " << SuperclassName.str() << " subclass '" 209 << *D << "' is missing a [super " << S.getAsString() << "] call"; 210 211 BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, 212 os.str(), DLoc); 213 } 214 } 215 } 216 } 217 218 219 //===----------------------------------------------------------------------===// 220 // Check registration. 221 //===----------------------------------------------------------------------===// 222 223 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { 224 Mgr.registerChecker<ObjCSuperCallChecker>(); 225 } 226 227 228 /* 229 ToDo list for expanding this check in the future, the list is not exhaustive. 230 There are also cases where calling super is suggested but not "mandatory". 231 In addition to be able to check the classes and methods below, architectural 232 improvements like being able to allow for the super-call to be done in a called 233 method would be good too. 234 235 UIDocument subclasses 236 - finishedHandlingError:recovered: (is multi-arg) 237 - finishedHandlingError:recovered: (is multi-arg) 238 239 UIViewController subclasses 240 - loadView (should *never* call super) 241 - transitionFromViewController:toViewController: 242 duration:options:animations:completion: (is multi-arg) 243 244 UICollectionViewController subclasses 245 - loadView (take care because UIViewController subclasses should NOT call super 246 in loadView, but UICollectionViewController subclasses should) 247 248 NSObject subclasses 249 - doesNotRecognizeSelector (it only has to call super if it doesn't throw) 250 251 UIPopoverBackgroundView subclasses (some of those are class methods) 252 - arrowDirection (should *never* call super) 253 - arrowOffset (should *never* call super) 254 - arrowBase (should *never* call super) 255 - arrowHeight (should *never* call super) 256 - contentViewInsets (should *never* call super) 257 258 UITextSelectionRect subclasses (some of those are properties) 259 - rect (should *never* call super) 260 - range (should *never* call super) 261 - writingDirection (should *never* call super) 262 - isVertical (should *never* call super) 263 - containsStart (should *never* call super) 264 - containsEnd (should *never* call super) 265 */ 266