Skip to content

Commit 6ef18cd

Browse files
committed
Merge pull request #797 from kballard/enum-constant-objc
Implement support for @objc(name) on enum cases. Changelog update coming next.
2 parents 2c4c7bd + f473851 commit 6ef18cd

File tree

6 files changed

+75
-14
lines changed

6 files changed

+75
-14
lines changed

include/swift/AST/Attr.def

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ SIMPLE_DECL_ATTR(final, Final,
9292

9393
DECL_ATTR(objc, ObjC,
9494
OnFunc | OnClass | OnProtocol | OnVar | OnSubscript |
95-
OnConstructor | OnDestructor | OnEnum, 3)
95+
OnConstructor | OnDestructor | OnEnum | OnEnumElement, 3)
9696

9797
SIMPLE_DECL_ATTR(required, Required,
9898
OnConstructor|DeclModifier, 4)

include/swift/AST/DiagnosticsSema.def

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2329,7 +2329,7 @@ ERROR(objc_setter_for_nonobjc_subscript,sema_tcd,none,
23292329
ERROR(objc_enum_generic,sema_tcd,none,
23302330
"'@objc' enum cannot be generic", ())
23312331
ERROR(objc_name_req_nullary,sema_objc,none,
2332-
"'@objc' %select{class|protocol|enum|property}0 must have a simple name", (int))
2332+
"'@objc' %select{class|protocol|enum|enum case|property}0 must have a simple name", (int))
23332333
ERROR(objc_name_subscript,sema_objc,none,
23342334
"'@objc' subscript cannot have a name; did you mean to put "
23352335
"the name on the getter or setter?", ())
@@ -2339,6 +2339,13 @@ ERROR(objc_name_func_mismatch,sema_objc,none,
23392339
"%select{initializer|method}0 has %select{one parameter|%3 parameters}4"
23402340
"%select{| (%select{|including }4the error parameter)}5",
23412341
(bool, unsigned, bool, unsigned, bool, bool))
2342+
ERROR(objc_enum_case_req_name,sema_objc,none,
2343+
"attribute has no effect; cases within an '@objc' enum are already "
2344+
"exposed to Objective-C", ())
2345+
ERROR(objc_enum_case_req_objc_enum,sema_objc,none,
2346+
"'@objc' enum case is not allowed outside of an '@objc' enum", ())
2347+
ERROR(objc_enum_case_multi,sema_objc,none,
2348+
"'@objc' enum case declaration defines multiple enum cases with the same Objective-C name", ())
23422349

23432350
// If you change this, also change enum ObjCReason
23442351
#define OBJC_ATTR_SELECT "select{marked dynamic|marked @objc|marked @IBOutlet|marked @NSManaged|a member of an @objc protocol|implicitly @objc|an @objc override}"

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,18 @@ namespace {
7171
};
7272
}
7373

74-
static Identifier getNameForObjC(const NominalTypeDecl *NTD,
74+
static Identifier getNameForObjC(const ValueDecl *VD,
7575
CustomNamesOnly_t customNamesOnly = Normal) {
76-
assert(isa<ClassDecl>(NTD) || isa<ProtocolDecl>(NTD) || isa<EnumDecl>(NTD));
77-
if (auto objc = NTD->getAttrs().getAttribute<ObjCAttr>()) {
76+
assert(isa<ClassDecl>(VD) || isa<ProtocolDecl>(VD)
77+
|| isa<EnumDecl>(VD) || isa<EnumElementDecl>(VD));
78+
if (auto objc = VD->getAttrs().getAttribute<ObjCAttr>()) {
7879
if (auto name = objc->getName()) {
7980
assert(name->getNumSelectorPieces() == 1);
8081
return name->getSelectorPieces().front();
8182
}
8283
}
8384

84-
return customNamesOnly ? Identifier() : NTD->getName();
85+
return customNamesOnly ? Identifier() : VD->getName();
8586
}
8687

8788

@@ -255,12 +256,18 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
255256
// Print the cases as the concatenation of the enum name with the case
256257
// name.
257258
os << " ";
258-
if (customName.empty()) {
259-
os << ED->getName();
259+
Identifier customEltName = getNameForObjC(Elt, CustomNamesOnly);
260+
if (customEltName.empty()) {
261+
if (customName.empty()) {
262+
os << ED->getName();
263+
} else {
264+
os << customName;
265+
}
266+
os << Elt->getName();
260267
} else {
261-
os << customName;
268+
os << customEltName
269+
<< " SWIFT_COMPILE_NAME(\"" << Elt->getName() << "\")";
262270
}
263-
os << Elt->getName();
264271

265272
if (auto ILE = cast_or_null<IntegerLiteralExpr>(Elt->getRawValueExpr())) {
266273
os << " = ";

lib/Sema/TypeCheckDecl.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6881,6 +6881,12 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
68816881
} else if (auto ED = dyn_cast<EnumDecl>(D)) {
68826882
if (ED->isGenericContext())
68836883
error = diag::objc_enum_generic;
6884+
} else if (auto EED = dyn_cast<EnumElementDecl>(D)) {
6885+
auto ED = EED->getParentEnum();
6886+
if (!ED->getAttrs().hasAttribute<ObjCAttr>())
6887+
error = diag::objc_enum_case_req_objc_enum;
6888+
else if (objcAttr->hasName() && EED->getParentCase()->getElements().size() > 1)
6889+
error = diag::objc_enum_case_multi;
68846890
} else if (isa<FuncDecl>(D)) {
68856891
auto func = cast<FuncDecl>(D);
68866892
if (!checkObjCDeclContext(D))
@@ -6910,16 +6916,17 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
69106916
// If there is a name, check whether the kind of name is
69116917
// appropriate.
69126918
if (auto objcName = objcAttr->getName()) {
6913-
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<EnumDecl>(D) ||
6914-
isa<VarDecl>(D)) {
6919+
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<VarDecl>(D)
6920+
|| isa<EnumDecl>(D) || isa<EnumElementDecl>(D)) {
69156921
// Types and properties can only have nullary
69166922
// names. Complain and recover by chopping off everything
69176923
// after the first name.
69186924
if (objcName->getNumArgs() > 0) {
69196925
int which = isa<ClassDecl>(D)? 0
69206926
: isa<ProtocolDecl>(D)? 1
69216927
: isa<EnumDecl>(D)? 2
6922-
: 3;
6928+
: isa<EnumElementDecl>(D)? 3
6929+
: 4;
69236930
SourceLoc firstNameLoc = objcAttr->getNameLocs().front();
69246931
SourceLoc afterFirstNameLoc =
69256932
Lexer::getLocForEndOfToken(TC.Context.SourceMgr, firstNameLoc);
@@ -6964,6 +6971,11 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
69646971
D->getAttrs().removeAttribute(objcAttr);
69656972
}
69666973
}
6974+
} else if (isa<EnumElementDecl>(D)) {
6975+
// Enum elements require names.
6976+
TC.diagnose(objcAttr->getLocation(), diag::objc_enum_case_req_name)
6977+
.fixItRemove(objcAttr->getRangeWithAt());
6978+
objcAttr->setInvalid();
69676979
}
69686980
}
69696981

test/PrintAsObjC/enums.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,18 @@ import Foundation
3636
case A, B, C
3737
}
3838

39+
// CHECK-LABEL: typedef SWIFT_ENUM(NSInteger, EnumWithNamedConstants) {
40+
// CHECK-NEXT: kEnumA SWIFT_COMPILE_NAME("A") = 0,
41+
// CHECK-NEXT: kEnumB SWIFT_COMPILE_NAME("B") = 1,
42+
// CHECK-NEXT: kEnumC SWIFT_COMPILE_NAME("C") = 2,
43+
// CHECK-NEXT: };
44+
45+
@objc enum EnumWithNamedConstants: Int {
46+
@objc(kEnumA) case A
47+
@objc(kEnumB) case B
48+
@objc(kEnumC) case C
49+
}
50+
3951
// CHECK-LABEL: typedef SWIFT_ENUM(unsigned int, ExplicitValues) {
4052
// CHECK-NEXT: ExplicitValuesZim = 0,
4153
// CHECK-NEXT: ExplicitValuesZang = 219,

test/attr/attr_objc.swift

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,16 +194,33 @@ extension subject_genericClass {
194194

195195
@objc
196196
enum subject_enum: Int {
197-
@objc // expected-error {{@objc cannot be applied to this declaration}} {{3-9=}}
197+
@objc // expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}}
198198
case subject_enumElement1
199199

200+
@objc(subject_enumElement2)
201+
case subject_enumElement2
202+
203+
@objc(subject_enumElement3)
204+
case subject_enumElement3, subject_enumElement4 // expected-error {{'@objc' enum case declaration defines multiple enum cases with the same Objective-C name}}{{3-8=}}
205+
206+
@objc // expected-error {{attribute has no effect; cases within an '@objc' enum are already exposed to Objective-C}} {{3-9=}}
207+
case subject_enumElement5, subject_enumElement6
208+
209+
@nonobjc // expected-error {{@nonobjc cannot be applied to this declaration}}
210+
case subject_enumElement7
211+
200212
@objc
201213
init() {} // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-9=}}
202214

203215
@objc
204216
func subject_instanceFunc() {} // expected-error {{@objc can only be used with members of classes, @objc protocols, and concrete extensions of classes}} {{3-8=}}
205217
}
206218

219+
enum subject_enum2 {
220+
@objc(subject_enum2Element1)
221+
case subject_enumElement1 // expected-error{{'@objc' enum case is not allowed outside of an '@objc' enum}}{{3-8=}}
222+
}
223+
207224
@objc
208225
protocol subject_protocol1 {
209226
@objc
@@ -1752,6 +1769,12 @@ protocol BadProto1 { }
17521769
@objc(Enum:) // expected-error{{'@objc' enum must have a simple name}}{{11-12=}}
17531770
enum BadEnum1: Int { case X }
17541771

1772+
@objc
1773+
enum BadEnum2: Int {
1774+
@objc(X:) // expected-error{{'@objc' enum case must have a simple name}}{{10-11=}}
1775+
case X
1776+
}
1777+
17551778
class BadClass2 {
17561779
@objc(badprop:foo:wibble:) // expected-error{{'@objc' property must have a simple name}}{{16-28=}}
17571780
var badprop: Int = 5

0 commit comments

Comments
 (0)