Skip to content

Implement support for @objc(name) on enum cases #797

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 6, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion include/swift/AST/Attr.def
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
9 changes: 8 additions & 1 deletion include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -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?", ())
Expand All @@ -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}"
Expand Down
23 changes: 15 additions & 8 deletions lib/PrintAsObjC/PrintAsObjC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,17 +71,18 @@ namespace {
};
}

static Identifier getNameForObjC(const NominalTypeDecl *NTD,
static Identifier getNameForObjC(const ValueDecl *VD,
CustomNamesOnly_t customNamesOnly = Normal) {
assert(isa<ClassDecl>(NTD) || isa<ProtocolDecl>(NTD) || isa<EnumDecl>(NTD));
if (auto objc = NTD->getAttrs().getAttribute<ObjCAttr>()) {
assert(isa<ClassDecl>(VD) || isa<ProtocolDecl>(VD)
|| isa<EnumDecl>(VD) || isa<EnumElementDecl>(VD));
if (auto objc = VD->getAttrs().getAttribute<ObjCAttr>()) {
if (auto name = objc->getName()) {
assert(name->getNumSelectorPieces() == 1);
return name->getSelectorPieces().front();
}
}

return customNamesOnly ? Identifier() : NTD->getName();
return customNamesOnly ? Identifier() : VD->getName();
}


Expand Down Expand Up @@ -255,12 +256,18 @@ class ObjCPrinter : private DeclVisitor<ObjCPrinter>,
// 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<IntegerLiteralExpr>(Elt->getRawValueExpr())) {
os << " = ";
Expand Down
18 changes: 15 additions & 3 deletions lib/Sema/TypeCheckDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7004,6 +7004,12 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
} else if (auto ED = dyn_cast<EnumDecl>(D)) {
if (ED->isGenericContext())
error = diag::objc_enum_generic;
} else if (auto EED = dyn_cast<EnumElementDecl>(D)) {
auto ED = EED->getParentEnum();
if (!ED->getAttrs().hasAttribute<ObjCAttr>())
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<FuncDecl>(D)) {
auto func = cast<FuncDecl>(D);
if (!checkObjCDeclContext(D))
Expand Down Expand Up @@ -7033,16 +7039,17 @@ 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<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<EnumDecl>(D) ||
isa<VarDecl>(D)) {
if (isa<ClassDecl>(D) || isa<ProtocolDecl>(D) || isa<VarDecl>(D)
|| isa<EnumDecl>(D) || isa<EnumElementDecl>(D)) {
// Types and properties can only have nullary
// names. Complain and recover by chopping off everything
// after the first name.
if (objcName->getNumArgs() > 0) {
int which = isa<ClassDecl>(D)? 0
: isa<ProtocolDecl>(D)? 1
: isa<EnumDecl>(D)? 2
: 3;
: isa<EnumElementDecl>(D)? 3
: 4;
SourceLoc firstNameLoc = objcAttr->getNameLocs().front();
SourceLoc afterFirstNameLoc =
Lexer::getLocForEndOfToken(TC.Context.SourceMgr, firstNameLoc);
Expand Down Expand Up @@ -7091,6 +7098,11 @@ static void validateAttributes(TypeChecker &TC, Decl *D) {
D->getAttrs().removeAttribute(objcAttr);
}
}
} else if (isa<EnumElementDecl>(D)) {
// Enum elements require names.
TC.diagnose(objcAttr->getLocation(), diag::objc_enum_case_req_name)
.fixItRemove(objcAttr->getRangeWithAt());
objcAttr->setInvalid();
}
}

Expand Down
12 changes: 12 additions & 0 deletions test/PrintAsObjC/enums.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
25 changes: 24 additions & 1 deletion test/attr/attr_objc.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,16 +194,33 @@ 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=}}

@objc
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
Expand Down Expand Up @@ -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
Expand Down