[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