[cfe-commits] r134449 - in /cfe/trunk: include/clang/Basic/DiagnosticSemaKinds.td include/clang/Basic/IdentifierTable.h lib/AST/DeclObjC.cpp lib/Basic/IdentifierTable.cpp lib/Sema/SemaDeclObjC.cpp lib/Sema/SemaExprObjC.cpp test/SemaObjC/arc-peformselector.m

Fariborz Jahanian fjahanian at apple.com
Tue Jul 5 15:38:59 PDT 2011


Author: fjahanian
Date: Tue Jul  5 17:38:59 2011
New Revision: 134449

URL: http://llvm.org/viewvc/llvm-project?rev=134449&view=rev
Log:
objc-arc: enforce performSelector rules in rejecting retaining selectors
passed to it, and unknown selectors causing potential leak.
// rdar://9659270

Added:
    cfe/trunk/test/SemaObjC/arc-peformselector.m
Modified:
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/IdentifierTable.h
    cfe/trunk/lib/AST/DeclObjC.cpp
    cfe/trunk/lib/Basic/IdentifierTable.cpp
    cfe/trunk/lib/Sema/SemaDeclObjC.cpp
    cfe/trunk/lib/Sema/SemaExprObjC.cpp

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=134449&r1=134448&r2=134449&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Tue Jul  5 17:38:59 2011
@@ -484,6 +484,10 @@
   " ivar or must explicitly name an ivar">;
 def error_synthesize_weak_non_arc_or_gc : Error<
   "@synthesize of 'weak' property is only allowed in ARC or GC mode">;
+def err_arc_perform_selector_retains : Error<
+  "performSelector names a selector which retains the object">;
+def warn_arc_perform_selector_leaks : Warning<
+  "performSelector may cause a leak because its selector is unknown">;
 
 def error_synthesized_ivar_yet_not_supported : Error<
   "instance variable synthesis not yet supported"

Modified: cfe/trunk/include/clang/Basic/IdentifierTable.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/IdentifierTable.h?rev=134449&r1=134448&r2=134449&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/IdentifierTable.h (original)
+++ cfe/trunk/include/clang/Basic/IdentifierTable.h Tue Jul  5 17:38:59 2011
@@ -488,7 +488,10 @@
   OMF_release,
   OMF_retain,
   OMF_retainCount,
-  OMF_self
+  OMF_self,
+
+  // performSelector families
+  OMF_performSelector
 };
 
 /// Enough bits to store any enumerator in ObjCMethodFamily or

Modified: cfe/trunk/lib/AST/DeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/DeclObjC.cpp?rev=134449&r1=134448&r2=134449&view=diff
==============================================================================
--- cfe/trunk/lib/AST/DeclObjC.cpp (original)
+++ cfe/trunk/lib/AST/DeclObjC.cpp Tue Jul  5 17:38:59 2011
@@ -452,6 +452,34 @@
     if (!isInstanceMethod())
       family = OMF_None;
     break;
+      
+  case OMF_performSelector:
+    if (!isInstanceMethod() ||
+        !getResultType()->isObjCIdType())
+      family = OMF_None;
+    else {
+      unsigned noParams = param_size();
+      if (noParams < 1 || noParams > 3)
+        family = OMF_None;
+      else {
+        ObjCMethodDecl::arg_type_iterator it = arg_type_begin();
+        QualType ArgT = (*it);
+        if (!ArgT->isObjCSelType()) {
+          family = OMF_None;
+          break;
+        }
+        while (--noParams) {
+          it++;
+          ArgT = (*it);
+          if (!ArgT->isObjCIdType()) {
+            family = OMF_None;
+            break;
+          }
+        }
+      }
+    }
+    break;
+      
   }
 
   // Cache the result.

Modified: cfe/trunk/lib/Basic/IdentifierTable.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Basic/IdentifierTable.cpp?rev=134449&r1=134448&r2=134449&view=diff
==============================================================================
--- cfe/trunk/lib/Basic/IdentifierTable.cpp (original)
+++ cfe/trunk/lib/Basic/IdentifierTable.cpp Tue Jul  5 17:38:59 2011
@@ -397,6 +397,8 @@
     if (name == "retainCount") return OMF_retainCount;
     if (name == "self") return OMF_self;
   }
+ 
+  if (name == "performSelector") return OMF_performSelector;
 
   // The other method families may begin with a prefix of underscores.
   while (!name.empty() && name.front() == '_')

Modified: cfe/trunk/lib/Sema/SemaDeclObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclObjC.cpp?rev=134449&r1=134448&r2=134449&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclObjC.cpp Tue Jul  5 17:38:59 2011
@@ -286,6 +286,11 @@
         method->hasAttr<NSReturnsAutoreleasedAttr>())
       return false;
     break;
+    
+  case OMF_performSelector:
+    // we don't annotate performSelector's
+    return true;
+      
   }
 
   method->addAttr(new (S.Context) NSReturnsRetainedAttr(SourceLocation(),
@@ -366,6 +371,7 @@
     case OMF_copy:
     case OMF_new:
     case OMF_self:
+    case OMF_performSelector:
       break;
     }
   }
@@ -1298,6 +1304,7 @@
   case OMF_dealloc:
   case OMF_retainCount:
   case OMF_self:
+  case OMF_performSelector:
     // Mismatches for these methods don't change ownership
     // conventions, so we don't care.
     return false;
@@ -2467,6 +2474,7 @@
     case OMF_mutableCopy:
     case OMF_release:
     case OMF_retainCount:
+    case OMF_performSelector:
       break;
       
     case OMF_alloc:

Modified: cfe/trunk/lib/Sema/SemaExprObjC.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprObjC.cpp?rev=134449&r1=134448&r2=134449&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExprObjC.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExprObjC.cpp Tue Jul  5 17:38:59 2011
@@ -204,6 +204,7 @@
     case OMF_mutableCopy:
     case OMF_new:
     case OMF_self:
+    case OMF_performSelector:
       break;
     }
   }
@@ -1417,6 +1418,53 @@
       Diag(Loc, diag::err_arc_illegal_explicit_message)
         << Sel << SelectorLoc;
       break;
+    
+    case OMF_performSelector:
+      if (Method && NumArgs >= 1) {
+        if (ObjCSelectorExpr *SelExp = dyn_cast<ObjCSelectorExpr>(Args[0])) {
+          Selector ArgSel = SelExp->getSelector();
+          ObjCMethodDecl *SelMethod = 
+            LookupInstanceMethodInGlobalPool(ArgSel,
+                                             SelExp->getSourceRange());
+          if (!SelMethod)
+            SelMethod =
+              LookupFactoryMethodInGlobalPool(ArgSel,
+                                              SelExp->getSourceRange());
+          if (SelMethod) {
+            ObjCMethodFamily SelFamily = SelMethod->getMethodFamily();
+            switch (SelFamily) {
+              case OMF_alloc:
+              case OMF_copy:
+              case OMF_mutableCopy:
+              case OMF_new:
+              case OMF_self:
+              case OMF_init:
+                // Issue error, unless ns_returns_not_retained.
+                if (!SelMethod->hasAttr<NSReturnsNotRetainedAttr>()) {
+                  // selector names a +1 method 
+                  Diag(SelectorLoc, 
+                       diag::err_arc_perform_selector_retains);
+                  Diag(SelMethod->getLocation(), diag::note_method_declared_at);
+                }
+                break;
+              default:
+                // +0 call. OK. unless ns_returns_retained.
+                if (SelMethod->hasAttr<NSReturnsRetainedAttr>()) {
+                  // selector names a +1 method
+                  Diag(SelectorLoc, 
+                       diag::err_arc_perform_selector_retains);
+                  Diag(SelMethod->getLocation(), diag::note_method_declared_at);
+                }
+                break;
+            }
+          }
+        } else {
+          // error (may leak).
+          Diag(SelectorLoc, diag::warn_arc_perform_selector_leaks);
+          Diag(Args[0]->getExprLoc(), diag::note_used_here);
+        }
+      }
+      break;
     }
   }
 

Added: cfe/trunk/test/SemaObjC/arc-peformselector.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaObjC/arc-peformselector.m?rev=134449&view=auto
==============================================================================
--- cfe/trunk/test/SemaObjC/arc-peformselector.m (added)
+++ cfe/trunk/test/SemaObjC/arc-peformselector.m Tue Jul  5 17:38:59 2011
@@ -0,0 +1,38 @@
+// RUN: %clang_cc1 -triple x86_64-apple-darwin11 -fobjc-nonfragile-abi -fsyntax-only -fobjc-arc -verify %s
+// rdar://9659270
+
+ at interface NSObject
+- (id)copy; // expected-note {{method declared here}}
+- (id) test __attribute__((ns_returns_retained)); // expected-note {{method declared here}}
++ (id) new ; // expected-note {{method declared here}}
+- (id) init __attribute__((ns_returns_not_retained));
+- (id)PlusZero;
+- (id)PlusOne __attribute__((ns_returns_retained)); // expected-note {{method declared here}}
+ at end
+
+ at interface I : NSObject
+{
+  SEL sel1;
+}
+- (id)performSelector:(SEL)aSelector;
+- (id)performSelector:(SEL)aSelector withObject:(id)object;
+- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
+ at end
+
+ at implementation I
+- (id) Meth {
+  return [self performSelector : @selector(copy)]; // expected-error {{performSelector names a selector which retains the object}}
+  return [self performSelector : @selector(test)]; // expected-error {{performSelector names a selector which retains the object}}
+  return [self performSelector : @selector(new)]; // expected-error {{performSelector names a selector which retains the object}}
+  return [self performSelector : @selector(init)];
+  return [self performSelector : sel1]; // expected-warning {{performSelector may cause a leak because its selector is unknown}} \
+					// expected-note {{used here}}
+
+  return [self performSelector : @selector(PlusZero)];
+  return [self performSelector : @selector(PlusOne)]; // expected-error {{performSelector names a selector which retains the object}}
+}
+
+- (id)performSelector:(SEL)aSelector { return 0; }
+- (id)performSelector:(SEL)aSelector withObject:(id)object { return 0; }
+- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2 { return 0; }
+ at end





More information about the cfe-commits mailing list