[cfe-commits] r170089 - in /cfe/trunk: lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp test/Analysis/superclass.m test/Analysis/viewcontroller.m
Jordan Rose
jordan_rose at apple.com
Wed Dec 12 19:06:45 PST 2012
Author: jrose
Date: Wed Dec 12 21:06:45 2012
New Revision: 170089
URL: http://llvm.org/viewvc/llvm-project?rev=170089&view=rev
Log:
[analyzer] Generalize ObjCMissingSuperCallChecker.
We now check a few methods for UIResponder, NSResponder, and NSDocument.
Patch by Julian Mayer!
Added:
cfe/trunk/test/Analysis/superclass.m
- copied, changed from r170081, cfe/trunk/test/Analysis/viewcontroller.m
Removed:
cfe/trunk/test/Analysis/viewcontroller.m
Modified:
cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
Modified: cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp?rev=170089&r1=170088&r2=170089&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp Wed Dec 12 21:06:45 2012
@@ -29,15 +29,11 @@
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;
+namespace {
+struct SelectorDescriptor {
+ const char *SelectorName;
+ unsigned ArgumentCount;
+};
}
//===----------------------------------------------------------------------===//
@@ -71,9 +67,102 @@
class ObjCSuperCallChecker : public Checker<
check::ASTDecl<ObjCImplementationDecl> > {
public:
+ ObjCSuperCallChecker() : IsInitialized(false) {}
+
void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
BugReporter &BR) const;
+private:
+ bool isCheckableClass(const ObjCImplementationDecl *D,
+ StringRef &SuperclassName) const;
+ void initializeSelectors(ASTContext &Ctx) const;
+ void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
+ StringRef ClassName) const;
+ mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass;
+ mutable bool IsInitialized;
};
+
+}
+
+/// \brief Determine whether the given class has a superclass that we want
+/// to check. The name of the found superclass is stored in SuperclassName.
+///
+/// \param ObjCImplementationDecl The declaration to check for superclasses.
+/// \param[out] SuperclassName On return, the found superclass name.
+bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
+ StringRef &SuperclassName) const {
+ const ObjCInterfaceDecl *ID = D->getClassInterface();
+ for ( ; ID ; ID = ID->getSuperClass())
+ {
+ SuperclassName = ID->getIdentifier()->getName();
+ if (SelectorsForClass.count(SuperclassName))
+ return true;
+ }
+ return false;
+}
+
+void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
+ ArrayRef<SelectorDescriptor> Sel,
+ StringRef ClassName) const {
+ llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
+ // Fill the Selectors SmallSet with all selectors we want to check.
+ for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
+ I != E; ++I) {
+ SelectorDescriptor Descriptor = *I;
+ assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
+
+ // Get the selector.
+ IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
+
+ Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
+ ClassSelectors.insert(Sel);
+ }
+}
+
+void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
+
+ { // Initialize selectors for: UIViewController
+ const SelectorDescriptor Selectors[] = {
+ { "addChildViewController", 1 },
+ { "viewDidAppear", 1 },
+ { "viewDidDisappear", 1 },
+ { "viewWillAppear", 1 },
+ { "viewWillDisappear", 1 },
+ { "removeFromParentViewController", 0 },
+ { "didReceiveMemoryWarning", 0 },
+ { "viewDidUnload", 0 },
+ { "viewDidLoad", 0 },
+ { "viewWillUnload", 0 },
+ { "updateViewConstraints", 0 },
+ { "encodeRestorableStateWithCoder", 1 },
+ { "restoreStateWithCoder", 1 }};
+
+ fillSelectors(Ctx, Selectors, "UIViewController");
+ }
+
+ { // Initialize selectors for: UIResponder
+ const SelectorDescriptor Selectors[] = {
+ { "resignFirstResponder", 0 }};
+
+ fillSelectors(Ctx, Selectors, "UIResponder");
+ }
+
+ { // Initialize selectors for: NSResponder
+ const SelectorDescriptor Selectors[] = {
+ { "encodeRestorableStateWithCoder", 1 },
+ { "restoreStateWithCoder", 1 }};
+
+ fillSelectors(Ctx, Selectors, "NSResponder");
+ }
+
+ { // Initialize selectors for: NSDocument
+ const SelectorDescriptor Selectors[] = {
+ { "encodeRestorableStateWithCoder", 1 },
+ { "restoreStateWithCoder", 1 }};
+
+ fillSelectors(Ctx, Selectors, "NSDocument");
+ }
+
+ IsInitialized = true;
}
void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
@@ -81,29 +170,15 @@
BugReporter &BR) const {
ASTContext &Ctx = BR.getContext();
- if (!isUIViewControllerSubclass(Ctx, D))
+ // We need to initialize the selector table once.
+ if (!IsInitialized)
+ initializeSelectors(Ctx);
+
+ // Find out whether this class has a superclass that we are supposed to check.
+ StringRef SuperclassName;
+ if (!isCheckableClass(D, SuperclassName))
return;
- 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];
-
- // 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 +186,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,12 +205,12 @@
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
- << "' is missing a [super " << S.getAsString() << "] call";
+ << "' instance method in " << SuperclassName.str() << " subclass '"
+ << *D << "' is missing a [super " << S.getAsString() << "] call";
BR.EmitBasicReport(MD, Name, categories::CoreFoundationObjectiveC,
os.str(), DLoc);
@@ -161,15 +236,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)
Copied: cfe/trunk/test/Analysis/superclass.m (from r170081, cfe/trunk/test/Analysis/viewcontroller.m)
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/superclass.m?p2=cfe/trunk/test/Analysis/superclass.m&p1=cfe/trunk/test/Analysis/viewcontroller.m&r1=170081&r2=170089&rev=170089&view=diff
==============================================================================
--- cfe/trunk/test/Analysis/viewcontroller.m (original)
+++ cfe/trunk/test/Analysis/superclass.m Wed Dec 12 21:06:45 2012
@@ -1,5 +1,6 @@
// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=alpha.osx.cocoa.MissingSuperCall -verify -Wno-objc-root-class %s
+// Define used Classes
@protocol NSObject
- (id)retain;
- (oneway void)release;
@@ -8,13 +9,15 @@
- (id)init;
+ (id)alloc;
@end
-
typedef char BOOL;
typedef double NSTimeInterval;
typedef enum UIViewAnimationOptions {
UIViewAnimationOptionLayoutSubviews = 1 << 0
} UIViewAnimationOptions;
+ at interface NSCoder : NSObject {}
+ at end
+// Define the Superclasses for our Checks
@interface UIViewController : NSObject {}
- (void)addChildViewController:(UIViewController *)childController;
- (void)viewDidAppear:(BOOL)animated;
@@ -32,8 +35,21 @@
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion;
@end
+ at interface UIResponder : NSObject {}
+- (BOOL)resignFirstResponder;
+ at end
+ at interface NSResponder : NSObject {}
+- (void)restoreStateWithCoder:(NSCoder *)coder;
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder;
+ at end
+ at interface NSDocument : NSObject {}
+- (void)restoreStateWithCoder:(NSCoder *)coder;
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder;
+ at end
-// Do not warn if UIViewController isn't our superclass
+// Checks
+
+// Do not warn if UIViewController/*Responder/NSDocument is not our superclass
@interface TestA
@end
@implementation TestA
@@ -48,7 +64,9 @@
- (void)viewWillDisappear:(BOOL)animated {}
- (void)didReceiveMemoryWarning {}
- (void)removeFromParentViewController {}
-
+- (BOOL)resignFirstResponder { return 0; }
+- (void)restoreStateWithCoder:(NSCoder *)coder {}
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {}
@end
// Warn if UIViewController is our superclass and we do not call super
@@ -72,7 +90,7 @@
- (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 {};
+- (void)shouldAutorotate {}
@end
// Do not warn if UIViewController is our superclass but we did call super
@@ -133,3 +151,72 @@
[self methodDoingStuff];
} // expected-warning {{The 'removeFromParentViewController' instance method in UIViewController subclass 'TestC' is missing a [super removeFromParentViewController] call}}
@end
+
+
+// Do warn for UIResponder subclasses that don't call super
+ at interface TestD : UIResponder {}
+ at end
+ at implementation TestD
+
+- (BOOL)resignFirstResponder {
+ return 0;
+} // expected-warning {{The 'resignFirstResponder' instance method in UIResponder subclass 'TestD' is missing a [super resignFirstResponder] call}}
+ at end
+
+// Do not warn for UIResponder subclasses that do the right thing
+ at interface TestE : UIResponder {}
+ at end
+ at implementation TestE
+
+- (BOOL)resignFirstResponder {
+ return [super resignFirstResponder];
+}
+ at end
+
+// Do warn for NSResponder subclasses that don't call super
+ at interface TestF : NSResponder {}
+ at end
+ at implementation TestF
+
+- (void)restoreStateWithCoder:(NSCoder *)coder {
+} // expected-warning {{The 'restoreStateWithCoder:' instance method in NSResponder subclass 'TestF' is missing a [super restoreStateWithCoder:] call}}
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
+} // expected-warning {{The 'encodeRestorableStateWithCoder:' instance method in NSResponder subclass 'TestF' is missing a [super encodeRestorableStateWithCoder:] call}}
+ at end
+
+// Do not warn for NSResponder subclasses that do the right thing
+ at interface TestG : NSResponder {}
+ at end
+ at implementation TestG
+
+- (void)restoreStateWithCoder:(NSCoder *)coder {
+ [super restoreStateWithCoder:coder];
+}
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
+ [super encodeRestorableStateWithCoder:coder];
+}
+ at end
+
+// Do warn for NSDocument subclasses that don't call super
+ at interface TestH : NSDocument {}
+ at end
+ at implementation TestH
+
+- (void)restoreStateWithCoder:(NSCoder *)coder {
+} // expected-warning {{The 'restoreStateWithCoder:' instance method in NSDocument subclass 'TestH' is missing a [super restoreStateWithCoder:] call}}
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
+} // expected-warning {{The 'encodeRestorableStateWithCoder:' instance method in NSDocument subclass 'TestH' is missing a [super encodeRestorableStateWithCoder:] call}}
+ at end
+
+// Do not warn for NSDocument subclasses that do the right thing
+ at interface TestI : NSDocument {}
+ at end
+ at implementation TestI
+
+- (void)restoreStateWithCoder:(NSCoder *)coder {
+ [super restoreStateWithCoder:coder];
+}
+- (void)encodeRestorableStateWithCoder:(NSCoder *)coder {
+ [super encodeRestorableStateWithCoder:coder];
+}
+ at end
\ No newline at end of file
Removed: cfe/trunk/test/Analysis/viewcontroller.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/viewcontroller.m?rev=170088&view=auto
==============================================================================
--- cfe/trunk/test/Analysis/viewcontroller.m (original)
+++ cfe/trunk/test/Analysis/viewcontroller.m (removed)
@@ -1,135 +0,0 @@
-// RUN: %clang_cc1 -fblocks -analyze -analyzer-checker=alpha.osx.cocoa.MissingSuperCall -verify -Wno-objc-root-class %s
-
- at protocol NSObject
-- (id)retain;
-- (oneway void)release;
- at end
- at interface NSObject <NSObject> {}
-- (id)init;
-+ (id)alloc;
- at end
-
-typedef char BOOL;
-typedef double NSTimeInterval;
-typedef enum UIViewAnimationOptions {
- UIViewAnimationOptionLayoutSubviews = 1 << 0
-} UIViewAnimationOptions;
-
- at 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;
- at end
-
-// Do not warn if UIViewController isn't our superclass
- at interface TestA
- at end
- at 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 {}
-
- at end
-
-// Warn if UIViewController is our superclass and we do not call super
- at interface TestB : UIViewController {}
- at end
- at 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 {};
- at end
-
-// Do not warn if UIViewController is our superclass but we did call super
- at interface TestC : UIViewController {}
- at end
- at 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}}
- at end
More information about the cfe-commits
mailing list