]> granicus.if.org Git - clang/commitdiff
[Objective-C] Add objc_subclassing_restricted attribute
authorAlex Lorenz <arphaman@gmail.com>
Fri, 28 Oct 2016 10:25:10 +0000 (10:25 +0000)
committerAlex Lorenz <arphaman@gmail.com>
Fri, 28 Oct 2016 10:25:10 +0000 (10:25 +0000)
This patch adds an objc_subclassing_restricted attribute into clang. This
attribute acts similarly to 'final' - Objective-C classes with this attribute
can't be subclassed. However, @interface declarations that have
objc_subclassing_restricted but don't have @implementation are allowed to
inherit other @interface declarations with objc_subclassing_restricted. This is
needed to describe the Swift class hierarchy in clang while making sure that
the Objective-C classes cannot subclass the Swift classes.

This attribute is already implemented in a fork of clang that's used for Swift
(https://github.com/apple/swift-clang) and this patch moves that code to the
upstream clang repository.

rdar://28937548

Differential Revision: https://reviews.llvm.org/D25993

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@285391 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Basic/Attr.td
include/clang/Basic/AttrDocs.td
include/clang/Basic/DiagnosticSemaKinds.td
lib/Sema/SemaDeclAttr.cpp
lib/Sema/SemaDeclObjC.cpp
test/SemaObjC/subclassing-restricted-attr.m [new file with mode: 0644]

index 3376f640a12743ac71857f1be5e47bec670da322..709af0a6dbd420015d975334b4ea32c692e1789d 100644 (file)
@@ -1285,6 +1285,12 @@ def ObjCRootClass : InheritableAttr {
   let Documentation = [Undocumented];
 }
 
+def ObjCSubclassingRestricted : InheritableAttr {
+  let Spellings = [GNU<"objc_subclassing_restricted">];
+  let Subjects = SubjectList<[ObjCInterface], ErrorDiag>;
+  let Documentation = [ObjCSubclassingRestrictedDocs];
+}
+
 def ObjCExplicitProtocolImpl : InheritableAttr {
   let Spellings = [GNU<"objc_protocol_requires_explicit_implementation">];
   let Subjects = SubjectList<[ObjCProtocol], ErrorDiag>;
index 44e0a8e400c419d57023d85bec71d1a5722a169e..f6ebda76566b33e721e450d07896b16806df5ec1 100644 (file)
@@ -2667,3 +2667,11 @@ transparent union should have the same calling convention as its first member.
 Transparent unions are not supported in C++.
   }];
 }
+
+def ObjCSubclassingRestrictedDocs : Documentation {
+  let Category = DocCatType;
+  let Content = [{
+This attribute can be added to an Objective-C ``@interface`` declaration to
+ensure that this class cannot be subclassed.
+  }];
+}
index 29a3e08991396120ee7b61dcd0b2cff1015767e3..a715731f2393bc0d3798930604c2ab3a60338c60 100644 (file)
@@ -781,6 +781,9 @@ def note_suppressed_class_declare : Note<
   "class with specified objc_requires_property_definitions attribute is declared here">;
 def err_objc_root_class_subclass : Error<
   "objc_root_class attribute may only be specified on a root class declaration">;
+def err_restricted_superclass_mismatch : Error<
+  "cannot subclass a class that was declared with the "
+  "'objc_subclassing_restricted' attribute">;
 def warn_objc_root_class_missing : Warning<
   "class %0 defined without specifying a base class">,
   InGroup<ObjCRootClass>;
index 9413b6e51aa442065c93b6a1cc9acbb151ff7596..936c29767d73ec62de99faeb0c6cbef90e0f115d 100644 (file)
@@ -5776,6 +5776,9 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
   case AttributeList::AT_ObjCRootClass:
     handleSimpleAttribute<ObjCRootClassAttr>(S, D, Attr);
     break;
+  case AttributeList::AT_ObjCSubclassingRestricted:
+    handleSimpleAttribute<ObjCSubclassingRestrictedAttr>(S, D, Attr);
+    break;
   case AttributeList::AT_ObjCExplicitProtocolImpl:
     handleObjCSuppresProtocolAttr(S, D, Attr);
     break;
index 79cdd6ead921686a5aadaf985408f0c54010158b..1b030801f23af529e38f797b607d46f1e55e6e1f 100644 (file)
@@ -3853,6 +3853,18 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
         Diag(IDecl->getLocation(), diag::err_objc_root_class_subclass);
       }
 
+      if (const ObjCInterfaceDecl *Super = IDecl->getSuperClass()) {
+        // An interface can subclass another interface with a
+        // objc_subclassing_restricted attribute when it has that attribute as
+        // well (because of interfaces imported from Swift). Therefore we have
+        // to check if we can subclass in the implementation as well.
+        if (IDecl->hasAttr<ObjCSubclassingRestrictedAttr>() &&
+            Super->hasAttr<ObjCSubclassingRestrictedAttr>()) {
+          Diag(IC->getLocation(), diag::err_restricted_superclass_mismatch);
+          Diag(Super->getLocation(), diag::note_class_declared);
+        }
+      }
+
       if (LangOpts.ObjCRuntime.isNonFragile()) {
         while (IDecl->getSuperClass()) {
           DiagnoseDuplicateIvars(IDecl, IDecl->getSuperClass());
@@ -3873,6 +3885,14 @@ Decl *Sema::ActOnAtEnd(Scope *S, SourceRange AtEnd, ArrayRef<Decl *> allMethods,
         ImplMethodsVsClassMethods(S, CatImplClass, Cat);
       }
     }
+  } else if (const auto *IntfDecl = dyn_cast<ObjCInterfaceDecl>(ClassDecl)) {
+    if (const ObjCInterfaceDecl *Super = IntfDecl->getSuperClass()) {
+      if (!IntfDecl->hasAttr<ObjCSubclassingRestrictedAttr>() &&
+          Super->hasAttr<ObjCSubclassingRestrictedAttr>()) {
+        Diag(IntfDecl->getLocation(), diag::err_restricted_superclass_mismatch);
+        Diag(Super->getLocation(), diag::note_class_declared);
+      }
+    }
   }
   if (isInterfaceDeclKind) {
     // Reject invalid vardecls.
diff --git a/test/SemaObjC/subclassing-restricted-attr.m b/test/SemaObjC/subclassing-restricted-attr.m
new file mode 100644 (file)
index 0000000..d0db5e4
--- /dev/null
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1  -fsyntax-only -verify -Wno-objc-root-class %s
+// RUN: %clang_cc1 -x objective-c++ -fsyntax-only -verify -Wno-objc-root-class %s
+// rdar://16560476
+
+__attribute__((objc_subclassing_restricted))
+@interface Leaf // okay
+@end
+
+__attribute__((objc_subclassing_restricted))
+@interface SubClassOfLeaf : Leaf // expected-note {{class is declared here}}
+@end
+
+
+@interface SubClass : SubClassOfLeaf // expected-error {{cannot subclass a class that was declared with the 'objc_subclassing_restricted' attribute}}
+@end
+
+__attribute__((objc_root_class))
+@interface PlainRoot
+@end
+
+__attribute__((objc_subclassing_restricted))
+@interface Sub2Class : PlainRoot // okay
+@end
+
+// rdar://28753587
+__attribute__((objc_subclassing_restricted))
+@interface SuperImplClass // expected-note {{class is declared here}}
+@end
+@implementation SuperImplClass
+@end
+
+__attribute__((objc_subclassing_restricted))
+@interface SubImplClass : SuperImplClass
+@end
+@implementation SubImplClass // expected-error {{cannot subclass a class that was declared with the 'objc_subclassing_restricted' attribute}}
+@end