Skip to content
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
18 changes: 12 additions & 6 deletions compiler/src/dmd/expressionsem.d
Original file line number Diff line number Diff line change
Expand Up @@ -2703,7 +2703,9 @@ Lagain:
{
// functions are checked after overloading
// templates are checked after matching constraints
if (!s.isFuncDeclaration() && !s.isTemplateDeclaration())
// enum members are checked once in getVarExp() below; skip here to avoid
// emitting the deprecation diagnostic twice for an enum member accessed by name
if (!s.isFuncDeclaration() && !s.isTemplateDeclaration() && !s.isEnumMember())
{
s.checkDeprecated(loc, sc);
if (d)
Expand All @@ -2715,7 +2717,7 @@ Lagain:
s = s.toAlias();

//printf("s = '%s', s.kind = '%s', s.needThis() = %p\n", s.toChars(), s.kind(), s.needThis());
if (s != olds && !s.isFuncDeclaration() && !s.isTemplateDeclaration())
if (s != olds && !s.isFuncDeclaration() && !s.isTemplateDeclaration() && !s.isEnumMember())
{
s.checkDeprecated(loc, sc);
if (d)
Expand Down Expand Up @@ -16006,14 +16008,18 @@ Expression dotIdSemanticProp(DotIdExp exp, Scope* sc, bool gag)
// if 's' is a tuple variable, the tuple is returned.
s = s.toAlias();

s.checkDeprecated(exp.loc, sc);
if (auto d = s.isDeclaration())
d.checkDisabled(exp.loc, sc);

if (auto em = s.isEnumMember())
{
// getVarExp() performs the deprecated/disabled checks itself; doing them
// here as well would emit the diagnostic twice for an enum member
// accessed by name (e.g. an anonymous enum member, or an ImportC enumerator).
return em.getVarExp(exp.loc, sc);
}

s.checkDeprecated(exp.loc, sc);
if (auto d = s.isDeclaration())
d.checkDisabled(exp.loc, sc);

if (auto v = s.isVarDeclaration())
{
//printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v.type.toErrMsg());
Expand Down
53 changes: 53 additions & 0 deletions compiler/test/compilable/deprecated_enum_member.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
REQUIRED_ARGS: -verrors=simple
TEST_OUTPUT:
---
compilable/deprecated_enum_member.d(33): Deprecation: enum member `deprecated_enum_member.anon` is deprecated
compilable/deprecated_enum_member.d(28): `anon` is declared here
compilable/deprecated_enum_member.d(36): Deprecation: enum member `deprecated_enum_member.E.named` is deprecated
compilable/deprecated_enum_member.d(29): `named` is declared here
compilable/deprecated_enum_member.d(39): Deprecation: enum member `deprecated_enum_member.E.named` is deprecated
compilable/deprecated_enum_member.d(29): `named` is declared here
compilable/deprecated_enum_member.d(42): Deprecation: enum member `deprecated_enum_member.F.withMsg` is deprecated - use namedB
compilable/deprecated_enum_member.d(30): `withMsg` is declared here
compilable/deprecated_enum_member.d(43): Deprecation: enum member `deprecated_enum_member.F.withMsg` is deprecated - use namedB
compilable/deprecated_enum_member.d(30): `withMsg` is declared here
compilable/deprecated_enum_member.d(47): Deprecation: enum member `deprecated_enum_member.anon` is deprecated
compilable/deprecated_enum_member.d(28): `anon` is declared here
compilable/deprecated_enum_member.d(47): Deprecation: enum member `deprecated_enum_member.anon` is deprecated
compilable/deprecated_enum_member.d(28): `anon` is declared here
---
*/

// A deprecated enum member must emit its deprecation diagnostic exactly once per
// use, regardless of how it is named. Previously an enum member accessed by its
// unqualified name (an anonymous-enum member, a member found via `with`, or an
// ImportC enumerator) reported the deprecation twice, because the symbol was
// checked both during name resolution and again in getVarExp(). See issue 23241.

enum { deprecated anon = 0, anonB }
enum E { deprecated named = 0, namedB }
enum F { deprecated("use namedB") withMsg = 0, plain }

// Anonymous enum member, accessed by its unqualified name.
int useAnon() { return anon; }

// Named enum member, accessed by its fully qualified name.
int useNamedQualified() { return E.named; }

// Named enum member, accessed by its unqualified name brought into scope by `with`.
int useNamedWith() { E e; with (e) return named; }

// A deprecated member carrying an explicit message reproduces that message.
int useWithMsgQualified() { return F.withMsg; }
int useWithMsgWith() { F f; with (f) return withMsg; }

// Each separate use emits its own diagnostic (the fix removes a duplicate per use,
// it does not globally deduplicate distinct uses).
int useAnonAgain() { return anon + anon; }

// Non-deprecated members of the same enums emit nothing.
int useClean() { return anonB + E.namedB + F.plain; }

// A use from within a deprecated scope is suppressed entirely.
deprecated int useFromDeprecated() { return anon + E.named + F.withMsg; }
Loading