Skip to content

Commit 2bed7fe

Browse files
authored
Account for right operands & fix a weird error message for leftmost nullish literals in checkNullishCoalesceOperands (#59569)
1 parent edc497b commit 2bed7fe

File tree

7 files changed

+612
-183
lines changed

7 files changed

+612
-183
lines changed

src/compiler/checker.ts

+34-14
Original file line numberDiff line numberDiff line change
@@ -39847,22 +39847,42 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3984739847
grammarErrorOnNode(right, Diagnostics._0_and_1_operations_cannot_be_mixed_without_parentheses, tokenToString(right.operatorToken.kind), tokenToString(operatorToken.kind));
3984839848
}
3984939849

39850-
const leftTarget = skipOuterExpressions(left, OuterExpressionKinds.All);
39851-
const nullishSemantics = getSyntacticNullishnessSemantics(leftTarget);
39852-
if (nullishSemantics !== PredicateSemantics.Sometimes) {
39853-
if (node.parent.kind === SyntaxKind.BinaryExpression) {
39854-
error(leftTarget, Diagnostics.This_binary_expression_is_never_nullish_Are_you_missing_parentheses);
39855-
}
39856-
else {
39857-
if (nullishSemantics === PredicateSemantics.Always) {
39858-
error(leftTarget, Diagnostics.This_expression_is_always_nullish);
39859-
}
39860-
else {
39861-
error(leftTarget, Diagnostics.Right_operand_of_is_unreachable_because_the_left_operand_is_never_nullish);
39862-
}
39863-
}
39850+
checkNullishCoalesceOperandLeft(node);
39851+
checkNullishCoalesceOperandRight(node);
39852+
}
39853+
}
39854+
39855+
function checkNullishCoalesceOperandLeft(node: BinaryExpression) {
39856+
const leftTarget = skipOuterExpressions(node.left, OuterExpressionKinds.All);
39857+
39858+
const nullishSemantics = getSyntacticNullishnessSemantics(leftTarget);
39859+
if (nullishSemantics !== PredicateSemantics.Sometimes) {
39860+
if (nullishSemantics === PredicateSemantics.Always) {
39861+
error(leftTarget, Diagnostics.This_expression_is_always_nullish);
3986439862
}
39863+
else {
39864+
error(leftTarget, Diagnostics.Right_operand_of_is_unreachable_because_the_left_operand_is_never_nullish);
39865+
}
39866+
}
39867+
}
39868+
39869+
function checkNullishCoalesceOperandRight(node: BinaryExpression) {
39870+
const rightTarget = skipOuterExpressions(node.right, OuterExpressionKinds.All);
39871+
const nullishSemantics = getSyntacticNullishnessSemantics(rightTarget);
39872+
if (isNotWithinNullishCoalesceExpression(node)) {
39873+
return;
3986539874
}
39875+
39876+
if (nullishSemantics === PredicateSemantics.Always) {
39877+
error(rightTarget, Diagnostics.This_expression_is_always_nullish);
39878+
}
39879+
else if (nullishSemantics === PredicateSemantics.Never) {
39880+
error(rightTarget, Diagnostics.This_expression_is_never_nullish);
39881+
}
39882+
}
39883+
39884+
function isNotWithinNullishCoalesceExpression(node: BinaryExpression) {
39885+
return !isBinaryExpression(node.parent) || node.parent.operatorToken.kind !== SyntaxKind.QuestionQuestionToken;
3986639886
}
3986739887

3986839888
function getSyntacticNullishnessSemantics(node: Node): PredicateSemantics {

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -3987,6 +3987,10 @@
39873987
"category": "Error",
39883988
"code": 2880
39893989
},
3990+
"This expression is never nullish.": {
3991+
"category": "Error",
3992+
"code": 2881
3993+
},
39903994

39913995
"Import declaration '{0}' is using private name '{1}'.": {
39923996
"category": "Error",

tests/baselines/reference/predicateSemantics.errors.txt

+117-32
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,61 @@
11
predicateSemantics.ts(7,16): error TS2871: This expression is always nullish.
22
predicateSemantics.ts(10,16): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
3-
predicateSemantics.ts(26,12): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
4-
predicateSemantics.ts(27,12): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
5-
predicateSemantics.ts(28,12): error TS2871: This expression is always nullish.
6-
predicateSemantics.ts(29,12): error TS2872: This kind of expression is always truthy.
7-
predicateSemantics.ts(30,12): error TS2872: This kind of expression is always truthy.
8-
predicateSemantics.ts(33,8): error TS2872: This kind of expression is always truthy.
9-
predicateSemantics.ts(34,11): error TS2872: This kind of expression is always truthy.
10-
predicateSemantics.ts(35,8): error TS2872: This kind of expression is always truthy.
11-
predicateSemantics.ts(36,8): error TS2872: This kind of expression is always truthy.
12-
predicateSemantics.ts(51,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
13-
predicateSemantics.ts(52,14): error TS2695: Left side of comma operator is unused and has no side effects.
14-
predicateSemantics.ts(52,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
15-
predicateSemantics.ts(70,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
16-
predicateSemantics.ts(71,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
3+
predicateSemantics.ts(26,13): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
4+
predicateSemantics.ts(27,13): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
5+
predicateSemantics.ts(28,13): error TS2871: This expression is always nullish.
6+
predicateSemantics.ts(29,13): error TS2871: This expression is always nullish.
7+
predicateSemantics.ts(30,13): error TS2872: This kind of expression is always truthy.
8+
predicateSemantics.ts(31,13): error TS2872: This kind of expression is always truthy.
9+
predicateSemantics.ts(32,13): error TS2871: This expression is always nullish.
10+
predicateSemantics.ts(32,21): error TS2871: This expression is always nullish.
11+
predicateSemantics.ts(33,13): error TS2871: This expression is always nullish.
12+
predicateSemantics.ts(34,13): error TS2871: This expression is always nullish.
13+
predicateSemantics.ts(34,22): error TS2871: This expression is always nullish.
14+
predicateSemantics.ts(36,20): error TS2871: This expression is always nullish.
15+
predicateSemantics.ts(37,20): error TS2871: This expression is always nullish.
16+
predicateSemantics.ts(38,21): error TS2871: This expression is always nullish.
17+
predicateSemantics.ts(39,21): error TS2871: This expression is always nullish.
18+
predicateSemantics.ts(40,21): error TS2871: This expression is always nullish.
19+
predicateSemantics.ts(40,29): error TS2871: This expression is always nullish.
20+
predicateSemantics.ts(41,21): error TS2871: This expression is always nullish.
21+
predicateSemantics.ts(42,20): error TS2881: This expression is never nullish.
22+
predicateSemantics.ts(43,21): error TS2881: This expression is never nullish.
23+
predicateSemantics.ts(45,13): error TS2871: This expression is always nullish.
24+
predicateSemantics.ts(45,21): error TS2871: This expression is always nullish.
25+
predicateSemantics.ts(45,29): error TS2871: This expression is always nullish.
26+
predicateSemantics.ts(46,13): error TS2871: This expression is always nullish.
27+
predicateSemantics.ts(46,21): error TS2881: This expression is never nullish.
28+
predicateSemantics.ts(47,13): error TS2871: This expression is always nullish.
29+
predicateSemantics.ts(47,22): error TS2881: This expression is never nullish.
30+
predicateSemantics.ts(50,8): error TS2872: This kind of expression is always truthy.
31+
predicateSemantics.ts(51,11): error TS2872: This kind of expression is always truthy.
32+
predicateSemantics.ts(52,8): error TS2872: This kind of expression is always truthy.
33+
predicateSemantics.ts(53,8): error TS2872: This kind of expression is always truthy.
34+
predicateSemantics.ts(70,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
35+
predicateSemantics.ts(71,14): error TS2695: Left side of comma operator is unused and has no side effects.
36+
predicateSemantics.ts(71,14): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
37+
predicateSemantics.ts(89,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
38+
predicateSemantics.ts(90,1): error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
1739

1840

19-
==== predicateSemantics.ts (16 errors) ====
20-
declare let cond: any;
41+
==== predicateSemantics.ts (38 errors) ====
42+
declare let opt: number | undefined;
2143

2244
// OK: One or other operand is possibly nullish
23-
const test1 = (cond ? undefined : 32) ?? "possibly reached";
45+
const test1 = (opt ? undefined : 32) ?? "possibly reached";
2446

2547
// Not OK: Both operands nullish
26-
const test2 = (cond ? undefined : null) ?? "always reached";
27-
~~~~~~~~~~~~~~~~~~~~~~~
48+
const test2 = (opt ? undefined : null) ?? "always reached";
49+
~~~~~~~~~~~~~~~~~~~~~~
2850
!!! error TS2871: This expression is always nullish.
2951

3052
// Not OK: Both operands non-nullish
31-
const test3 = (cond ? 132 : 17) ?? "unreachable";
32-
~~~~~~~~~~~~~~~
53+
const test3 = (opt ? 132 : 17) ?? "unreachable";
54+
~~~~~~~~~~~~~~
3355
!!! error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
3456

3557
// Parens
36-
const test4 = (cond ? (undefined) : (17)) ?? 42;
58+
const test4 = (opt ? (undefined) : (17)) ?? 42;
3759

3860
// Should be OK (special case)
3961
if (!!true) {
@@ -46,21 +68,82 @@ predicateSemantics.ts(71,1): error TS2869: Right operand of ?? is unreachable be
4668
while (true) { }
4769
while (false) { }
4870

49-
const p5 = {} ?? null;
50-
~~
71+
const p01 = {} ?? null;
72+
~~
5173
!!! error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
52-
const p6 = 0 > 1 ?? null;
53-
~~~~~
74+
const p02 = 0 > 1 ?? null;
75+
~~~~~
5476
!!! error TS2869: Right operand of ?? is unreachable because the left operand is never nullish.
55-
const p7 = null ?? null;
56-
~~~~
77+
const p03 = null ?? 1;
78+
~~~~
79+
!!! error TS2871: This expression is always nullish.
80+
const p04 = null ?? null;
81+
~~~~
5782
!!! error TS2871: This expression is always nullish.
58-
const p8 = (class foo { }) && null;
59-
~~~~~~~~~~~~~~~
83+
const p05 = (class foo { }) && null;
84+
~~~~~~~~~~~~~~~
6085
!!! error TS2872: This kind of expression is always truthy.
61-
const p9 = (class foo { }) || null;
62-
~~~~~~~~~~~~~~~
86+
const p06 = (class foo { }) || null;
87+
~~~~~~~~~~~~~~~
6388
!!! error TS2872: This kind of expression is always truthy.
89+
const p07 = null ?? null ?? null;
90+
~~~~
91+
!!! error TS2871: This expression is always nullish.
92+
~~~~
93+
!!! error TS2871: This expression is always nullish.
94+
const p08 = null ?? opt ?? null;
95+
~~~~
96+
!!! error TS2871: This expression is always nullish.
97+
const p09 = null ?? (opt ? null : undefined) ?? null;
98+
~~~~
99+
!!! error TS2871: This expression is always nullish.
100+
~~~~~~~~~~~~~~~~~~~~~~
101+
!!! error TS2871: This expression is always nullish.
102+
103+
const p10 = opt ?? null ?? 1;
104+
~~~~
105+
!!! error TS2871: This expression is always nullish.
106+
const p11 = opt ?? null ?? null;
107+
~~~~
108+
!!! error TS2871: This expression is always nullish.
109+
const p12 = opt ?? (null ?? 1);
110+
~~~~
111+
!!! error TS2871: This expression is always nullish.
112+
const p13 = opt ?? (null ?? null);
113+
~~~~
114+
!!! error TS2871: This expression is always nullish.
115+
const p14 = opt ?? (null ?? null ?? null);
116+
~~~~
117+
!!! error TS2871: This expression is always nullish.
118+
~~~~
119+
!!! error TS2871: This expression is always nullish.
120+
const p15 = opt ?? (opt ? null : undefined) ?? null;
121+
~~~~~~~~~~~~~~~~~~~~~~
122+
!!! error TS2871: This expression is always nullish.
123+
const p16 = opt ?? 1 ?? 2;
124+
~
125+
!!! error TS2881: This expression is never nullish.
126+
const p17 = opt ?? (opt ? 1 : 2) ?? 3;
127+
~~~~~~~~~~~
128+
!!! error TS2881: This expression is never nullish.
129+
130+
const p21 = null ?? null ?? null ?? null;
131+
~~~~
132+
!!! error TS2871: This expression is always nullish.
133+
~~~~
134+
!!! error TS2871: This expression is always nullish.
135+
~~~~
136+
!!! error TS2871: This expression is always nullish.
137+
const p22 = null ?? 1 ?? 1;
138+
~~~~
139+
!!! error TS2871: This expression is always nullish.
140+
~
141+
!!! error TS2881: This expression is never nullish.
142+
const p23 = null ?? (opt ? 1 : 2) ?? 1;
143+
~~~~
144+
!!! error TS2871: This expression is always nullish.
145+
~~~~~~~~~~~
146+
!!! error TS2881: This expression is never nullish.
64147

65148
// Outer expression tests
66149
while ({} as any) { }
@@ -76,6 +159,8 @@ predicateSemantics.ts(71,1): error TS2869: Right operand of ?? is unreachable be
76159
~~~~~~
77160
!!! error TS2872: This kind of expression is always truthy.
78161

162+
declare let cond: any;
163+
79164
// Should be OK
80165
console.log((cond || undefined) && 1 / cond);
81166

0 commit comments

Comments
 (0)