Skip to content

Commit e073c05

Browse files
committed
Add errors for invalid assignments and a trailing '?.'
1 parent 7be47ab commit e073c05

13 files changed

+988
-8
lines changed

src/compiler/checker.ts

+28-7
Original file line numberDiff line numberDiff line change
@@ -24123,13 +24123,17 @@ namespace ts {
2412324123
return false;
2412424124
}
2412524125

24126-
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage): boolean {
24126+
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean {
2412724127
// References are combinations of identifiers, parentheses, and property accesses.
2412824128
const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses);
2412924129
if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
2413024130
error(expr, invalidReferenceMessage);
2413124131
return false;
2413224132
}
24133+
if (node.flags & NodeFlags.OptionalChain) {
24134+
error(expr, invalidOptionalChainMessage);
24135+
return false;
24136+
}
2413324137
return true;
2413424138
}
2413524139

@@ -24239,7 +24243,10 @@ namespace ts {
2423924243
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
2424024244
if (ok) {
2424124245
// run check only if former checks succeeded to avoid reporting cascading errors
24242-
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
24246+
checkReferenceExpression(
24247+
node.operand,
24248+
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
24249+
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access);
2424324250
}
2424424251
return getUnaryResultType(operandType);
2424524252
}
@@ -24257,7 +24264,10 @@ namespace ts {
2425724264
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
2425824265
if (ok) {
2425924266
// run check only if former checks succeeded to avoid reporting cascading errors
24260-
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
24267+
checkReferenceExpression(
24268+
node.operand,
24269+
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
24270+
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access);
2426124271
}
2426224272
return getUnaryResultType(operandType);
2426324273
}
@@ -24507,7 +24517,10 @@ namespace ts {
2450724517
const error = target.parent.kind === SyntaxKind.SpreadAssignment ?
2450824518
Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access :
2450924519
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access;
24510-
if (checkReferenceExpression(target, error)) {
24520+
const optionalError = target.parent.kind === SyntaxKind.SpreadAssignment ?
24521+
Diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access :
24522+
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access;
24523+
if (checkReferenceExpression(target, error, optionalError)) {
2451124524
checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target);
2451224525
}
2451324526
return sourceType;
@@ -24851,7 +24864,9 @@ namespace ts {
2485124864
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
2485224865
// and the type of the non-compound operation to be assignable to the type of VarExpr.
2485324866

24854-
if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)
24867+
if (checkReferenceExpression(left,
24868+
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access,
24869+
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access)
2485524870
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
2485624871
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
2485724872
checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right);
@@ -28144,7 +28159,10 @@ namespace ts {
2814428159
}
2814528160
else {
2814628161
const leftType = checkExpression(varExpr);
28147-
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access);
28162+
checkReferenceExpression(
28163+
varExpr,
28164+
Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access,
28165+
Diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access);
2814828166

2814928167
// iteratedType will be undefined if the rightType was missing properties/signatures
2815028168
// required to get its iteratedType (like [Symbol.iterator] or next). This may be
@@ -28194,7 +28212,10 @@ namespace ts {
2819428212
}
2819528213
else {
2819628214
// run check only former check succeeded to avoid cascading errors
28197-
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access);
28215+
checkReferenceExpression(
28216+
varExpr,
28217+
Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access,
28218+
Diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access);
2819828219
}
2819928220
}
2820028221

src/compiler/diagnosticMessages.json

+20
Original file line numberDiff line numberDiff line change
@@ -2697,6 +2697,26 @@
26972697
"category": "Error",
26982698
"code": 2773
26992699
},
2700+
"The operand of an increment or decrement operator may not be an optional property access.": {
2701+
"category": "Error",
2702+
"code": 2774
2703+
},
2704+
"The target of an object rest assignment may not be an optional property access.": {
2705+
"category": "Error",
2706+
"code": 2775
2707+
},
2708+
"The left-hand side of an assignment expression may not be an optional property access.": {
2709+
"category": "Error",
2710+
"code": 2776
2711+
},
2712+
"The left-hand side of a 'for...in' statement may not be an optional property access.": {
2713+
"category": "Error",
2714+
"code": 2777
2715+
},
2716+
"The left-hand side of a 'for...of' statement may not be an optional property access.": {
2717+
"category": "Error",
2718+
"code": 2778
2719+
},
27002720

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

src/compiler/parser.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4688,6 +4688,7 @@ namespace ts {
46884688

46894689
function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
46904690
while (true) {
4691+
noop(ts);
46914692
expression = parseMemberExpressionRest(expression, /*allowOptionalChain*/ true);
46924693
const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken);
46934694

@@ -4732,7 +4733,7 @@ namespace ts {
47324733
const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos) as PropertyAccessExpression;
47334734
propertyAccess.expression = expression;
47344735
propertyAccess.questionDotToken = questionDotToken;
4735-
propertyAccess.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false);
4736+
propertyAccess.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Identifier_expected);
47364737
propertyAccess.flags |= NodeFlags.OptionalChain;
47374738
expression = finishNode(propertyAccess);
47384739
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(3,1): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
2+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(4,1): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
3+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(5,1): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
4+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(6,1): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
5+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(8,3): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
6+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(9,3): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
7+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(10,3): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
8+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(11,3): error TS2774: The operand of an increment or decrement operator may not be an optional property access.
9+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(13,1): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
10+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(14,1): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
11+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(15,1): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
12+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(16,1): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
13+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(18,6): error TS2777: The left-hand side of a 'for...in' statement may not be an optional property access.
14+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(19,6): error TS2777: The left-hand side of a 'for...in' statement may not be an optional property access.
15+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(20,6): error TS2778: The left-hand side of a 'for...of' statement may not be an optional property access.
16+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(21,6): error TS2778: The left-hand side of a 'for...of' statement may not be an optional property access.
17+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(23,7): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
18+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(24,7): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
19+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(25,7): error TS2775: The target of an object rest assignment may not be an optional property access.
20+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(26,7): error TS2775: The target of an object rest assignment may not be an optional property access.
21+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(27,5): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
22+
tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts(28,5): error TS2776: The left-hand side of an assignment expression may not be an optional property access.
23+
24+
25+
==== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts (22 errors) ====
26+
declare const obj: any;
27+
28+
obj?.["a"]++;
29+
~~~~~~~~~~
30+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
31+
obj?.a["b"]++;
32+
~~~~~~~~~~~
33+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
34+
obj?.["a"]--;
35+
~~~~~~~~~~
36+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
37+
obj?.a["b"]--;
38+
~~~~~~~~~~~
39+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
40+
41+
++obj?.["a"];
42+
~~~~~~~~~~
43+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
44+
++obj?.a["b"];
45+
~~~~~~~~~~~
46+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
47+
--obj?.["a"];
48+
~~~~~~~~~~
49+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
50+
--obj?.a["b"];
51+
~~~~~~~~~~~
52+
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
53+
54+
obj?.["a"] = 1;
55+
~~~~~~~~~~
56+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
57+
obj?.a["b"] = 1;
58+
~~~~~~~~~~~
59+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
60+
obj?.["a"] += 1;
61+
~~~~~~~~~~
62+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
63+
obj?.a["b"] += 1;
64+
~~~~~~~~~~~
65+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
66+
67+
for (obj?.["a"] in {});
68+
~~~~~~~~~~
69+
!!! error TS2777: The left-hand side of a 'for...in' statement may not be an optional property access.
70+
for (obj?.a["b"] in {});
71+
~~~~~~~~~~~
72+
!!! error TS2777: The left-hand side of a 'for...in' statement may not be an optional property access.
73+
for (obj?.["a"] of []);
74+
~~~~~~~~~~
75+
!!! error TS2778: The left-hand side of a 'for...of' statement may not be an optional property access.
76+
for (obj?.a["b"] of []);
77+
~~~~~~~~~~~
78+
!!! error TS2778: The left-hand side of a 'for...of' statement may not be an optional property access.
79+
80+
({ a: obj?.["a"] } = { a: 1 });
81+
~~~~~~~~~~
82+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
83+
({ a: obj?.a["b"] } = { a: 1 });
84+
~~~~~~~~~~~
85+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
86+
({ ...obj?.["a"] } = { a: 1 });
87+
~~~~~~~~~~
88+
!!! error TS2775: The target of an object rest assignment may not be an optional property access.
89+
({ ...obj?.a["b"] } = { a: 1 });
90+
~~~~~~~~~~~
91+
!!! error TS2775: The target of an object rest assignment may not be an optional property access.
92+
[...obj?.["a"]] = [];
93+
~~~~~~~~~~
94+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
95+
[...obj?.a["b"]] = [];
96+
~~~~~~~~~~~
97+
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
98+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
//// [elementAccessChain.3.ts]
2+
declare const obj: any;
3+
4+
obj?.["a"]++;
5+
obj?.a["b"]++;
6+
obj?.["a"]--;
7+
obj?.a["b"]--;
8+
9+
++obj?.["a"];
10+
++obj?.a["b"];
11+
--obj?.["a"];
12+
--obj?.a["b"];
13+
14+
obj?.["a"] = 1;
15+
obj?.a["b"] = 1;
16+
obj?.["a"] += 1;
17+
obj?.a["b"] += 1;
18+
19+
for (obj?.["a"] in {});
20+
for (obj?.a["b"] in {});
21+
for (obj?.["a"] of []);
22+
for (obj?.a["b"] of []);
23+
24+
({ a: obj?.["a"] } = { a: 1 });
25+
({ a: obj?.a["b"] } = { a: 1 });
26+
({ ...obj?.["a"] } = { a: 1 });
27+
({ ...obj?.a["b"] } = { a: 1 });
28+
[...obj?.["a"]] = [];
29+
[...obj?.a["b"]] = [];
30+
31+
32+
//// [elementAccessChain.3.js]
33+
"use strict";
34+
var __rest = (this && this.__rest) || function (s, e) {
35+
var t = {};
36+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
37+
t[p] = s[p];
38+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
39+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
40+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
41+
t[p[i]] = s[p[i]];
42+
}
43+
return t;
44+
};
45+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
46+
((_a = obj) === null || _a === void 0 ? void 0 : _a["a"])++;
47+
((_b = obj) === null || _b === void 0 ? void 0 : _b.a["b"])++;
48+
((_c = obj) === null || _c === void 0 ? void 0 : _c["a"])--;
49+
((_d = obj) === null || _d === void 0 ? void 0 : _d.a["b"])--;
50+
++((_e = obj) === null || _e === void 0 ? void 0 : _e["a"]);
51+
++((_f = obj) === null || _f === void 0 ? void 0 : _f.a["b"]);
52+
--((_g = obj) === null || _g === void 0 ? void 0 : _g["a"]);
53+
--((_h = obj) === null || _h === void 0 ? void 0 : _h.a["b"]);
54+
(_j = obj) === null || _j === void 0 ? void 0 : _j["a"] = 1;
55+
(_k = obj) === null || _k === void 0 ? void 0 : _k.a["b"] = 1;
56+
(_l = obj) === null || _l === void 0 ? void 0 : _l["a"] += 1;
57+
(_m = obj) === null || _m === void 0 ? void 0 : _m.a["b"] += 1;
58+
for ((_o = obj) === null || _o === void 0 ? void 0 : _o["a"] in {})
59+
;
60+
for ((_p = obj) === null || _p === void 0 ? void 0 : _p.a["b"] in {})
61+
;
62+
for (var _i = 0, _y = []; _i < _y.length; _i++) {
63+
(_q = obj) === null || _q === void 0 ? void 0 : _q["a"] = _y[_i];
64+
;
65+
}
66+
for (var _z = 0, _0 = []; _z < _0.length; _z++) {
67+
(_r = obj) === null || _r === void 0 ? void 0 : _r.a["b"] = _0[_z];
68+
;
69+
}
70+
((_s = obj) === null || _s === void 0 ? void 0 : _s["a"] = { a: 1 }.a);
71+
((_t = obj) === null || _t === void 0 ? void 0 : _t.a["b"] = { a: 1 }.a);
72+
((_u = obj) === null || _u === void 0 ? void 0 : _u["a"] = __rest({ a: 1 }, []));
73+
((_v = obj) === null || _v === void 0 ? void 0 : _v.a["b"] = __rest({ a: 1 }, []));
74+
(_w = obj) === null || _w === void 0 ? void 0 : _w["a"] = [].slice(0);
75+
(_x = obj) === null || _x === void 0 ? void 0 : _x.a["b"] = [].slice(0);

0 commit comments

Comments
 (0)