]> granicus.if.org Git - clang/commitdiff
Add support for attributes on @implementations in Objective-C
authorErik Pilkington <erik.pilkington@gmail.com>
Thu, 11 Apr 2019 17:55:30 +0000 (17:55 +0000)
committerErik Pilkington <erik.pilkington@gmail.com>
Thu, 11 Apr 2019 17:55:30 +0000 (17:55 +0000)
We want to make objc_nonlazy_class apply to implementations, but ran into this.
There doesn't seem to be any reason that this isn't supported.

Differential revision: https://reviews.llvm.org/D60542

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

15 files changed:
include/clang/Basic/Attr.td
include/clang/Basic/DiagnosticParseKinds.td
include/clang/Parse/Parser.h
include/clang/Sema/Sema.h
lib/Parse/ParseObjc.cpp
lib/Parse/Parser.cpp
lib/Sema/SemaDeclObjC.cpp
test/FixIt/fixit-pragma-attribute.cpp
test/Misc/pragma-attribute-supported-attributes-list.test
test/Parser/attributes.mm
test/Parser/objc-implementation-attrs.m [new file with mode: 0644]
test/Parser/placeholder-recovery.m
test/Sema/pragma-attribute-strict-subjects.c
test/SemaObjC/attr-objc-non-lazy.m
test/SemaObjC/objc-asm-attribute-neg-test.m

index 091e0a5a2c3fad04351b07ac4c697152e8f1aeb5..c9086ee22b3aaf4e51e937db3bd653a46ae37ead 100644 (file)
@@ -419,6 +419,10 @@ def SubjectMatcherForObjCCategory : AttrSubjectMatcherRule<"objc_category",
                                                            [ObjCCategory]> {
   let LangOpts = [ObjC];
 }
+def SubjectMatcherForObjCImplementation :
+    AttrSubjectMatcherRule<"objc_implementation", [ObjCImpl]> {
+  let LangOpts = [ObjC];
+}
 def SubjectMatcherForObjCMethod : AttrSubjectMatcherRule<"objc_method",
                                                          [ObjCMethod], [
   AttrSubjectMatcherSubRule<"is_instance", [ObjCInstanceMethod]>
index 5ddae84cae306e2a7f64963f0c66f589f92377f6..84c24836f7d837e87a77565506d6d46708767abc 100644 (file)
@@ -433,7 +433,7 @@ def err_objc_expected_property_attr : Error<"unknown property attribute %0">;
 def err_objc_properties_require_objc2 : Error<
   "properties are an Objective-C 2 feature">;
 def err_objc_unexpected_attr : Error<
-  "prefix attribute must be followed by an interface or protocol">;
+  "prefix attribute must be followed by an interface, protocol, or implementation">;
 def err_objc_postfix_attribute : Error <
   "postfix attributes are not allowed on Objective-C directives">;
 def err_objc_postfix_attribute_hint : Error <
index d8f6c720b49bdce2efb1ff79bb5680dd53552a71..c77011d759c355f4bde3553e12cc4856e2981918 100644 (file)
@@ -1573,7 +1573,8 @@ private:
   ObjCImplParsingDataRAII *CurParsedObjCImpl;
   void StashAwayMethodOrFunctionBodyTokens(Decl *MDecl);
 
-  DeclGroupPtrTy ParseObjCAtImplementationDeclaration(SourceLocation AtLoc);
+  DeclGroupPtrTy ParseObjCAtImplementationDeclaration(SourceLocation AtLoc,
+                                                      ParsedAttributes &Attrs);
   DeclGroupPtrTy ParseObjCAtEndDeclaration(SourceRange atEnd);
   Decl *ParseObjCAtAliasDeclaration(SourceLocation atLoc);
   Decl *ParseObjCPropertySynthesize(SourceLocation atLoc);
index 71b08e36beb3138675e20bb118eb269a39ad96b3..41f331ca789dbb74ca3ac4faa2723dc660aab782 100644 (file)
@@ -8105,17 +8105,19 @@ public:
       const SourceLocation *ProtoLocs, SourceLocation EndProtoLoc,
       const ParsedAttributesView &AttrList);
 
-  Decl *ActOnStartClassImplementation(
-                    SourceLocation AtClassImplLoc,
-                    IdentifierInfo *ClassName, SourceLocation ClassLoc,
-                    IdentifierInfo *SuperClassname,
-                    SourceLocation SuperClassLoc);
+  Decl *ActOnStartClassImplementation(SourceLocation AtClassImplLoc,
+                                      IdentifierInfo *ClassName,
+                                      SourceLocation ClassLoc,
+                                      IdentifierInfo *SuperClassname,
+                                      SourceLocation SuperClassLoc,
+                                      const ParsedAttributesView &AttrList);
 
   Decl *ActOnStartCategoryImplementation(SourceLocation AtCatImplLoc,
                                          IdentifierInfo *ClassName,
                                          SourceLocation ClassLoc,
                                          IdentifierInfo *CatName,
-                                         SourceLocation CatLoc);
+                                         SourceLocation CatLoc,
+                                         const ParsedAttributesView &AttrList);
 
   DeclGroupPtrTy ActOnFinishObjCImplementation(Decl *ObjCImpDecl,
                                                ArrayRef<Decl *> Decls);
index 77bb5806825a41beee3aa498c2e39eb75fc9431d..274ea879e63ba836bf0b341ea96b0365a4f1d5f8 100644 (file)
@@ -64,7 +64,7 @@ Parser::ParseObjCAtDirectives(ParsedAttributesWithRange &Attrs) {
   case tok::objc_protocol:
     return ParseObjCAtProtocolDeclaration(AtLoc, Attrs);
   case tok::objc_implementation:
-    return ParseObjCAtImplementationDeclaration(AtLoc);
+    return ParseObjCAtImplementationDeclaration(AtLoc, Attrs);
   case tok::objc_end:
     return ParseObjCAtEndDeclaration(AtLoc);
   case tok::objc_compatibility_alias:
@@ -2097,7 +2097,8 @@ Parser::ParseObjCAtProtocolDeclaration(SourceLocation AtLoc,
 ///   objc-category-implementation-prologue:
 ///     @implementation identifier ( identifier )
 Parser::DeclGroupPtrTy
-Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
+Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc,
+                                             ParsedAttributes &Attrs) {
   assert(Tok.isObjCAtKeyword(tok::objc_implementation) &&
          "ParseObjCAtImplementationDeclaration(): Expected @implementation");
   CheckNestedObjCContexts(AtLoc);
@@ -2174,8 +2175,7 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
                                         /*consumeLastToken=*/true);
     }
     ObjCImpDecl = Actions.ActOnStartCategoryImplementation(
-                                    AtLoc, nameId, nameLoc, categoryId,
-                                    categoryLoc);
+        AtLoc, nameId, nameLoc, categoryId, categoryLoc, Attrs);
 
   } else {
     // We have a class implementation
@@ -2189,8 +2189,7 @@ Parser::ParseObjCAtImplementationDeclaration(SourceLocation AtLoc) {
       superClassLoc = ConsumeToken(); // Consume super class name
     }
     ObjCImpDecl = Actions.ActOnStartClassImplementation(
-                                    AtLoc, nameId, nameLoc,
-                                    superClassId, superClassLoc);
+        AtLoc, nameId, nameLoc, superClassId, superClassLoc, Attrs);
 
     if (Tok.is(tok::l_brace)) // we have ivars
       ParseObjCClassInstanceVariables(ObjCImpDecl, tok::objc_private, AtLoc);
index 9c8faae2544ebb0bad78b5ac86937aa9f4c40000..b477e1f8bbc4ec88c09bebae4b5d62290631a158 100644 (file)
@@ -980,9 +980,10 @@ Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs,
   if (getLangOpts().ObjC && Tok.is(tok::at)) {
     SourceLocation AtLoc = ConsumeToken(); // the "@"
     if (!Tok.isObjCAtKeyword(tok::objc_interface) &&
-        !Tok.isObjCAtKeyword(tok::objc_protocol)) {
+        !Tok.isObjCAtKeyword(tok::objc_protocol) &&
+        !Tok.isObjCAtKeyword(tok::objc_implementation)) {
       Diag(Tok, diag::err_objc_unexpected_attr);
-      SkipUntil(tok::semi); // FIXME: better skip?
+      SkipUntil(tok::semi);
       return nullptr;
     }
 
@@ -997,6 +998,9 @@ Parser::ParseDeclOrFunctionDefInternal(ParsedAttributesWithRange &attrs,
     if (Tok.isObjCAtKeyword(tok::objc_protocol))
       return ParseObjCAtProtocolDeclaration(AtLoc, DS.getAttributes());
 
+    if (Tok.isObjCAtKeyword(tok::objc_implementation))
+      return ParseObjCAtImplementationDeclaration(AtLoc, DS.getAttributes());
+
     return Actions.ConvertDeclToDeclGroup(
             ParseObjCAtInterfaceDeclaration(AtLoc, DS.getAttributes()));
   }
index 7fa561012ffed4298b201d4a23b2cf7eff21b824..d0674c641a7ec1f8dbdacd7f4cc345bf2170e69b 100644 (file)
@@ -1892,7 +1892,8 @@ Decl *Sema::ActOnStartCategoryInterface(
 Decl *Sema::ActOnStartCategoryImplementation(
                       SourceLocation AtCatImplLoc,
                       IdentifierInfo *ClassName, SourceLocation ClassLoc,
-                      IdentifierInfo *CatName, SourceLocation CatLoc) {
+                      IdentifierInfo *CatName, SourceLocation CatLoc,
+                      const ParsedAttributesView &Attrs) {
   ObjCInterfaceDecl *IDecl = getObjCInterfaceDecl(ClassName, ClassLoc, true);
   ObjCCategoryDecl *CatIDecl = nullptr;
   if (IDecl && IDecl->hasDefinition()) {
@@ -1920,6 +1921,9 @@ Decl *Sema::ActOnStartCategoryImplementation(
     CDecl->setInvalidDecl();
   }
 
+  ProcessDeclAttributeList(TUScope, CDecl, Attrs);
+  AddPragmaAttributes(TUScope, CDecl);
+
   // FIXME: PushOnScopeChains?
   CurContext->addDecl(CDecl);
 
@@ -1955,7 +1959,8 @@ Decl *Sema::ActOnStartClassImplementation(
                       SourceLocation AtClassImplLoc,
                       IdentifierInfo *ClassName, SourceLocation ClassLoc,
                       IdentifierInfo *SuperClassname,
-                      SourceLocation SuperClassLoc) {
+                      SourceLocation SuperClassLoc,
+                      const ParsedAttributesView &Attrs) {
   ObjCInterfaceDecl *IDecl = nullptr;
   // Check for another declaration kind with the same name.
   NamedDecl *PrevDecl
@@ -2049,6 +2054,9 @@ Decl *Sema::ActOnStartClassImplementation(
     ObjCImplementationDecl::Create(Context, CurContext, IDecl, SDecl,
                                    ClassLoc, AtClassImplLoc, SuperClassLoc);
 
+  ProcessDeclAttributeList(TUScope, IMPDecl, Attrs);
+  AddPragmaAttributes(TUScope, IMPDecl);
+
   if (CheckObjCDeclScope(IMPDecl))
     return ActOnObjCContainerStartDefinition(IMPDecl);
 
index 8e3f6d9392f0ca573cbb01820c19c8bdc04293d1..9d772fd66727f3d210a94b98c77818015959dc80 100644 (file)
@@ -16,8 +16,8 @@
 // CHECK: fix-it:{{.*}}:{[[@LINE-2]]:133-[[@LINE-2]]:153}:""
 
 #pragma clang attribute push (__attribute__((annotate("subRuleContradictions"))), apply_to = any(variable, variable(is_parameter), function(is_member), variable(is_global)))
-// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:108-[[@LINE-1]]:132}:""
-// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:153-[[@LINE-2]]:172}:""
+// CHECK: fix-it:{{.*}}:{[[@LINE-1]]:153-[[@LINE-1]]:172}:""
+// CHECK: fix-it:{{.*}}:{[[@LINE-2]]:108-[[@LINE-2]]:132}:""
 
 #pragma clang attribute pop
 
index 5cd2ba439ce4593b9e058272264987653e4709cf..d083f10acc7de9f7e54ea6395dde3db5e3934615 100644 (file)
@@ -18,7 +18,7 @@
 // CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType)
 // CHECK-NEXT: ArcWeakrefUnavailable (SubjectMatchRule_objc_interface)
 // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function)
-// CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
+// CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
 // CHECK-NEXT: CFAuditedTransfer (SubjectMatchRule_function)
 // CHECK-NEXT: CFConsumed (SubjectMatchRule_variable_is_parameter)
 // CHECK-NEXT: CFUnknownTransfer (SubjectMatchRule_function)
@@ -49,7 +49,7 @@
 // CHECK-NEXT: EnableIf (SubjectMatchRule_function)
 // CHECK-NEXT: EnumExtensibility (SubjectMatchRule_enum)
 // CHECK-NEXT: ExcludeFromExplicitInstantiation (SubjectMatchRule_variable, SubjectMatchRule_function, SubjectMatchRule_record)
-// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
+// CHECK-NEXT: ExternalSourceSymbol ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable))
 // CHECK-NEXT: FlagEnum (SubjectMatchRule_enum)
 // CHECK-NEXT: Flatten (SubjectMatchRule_function)
 // CHECK-NEXT: GNUInline (SubjectMatchRule_function)
index 024606bed3a106c2ee2ee2610d4bfc6f70b82842..e583df039f38ad4ef6bb2bcbfa6df196c2117f52 100644 (file)
@@ -1,6 +1,8 @@
 // RUN: %clang_cc1 -verify -fsyntax-only -Wno-objc-root-class %s
 
-__attribute__((deprecated)) @class B; // expected-error {{prefix attribute must be followed by an interface or protocol}}
+// FIXME: Why isn't this supported? Seems useful for availability attributes at
+// the very least.
+__attribute__((deprecated)) @class B; // expected-error {{prefix attribute must be followed by an interface, protocol, or implementation}}
 
 __attribute__((deprecated)) @interface A @end
 __attribute__((deprecated)) @protocol P0;
@@ -15,11 +17,10 @@ EXP class C2 {}; // expected-warning {{attribute 'visibility' is ignored, place
 EXP @interface I2 @end
 
 @implementation EXP I @end // expected-error-re {{postfix attributes are not allowed on Objective-C directives{{$}}}}
-// FIXME: Prefix attribute recovery skips until ';'
-EXP @implementation I2 @end; // expected-error {{prefix attribute must be followed by an interface or protocol}}
+EXP @implementation I2 @end
 
 @class EXP OC; // expected-error-re {{postfix attributes are not allowed on Objective-C directives{{$}}}}
-EXP @class OC2; // expected-error {{prefix attribute must be followed by an interface or protocol}}
+EXP @class OC2; // expected-error {{prefix attribute must be followed by an interface, protocol, or implementation}}
 
 @protocol EXP P @end // expected-error {{postfix attributes are not allowed on Objective-C directives, place them in front of '@protocol'}}
 EXP @protocol P2 @end
diff --git a/test/Parser/objc-implementation-attrs.m b/test/Parser/objc-implementation-attrs.m
new file mode 100644 (file)
index 0000000..76d9714
--- /dev/null
@@ -0,0 +1,53 @@
+// RUN: %clang_cc1 -triple x86_64-apple-macosx10.14.0 -fsyntax-only -Wno-objc-root-class -verify %s
+
+@interface I1 @end
+
+// expected-warning@+1 {{'always_inline' attribute only applies to functions}}
+__attribute__((always_inline))
+@implementation I1 @end
+
+// expected-warning@+1 {{'always_inline' attribute only applies to functions}}
+__attribute__((always_inline))
+@implementation I1 (MyCat) @end
+
+// expected-warning@+1 {{'always_inline' attribute only applies to functions}}
+__attribute__((always_inline))
+// expected-warning@+1 {{cannot find interface declaration for 'I2'}}
+@implementation I2 @end
+
+// expected-error@+1 {{only applies to Objective-C interfaces}}
+__attribute__((objc_root_class))
+// expected-warning@+1 {{cannot find interface declaration for 'I3'}}
+@implementation I3 @end
+
+#define AVAIL_ATTR __attribute__((availability(macos, introduced=1000)))
+
+typedef int AVAIL_ATTR unavail_int; // expected-note {{marked as being introduced}}
+
+@interface I4 @end // expected-note {{annotate}}
+@implementation I4 {
+  unavail_int x; // expected-warning {{'unavail_int' is only available on macOS 1000 or newer}}
+}
+@end
+
+@interface I5 @end
+
+#pragma clang attribute push (AVAIL_ATTR, apply_to=objc_implementation)
+@implementation I5 {
+  unavail_int x;
+}
+@end
+#pragma clang attribute pop
+
+I5 *i5;
+
+// expected-error@+1 2 {{'annotate' attribute takes one argument}}
+#pragma clang attribute push (__attribute__((annotate)), apply_to=objc_implementation)
+@interface I6 @end
+@interface I6 (MyCat) @end
+@interface I6 () @end
+
+@implementation I6 @end // expected-note {{when applied to this declaration}}
+@implementation I6 (MyCat) @end // expected-note {{when applied to this declaration}}
+
+#pragma clang attribute pop
index 4f22ea770da920c8cb59ae1ceab059f494cbfe4f..d49c8efb78eb987838231b9ca9ffe096a9767e3d 100644 (file)
@@ -11,4 +11,4 @@
 // bogus 'archaic' warnings with bad location info.
 <#methods#> // expected-error {{editor placeholder in source file}}
 
-@end // expected-error {{prefix attribute must be followed by an interface or protocol}} expected-error {{missing '@end'}}
+@end // expected-error {{prefix attribute must be followed by an interface, protocol, or implementation}} expected-error {{missing '@end'}}
index a84e2bde38d5d51eeadbba991dba765fe75467f0..42e3e20e766a829c7ee30ccd00e109cb1c2be02f 100644 (file)
@@ -56,7 +56,8 @@
 #pragma clang attribute pop
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(enum_constant, function, record(unless(is_union)), variable, variable(is_parameter)))
-// expected-error@-1 {{attribute 'abi_tag' can't be applied to 'variable(is_parameter)', and 'enum_constant'}}
+// FIXME: comma in this diagnostic is wrong.
+// expected-error@-2 {{attribute 'abi_tag' can't be applied to 'enum_constant', and 'variable(is_parameter)'}}
 #pragma clang attribute pop
 
 #pragma clang attribute push (__attribute__((abi_tag("a"))), apply_to = any(function, record(unless(is_union)), enum))
index 5dc88f7935ebd027321f712e9672f0ad89fe6e4f..e5de24e422b6a7f7d7d78847b05aacc6158e3f56 100644 (file)
@@ -29,6 +29,7 @@ void foo();
 
 @interface E
 @end
+// expected-error@+1 {{'objc_nonlazy_class' attribute only applies to Objective-C interfaces}}
 __attribute__((objc_nonlazy_class))
-@implementation E // expected-error {{prefix attribute must be followed by an interface or protocol}}
+@implementation E
 @end
index f32abd22063bc4fa22689616b5816e594e13b537..9941189357ba1efa37983962339c72bcfaa5ba1d 100644 (file)
@@ -19,7 +19,7 @@ __attribute__((objc_runtime_name("MySecretNamespace.Message"))) // expected-erro
   id MyIVAR;
 }
 __attribute__((objc_runtime_name("MySecretNamespace.Message")))
-@property int MyProperty; // expected-error {{prefix attribute must be followed by an interface or protocol}}}}
+@property int MyProperty; // expected-error {{prefix attribute must be followed by an interface, protocol, or implementation}}}}
 
 - (int) getMyProperty __attribute__((objc_runtime_name("MySecretNamespace.Message"))); // expected-error {{'objc_runtime_name' attribute only applies to}}
 
@@ -28,7 +28,7 @@ __attribute__((objc_runtime_name("MySecretNamespace.Message")))
 @end
 
 __attribute__((objc_runtime_name("MySecretNamespace.ForwardClass")))
-@class ForwardClass; // expected-error {{prefix attribute must be followed by an interface or protocol}}
+@class ForwardClass; // expected-error {{prefix attribute must be followed by an interface, protocol, or implementation}}
 
 __attribute__((objc_runtime_name("MySecretNamespace.ForwardProtocol")))
 @protocol ForwardProtocol;
@@ -45,6 +45,6 @@ __attribute__((objc_runtime_name("MySecretNamespace.ForwardProtocol")))
 
 @interface NoImpl @end
 
+// expected-error@+1 {{'objc_runtime_name' attribute only applies to Objective-C interfaces and Objective-C protocols}}
 __attribute__((objc_runtime_name("MySecretNamespace.Message")))
-// expected-error@+1 {{prefix attribute must be followed by an interface or protocol}}
 @implementation NoImpl @end