clang API Documentation
00001 //==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This file defines a ObjCMissingSuperCallChecker, a checker that 00011 // analyzes a UIViewController implementation to determine if it 00012 // correctly calls super in the methods where this is mandatory. 00013 // 00014 //===----------------------------------------------------------------------===// 00015 00016 #include "ClangSACheckers.h" 00017 #include "clang/AST/DeclObjC.h" 00018 #include "clang/AST/Expr.h" 00019 #include "clang/AST/ExprObjC.h" 00020 #include "clang/AST/RecursiveASTVisitor.h" 00021 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 00022 #include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 00023 #include "clang/StaticAnalyzer/Core/Checker.h" 00024 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 00025 #include "llvm/ADT/SmallSet.h" 00026 #include "llvm/ADT/SmallString.h" 00027 #include "llvm/Support/raw_ostream.h" 00028 00029 using namespace clang; 00030 using namespace ento; 00031 00032 namespace { 00033 struct SelectorDescriptor { 00034 const char *SelectorName; 00035 unsigned ArgumentCount; 00036 }; 00037 } 00038 00039 //===----------------------------------------------------------------------===// 00040 // FindSuperCallVisitor - Identify specific calls to the superclass. 00041 //===----------------------------------------------------------------------===// 00042 00043 class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 00044 public: 00045 explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} 00046 00047 bool VisitObjCMessageExpr(ObjCMessageExpr *E) { 00048 if (E->getSelector() == Sel) 00049 if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) 00050 DoesCallSuper = true; 00051 00052 // Recurse if we didn't find the super call yet. 00053 return !DoesCallSuper; 00054 } 00055 00056 bool DoesCallSuper; 00057 00058 private: 00059 Selector Sel; 00060 }; 00061 00062 //===----------------------------------------------------------------------===// 00063 // ObjCSuperCallChecker 00064 //===----------------------------------------------------------------------===// 00065 00066 namespace { 00067 class ObjCSuperCallChecker : public Checker< 00068 check::ASTDecl<ObjCImplementationDecl> > { 00069 public: 00070 ObjCSuperCallChecker() : IsInitialized(false) {} 00071 00072 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 00073 BugReporter &BR) const; 00074 private: 00075 bool isCheckableClass(const ObjCImplementationDecl *D, 00076 StringRef &SuperclassName) const; 00077 void initializeSelectors(ASTContext &Ctx) const; 00078 void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, 00079 StringRef ClassName) const; 00080 mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; 00081 mutable bool IsInitialized; 00082 }; 00083 00084 } 00085 00086 /// \brief Determine whether the given class has a superclass that we want 00087 /// to check. The name of the found superclass is stored in SuperclassName. 00088 /// 00089 /// \param D The declaration to check for superclasses. 00090 /// \param[out] SuperclassName On return, the found superclass name. 00091 bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, 00092 StringRef &SuperclassName) const { 00093 const ObjCInterfaceDecl *ID = D->getClassInterface(); 00094 for ( ; ID ; ID = ID->getSuperClass()) 00095 { 00096 SuperclassName = ID->getIdentifier()->getName(); 00097 if (SelectorsForClass.count(SuperclassName)) 00098 return true; 00099 } 00100 return false; 00101 } 00102 00103 void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, 00104 ArrayRef<SelectorDescriptor> Sel, 00105 StringRef ClassName) const { 00106 llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; 00107 // Fill the Selectors SmallSet with all selectors we want to check. 00108 for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); 00109 I != E; ++I) { 00110 SelectorDescriptor Descriptor = *I; 00111 assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. 00112 00113 // Get the selector. 00114 IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); 00115 00116 Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); 00117 ClassSelectors.insert(Sel); 00118 } 00119 } 00120 00121 void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { 00122 00123 { // Initialize selectors for: UIViewController 00124 const SelectorDescriptor Selectors[] = { 00125 { "addChildViewController", 1 }, 00126 { "viewDidAppear", 1 }, 00127 { "viewDidDisappear", 1 }, 00128 { "viewWillAppear", 1 }, 00129 { "viewWillDisappear", 1 }, 00130 { "removeFromParentViewController", 0 }, 00131 { "didReceiveMemoryWarning", 0 }, 00132 { "viewDidUnload", 0 }, 00133 { "viewDidLoad", 0 }, 00134 { "viewWillUnload", 0 }, 00135 { "updateViewConstraints", 0 }, 00136 { "encodeRestorableStateWithCoder", 1 }, 00137 { "restoreStateWithCoder", 1 }}; 00138 00139 fillSelectors(Ctx, Selectors, "UIViewController"); 00140 } 00141 00142 { // Initialize selectors for: UIResponder 00143 const SelectorDescriptor Selectors[] = { 00144 { "resignFirstResponder", 0 }}; 00145 00146 fillSelectors(Ctx, Selectors, "UIResponder"); 00147 } 00148 00149 { // Initialize selectors for: NSResponder 00150 const SelectorDescriptor Selectors[] = { 00151 { "encodeRestorableStateWithCoder", 1 }, 00152 { "restoreStateWithCoder", 1 }}; 00153 00154 fillSelectors(Ctx, Selectors, "NSResponder"); 00155 } 00156 00157 { // Initialize selectors for: NSDocument 00158 const SelectorDescriptor Selectors[] = { 00159 { "encodeRestorableStateWithCoder", 1 }, 00160 { "restoreStateWithCoder", 1 }}; 00161 00162 fillSelectors(Ctx, Selectors, "NSDocument"); 00163 } 00164 00165 IsInitialized = true; 00166 } 00167 00168 void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 00169 AnalysisManager &Mgr, 00170 BugReporter &BR) const { 00171 ASTContext &Ctx = BR.getContext(); 00172 00173 // We need to initialize the selector table once. 00174 if (!IsInitialized) 00175 initializeSelectors(Ctx); 00176 00177 // Find out whether this class has a superclass that we are supposed to check. 00178 StringRef SuperclassName; 00179 if (!isCheckableClass(D, SuperclassName)) 00180 return; 00181 00182 00183 // Iterate over all instance methods. 00184 for (auto *MD : D->instance_methods()) { 00185 Selector S = MD->getSelector(); 00186 // Find out whether this is a selector that we want to check. 00187 if (!SelectorsForClass[SuperclassName].count(S)) 00188 continue; 00189 00190 // Check if the method calls its superclass implementation. 00191 if (MD->getBody()) 00192 { 00193 FindSuperCallVisitor Visitor(S); 00194 Visitor.TraverseDecl(MD); 00195 00196 // It doesn't call super, emit a diagnostic. 00197 if (!Visitor.DoesCallSuper) { 00198 PathDiagnosticLocation DLoc = 00199 PathDiagnosticLocation::createEnd(MD->getBody(), 00200 BR.getSourceManager(), 00201 Mgr.getAnalysisDeclContext(D)); 00202 00203 const char *Name = "Missing call to superclass"; 00204 SmallString<320> Buf; 00205 llvm::raw_svector_ostream os(Buf); 00206 00207 os << "The '" << S.getAsString() 00208 << "' instance method in " << SuperclassName.str() << " subclass '" 00209 << *D << "' is missing a [super " << S.getAsString() << "] call"; 00210 00211 BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, 00212 os.str(), DLoc); 00213 } 00214 } 00215 } 00216 } 00217 00218 00219 //===----------------------------------------------------------------------===// 00220 // Check registration. 00221 //===----------------------------------------------------------------------===// 00222 00223 void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { 00224 Mgr.registerChecker<ObjCSuperCallChecker>(); 00225 } 00226 00227 00228 /* 00229 ToDo list for expanding this check in the future, the list is not exhaustive. 00230 There are also cases where calling super is suggested but not "mandatory". 00231 In addition to be able to check the classes and methods below, architectural 00232 improvements like being able to allow for the super-call to be done in a called 00233 method would be good too. 00234 00235 UIDocument subclasses 00236 - finishedHandlingError:recovered: (is multi-arg) 00237 - finishedHandlingError:recovered: (is multi-arg) 00238 00239 UIViewController subclasses 00240 - loadView (should *never* call super) 00241 - transitionFromViewController:toViewController: 00242 duration:options:animations:completion: (is multi-arg) 00243 00244 UICollectionViewController subclasses 00245 - loadView (take care because UIViewController subclasses should NOT call super 00246 in loadView, but UICollectionViewController subclasses should) 00247 00248 NSObject subclasses 00249 - doesNotRecognizeSelector (it only has to call super if it doesn't throw) 00250 00251 UIPopoverBackgroundView subclasses (some of those are class methods) 00252 - arrowDirection (should *never* call super) 00253 - arrowOffset (should *never* call super) 00254 - arrowBase (should *never* call super) 00255 - arrowHeight (should *never* call super) 00256 - contentViewInsets (should *never* call super) 00257 00258 UITextSelectionRect subclasses (some of those are properties) 00259 - rect (should *never* call super) 00260 - range (should *never* call super) 00261 - writingDirection (should *never* call super) 00262 - isVertical (should *never* call super) 00263 - containsStart (should *never* call super) 00264 - containsEnd (should *never* call super) 00265 */