Skip to content

Commit 937cc4b

Browse files
committed
Fix #23241 - duplicate deprecation message for enum member accessed by name
A deprecated enum member accessed by its unqualified name (an anonymous enum member, a member found via `with`, or an ImportC enumerator) reported the deprecation twice: once during name resolution and again in getVarExp(), which already performs the deprecated/disabled checks itself. Skip the redundant check for enum members in the two name-resolution paths so the diagnostic is emitted exactly once.
1 parent 940287b commit 937cc4b

2 files changed

Lines changed: 65 additions & 6 deletions

File tree

compiler/src/dmd/expressionsem.d

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2703,7 +2703,9 @@ Lagain:
27032703
{
27042704
// functions are checked after overloading
27052705
// templates are checked after matching constraints
2706-
if (!s.isFuncDeclaration() && !s.isTemplateDeclaration())
2706+
// enum members are checked once in getVarExp() below; skip here to avoid
2707+
// emitting the deprecation diagnostic twice for an enum member accessed by name
2708+
if (!s.isFuncDeclaration() && !s.isTemplateDeclaration() && !s.isEnumMember())
27072709
{
27082710
s.checkDeprecated(loc, sc);
27092711
if (d)
@@ -2715,7 +2717,7 @@ Lagain:
27152717
s = s.toAlias();
27162718

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

16009-
s.checkDeprecated(exp.loc, sc);
16010-
if (auto d = s.isDeclaration())
16011-
d.checkDisabled(exp.loc, sc);
16012-
1601316011
if (auto em = s.isEnumMember())
1601416012
{
16013+
// getVarExp() performs the deprecated/disabled checks itself; doing them
16014+
// here as well would emit the diagnostic twice for an enum member
16015+
// accessed by name (e.g. an anonymous enum member, or an ImportC enumerator).
1601516016
return em.getVarExp(exp.loc, sc);
1601616017
}
16018+
16019+
s.checkDeprecated(exp.loc, sc);
16020+
if (auto d = s.isDeclaration())
16021+
d.checkDisabled(exp.loc, sc);
16022+
1601716023
if (auto v = s.isVarDeclaration())
1601816024
{
1601916025
//printf("DotIdExp:: Identifier '%s' is a variable, type '%s'\n", toChars(), v.type.toErrMsg());
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
REQUIRED_ARGS: -verrors=simple
3+
TEST_OUTPUT:
4+
---
5+
compilable/deprecated_enum_member.d(33): Deprecation: enum member `deprecated_enum_member.anon` is deprecated
6+
compilable/deprecated_enum_member.d(28): `anon` is declared here
7+
compilable/deprecated_enum_member.d(36): Deprecation: enum member `deprecated_enum_member.E.named` is deprecated
8+
compilable/deprecated_enum_member.d(29): `named` is declared here
9+
compilable/deprecated_enum_member.d(39): Deprecation: enum member `deprecated_enum_member.E.named` is deprecated
10+
compilable/deprecated_enum_member.d(29): `named` is declared here
11+
compilable/deprecated_enum_member.d(42): Deprecation: enum member `deprecated_enum_member.F.withMsg` is deprecated - use namedB
12+
compilable/deprecated_enum_member.d(30): `withMsg` is declared here
13+
compilable/deprecated_enum_member.d(43): Deprecation: enum member `deprecated_enum_member.F.withMsg` is deprecated - use namedB
14+
compilable/deprecated_enum_member.d(30): `withMsg` is declared here
15+
compilable/deprecated_enum_member.d(47): Deprecation: enum member `deprecated_enum_member.anon` is deprecated
16+
compilable/deprecated_enum_member.d(28): `anon` is declared here
17+
compilable/deprecated_enum_member.d(47): Deprecation: enum member `deprecated_enum_member.anon` is deprecated
18+
compilable/deprecated_enum_member.d(28): `anon` is declared here
19+
---
20+
*/
21+
22+
// A deprecated enum member must emit its deprecation diagnostic exactly once per
23+
// use, regardless of how it is named. Previously an enum member accessed by its
24+
// unqualified name (an anonymous-enum member, a member found via `with`, or an
25+
// ImportC enumerator) reported the deprecation twice, because the symbol was
26+
// checked both during name resolution and again in getVarExp(). See issue 23241.
27+
28+
enum { deprecated anon = 0, anonB }
29+
enum E { deprecated named = 0, namedB }
30+
enum F { deprecated("use namedB") withMsg = 0, plain }
31+
32+
// Anonymous enum member, accessed by its unqualified name.
33+
int useAnon() { return anon; }
34+
35+
// Named enum member, accessed by its fully qualified name.
36+
int useNamedQualified() { return E.named; }
37+
38+
// Named enum member, accessed by its unqualified name brought into scope by `with`.
39+
int useNamedWith() { E e; with (e) return named; }
40+
41+
// A deprecated member carrying an explicit message reproduces that message.
42+
int useWithMsgQualified() { return F.withMsg; }
43+
int useWithMsgWith() { F f; with (f) return withMsg; }
44+
45+
// Each separate use emits its own diagnostic (the fix removes a duplicate per use,
46+
// it does not globally deduplicate distinct uses).
47+
int useAnonAgain() { return anon + anon; }
48+
49+
// Non-deprecated members of the same enums emit nothing.
50+
int useClean() { return anonB + E.namedB + F.plain; }
51+
52+
// A use from within a deprecated scope is suppressed entirely.
53+
deprecated int useFromDeprecated() { return anon + E.named + F.withMsg; }

0 commit comments

Comments
 (0)