ObjCMissingSuperCallChecker.cpp (243830) | ObjCMissingSuperCallChecker.cpp (249423) |
---|---|
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" | 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/StaticAnalyzer/Core/Checker.h" 18#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" 19#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" 20#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 21#include "clang/AST/ExprObjC.h" 22#include "clang/AST/Expr.h" | |
23#include "clang/AST/DeclObjC.h" | 17#include "clang/AST/DeclObjC.h" |
18#include "clang/AST/Expr.h" 19#include "clang/AST/ExprObjC.h" |
|
24#include "clang/AST/RecursiveASTVisitor.h" | 20#include "clang/AST/RecursiveASTVisitor.h" |
25#include "llvm/ADT/SmallString.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" |
26#include "llvm/ADT/SmallSet.h" | 25#include "llvm/ADT/SmallSet.h" |
26#include "llvm/ADT/SmallString.h" |
|
27#include "llvm/Support/raw_ostream.h" 28 29using namespace clang; 30using namespace ento; 31 | 27#include "llvm/Support/raw_ostream.h" 28 29using namespace clang; 30using namespace ento; 31 |
32static 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; | 32namespace { 33struct SelectorDescriptor { 34 const char *SelectorName; 35 unsigned ArgumentCount; 36}; |
41} 42 43//===----------------------------------------------------------------------===// 44// FindSuperCallVisitor - Identify specific calls to the superclass. 45//===----------------------------------------------------------------------===// 46 47class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 48public: --- 17 unchanged lines hidden (view full) --- 66//===----------------------------------------------------------------------===// 67// ObjCSuperCallChecker 68//===----------------------------------------------------------------------===// 69 70namespace { 71class ObjCSuperCallChecker : public Checker< 72 check::ASTDecl<ObjCImplementationDecl> > { 73public: | 37} 38 39//===----------------------------------------------------------------------===// 40// FindSuperCallVisitor - Identify specific calls to the superclass. 41//===----------------------------------------------------------------------===// 42 43class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { 44public: --- 17 unchanged lines hidden (view full) --- 62//===----------------------------------------------------------------------===// 63// ObjCSuperCallChecker 64//===----------------------------------------------------------------------===// 65 66namespace { 67class ObjCSuperCallChecker : public Checker< 68 check::ASTDecl<ObjCImplementationDecl> > { 69public: |
70 ObjCSuperCallChecker() : IsInitialized(false) {} 71 |
|
74 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 75 BugReporter &BR) const; | 72 void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, 73 BugReporter &BR) const; |
74private: 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; |
|
76}; | 82}; |
83 |
|
77} 78 | 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. 91bool 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 103void 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 121void 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 |
|
79void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 80 AnalysisManager &Mgr, 81 BugReporter &BR) const { 82 ASTContext &Ctx = BR.getContext(); 83 | 168void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, 169 AnalysisManager &Mgr, 170 BugReporter &BR) const { 171 ASTContext &Ctx = BR.getContext(); 172 |
84 if (!isUIViewControllerSubclass(Ctx, D)) | 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)) |
85 return; 86 | 180 return; 181 |
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 | 182 |
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. | 183 // Iterate over all instance methods. 184 for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), 185 E = D->instmeth_end(); 186 I != E; ++I) { 187 Selector S = (*I)->getSelector(); 188 // Find out whether this is a selector that we want to check. |
114 if (!Selectors.count(S)) | 189 if (!SelectorsForClass[SuperclassName].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"; | 190 continue; 191 192 ObjCMethodDecl *MD = *I; 193 194 // Check if the method calls its superclass implementation. 195 if (MD->getBody()) 196 { 197 FindSuperCallVisitor Visitor(S); 198 Visitor.TraverseDecl(MD); 199 200 // It doesn't call super, emit a diagnostic. 201 if (!Visitor.DoesCallSuper) { 202 PathDiagnosticLocation DLoc = 203 PathDiagnosticLocation::createEnd(MD->getBody(), 204 BR.getSourceManager(), 205 Mgr.getAnalysisDeclContext(D)); 206 207 const char *Name = "Missing call to superclass"; |
133 SmallString<256> Buf; | 208 SmallString<320> Buf; |
134 llvm::raw_svector_ostream os(Buf); 135 136 os << "The '" << S.getAsString() | 209 llvm::raw_svector_ostream os(Buf); 210 211 os << "The '" << S.getAsString() |
137 << "' instance method in UIViewController subclass '" << *D 138 << "' is missing a [super " << S.getAsString() << "] call"; | 212 << "' instance method in " << SuperclassName.str() << " subclass '" 213 << *D << "' is missing a [super " << S.getAsString() << "] call"; |
139 140 BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, 141 os.str(), DLoc); 142 } 143 } 144 } 145} 146 --- 9 unchanged lines hidden (view full) --- 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 | 214 215 BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC, 216 os.str(), DLoc); 217 } 218 } 219 } 220} 221 --- 9 unchanged lines hidden (view full) --- 231 232/* 233 ToDo list for expanding this check in the future, the list is not exhaustive. 234 There are also cases where calling super is suggested but not "mandatory". 235 In addition to be able to check the classes and methods below, architectural 236 improvements like being able to allow for the super-call to be done in a called 237 method would be good too. 238 |
164*** trivial cases: 165UIResponder subclasses 166- resignFirstResponder 167 168NSResponder subclasses 169- cursorUpdate 170 171*** more difficult cases: 172 | |
173UIDocument subclasses 174- finishedHandlingError:recovered: (is multi-arg) 175- finishedHandlingError:recovered: (is multi-arg) 176 177UIViewController subclasses 178- loadView (should *never* call super) 179- transitionFromViewController:toViewController: 180 duration:options:animations:completion: (is multi-arg) --- 23 unchanged lines hidden --- | 239UIDocument subclasses 240- finishedHandlingError:recovered: (is multi-arg) 241- finishedHandlingError:recovered: (is multi-arg) 242 243UIViewController subclasses 244- loadView (should *never* call super) 245- transitionFromViewController:toViewController: 246 duration:options:animations:completion: (is multi-arg) --- 23 unchanged lines hidden --- |