diff --git a/changelog/enum_attributes.dd b/changelog/enum_attributes.dd new file mode 100644 index 000000000000..764860f7fc59 --- /dev/null +++ b/changelog/enum_attributes.dd @@ -0,0 +1,24 @@ +D now supports attributes on enum members + +Example +--- +template AliasSeq(TList...) +{ + alias AliasSeq = TList; +} + +enum MyEnum +{ + @("uda0") value0, + @disable value1, + deprecated value2 // Deprecation: enum member `main.MyEnum.value2` is deprecated +} + +static assert(__traits(getAttributes, MyEnum.value0) == AliasSeq!("uda0")); + +void main() +{ + auto v1 = MyEnum.value1; // Error: enum member `main.MyEnum.value1` cannot be used because it is annotated with `@disable` +} +--- + diff --git a/src/dmd/astbase.d b/src/dmd/astbase.d index 2d603a358186..45b8fea665e6 100644 --- a/src/dmd/astbase.d +++ b/src/dmd/astbase.d @@ -1424,6 +1424,15 @@ struct ASTBase this.origType = origType; } + extern(D) this(Loc loc, Identifier id, Expression value, Type memtype, + StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) + { + this(loc, id, value, memtype); + storage_class = stc; + userAttribDecl = uad; + depdecl = dd; + } + override void accept(Visitor v) { v.visit(this); diff --git a/src/dmd/declaration.d b/src/dmd/declaration.d index 5436accf29cd..2a969ca7aa42 100644 --- a/src/dmd/declaration.d +++ b/src/dmd/declaration.d @@ -340,7 +340,7 @@ extern (C++) abstract class Declaration : Dsymbol return false; } } - error(loc, "is not callable because it is annotated with `@disable`"); + error(loc, "cannot be used because it is annotated with `@disable`"); } } return true; diff --git a/src/dmd/denum.d b/src/dmd/denum.d index 8b976a19526e..1d8659e0c20a 100644 --- a/src/dmd/denum.d +++ b/src/dmd/denum.d @@ -14,6 +14,7 @@ module dmd.denum; import core.stdc.stdio; +import dmd.attrib; import dmd.gluelayer; import dmd.declaration; import dmd.dscope; @@ -374,6 +375,7 @@ extern (C++) final class EnumMember : VarDeclaration Type origType; EnumDeclaration ed; + bool isdeprecated; extern (D) this(const ref Loc loc, Identifier id, Expression value, Type origType) { @@ -382,6 +384,15 @@ extern (C++) final class EnumMember : VarDeclaration this.origType = origType; } + extern(D) this(Loc loc, Identifier id, Expression value, Type memtype, + StorageClass stc, UserAttributeDeclaration uad, DeprecatedDeclaration dd) + { + this(loc, id, value, memtype); + storage_class = stc; + userAttribDecl = uad; + depdecl = dd; + } + override Dsymbol syntaxCopy(Dsymbol s) { assert(!s); @@ -396,6 +407,9 @@ extern (C++) final class EnumMember : VarDeclaration Expression getVarExp(const ref Loc loc, Scope* sc) { dsymbolSemantic(this, sc); + if (errors) + return new ErrorExp(); + checkDisabled(loc, sc); if (errors) return new ErrorExp(); Expression e = new VarExp(loc, this); diff --git a/src/dmd/dsymbolsem.d b/src/dmd/dsymbolsem.d index 3a819787f0e8..59bd57b7f7a4 100644 --- a/src/dmd/dsymbolsem.d +++ b/src/dmd/dsymbolsem.d @@ -2171,6 +2171,7 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor return errorReturn(); } assert(em.ed); + em.ed.dsymbolSemantic(sc); if (em.ed.errors) return errorReturn(); @@ -2185,9 +2186,19 @@ private extern(C++) final class DsymbolSemanticVisitor : Visitor em.semanticRun = PASS.semantic; em.protection = em.ed.isAnonymous() ? em.ed.protection : Prot(Prot.Kind.public_); + if (sc.stc & STC.deprecated_ || em.isDeprecated()) + em.isdeprecated = true; em.linkage = LINK.d; - em.storage_class = STC.manifest; - em.userAttribDecl = em.ed.isAnonymous() ? em.ed.userAttribDecl : null; + em.storage_class |= STC.manifest; + + // https://issues.dlang.org/show_bug.cgi?id=9701 + if (em.ed.isAnonymous()) + { + if (em.userAttribDecl) + em.userAttribDecl.userAttribDecl = em.ed.userAttribDecl; + else + em.userAttribDecl = em.ed.userAttribDecl; + } // The first enum member is special bool first = (em == (*em.ed.members)[0]); diff --git a/src/dmd/expressionsem.d b/src/dmd/expressionsem.d index ab0c1102c6a6..5c0b5103316d 100644 --- a/src/dmd/expressionsem.d +++ b/src/dmd/expressionsem.d @@ -2494,12 +2494,6 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor { printf("VarExp::semantic(%s)\n", e.toChars()); } - if (auto fd = e.var.isFuncDeclaration()) - { - //printf("L%d fd = %s\n", __LINE__, f.toChars()); - if (!fd.functionSemantic()) - return setError(); - } if (!e.type) e.type = e.var.type; @@ -2512,6 +2506,8 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor */ //checkAccess(loc, sc, NULL, var); + e.checkDeprecated(sc, e.var); + if (auto vd = e.var.isVarDeclaration()) { if (vd.checkNestedReference(sc, e.loc)) @@ -2524,6 +2520,10 @@ private extern (C++) final class ExpressionSemanticVisitor : Visitor } else if (auto fd = e.var.isFuncDeclaration()) { + //printf("L%d fd = %s\n", __LINE__, f.toChars()); + if (!fd.functionSemantic()) + return setError(); + // TODO: If fd isn't yet resolved its overload, the checkNestedReference // call would cause incorrect validation. // Maybe here should be moved in CallExp, or AddrExp for functions. diff --git a/src/dmd/parse.d b/src/dmd/parse.d index 7f9de91628d0..c363997ea618 100644 --- a/src/dmd/parse.d +++ b/src/dmd/parse.d @@ -392,6 +392,23 @@ final class Parser(AST) : Lexer return new AST.Dsymbols(); } + private StorageClass parseDeprecatedAttribute(ref AST.Expression msg) + { + if (peek(&token).value != TOK.leftParentheses) + return AST.STC.deprecated_; + + nextToken(); + check(TOK.leftParentheses); + AST.Expression e = parseAssignExp(); + check(TOK.rightParentheses); + if (msg) + { + error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", msg.toChars(), e.toChars()); + } + msg = e; + return AST.STC.undefined_; + } + AST.Dsymbols* parseDeclDefs(int once, AST.Dsymbol* pLastDecl = null, PrefixAttributes!AST* pAttrs = null) { AST.Dsymbol lastDecl = null; // used to link unittest to its previous declaration @@ -811,20 +828,12 @@ final class Parser(AST) : Lexer case TOK.deprecated_: { - if (peek(&token).value != TOK.leftParentheses) + AST.Expression e; + if (StorageClass _stc = parseDeprecatedAttribute(pAttrs.depmsg)) { - stc = AST.STC.deprecated_; + stc = _stc; goto Lstc; } - nextToken(); - check(TOK.leftParentheses); - AST.Expression e = parseAssignExp(); - check(TOK.rightParentheses); - if (pAttrs.depmsg) - { - error("conflicting storage class `deprecated(%s)` and `deprecated(%s)`", pAttrs.depmsg.toChars(), e.toChars()); - } - pAttrs.depmsg = e; a = parseBlock(pLastDecl, pAttrs); if (pAttrs.depmsg) { @@ -2955,7 +2964,7 @@ final class Parser(AST) : Lexer AST.Type memtype; auto loc = token.loc; - //printf("Parser::parseEnum()\n"); + // printf("Parser::parseEnum()\n"); nextToken(); if (token.value == TOK.identifier) { @@ -2982,34 +2991,93 @@ final class Parser(AST) : Lexer nextToken(); else if (token.value == TOK.leftCurly) { + bool isAnonymousEnum = !id; + //printf("enum definition\n"); e.members = new AST.Dsymbols(); nextToken(); const(char)* comment = token.blockComment; while (token.value != TOK.rightCurly) { - /* Can take the following forms: + /* Can take the following forms... * 1. ident * 2. ident = value * 3. type ident = value + * ... prefixed by valid attributes */ loc = token.loc; AST.Type type = null; Identifier ident = null; - Token* tp = peek(&token); - if (token.value == TOK.identifier && (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly)) + + AST.Expressions* udas; + StorageClass stc; + AST.Expression deprecationMessage; + enum attributeErrorMessage = "`%s` is not a valid attribute for enum members"; + while(token.value != TOK.rightCurly + && token.value != TOK.comma + && token.value != TOK.assign) { - ident = token.ident; - type = null; - nextToken(); + switch(token.value) + { + case TOK.at: + if (StorageClass _stc = parseAttribute(&udas)) + { + if (_stc == AST.STC.disable) + stc |= _stc; + else + { + OutBuffer buf; + AST.stcToBuffer(&buf, _stc); + error(attributeErrorMessage, buf.peekString()); + } + nextToken(); + } + break; + case TOK.deprecated_: + if (StorageClass _stc = parseDeprecatedAttribute(deprecationMessage)) + { + stc |= _stc; + nextToken(); + } + break; + case TOK.identifier: + Token* tp = peek(&token); + if (tp.value == TOK.assign || tp.value == TOK.comma || tp.value == TOK.rightCurly) + { + ident = token.ident; + type = null; + nextToken(); + } + else + { + goto default; + } + break; + default: + if (isAnonymousEnum) + { + type = parseType(&ident, null); + if (type == AST.Type.terror) + { + type = null; + nextToken(); + } + } + else + { + error(attributeErrorMessage, token.toChars()); + nextToken(); + } + break; + } } - else + + if (type && type != AST.Type.terror) { - type = parseType(&ident, null); if (!ident) error("no identifier for declarator `%s`", type.toChars()); - if (id || memtype) + if (!isAnonymousEnum) error("type only allowed if anonymous enum and no enum type"); } @@ -3022,11 +3090,19 @@ final class Parser(AST) : Lexer else { value = null; - if (type) + if (type && type != AST.Type.terror && isAnonymousEnum) error("if type, there must be an initializer"); } - auto em = new AST.EnumMember(loc, ident, value, type); + AST.UserAttributeDeclaration uad; + if (udas) + uad = new AST.UserAttributeDeclaration(udas, null); + + AST.DeprecatedDeclaration dd; + if (deprecationMessage) + dd = new AST.DeprecatedDeclaration(deprecationMessage, null); + + auto em = new AST.EnumMember(loc, ident, value, type, stc, uad, dd); e.members.push(em); if (token.value == TOK.rightCurly) diff --git a/src/dmd/traits.d b/src/dmd/traits.d index 54fc523672d5..0bded285d540 100644 --- a/src/dmd/traits.d +++ b/src/dmd/traits.d @@ -20,6 +20,7 @@ import dmd.arraytypes; import dmd.canthrow; import dmd.dclass; import dmd.declaration; +import dmd.denum; import dmd.dscope; import dmd.dsymbol; import dmd.dsymbolsem; @@ -510,6 +511,8 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) auto y = s.isDeclaration(); static if (is(T == FuncDeclaration)) auto y = s.isFuncDeclaration(); + static if (is(T == EnumMember)) + auto y = s.isEnumMember(); if (!y || !fp(y)) return False(); @@ -521,6 +524,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) alias isDsymX = isX!Dsymbol; alias isDeclX = isX!Declaration; alias isFuncX = isX!FuncDeclaration; + alias isEnumMemX = isX!EnumMember; if (e.ident == Id.isArithmetic) { @@ -634,7 +638,7 @@ extern (C++) Expression semanticTraits(TraitsExp e, Scope* sc) if (dim != 1) return dimError(1); - return isFuncX(f => f.isDisabled()); + return isDeclX(f => f.isDisabled()); } if (e.ident == Id.isAbstractFunction) { diff --git a/test/compilable/test9701.d b/test/compilable/test9701.d new file mode 100644 index 000000000000..f702481462ec --- /dev/null +++ b/test/compilable/test9701.d @@ -0,0 +1,56 @@ +// https://issues.dlang.org/show_bug.cgi?id=9701 + +template AliasSeq(TList...) +{ + alias AliasSeq = TList; +} + +enum +{ + uda4, + uda5, + uda6, + uda8, + uda9 +} + +enum Enum +{ + value0, + @("uda1") value1, + @("uda2", "uda3", 42) value2, + @uda4 value3, + @uda5 @uda6 value4, + @("uda7") @uda8 value5, + @uda9 @("uda10") value6, + deprecated value7 +} + +@("uda0") +enum +{ + value0, + @("uda1") value1, + @("uda2") @("uda3") value2, + @uda4 value3, + @uda5 @uda6 value4, + @("uda7") @uda8 value5, + @uda9 @("uda10") value6 +} + +static assert(__traits(getAttributes, Enum.value0).length == 0); +static assert(__traits(getAttributes, Enum.value1) == AliasSeq!("uda1")); +static assert(__traits(getAttributes, Enum.value2) == AliasSeq!("uda2", "uda3", 42)); +static assert(__traits(getAttributes, Enum.value3) == AliasSeq!(uda4)); +static assert(__traits(getAttributes, Enum.value4) == AliasSeq!(uda5, uda6)); +static assert(__traits(getAttributes, Enum.value5) == AliasSeq!("uda7", uda8)); +static assert(__traits(getAttributes, Enum.value6) == AliasSeq!(uda9, "uda10")); +static assert(__traits(isDeprecated, Enum.value7)); + +static assert(__traits(getAttributes, value0) == AliasSeq!("uda0")); +static assert(__traits(getAttributes, value1) == AliasSeq!("uda0", "uda1")); +static assert(__traits(getAttributes, value2) == AliasSeq!("uda0", "uda2", "uda3")); +static assert(__traits(getAttributes, value3) == AliasSeq!("uda0", uda4)); +static assert(__traits(getAttributes, value4) == AliasSeq!("uda0", uda5, uda6)); +static assert(__traits(getAttributes, value5) == AliasSeq!("uda0", "uda7", uda8)); +static assert(__traits(getAttributes, value6) == AliasSeq!("uda0", uda9, "uda10")); diff --git a/test/fail_compilation/disable.d b/test/fail_compilation/disable.d index 4f0a139c5e11..7e7d9c2d48d5 100644 --- a/test/fail_compilation/disable.d +++ b/test/fail_compilation/disable.d @@ -1,15 +1,16 @@ /* TEST_OUTPUT: --- -fail_compilation/disable.d(50): Error: function `disable.DisabledOpAssign.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(53): Error: function `disable.DisabledPostblit.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(56): Error: function `disable.HasDtor.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(60): Error: generated function `disable.Nested!(DisabledOpAssign).Nested.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(63): Error: generated function `disable.Nested!(DisabledPostblit).Nested.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(66): Error: generated function `disable.Nested!(HasDtor).Nested.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(70): Error: generated function `disable.NestedDtor!(DisabledOpAssign).NestedDtor.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(73): Error: generated function `disable.NestedDtor!(DisabledPostblit).NestedDtor.opAssign` is not callable because it is annotated with `@disable` -fail_compilation/disable.d(76): Error: generated function `disable.NestedDtor!(HasDtor).NestedDtor.opAssign` is not callable because it is annotated with `@disable` +fail_compilation/disable.d(56): Error: function `disable.DisabledOpAssign.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(59): Error: function `disable.DisabledPostblit.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(62): Error: function `disable.HasDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(66): Error: generated function `disable.Nested!(DisabledOpAssign).Nested.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(69): Error: generated function `disable.Nested!(DisabledPostblit).Nested.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(72): Error: generated function `disable.Nested!(HasDtor).Nested.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(76): Error: generated function `disable.NestedDtor!(DisabledOpAssign).NestedDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(79): Error: generated function `disable.NestedDtor!(DisabledPostblit).NestedDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(82): Error: generated function `disable.NestedDtor!(HasDtor).NestedDtor.opAssign` cannot be used because it is annotated with `@disable` +fail_compilation/disable.d(84): Error: enum member `disable.Enum1.value` cannot be used because it is annotated with `@disable` --- */ struct DisabledOpAssign { @@ -44,6 +45,11 @@ struct NestedDtor (T) ~this() {} } +enum Enum1 +{ + @disable value +} + void main () { DisabledOpAssign o; @@ -74,4 +80,6 @@ void main () NestedDtor!(HasDtor) ndd; ndd = NestedDtor!(HasDtor)(); + + auto v1 = Enum1.value; } diff --git a/test/fail_compilation/disable_new.d b/test/fail_compilation/disable_new.d index fe15242343b3..d5f1ecf074ea 100644 --- a/test/fail_compilation/disable_new.d +++ b/test/fail_compilation/disable_new.d @@ -1,8 +1,8 @@ /* TEST_OUTPUT: --- -fail_compilation/disable_new.d(23): Error: allocator `disable_new.C.new` is not callable because it is annotated with `@disable` -fail_compilation/disable_new.d(24): Error: allocator `disable_new.S.new` is not callable because it is annotated with `@disable` +fail_compilation/disable_new.d(23): Error: allocator `disable_new.C.new` cannot be used because it is annotated with `@disable` +fail_compilation/disable_new.d(24): Error: allocator `disable_new.S.new` cannot be used because it is annotated with `@disable` --- */ diff --git a/test/fail_compilation/fail15044.d b/test/fail_compilation/fail15044.d index 075777dad29a..964dcf913c2a 100644 --- a/test/fail_compilation/fail15044.d +++ b/test/fail_compilation/fail15044.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/fail15044.d(30): Error: generated function `fail15044.V.opAssign` is not callable because it is annotated with `@disable` +fail_compilation/fail15044.d(30): Error: generated function `fail15044.V.opAssign` cannot be used because it is annotated with `@disable` --- */ diff --git a/test/fail_compilation/fail341.d b/test/fail_compilation/fail341.d index 05632aa65a25..a883e6e70770 100644 --- a/test/fail_compilation/fail341.d +++ b/test/fail_compilation/fail341.d @@ -2,7 +2,7 @@ TEST_OUTPUT: --- fail_compilation/fail341.d(26): Error: struct `fail341.S` is not copyable because it is annotated with `@disable` -fail_compilation/fail341.d(27): Error: function `fail341.foo` is not callable because it is annotated with `@disable` +fail_compilation/fail341.d(27): Error: function `fail341.foo` cannot be used because it is annotated with `@disable` --- */ diff --git a/test/fail_compilation/test17908a.d b/test/fail_compilation/test17908a.d index 5cc5c0a8ceb2..907239734d43 100644 --- a/test/fail_compilation/test17908a.d +++ b/test/fail_compilation/test17908a.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test17908a.d(10): Error: function `test17908a.foo` is not callable because it is annotated with `@disable` +fail_compilation/test17908a.d(10): Error: function `test17908a.foo` cannot be used because it is annotated with `@disable` --- */ diff --git a/test/fail_compilation/test17908b.d b/test/fail_compilation/test17908b.d index 5000f97969ec..e2c4d84abedd 100644 --- a/test/fail_compilation/test17908b.d +++ b/test/fail_compilation/test17908b.d @@ -1,7 +1,7 @@ /* TEST_OUTPUT: --- -fail_compilation/test17908b.d(13): Error: function `test17908b.foobar` is not callable because it is annotated with `@disable` +fail_compilation/test17908b.d(13): Error: function `test17908b.foobar` cannot be used because it is annotated with `@disable` --- */ void foobar() {} diff --git a/test/fail_compilation/test9701.d b/test/fail_compilation/test9701.d new file mode 100644 index 000000000000..384c51444b17 --- /dev/null +++ b/test/fail_compilation/test9701.d @@ -0,0 +1,63 @@ +/* +TEST_OUTPUT +--- +fail_compilation/test9701.d(38): Error: `@safe` is not a valid attribute for enum members +fail_compilation/test9701.d(39): Error: `@system` is not a valid attribute for enum members +fail_compilation/test9701.d(40): Error: `@trusted` is not a valid attribute for enum members +fail_compilation/test9701.d(41): Error: `@nogc` is not a valid attribute for enum members +fail_compilation/test9701.d(42): Error: `pure` is not a valid attribute for enum members +fail_compilation/test9701.d(43): Error: `shared` is not a valid attribute for enum members +fail_compilation/test9701.d(44): Error: `inout` is not a valid attribute for enum members +fail_compilation/test9701.d(45): Error: `immutable` is not a valid attribute for enum members +fail_compilation/test9701.d(46): Error: `const` is not a valid attribute for enum members +fail_compilation/test9701.d(47): Error: `synchronized` is not a valid attribute for enum members +fail_compilation/test9701.d(48): Error: `scope` is not a valid attribute for enum members +fail_compilation/test9701.d(49): Error: `auto` is not a valid attribute for enum members +fail_compilation/test9701.d(50): Error: `ref` is not a valid attribute for enum members +fail_compilation/test9701.d(51): Error: `__gshared` is not a valid attribute for enum members +fail_compilation/test9701.d(52): Error: `final` is not a valid attribute for enum members +fail_compilation/test9701.d(53): Error: `extern` is not a valid attribute for enum members +fail_compilation/test9701.d(54): Error: `export` is not a valid attribute for enum members +fail_compilation/test9701.d(55): Error: `nothrow` is not a valid attribute for enum members +fail_compilation/test9701.d(56): Error: `public` is not a valid attribute for enum members +fail_compilation/test9701.d(57): Error: `private` is not a valid attribute for enum members +fail_compilation/test9701.d(58): Error: `package` is not a valid attribute for enum members +fail_compilation/test9701.d(59): Error: `static` is not a valid attribute for enum members +fail_compilation/test9701.d(60): Error: `static` is not a valid attribute for enum members +fail_compilation/test9701.d(61): Error: `static` is not a valid attribute for enum members +fail_compilation/test9701.d(62): Error: `static` is not a valid attribute for enum members +--- +*/ + +// This test exists to verify that parsing of enum member attributes rejects invalid attributes + +// https://issues.dlang.org/show_bug.cgi?id=9701 + +enum Enum +{ + @safe safe, + @system system, + @trusted trusted, + @nogc nogc, + pure pure_, + shared shared_, + inout inout_, + immutable immutable_, + const const_, + synchronized synchronized_, + scope scope_, + auto auto_, + ref ref_, + __gshared __gshared_, + final final_, + extern extern_, + export export_, + nothrow nothrow_, + public public_, + private private_, + package package_, + static static1, + @("a") static static2, + static @("a") static3, + @("a") static @("b") static3, +} diff --git a/test/fail_compilation/test9701b.d b/test/fail_compilation/test9701b.d new file mode 100644 index 000000000000..fc9512618f7c --- /dev/null +++ b/test/fail_compilation/test9701b.d @@ -0,0 +1,19 @@ +/* +REQUIRED_ARGS: -de +TEST_OUTPUT +--- +fail_compilation/test9701b.d(13): Deprecation: enum member `test9701b.Enum.e0` is deprecated +--- +*/ + +// https://issues.dlang.org/show_bug.cgi?id=9701 + +enum Enum +{ + deprecated e0, +} + +void main() +{ + auto value = Enum.e0; +}