Hi jordan_rose,
this is an update to the new ObjCMissingSuperCallChecker. the main change in
this patch is to check other classes than UIViewController too, i.e.
NS/UIResponder and NSDocument.
http://llvm-reviews.chandlerc.com/D129
Files:
test/Analysis/viewcontroller.m
test/Analysis/superclass.m
lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
Index: test/Analysis/viewcontroller.m
===================================================================
--- test/Analysis/viewcontroller.m
+++ test/Analysis/viewcontroller.m
@@ -1,135 +0,0 @@
-// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=alpha.osx.cocoa.MissingSuperCall -verify -Wno-objc-root-class %s
-
-@protocol NSObject
-- (id)retain;
-- (oneway void)release;
-@end
-@interface NSObject <NSObject> {}
-- (id)init;
-+ (id)alloc;
-@end
-
-typedef char BOOL;
-typedef double NSTimeInterval;
-typedef enum UIViewAnimationOptions {
- UIViewAnimationOptionLayoutSubviews = 1 << 0
-} UIViewAnimationOptions;
-
-@interface UIViewController : NSObject {}
-- (void)addChildViewController:(UIViewController *)childController;
-- (void)viewDidAppear:(BOOL)animated;
-- (void)viewDidDisappear:(BOOL)animated;
-- (void)viewDidUnload;
-- (void)viewDidLoad;
-- (void)viewWillUnload;
-- (void)viewWillAppear:(BOOL)animated;
-- (void)viewWillDisappear:(BOOL)animated;
-- (void)didReceiveMemoryWarning;
-- (void)removeFromParentViewController;
-- (void)transitionFromViewController:(UIViewController *)fromViewController
- toViewController:(UIViewController *)toViewController
- duration:(NSTimeInterval)duration options:(UIViewAnimationOptions)options
- animations:(void (^)(void))animations
- completion:(void (^)(BOOL finished))completion;
-@end
-
-// Do not warn if UIViewController isn't our superclass
-@interface TestA
-@end
-@implementation TestA
-
-- (void)addChildViewController:(UIViewController *)childController {}
-- (void)viewDidAppear:(BOOL)animated {}
-- (void)viewDidDisappear:(BOOL)animated {}
-- (void)viewDidUnload {}
-- (void)viewDidLoad {}
-- (void)viewWillUnload {}
-- (void)viewWillAppear:(BOOL)animated {}
-- (void)viewWillDisappear:(BOOL)animated {}
-- (void)didReceiveMemoryWarning {}
-- (void)removeFromParentViewController {}
-
-@end
-
-// Warn if UIViewController is our superclass and we do not call super
-@interface TestB : UIViewController {}
-@end
-@implementation TestB
-
-- (void)addChildViewController:(UIViewController *)childController {
- int addChildViewController = 5;
- for (int i = 0; i < addChildViewController; i++)
- [self viewDidAppear:i];
-} // expected-warning {{The 'addChildViewController:' instance method in UIViewController subclass 'TestB' is missing a [super addChildViewController:] call}}
-- (void)viewDidAppear:(BOOL)animated {} // expected-warning {{The 'viewDidAppear:' instance method in UIViewController subclass 'TestB' is missing a [super viewDidAppear:] call}}
-- (void)viewDidDisappear:(BOOL)animated {} // expected-warning {{The 'viewDidDisappear:' instance method in UIViewController subclass 'TestB' is missing a [super viewDidDisappear:] call}}
-- (void)viewDidUnload {} // expected-warning {{The 'viewDidUnload' instance method in UIViewController subclass 'TestB' is missing a [super viewDidUnload] call}}
-- (void)viewDidLoad {} // expected-warning {{The 'viewDidLoad' instance method in UIViewController subclass 'TestB' is missing a [super viewDidLoad] call}}
-- (void)viewWillUnload {} // expected-warning {{The 'viewWillUnload' instance method in UIViewController subclass 'TestB' is missing a [super viewWillUnload] call}}
-- (void)viewWillAppear:(BOOL)animated {} // expected-warning {{The 'viewWillAppear:' instance method in UIViewController subclass 'TestB' is missing a [super viewWillAppear:] call}}
-- (void)viewWillDisappear:(BOOL)animated {} // expected-warning {{The 'viewWillDisappear:' instance method in UIViewController subclass 'TestB' is missing a [super viewWillDisappear:] call}}
-- (void)didReceiveMemoryWarning {} // expected-warning {{The 'didReceiveMemoryWarning' instance method in UIViewController subclass 'TestB' is missing a [super didReceiveMemoryWarning] call}}
-- (void)removeFromParentViewController {} // expected-warning {{The 'removeFromParentViewController' instance method in UIViewController subclass 'TestB' is missing a [super removeFromParentViewController] call}}
-
-// Do not warn for methods were it shouldn't
-- (void)shouldAutorotate {};
-@end
-
-// Do not warn if UIViewController is our superclass but we did call super
-@interface TestC : UIViewController {}
-@end
-@implementation TestC
-
-- (BOOL)methodReturningStuff {
- return 1;
-}
-
-- (void)methodDoingStuff {
- [super removeFromParentViewController];
-}
-
-- (void)addChildViewController:(UIViewController *)childController {
- [super addChildViewController:childController];
-}
-
-- (void)viewDidAppear:(BOOL)animated {
- [super viewDidAppear:animated];
-}
-
-- (void)viewDidDisappear:(BOOL)animated {
- [super viewDidDisappear:animated];
-}
-
-- (void)viewDidUnload {
- [super viewDidUnload];
-}
-
-- (void)viewDidLoad {
- [super viewDidLoad];
-}
-
-- (void)viewWillUnload {
- [super viewWillUnload];
-}
-
-- (void)viewWillAppear:(BOOL)animated {
- int i = 0; // Also don't start warning just because we do additional stuff
- i++;
- [self viewDidDisappear:i];
- [super viewWillAppear:animated];
-}
-
-- (void)viewWillDisappear:(BOOL)animated {
- [super viewWillDisappear:[self methodReturningStuff]];
-}
-
-- (void)didReceiveMemoryWarning {
- [super didReceiveMemoryWarning];
-}
-
-// We expect a warning here because at the moment the super-call can't be
-// done from another method.
-- (void)removeFromParentViewController {
- [self methodDoingStuff];
-} // expected-warning {{The 'removeFromParentViewController' instance method in UIViewController subclass 'TestC' is missing a [super removeFromParentViewController] call}}
-@end
Index: test/Analysis/superclass.m
===================================================================
--- test/Analysis/superclass.m
+++ test/Analysis/superclass.m
@@ -33,6 +33,12 @@
completion:(void (^)(BOOL finished))completion;
@end
+@interface UIResponder : NSObject {}
+- (BOOL)resignFirstResponder;
+@end
+
+
+
// Do not warn if UIViewController isn't our superclass
@interface TestA
@end
@@ -133,3 +139,15 @@
[self methodDoingStuff];
} // expected-warning {{The 'removeFromParentViewController' instance method in UIViewController subclass 'TestC' is missing a [super removeFromParentViewController] call}}
@end
+
+
+// Do warn for UIResponder subclasses
+@interface TestD : UIResponder {}
+@end
+@implementation TestD
+
+- (BOOL)resignFirstResponder {
+ return 0;
+} // expected-warning {{The 'resignFirstResponder' instance method in UIResponder subclass 'TestD' is missing a [super resignFirstResponder] call}}
+
+@end
\ No newline at end of file
Index: lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
===================================================================
--- lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -29,16 +29,7 @@
using namespace clang;
using namespace ento;
-static bool isUIViewControllerSubclass(ASTContext &Ctx,
- const ObjCImplementationDecl *D) {
- IdentifierInfo *ViewControllerII = &Ctx.Idents.get("UIViewController");
- const ObjCInterfaceDecl *ID = D->getClassInterface();
- for ( ; ID; ID = ID->getSuperClass())
- if (ID->getIdentifier() == ViewControllerII)
- return true;
- return false;
-}
//===----------------------------------------------------------------------===//
// FindSuperCallVisitor - Identify specific calls to the superclass.
@@ -71,39 +62,111 @@
class ObjCSuperCallChecker : public Checker<
check::ASTDecl<ObjCImplementationDecl> > {
public:
+ ObjCSuperCallChecker() : IsInitialized(0) {}
+
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
BugReporter &BR) const;
+private:
+ bool isCheckableClass(const ObjCImplementationDecl *D,
+ std::string &SuperclassName) const;
+ void initializeSelectors(ASTContext &Ctx) const;
+ void fillSelectors(ASTContext &Ctx, const size_t Count,
+ const char **SelectorNames, const unsigned *ArgumentCounts,
+ const char *ClassName) const;
+ mutable std::map<std::string,llvm::SmallSet<Selector, 16> > SelectorsForClass;
+ mutable bool IsInitialized;
};
+
}
+bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
+ std::string &SuperclassName) const {
+ const ObjCInterfaceDecl *ID = D->getClassInterface();
+ for ( ; ID ; ID = ID->getSuperClass())
+ {
+ SuperclassName = ID->getIdentifier()->getName().str();
+ if (SelectorsForClass.find(SuperclassName) != SelectorsForClass.end())
+ return true;
+ }
+ return false;
+}
+
+void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, const size_t Count,
+ const char **SelectorNames,
+ const unsigned *ArgumentCounts,
+ const char *ClassName) const {
+ // Fill the Selectors SmallSet with all selectors we want to check.
+ for (size_t i = 0; i < Count; i++) {
+ unsigned ArgumentCount = ArgumentCounts[i];
+ const char *SelectorCString = SelectorNames[i];
+
+ // Get the selector.
+ IdentifierInfo *II = &Ctx.Idents.get(SelectorCString);
+ Selector Sel = Ctx.Selectors.getSelector(ArgumentCount, &II);
+ SelectorsForClass[ClassName].insert(Sel);
+ }
+}
+
+#define len(x) llvm::array_lengthof((x))
+
+void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
+ {
+ const char *SelectorNames[] = {"addChildViewController", "viewDidAppear",
+ "viewDidDisappear", "viewWillAppear", "viewWillDisappear",
+ "removeFromParentViewController", "didReceiveMemoryWarning",
+ "viewDidUnload", "viewWillUnload", "viewDidLoad", "updateViewConstraints",
+ "encodeRestorableStateWithCoder", "restoreStateWithCoder"};
+ const unsigned SelectorArgumentCounts[] =
+ {1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1};
+ assert(len(SelectorArgumentCounts) == len(SelectorNames));
+
+ fillSelectors(Ctx, len(SelectorNames), SelectorNames,
+ SelectorArgumentCounts, "UIViewController");
+ }
+ {
+ const char *SelectorNames[] = {"resignFirstResponder"};
+ const unsigned SelectorArgumentCounts[] = {0};
+ assert(len(SelectorArgumentCounts) == len(SelectorNames));
+
+ fillSelectors(Ctx, len(SelectorNames), SelectorNames,
+ SelectorArgumentCounts, "UIResponder");
+ }
+ {
+ const char *SelectorNames[] = {"encodeRestorableStateWithCoder",
+ "restoreStateWithCoder"};
+ const unsigned SelectorArgumentCounts[] = {1,1};
+ assert(len(SelectorArgumentCounts) == len(SelectorNames));
+
+ fillSelectors(Ctx, len(SelectorNames), SelectorNames,
+ SelectorArgumentCounts, "NSResponder");
+ }
+ {
+ const char *SelectorNames[] = {"encodeRestorableStateWithCoder",
+ "restoreStateWithCoder"};
+ const unsigned SelectorArgumentCounts[] = {1,1};
+ assert(len(SelectorArgumentCounts) == len(SelectorNames));
+
+ fillSelectors(Ctx, len(SelectorNames), SelectorNames,
+ SelectorArgumentCounts, "NSDocument");
+ }
+ IsInitialized = true;
+}
+
void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
AnalysisManager &Mgr,
BugReporter &BR) const {
ASTContext &Ctx = BR.getContext();
- if (!isUIViewControllerSubclass(Ctx, D))
- return;
+ // We need to initialize the selector table once.
+ if (!IsInitialized)
+ initializeSelectors(Ctx);
- const char *SelectorNames[] =
- {"addChildViewController", "viewDidAppear", "viewDidDisappear",
- "viewWillAppear", "viewWillDisappear", "removeFromParentViewController",
- "didReceiveMemoryWarning", "viewDidUnload", "viewWillUnload",
- "viewDidLoad"};
- const unsigned SelectorArgumentCounts[] =
- {1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
- const size_t SelectorCount = llvm::array_lengthof(SelectorNames);
- assert(llvm::array_lengthof(SelectorArgumentCounts) == SelectorCount);
- // Fill the Selectors SmallSet with all selectors we want to check.
- llvm::SmallSet<Selector, 16> Selectors;
- for (size_t i = 0; i < SelectorCount; i++) {
- unsigned ArgumentCount = SelectorArgumentCounts[i];
- const char *SelectorCString = SelectorNames[i];
+ // Find out whether this class has a superclass that we are supposed to check.
+ std::string SuperclassName;
+ if (!isCheckableClass(D, SuperclassName))
+ return;
- // Get the selector.
- IdentifierInfo *II = &Ctx.Idents.get(SelectorCString);
- Selectors.insert(Ctx.Selectors.getSelector(ArgumentCount, &II));
- }
// Iterate over all instance methods.
for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(),
@@ -111,7 +174,7 @@
I != E; ++I) {
Selector S = (*I)->getSelector();
// Find out whether this is a selector that we want to check.
- if (!Selectors.count(S))
+ if (!SelectorsForClass[SuperclassName].count(S))
continue;
ObjCMethodDecl *MD = *I;
@@ -130,11 +193,11 @@
Mgr.getAnalysisDeclContext(D));
const char *Name = "Missing call to superclass";
- SmallString<256> Buf;
+ SmallString<320> Buf;
llvm::raw_svector_ostream os(Buf);
os << "The '" << S.getAsString()
- << "' instance method in UIViewController subclass '" << *D
+ << "' instance method in "<< SuperclassName << " subclass '" << *D
<< "' is missing a [super " << S.getAsString() << "] call";
BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
@@ -161,15 +224,6 @@
improvements like being able to allow for the super-call to be done in a called
method would be good too.
-*** trivial cases:
-UIResponder subclasses
-- resignFirstResponder
-
-NSResponder subclasses
-- cursorUpdate
-
-*** more difficult cases:
-
UIDocument subclasses
- finishedHandlingError:recovered: (is multi-arg)
- finishedHandlingError:recovered: (is multi-arg)
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits