Skip to content

Commit 93e6b9d

Browse files
Enforce identical enum values in compatibility checks (#55924)
1 parent 63babdf commit 93e6b9d

18 files changed

+1108
-27
lines changed

src/compiler/checker.ts

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20883,19 +20883,59 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2088320883
return !!(entry & RelationComparisonResult.Succeeded);
2088420884
}
2088520885
const targetEnumType = getTypeOfSymbol(targetSymbol);
20886-
for (const property of getPropertiesOfType(getTypeOfSymbol(sourceSymbol))) {
20887-
if (property.flags & SymbolFlags.EnumMember) {
20888-
const targetProperty = getPropertyOfType(targetEnumType, property.escapedName);
20886+
for (const sourceProperty of getPropertiesOfType(getTypeOfSymbol(sourceSymbol))) {
20887+
if (sourceProperty.flags & SymbolFlags.EnumMember) {
20888+
const targetProperty = getPropertyOfType(targetEnumType, sourceProperty.escapedName);
2088920889
if (!targetProperty || !(targetProperty.flags & SymbolFlags.EnumMember)) {
2089020890
if (errorReporter) {
20891-
errorReporter(Diagnostics.Property_0_is_missing_in_type_1, symbolName(property), typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
20891+
errorReporter(Diagnostics.Property_0_is_missing_in_type_1, symbolName(sourceProperty), typeToString(getDeclaredTypeOfSymbol(targetSymbol), /*enclosingDeclaration*/ undefined, TypeFormatFlags.UseFullyQualifiedType));
2089220892
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
2089320893
}
2089420894
else {
2089520895
enumRelation.set(id, RelationComparisonResult.Failed);
2089620896
}
2089720897
return false;
2089820898
}
20899+
const sourceValue = getEnumMemberValue(getDeclarationOfKind(sourceProperty, SyntaxKind.EnumMember)!);
20900+
const targetValue = getEnumMemberValue(getDeclarationOfKind(targetProperty, SyntaxKind.EnumMember)!);
20901+
if (sourceValue !== targetValue) {
20902+
const sourceIsString = typeof sourceValue === "string";
20903+
const targetIsString = typeof targetValue === "string";
20904+
20905+
// If we have 2 enums with *known* values that differ, they are incompatible.
20906+
if (sourceValue !== undefined && targetValue !== undefined) {
20907+
if (!errorReporter) {
20908+
enumRelation.set(id, RelationComparisonResult.Failed);
20909+
}
20910+
else {
20911+
const escapedSource = sourceIsString ? `"${escapeString(sourceValue)}"` : sourceValue;
20912+
const escapedTarget = targetIsString ? `"${escapeString(targetValue)}"` : targetValue;
20913+
errorReporter(Diagnostics.Each_declaration_of_0_1_differs_in_its_value_where_2_was_expected_but_3_was_given, symbolName(targetSymbol), symbolName(targetProperty), escapedTarget, escapedSource);
20914+
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
20915+
}
20916+
return false;
20917+
}
20918+
20919+
// At this point we know that at least one of the values is 'undefined'.
20920+
// This may mean that we have an opaque member from an ambient enum declaration,
20921+
// or that we were not able to calculate it (which is basically an error).
20922+
//
20923+
// Either way, we can assume that it's numeric.
20924+
// If the other is a string, we have a mismatch in types.
20925+
if (sourceIsString || targetIsString) {
20926+
if (!errorReporter) {
20927+
enumRelation.set(id, RelationComparisonResult.Failed);
20928+
}
20929+
else {
20930+
const knownStringValue = sourceValue ?? targetValue;
20931+
Debug.assert(typeof knownStringValue === "string");
20932+
const escapedValue = `"${escapeString(knownStringValue)}"`;
20933+
errorReporter(Diagnostics.One_value_of_0_1_is_the_string_2_and_the_other_is_assumed_to_be_an_unknown_numeric_value, symbolName(targetSymbol), symbolName(targetProperty), escapedValue);
20934+
enumRelation.set(id, RelationComparisonResult.Failed | RelationComparisonResult.Reported);
20935+
}
20936+
return false;
20937+
}
20938+
}
2089920939
}
2090020940
}
2090120941
enumRelation.set(id, RelationComparisonResult.Succeeded);

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4176,6 +4176,14 @@
41764176
"category": "Error",
41774177
"code": 4124
41784178
},
4179+
"Each declaration of '{0}.{1}' differs in its value, where '{2}' was expected but '{3}' was given.": {
4180+
"category": "Error",
4181+
"code": 4125
4182+
},
4183+
"One value of '{0}.{1}' is the string '{2}', and the other is assumed to be an unknown numeric value.": {
4184+
"category": "Error",
4185+
"code": 4126
4186+
},
41794187

41804188
"The current host does not support the '{0}' option.": {
41814189
"category": "Error",

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,7 @@ export const enum JsxFlags {
916916
// dprint-ignore
917917
/** @internal */
918918
export const enum RelationComparisonResult {
919+
None = 0,
919920
Succeeded = 1 << 0, // Should be truthy
920921
Failed = 1 << 1,
921922
Reported = 1 << 2,

tests/baselines/reference/enumAssignmentCompat3.errors.txt

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
enumAssignmentCompat3.ts(68,1): error TS2322: Type 'Abcd.E' is not assignable to type 'First.E'.
22
Property 'd' is missing in type 'First.E'.
33
enumAssignmentCompat3.ts(70,1): error TS2322: Type 'Cd.E' is not assignable to type 'First.E'.
4-
Property 'd' is missing in type 'First.E'.
4+
Each declaration of 'E.c' differs in its value, where '2' was expected but '0' was given.
55
enumAssignmentCompat3.ts(71,1): error TS2322: Type 'Nope' is not assignable to type 'E'.
6+
enumAssignmentCompat3.ts(72,1): error TS2322: Type 'Decl.E' is not assignable to type 'First.E'.
7+
Each declaration of 'E.c' differs in its value, where '2' was expected but '3' was given.
68
enumAssignmentCompat3.ts(75,1): error TS2322: Type 'First.E' is not assignable to type 'Ab.E'.
79
Property 'c' is missing in type 'Ab.E'.
810
enumAssignmentCompat3.ts(76,1): error TS2322: Type 'First.E' is not assignable to type 'Cd.E'.
911
Property 'a' is missing in type 'Cd.E'.
1012
enumAssignmentCompat3.ts(77,1): error TS2322: Type 'E' is not assignable to type 'Nope'.
13+
enumAssignmentCompat3.ts(78,1): error TS2322: Type 'First.E' is not assignable to type 'Decl.E'.
14+
Each declaration of 'E.c' differs in its value, where '3' was expected but '2' was given.
1115
enumAssignmentCompat3.ts(82,1): error TS2322: Type 'Const.E' is not assignable to type 'First.E'.
1216
enumAssignmentCompat3.ts(83,1): error TS2322: Type 'First.E' is not assignable to type 'Const.E'.
1317
enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged.E' is not assignable to type 'First.E'.
14-
Property 'd' is missing in type 'First.E'.
18+
Each declaration of 'E.c' differs in its value, where '2' was expected but '3' was given.
19+
enumAssignmentCompat3.ts(87,1): error TS2322: Type 'First.E' is not assignable to type 'Merged.E'.
20+
Each declaration of 'E.c' differs in its value, where '3' was expected but '2' was given.
1521

1622

17-
==== enumAssignmentCompat3.ts (9 errors) ====
23+
==== enumAssignmentCompat3.ts (12 errors) ====
1824
namespace First {
1925
export enum E {
2026
a, b, c,
@@ -90,11 +96,14 @@ enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged.E' is not assignable
9096
abc = secondCd; // missing 'd'
9197
~~~
9298
!!! error TS2322: Type 'Cd.E' is not assignable to type 'First.E'.
93-
!!! error TS2322: Property 'd' is missing in type 'First.E'.
99+
!!! error TS2322: Each declaration of 'E.c' differs in its value, where '2' was expected but '0' was given.
94100
abc = nope; // nope!
95101
~~~
96102
!!! error TS2322: Type 'Nope' is not assignable to type 'E'.
97-
abc = decl; // ok
103+
abc = decl; // bad - value of 'c' differs between these enums
104+
~~~
105+
!!! error TS2322: Type 'Decl.E' is not assignable to type 'First.E'.
106+
!!! error TS2322: Each declaration of 'E.c' differs in its value, where '2' was expected but '3' was given.
98107
secondAbc = abc; // ok
99108
secondAbcd = abc; // ok
100109
secondAb = abc; // missing 'c'
@@ -108,7 +117,10 @@ enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged.E' is not assignable
108117
nope = abc; // nope!
109118
~~~~
110119
!!! error TS2322: Type 'E' is not assignable to type 'Nope'.
111-
decl = abc; // ok
120+
decl = abc; // bad - value of 'c' differs between these enums
121+
~~~~
122+
!!! error TS2322: Type 'First.E' is not assignable to type 'Decl.E'.
123+
!!! error TS2322: Each declaration of 'E.c' differs in its value, where '3' was expected but '2' was given.
112124

113125
// const is only assignable to itself
114126
k = k;
@@ -123,7 +135,10 @@ enumAssignmentCompat3.ts(86,1): error TS2322: Type 'Merged.E' is not assignable
123135
abc = merged; // missing 'd'
124136
~~~
125137
!!! error TS2322: Type 'Merged.E' is not assignable to type 'First.E'.
126-
!!! error TS2322: Property 'd' is missing in type 'First.E'.
127-
merged = abc; // ok
138+
!!! error TS2322: Each declaration of 'E.c' differs in its value, where '2' was expected but '3' was given.
139+
merged = abc; // bad - value of 'c' differs between these enums
140+
~~~~~~
141+
!!! error TS2322: Type 'First.E' is not assignable to type 'Merged.E'.
142+
!!! error TS2322: Each declaration of 'E.c' differs in its value, where '3' was expected but '2' was given.
128143
abc = merged2; // ok
129144
merged2 = abc; // ok

tests/baselines/reference/enumAssignmentCompat3.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,13 @@ abc = secondAbcd; // missing 'd'
7272
abc = secondAb; // ok
7373
abc = secondCd; // missing 'd'
7474
abc = nope; // nope!
75-
abc = decl; // ok
75+
abc = decl; // bad - value of 'c' differs between these enums
7676
secondAbc = abc; // ok
7777
secondAbcd = abc; // ok
7878
secondAb = abc; // missing 'c'
7979
secondCd = abc; // missing 'a' and 'b'
8080
nope = abc; // nope!
81-
decl = abc; // ok
81+
decl = abc; // bad - value of 'c' differs between these enums
8282

8383
// const is only assignable to itself
8484
k = k;
@@ -87,7 +87,7 @@ k = abc;
8787

8888
// merged enums compare all their members
8989
abc = merged; // missing 'd'
90-
merged = abc; // ok
90+
merged = abc; // bad - value of 'c' differs between these enums
9191
abc = merged2; // ok
9292
merged2 = abc; // ok
9393

@@ -184,19 +184,19 @@ abc = secondAbcd; // missing 'd'
184184
abc = secondAb; // ok
185185
abc = secondCd; // missing 'd'
186186
abc = nope; // nope!
187-
abc = decl; // ok
187+
abc = decl; // bad - value of 'c' differs between these enums
188188
secondAbc = abc; // ok
189189
secondAbcd = abc; // ok
190190
secondAb = abc; // missing 'c'
191191
secondCd = abc; // missing 'a' and 'b'
192192
nope = abc; // nope!
193-
decl = abc; // ok
193+
decl = abc; // bad - value of 'c' differs between these enums
194194
// const is only assignable to itself
195195
k = k;
196196
abc = k; // error
197197
k = abc;
198198
// merged enums compare all their members
199199
abc = merged; // missing 'd'
200-
merged = abc; // ok
200+
merged = abc; // bad - value of 'c' differs between these enums
201201
abc = merged2; // ok
202202
merged2 = abc; // ok

tests/baselines/reference/enumAssignmentCompat3.symbols

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ abc = nope; // nope!
200200
>abc : Symbol(abc, Decl(enumAssignmentCompat3.ts, 56, 3))
201201
>nope : Symbol(nope, Decl(enumAssignmentCompat3.ts, 61, 3))
202202

203-
abc = decl; // ok
203+
abc = decl; // bad - value of 'c' differs between these enums
204204
>abc : Symbol(abc, Decl(enumAssignmentCompat3.ts, 56, 3))
205205
>decl : Symbol(decl, Decl(enumAssignmentCompat3.ts, 63, 3))
206206

@@ -224,7 +224,7 @@ nope = abc; // nope!
224224
>nope : Symbol(nope, Decl(enumAssignmentCompat3.ts, 61, 3))
225225
>abc : Symbol(abc, Decl(enumAssignmentCompat3.ts, 56, 3))
226226

227-
decl = abc; // ok
227+
decl = abc; // bad - value of 'c' differs between these enums
228228
>decl : Symbol(decl, Decl(enumAssignmentCompat3.ts, 63, 3))
229229
>abc : Symbol(abc, Decl(enumAssignmentCompat3.ts, 56, 3))
230230

@@ -246,7 +246,7 @@ abc = merged; // missing 'd'
246246
>abc : Symbol(abc, Decl(enumAssignmentCompat3.ts, 56, 3))
247247
>merged : Symbol(merged, Decl(enumAssignmentCompat3.ts, 64, 3))
248248

249-
merged = abc; // ok
249+
merged = abc; // bad - value of 'c' differs between these enums
250250
>merged : Symbol(merged, Decl(enumAssignmentCompat3.ts, 64, 3))
251251
>abc : Symbol(abc, Decl(enumAssignmentCompat3.ts, 56, 3))
252252

tests/baselines/reference/enumAssignmentCompat3.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ abc = nope; // nope!
196196
>abc : First.E
197197
>nope : Abc.Nope
198198

199-
abc = decl; // ok
199+
abc = decl; // bad - value of 'c' differs between these enums
200200
>abc = decl : Decl.E
201201
>abc : First.E
202202
>decl : Decl.E
@@ -226,7 +226,7 @@ nope = abc; // nope!
226226
>nope : Abc.Nope
227227
>abc : First.E
228228

229-
decl = abc; // ok
229+
decl = abc; // bad - value of 'c' differs between these enums
230230
>decl = abc : First.E
231231
>decl : Decl.E
232232
>abc : First.E
@@ -253,7 +253,7 @@ abc = merged; // missing 'd'
253253
>abc : First.E
254254
>merged : Merged.E
255255

256-
merged = abc; // ok
256+
merged = abc; // bad - value of 'c' differs between these enums
257257
>merged = abc : First.E
258258
>merged : Merged.E
259259
>abc : First.E
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
f.ts(37,5): error TS2322: Type 'strings.DiagnosticCategory' is not assignable to type 'numerics.DiagnosticCategory'.
2+
Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '0' was expected but '"Warning"' was given.
3+
f.ts(38,5): error TS2322: Type 'numerics.DiagnosticCategory' is not assignable to type 'strings.DiagnosticCategory'.
4+
Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '"Warning"' was expected but '0' was given.
5+
f.ts(42,5): error TS2322: Type 'DiagnosticCategory' is not assignable to type 'DiagnosticCategory2'.
6+
f.ts(43,5): error TS2322: Type 'DiagnosticCategory2' is not assignable to type 'DiagnosticCategory'.
7+
f.ts(52,5): error TS2322: Type 'ambients.DiagnosticCategory' is not assignable to type 'strings.DiagnosticCategory'.
8+
One value of 'DiagnosticCategory.Warning' is the string '"Warning"', and the other is assumed to be an unknown numeric value.
9+
f.ts(53,5): error TS2322: Type 'strings.DiagnosticCategory' is not assignable to type 'ambients.DiagnosticCategory'.
10+
One value of 'DiagnosticCategory.Warning' is the string '"Warning"', and the other is assumed to be an unknown numeric value.
11+
f.ts(73,9): error TS2322: Type 'DiagnosticCategory' is not assignable to type 'import("f").DiagnosticCategory'.
12+
Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '0' was expected but '"Warning"' was given.
13+
f.ts(74,9): error TS2322: Type 'import("f").DiagnosticCategory' is not assignable to type 'DiagnosticCategory'.
14+
Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '"Warning"' was expected but '0' was given.
15+
16+
17+
==== f.ts (8 errors) ====
18+
// @filename a.ts
19+
namespace numerics {
20+
export enum DiagnosticCategory {
21+
Warning,
22+
Error,
23+
Suggestion,
24+
Message,
25+
}
26+
27+
export enum DiagnosticCategory2 {
28+
Warning,
29+
Error,
30+
Suggestion,
31+
Message,
32+
}
33+
}
34+
35+
namespace strings {
36+
export enum DiagnosticCategory {
37+
Warning = "Warning",
38+
Error = "Error",
39+
Suggestion = "Suggestion",
40+
Message = "Message",
41+
}
42+
}
43+
44+
declare namespace ambients {
45+
export enum DiagnosticCategory {
46+
Warning,
47+
Error,
48+
Suggestion,
49+
Message,
50+
}
51+
}
52+
53+
function f(x: numerics.DiagnosticCategory, y: strings.DiagnosticCategory) {
54+
x = y;
55+
~
56+
!!! error TS2322: Type 'strings.DiagnosticCategory' is not assignable to type 'numerics.DiagnosticCategory'.
57+
!!! error TS2322: Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '0' was expected but '"Warning"' was given.
58+
y = x;
59+
~
60+
!!! error TS2322: Type 'numerics.DiagnosticCategory' is not assignable to type 'strings.DiagnosticCategory'.
61+
!!! error TS2322: Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '"Warning"' was expected but '0' was given.
62+
}
63+
64+
function g(x: numerics.DiagnosticCategory2, y: strings.DiagnosticCategory) {
65+
x = y;
66+
~
67+
!!! error TS2322: Type 'DiagnosticCategory' is not assignable to type 'DiagnosticCategory2'.
68+
y = x;
69+
~
70+
!!! error TS2322: Type 'DiagnosticCategory2' is not assignable to type 'DiagnosticCategory'.
71+
}
72+
73+
function h(x: numerics.DiagnosticCategory, y: ambients.DiagnosticCategory) {
74+
x = y;
75+
y = x;
76+
}
77+
78+
function i(x: strings.DiagnosticCategory, y: ambients.DiagnosticCategory) {
79+
x = y;
80+
~
81+
!!! error TS2322: Type 'ambients.DiagnosticCategory' is not assignable to type 'strings.DiagnosticCategory'.
82+
!!! error TS2322: One value of 'DiagnosticCategory.Warning' is the string '"Warning"', and the other is assumed to be an unknown numeric value.
83+
y = x;
84+
~
85+
!!! error TS2322: Type 'strings.DiagnosticCategory' is not assignable to type 'ambients.DiagnosticCategory'.
86+
!!! error TS2322: One value of 'DiagnosticCategory.Warning' is the string '"Warning"', and the other is assumed to be an unknown numeric value.
87+
}
88+
89+
export enum DiagnosticCategory {
90+
Warning,
91+
Error,
92+
Suggestion,
93+
Message,
94+
}
95+
96+
export let x: DiagnosticCategory;
97+
98+
(() => {
99+
enum DiagnosticCategory {
100+
Warning = "Warning",
101+
Error = "Error",
102+
Suggestion = "Suggestion",
103+
Message = "Message",
104+
}
105+
function f(y: DiagnosticCategory) {
106+
x = y;
107+
~
108+
!!! error TS2322: Type 'DiagnosticCategory' is not assignable to type 'import("f").DiagnosticCategory'.
109+
!!! error TS2322: Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '0' was expected but '"Warning"' was given.
110+
y = x;
111+
~
112+
!!! error TS2322: Type 'import("f").DiagnosticCategory' is not assignable to type 'DiagnosticCategory'.
113+
!!! error TS2322: Each declaration of 'DiagnosticCategory.Warning' differs in its value, where '"Warning"' was expected but '0' was given.
114+
}
115+
})()

0 commit comments

Comments
 (0)