diff --git a/include/swift/AST/Attr.def b/include/swift/AST/Attr.def index ede024933dc93..a9aa7c5e2d213 100644 --- a/include/swift/AST/Attr.def +++ b/include/swift/AST/Attr.def @@ -92,7 +92,7 @@ SIMPLE_DECL_ATTR(final, Final, DECL_ATTR(objc, ObjC, OnFunc | OnClass | OnProtocol | OnVar | OnSubscript | - OnConstructor | OnDestructor | OnEnum, 3) + OnConstructor | OnDestructor | OnEnum | OnEnumElement, 3) SIMPLE_DECL_ATTR(required, Required, OnConstructor|DeclModifier, 4) diff --git a/include/swift/AST/DiagnosticsSema.def b/include/swift/AST/DiagnosticsSema.def index 24b15fda701c8..bbdd4f360ef66 100644 --- a/include/swift/AST/DiagnosticsSema.def +++ b/include/swift/AST/DiagnosticsSema.def @@ -2333,7 +2333,7 @@ ERROR(objc_setter_for_nonobjc_subscript,sema_tcd,none, ERROR(objc_enum_generic,sema_tcd,none, "'@objc' enum cannot be generic", ()) ERROR(objc_name_req_nullary,sema_objc,none, - "'@objc' %select{class|protocol|enum|property}0 must have a simple name", (int)) + "'@objc' %select{class|protocol|enum|enum case|property}0 must have a simple name", (int)) ERROR(objc_name_subscript,sema_objc,none, "'@objc' subscript cannot have a name; did you mean to put " "the name on the getter or setter?", ()) @@ -2343,6 +2343,13 @@ ERROR(objc_name_func_mismatch,sema_objc,none, "%select{initializer|method}0 has %select{one parameter|%3 parameters}4" "%select{| (%select{|including }4the error parameter)}5", (bool, unsigned, bool, unsigned, bool, bool)) +ERROR(objc_enum_case_req_name,sema_objc,none, + "attribute has no effect; cases within an '@objc' enum are already " + "exposed to Objective-C", ()) +ERROR(objc_enum_case_req_objc_enum,sema_objc,none, + "'@objc' enum case is not allowed outside of an '@objc' enum", ()) +ERROR(objc_enum_case_multi,sema_objc,none, + "'@objc' enum case declaration defines multiple enum cases with the same Objective-C name", ()) // If you change this, also change enum ObjCReason #define OBJC_ATTR_SELECT "select{marked dynamic|marked @objc|marked @IBOutlet|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override}" diff --git a/lib/PrintAsObjC/PrintAsObjC.cpp b/lib/PrintAsObjC/PrintAsObjC.cpp index f99578da7db9a..0ba36487c59e6 100644 --- a/lib/PrintAsObjC/PrintAsObjC.cpp +++ b/lib/PrintAsObjC/PrintAsObjC.cpp @@ -71,17 +71,18 @@ namespace { }; } -static Identifier getNameForObjC(const NominalTypeDecl *NTD, +static Identifier getNameForObjC(const ValueDecl *VD, CustomNamesOnly_t customNamesOnly = Normal) { - assert(isa(NTD) || isa(NTD) || isa(NTD)); - if (auto objc = NTD->getAttrs().getAttribute()) { + assert(isa(VD) || isa(VD) + || isa(VD) || isa(VD)); + if (auto objc = VD->getAttrs().getAttribute()) { if (auto name = objc->getName()) { assert(name->getNumSelectorPieces() == 1); return name->getSelectorPieces().front(); } } - return customNamesOnly ? Identifier() : NTD->getName(); + return customNamesOnly ? Identifier() : VD->getName(); } @@ -255,12 +256,18 @@ class ObjCPrinter : private DeclVisitor, // Print the cases as the concatenation of the enum name with the case // name. os << " "; - if (customName.empty()) { - os << ED->getName(); + Identifier customEltName = getNameForObjC(Elt, CustomNamesOnly); + if (customEltName.empty()) { + if (customName.empty()) { + os << ED->getName(); + } else { + os << customName; + } + os << Elt->getName(); } else { - os << customName; + os << customEltName + << " SWIFT_COMPILE_NAME(\"" << Elt->getName() << "\")"; } - os << Elt->getName(); if (auto ILE = cast_or_null(Elt->getRawValueExpr())) { os << " = "; diff --git a/lib/Sema/TypeCheckDecl.cpp b/lib/Sema/TypeCheckDecl.cpp index 9af029b9504f3..b14bfa6a53aa5 100644 --- a/lib/Sema/TypeCheckDecl.cpp +++ b/lib/Sema/TypeCheckDecl.cpp @@ -7004,6 +7004,12 @@ static void validateAttributes(TypeChecker &TC, Decl *D) { } else if (auto ED = dyn_cast(D)) { if (ED->isGenericContext()) error = diag::objc_enum_generic; + } else if (auto EED = dyn_cast(D)) { + auto ED = EED->getParentEnum(); + if (!ED->getAttrs().hasAttribute()) + error = diag::objc_enum_case_req_objc_enum; + else if (objcAttr->hasName() && EED->getParentCase()->getElements().size() > 1) + error = diag::objc_enum_case_multi; } else if (isa(D)) { auto func = cast(D); if (!checkObjCDeclContext(D)) @@ -7033,8 +7039,8 @@ static void validateAttributes(TypeChecker &TC, Decl *D) { // If there is a name, check whether the kind of name is // appropriate. if (auto objcName = objcAttr->getName()) { - if (isa(D) || isa(D) || isa(D) || - isa(D)) { + if (isa(D) || isa(D) || isa(D) + || isa(D) || isa(D)) { // Types and properties can only have nullary // names. Complain and recover by chopping off everything // after the first name. @@ -7042,7 +7048,8 @@ static void validateAttributes(TypeChecker &TC, Decl *D) { int which = isa(D)? 0 : isa(D)? 1 : isa(D)? 2 - : 3; + : isa(D)? 3 + : 4; SourceLoc firstNameLoc = objcAttr->getNameLocs().front(); SourceLoc afterFirstNameLoc = Lexer::getLocForEndOfToken(TC.Context.SourceMgr, firstNameLoc); @@ -7091,6 +7098,11 @@ static void validateAttributes(TypeChecker &TC, Decl *D) { D->getAttrs().removeAttribute(objcAttr); } } + } else if (isa(D)) { + // Enum elements require names. + TC.diagnose(objcAttr->getLocation(), diag::objc_enum_case_req_name) + .fixItRemove(objcAttr->getRangeWithAt()); + objcAttr->setInvalid(); } } diff --git a/test/PrintAsObjC/enums.swift b/test/PrintAsObjC/enums.swift index 89b0ca2b149fd..45bb5e8c94c3f 100644 --- a/test/PrintAsObjC/enums.swift +++ b/test/PrintAsObjC/enums.swift @@ -36,6 +36,18 @@ import Foundation case A, B, C } +// CHECK-LABEL: typedef SWIFT_ENUM(NSInteger, EnumWithNamedConstants) { +// CHECK-NEXT: kEnumA SWIFT_COMPILE_NAME("A") = 0, +// CHECK-NEXT: kEnumB SWIFT_COMPILE_NAME("B") = 1, +// CHECK-NEXT: kEnumC SWIFT_COMPILE_NAME("C") = 2, +// CHECK-NEXT: }; + +@objc enum EnumWithNamedConstants: Int { + @objc(kEnumA) case A + @objc(kEnumB) case B + @objc(kEnumC) case C +} + // CHECK-LABEL: typedef SWIFT_ENUM(unsigned int, ExplicitValues) { // CHECK-NEXT: ExplicitValuesZim = 0, // CHECK-NEXT: ExplicitValuesZang = 219, diff --git a/test/attr/attr_objc.swift b/test/attr/attr_objc.swift index 9aa4573c88c1a..08ef18a1a3258 100644 --- a/test/attr/attr_objc.swift +++ b/test/attr/attr_objc.swift @@ -194,9 +194,21 @@ extension subject_genericClass { @objc enum subject_enum: Int { - @objc // expected-error {{@objc cannot be applied to this declaration}} {{3-9=}} + @objc // expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}} case subject_enumElement1 + @objc(subject_enumElement2) + case subject_enumElement2 + + @objc(subject_enumElement3) + case subject_enumElement3, subject_enumElement4 // expected-error {{'@objc' enum case declaration defines multiple enum cases with the same Objective-C name}}{{3-8=}} + + @objc // expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}} + case subject_enumElement5, subject_enumElement6 + + @nonobjc // expected-error {{@nonobjc cannot be applied to this declaration}} + case subject_enumElement7 + @objc init() {} // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}} @@ -204,6 +216,11 @@ enum subject_enum: Int { func subject_instanceFunc() {} // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-8=}} } +enum subject_enum2 { + @objc(subject_enum2Element1) + case subject_enumElement1 // expected-error{{'@objc' enum case is not allowed outside of an '@objc' enum}}{{3-8=}} +} + @objc protocol subject_protocol1 { @objc @@ -1752,6 +1769,12 @@ protocol BadProto1 { } @objc(Enum:) // expected-error{{'@objc' enum must have a simple name}}{{11-12=}} enum BadEnum1: Int { case X } +@objc +enum BadEnum2: Int { + @objc(X:) // expected-error{{'@objc' enum case must have a simple name}}{{10-11=}} + case X +} + class BadClass2 { @objc(badprop:foo:wibble:) // expected-error{{'@objc' property must have a simple name}}{{16-28=}} var badprop: Int = 5