Skip to content

Commit e6ba82b

Browse files
authored
Error on references to literal enum members in conditions (#58264)
1 parent 5b69fdf commit e6ba82b

6 files changed

+495
-4
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38493,7 +38493,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3849338493
parent = parent.parent;
3849438494
}
3849538495
if (operator === SyntaxKind.AmpersandAmpersandToken || isIfStatement(parent)) {
38496-
checkTestingKnownTruthyCallableOrAwaitableType(node.left, leftType, isIfStatement(parent) ? parent.thenStatement : undefined);
38496+
checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(node.left, leftType, isIfStatement(parent) ? parent.thenStatement : undefined);
3849738497
}
3849838498
checkTruthinessOfType(leftType, node.left);
3849938499
}
@@ -39127,7 +39127,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3912739127

3912839128
function checkConditionalExpression(node: ConditionalExpression, checkMode?: CheckMode): Type {
3912939129
const type = checkTruthinessExpression(node.condition, checkMode);
39130-
checkTestingKnownTruthyCallableOrAwaitableType(node.condition, type, node.whenTrue);
39130+
checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(node.condition, type, node.whenTrue);
3913139131
const type1 = checkExpression(node.whenTrue, checkMode);
3913239132
const type2 = checkExpression(node.whenFalse, checkMode);
3913339133
return getUnionType([type1, type2], UnionReduction.Subtype);
@@ -43101,7 +43101,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4310143101
// Grammar checking
4310243102
checkGrammarStatementInAmbientContext(node);
4310343103
const type = checkTruthinessExpression(node.expression);
43104-
checkTestingKnownTruthyCallableOrAwaitableType(node.expression, type, node.thenStatement);
43104+
checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(node.expression, type, node.thenStatement);
4310543105
checkSourceElement(node.thenStatement);
4310643106

4310743107
if (node.thenStatement.kind === SyntaxKind.EmptyStatement) {
@@ -43111,7 +43111,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4311143111
checkSourceElement(node.elseStatement);
4311243112
}
4311343113

43114-
function checkTestingKnownTruthyCallableOrAwaitableType(condExpr: Expression, condType: Type, body?: Statement | Expression) {
43114+
function checkTestingKnownTruthyCallableOrAwaitableOrEnumMemberType(condExpr: Expression, condType: Type, body?: Statement | Expression) {
4311543115
if (!strictNullChecks) return;
4311643116
bothHelper(condExpr, body);
4311743117

@@ -43136,6 +43136,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4313643136
return;
4313743137
}
4313843138
const type = location === condExpr ? condType : checkTruthinessExpression(location);
43139+
if (type.flags & TypeFlags.EnumLiteral && isPropertyAccessExpression(location) && (getNodeLinks(location.expression).resolvedSymbol ?? unknownSymbol).flags & SymbolFlags.Enum) {
43140+
// EnumLiteral type at condition with known value is always truthy or always falsy, likely an error
43141+
error(location, Diagnostics.This_condition_will_always_return_0, !!(type as LiteralType).value ? "true" : "false");
43142+
return;
43143+
}
4313943144
const isPropertyExpressionCast = isPropertyAccessExpression(location) && isTypeAssertion(location.expression);
4314043145
if (!hasTypeFacts(type, TypeFacts.Truthy) || isPropertyExpressionCast) return;
4314143146

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
errorOnEnumReferenceInCondition.ts(6,11): error TS2845: This condition will always return 'false'.
2+
errorOnEnumReferenceInCondition.ts(7,11): error TS2845: This condition will always return 'true'.
3+
errorOnEnumReferenceInCondition.ts(9,5): error TS2845: This condition will always return 'false'.
4+
errorOnEnumReferenceInCondition.ts(17,5): error TS2845: This condition will always return 'true'.
5+
errorOnEnumReferenceInCondition.ts(30,11): error TS2845: This condition will always return 'false'.
6+
errorOnEnumReferenceInCondition.ts(31,11): error TS2845: This condition will always return 'true'.
7+
errorOnEnumReferenceInCondition.ts(33,5): error TS2845: This condition will always return 'false'.
8+
errorOnEnumReferenceInCondition.ts(41,5): error TS2845: This condition will always return 'true'.
9+
10+
11+
==== errorOnEnumReferenceInCondition.ts (8 errors) ====
12+
enum Nums {
13+
Zero = 0,
14+
One = 1,
15+
}
16+
17+
const a = Nums.Zero ? "a" : "b";
18+
~~~~~~~~~
19+
!!! error TS2845: This condition will always return 'false'.
20+
const b = Nums.One ? "a" : "b";
21+
~~~~~~~~
22+
!!! error TS2845: This condition will always return 'true'.
23+
24+
if (Nums.Zero) {
25+
~~~~~~~~~
26+
!!! error TS2845: This condition will always return 'false'.
27+
Nums;
28+
}
29+
else {
30+
Nums;
31+
}
32+
33+
34+
if (Nums.One) {
35+
~~~~~~~~
36+
!!! error TS2845: This condition will always return 'true'.
37+
Nums;
38+
}
39+
else {
40+
Nums;
41+
}
42+
43+
44+
enum Strs {
45+
Empty = "",
46+
A = "A",
47+
}
48+
49+
const c = Strs.Empty ? "a" : "b";
50+
~~~~~~~~~~
51+
!!! error TS2845: This condition will always return 'false'.
52+
const d = Strs.A ? "a" : "b";
53+
~~~~~~
54+
!!! error TS2845: This condition will always return 'true'.
55+
56+
if (Strs.Empty) {
57+
~~~~~~~~~~
58+
!!! error TS2845: This condition will always return 'false'.
59+
Strs;
60+
}
61+
else {
62+
Strs;
63+
}
64+
65+
66+
if (Strs.A) {
67+
~~~~~~
68+
!!! error TS2845: This condition will always return 'true'.
69+
Strs;
70+
}
71+
else {
72+
Strs;
73+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//// [tests/cases/compiler/errorOnEnumReferenceInCondition.ts] ////
2+
3+
//// [errorOnEnumReferenceInCondition.ts]
4+
enum Nums {
5+
Zero = 0,
6+
One = 1,
7+
}
8+
9+
const a = Nums.Zero ? "a" : "b";
10+
const b = Nums.One ? "a" : "b";
11+
12+
if (Nums.Zero) {
13+
Nums;
14+
}
15+
else {
16+
Nums;
17+
}
18+
19+
20+
if (Nums.One) {
21+
Nums;
22+
}
23+
else {
24+
Nums;
25+
}
26+
27+
28+
enum Strs {
29+
Empty = "",
30+
A = "A",
31+
}
32+
33+
const c = Strs.Empty ? "a" : "b";
34+
const d = Strs.A ? "a" : "b";
35+
36+
if (Strs.Empty) {
37+
Strs;
38+
}
39+
else {
40+
Strs;
41+
}
42+
43+
44+
if (Strs.A) {
45+
Strs;
46+
}
47+
else {
48+
Strs;
49+
}
50+
51+
//// [errorOnEnumReferenceInCondition.js]
52+
"use strict";
53+
var Nums;
54+
(function (Nums) {
55+
Nums[Nums["Zero"] = 0] = "Zero";
56+
Nums[Nums["One"] = 1] = "One";
57+
})(Nums || (Nums = {}));
58+
var a = Nums.Zero ? "a" : "b";
59+
var b = Nums.One ? "a" : "b";
60+
if (Nums.Zero) {
61+
Nums;
62+
}
63+
else {
64+
Nums;
65+
}
66+
if (Nums.One) {
67+
Nums;
68+
}
69+
else {
70+
Nums;
71+
}
72+
var Strs;
73+
(function (Strs) {
74+
Strs["Empty"] = "";
75+
Strs["A"] = "A";
76+
})(Strs || (Strs = {}));
77+
var c = Strs.Empty ? "a" : "b";
78+
var d = Strs.A ? "a" : "b";
79+
if (Strs.Empty) {
80+
Strs;
81+
}
82+
else {
83+
Strs;
84+
}
85+
if (Strs.A) {
86+
Strs;
87+
}
88+
else {
89+
Strs;
90+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
//// [tests/cases/compiler/errorOnEnumReferenceInCondition.ts] ////
2+
3+
=== errorOnEnumReferenceInCondition.ts ===
4+
enum Nums {
5+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
6+
7+
Zero = 0,
8+
>Zero : Symbol(Nums.Zero, Decl(errorOnEnumReferenceInCondition.ts, 0, 11))
9+
10+
One = 1,
11+
>One : Symbol(Nums.One, Decl(errorOnEnumReferenceInCondition.ts, 1, 13))
12+
}
13+
14+
const a = Nums.Zero ? "a" : "b";
15+
>a : Symbol(a, Decl(errorOnEnumReferenceInCondition.ts, 5, 5))
16+
>Nums.Zero : Symbol(Nums.Zero, Decl(errorOnEnumReferenceInCondition.ts, 0, 11))
17+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
18+
>Zero : Symbol(Nums.Zero, Decl(errorOnEnumReferenceInCondition.ts, 0, 11))
19+
20+
const b = Nums.One ? "a" : "b";
21+
>b : Symbol(b, Decl(errorOnEnumReferenceInCondition.ts, 6, 5))
22+
>Nums.One : Symbol(Nums.One, Decl(errorOnEnumReferenceInCondition.ts, 1, 13))
23+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
24+
>One : Symbol(Nums.One, Decl(errorOnEnumReferenceInCondition.ts, 1, 13))
25+
26+
if (Nums.Zero) {
27+
>Nums.Zero : Symbol(Nums.Zero, Decl(errorOnEnumReferenceInCondition.ts, 0, 11))
28+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
29+
>Zero : Symbol(Nums.Zero, Decl(errorOnEnumReferenceInCondition.ts, 0, 11))
30+
31+
Nums;
32+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
33+
}
34+
else {
35+
Nums;
36+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
37+
}
38+
39+
40+
if (Nums.One) {
41+
>Nums.One : Symbol(Nums.One, Decl(errorOnEnumReferenceInCondition.ts, 1, 13))
42+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
43+
>One : Symbol(Nums.One, Decl(errorOnEnumReferenceInCondition.ts, 1, 13))
44+
45+
Nums;
46+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
47+
}
48+
else {
49+
Nums;
50+
>Nums : Symbol(Nums, Decl(errorOnEnumReferenceInCondition.ts, 0, 0))
51+
}
52+
53+
54+
enum Strs {
55+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
56+
57+
Empty = "",
58+
>Empty : Symbol(Strs.Empty, Decl(errorOnEnumReferenceInCondition.ts, 24, 11))
59+
60+
A = "A",
61+
>A : Symbol(Strs.A, Decl(errorOnEnumReferenceInCondition.ts, 25, 15))
62+
}
63+
64+
const c = Strs.Empty ? "a" : "b";
65+
>c : Symbol(c, Decl(errorOnEnumReferenceInCondition.ts, 29, 5))
66+
>Strs.Empty : Symbol(Strs.Empty, Decl(errorOnEnumReferenceInCondition.ts, 24, 11))
67+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
68+
>Empty : Symbol(Strs.Empty, Decl(errorOnEnumReferenceInCondition.ts, 24, 11))
69+
70+
const d = Strs.A ? "a" : "b";
71+
>d : Symbol(d, Decl(errorOnEnumReferenceInCondition.ts, 30, 5))
72+
>Strs.A : Symbol(Strs.A, Decl(errorOnEnumReferenceInCondition.ts, 25, 15))
73+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
74+
>A : Symbol(Strs.A, Decl(errorOnEnumReferenceInCondition.ts, 25, 15))
75+
76+
if (Strs.Empty) {
77+
>Strs.Empty : Symbol(Strs.Empty, Decl(errorOnEnumReferenceInCondition.ts, 24, 11))
78+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
79+
>Empty : Symbol(Strs.Empty, Decl(errorOnEnumReferenceInCondition.ts, 24, 11))
80+
81+
Strs;
82+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
83+
}
84+
else {
85+
Strs;
86+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
87+
}
88+
89+
90+
if (Strs.A) {
91+
>Strs.A : Symbol(Strs.A, Decl(errorOnEnumReferenceInCondition.ts, 25, 15))
92+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
93+
>A : Symbol(Strs.A, Decl(errorOnEnumReferenceInCondition.ts, 25, 15))
94+
95+
Strs;
96+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
97+
}
98+
else {
99+
Strs;
100+
>Strs : Symbol(Strs, Decl(errorOnEnumReferenceInCondition.ts, 21, 1))
101+
}

0 commit comments

Comments
 (0)