Skip to content

Commit

Permalink
Add errors for invalid assignments and a trailing '?.'
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Sep 24, 2019
1 parent 7be47ab commit e073c05
Show file tree
Hide file tree
Showing 13 changed files with 988 additions and 8 deletions.
35 changes: 28 additions & 7 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24123,13 +24123,17 @@ namespace ts {
return false;
}

function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage): boolean {
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean {
// References are combinations of identifiers, parentheses, and property accesses.
const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses);
if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
error(expr, invalidReferenceMessage);
return false;
}
if (node.flags & NodeFlags.OptionalChain) {
error(expr, invalidOptionalChainMessage);
return false;
}
return true;
}

Expand Down Expand Up @@ -24239,7 +24243,10 @@ namespace ts {
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
if (ok) {
// run check only if former checks succeeded to avoid reporting cascading errors
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
checkReferenceExpression(
node.operand,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access);
}
return getUnaryResultType(operandType);
}
Expand All @@ -24257,7 +24264,10 @@ namespace ts {
Diagnostics.An_arithmetic_operand_must_be_of_type_any_number_bigint_or_an_enum_type);
if (ok) {
// run check only if former checks succeeded to avoid reporting cascading errors
checkReferenceExpression(node.operand, Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access);
checkReferenceExpression(
node.operand,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_must_be_a_variable_or_a_property_access,
Diagnostics.The_operand_of_an_increment_or_decrement_operator_may_not_be_an_optional_property_access);
}
return getUnaryResultType(operandType);
}
Expand Down Expand Up @@ -24507,7 +24517,10 @@ namespace ts {
const error = target.parent.kind === SyntaxKind.SpreadAssignment ?
Diagnostics.The_target_of_an_object_rest_assignment_must_be_a_variable_or_a_property_access :
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access;
if (checkReferenceExpression(target, error)) {
const optionalError = target.parent.kind === SyntaxKind.SpreadAssignment ?
Diagnostics.The_target_of_an_object_rest_assignment_may_not_be_an_optional_property_access :
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access;
if (checkReferenceExpression(target, error, optionalError)) {
checkTypeAssignableToAndOptionallyElaborate(sourceType, targetType, target, target);
}
return sourceType;
Expand Down Expand Up @@ -24851,7 +24864,9 @@ namespace ts {
// A compound assignment furthermore requires VarExpr to be classified as a reference (section 4.1)
// and the type of the non-compound operation to be assignable to the type of VarExpr.

if (checkReferenceExpression(left, Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access)
if (checkReferenceExpression(left,
Diagnostics.The_left_hand_side_of_an_assignment_expression_must_be_a_variable_or_a_property_access,
Diagnostics.The_left_hand_side_of_an_assignment_expression_may_not_be_an_optional_property_access)
&& (!isIdentifier(left) || unescapeLeadingUnderscores(left.escapedText) !== "exports")) {
// to avoid cascading errors check assignability only if 'isReference' check succeeded and no errors were reported
checkTypeAssignableToAndOptionallyElaborate(valueType, leftType, left, right);
Expand Down Expand Up @@ -28144,7 +28159,10 @@ namespace ts {
}
else {
const leftType = checkExpression(varExpr);
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access);
checkReferenceExpression(
varExpr,
Diagnostics.The_left_hand_side_of_a_for_of_statement_must_be_a_variable_or_a_property_access,
Diagnostics.The_left_hand_side_of_a_for_of_statement_may_not_be_an_optional_property_access);

// iteratedType will be undefined if the rightType was missing properties/signatures
// required to get its iteratedType (like [Symbol.iterator] or next). This may be
Expand Down Expand Up @@ -28194,7 +28212,10 @@ namespace ts {
}
else {
// run check only former check succeeded to avoid cascading errors
checkReferenceExpression(varExpr, Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access);
checkReferenceExpression(
varExpr,
Diagnostics.The_left_hand_side_of_a_for_in_statement_must_be_a_variable_or_a_property_access,
Diagnostics.The_left_hand_side_of_a_for_in_statement_may_not_be_an_optional_property_access);
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2697,6 +2697,26 @@
"category": "Error",
"code": 2773
},
"The operand of an increment or decrement operator may not be an optional property access.": {
"category": "Error",
"code": 2774
},
"The target of an object rest assignment may not be an optional property access.": {
"category": "Error",
"code": 2775
},
"The left-hand side of an assignment expression may not be an optional property access.": {
"category": "Error",
"code": 2776
},
"The left-hand side of a 'for...in' statement may not be an optional property access.": {
"category": "Error",
"code": 2777
},
"The left-hand side of a 'for...of' statement may not be an optional property access.": {
"category": "Error",
"code": 2778
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
3 changes: 2 additions & 1 deletion src/compiler/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4688,6 +4688,7 @@ namespace ts {

function parseCallExpressionRest(expression: LeftHandSideExpression): LeftHandSideExpression {
while (true) {
noop(ts);
expression = parseMemberExpressionRest(expression, /*allowOptionalChain*/ true);
const questionDotToken = parseOptionalToken(SyntaxKind.QuestionDotToken);

Expand Down Expand Up @@ -4732,7 +4733,7 @@ namespace ts {
const propertyAccess = createNode(SyntaxKind.PropertyAccessExpression, expression.pos) as PropertyAccessExpression;
propertyAccess.expression = expression;
propertyAccess.questionDotToken = questionDotToken;
propertyAccess.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false);
propertyAccess.name = createMissingNode(SyntaxKind.Identifier, /*reportAtCurrentPosition*/ false, Diagnostics.Identifier_expected);
propertyAccess.flags |= NodeFlags.OptionalChain;
expression = finishNode(propertyAccess);
}
Expand Down
98 changes: 98 additions & 0 deletions tests/baselines/reference/elementAccessChain.3.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.


==== tests/cases/conformance/expressions/optionalChaining/elementAccessChain/elementAccessChain.3.ts (22 errors) ====
declare const obj: any;

obj?.["a"]++;
~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
obj?.a["b"]++;
~~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
obj?.["a"]--;
~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
obj?.a["b"]--;
~~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.

++obj?.["a"];
~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
++obj?.a["b"];
~~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
--obj?.["a"];
~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.
--obj?.a["b"];
~~~~~~~~~~~
!!! error TS2774: The operand of an increment or decrement operator may not be an optional property access.

obj?.["a"] = 1;
~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
obj?.a["b"] = 1;
~~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
obj?.["a"] += 1;
~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
obj?.a["b"] += 1;
~~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.

for (obj?.["a"] in {});
~~~~~~~~~~
!!! error TS2777: The left-hand side of a 'for...in' statement may not be an optional property access.
for (obj?.a["b"] in {});
~~~~~~~~~~~
!!! error TS2777: The left-hand side of a 'for...in' statement may not be an optional property access.
for (obj?.["a"] of []);
~~~~~~~~~~
!!! error TS2778: The left-hand side of a 'for...of' statement may not be an optional property access.
for (obj?.a["b"] of []);
~~~~~~~~~~~
!!! error TS2778: The left-hand side of a 'for...of' statement may not be an optional property access.

({ a: obj?.["a"] } = { a: 1 });
~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
({ a: obj?.a["b"] } = { a: 1 });
~~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
({ ...obj?.["a"] } = { a: 1 });
~~~~~~~~~~
!!! error TS2775: The target of an object rest assignment may not be an optional property access.
({ ...obj?.a["b"] } = { a: 1 });
~~~~~~~~~~~
!!! error TS2775: The target of an object rest assignment may not be an optional property access.
[...obj?.["a"]] = [];
~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.
[...obj?.a["b"]] = [];
~~~~~~~~~~~
!!! error TS2776: The left-hand side of an assignment expression may not be an optional property access.

75 changes: 75 additions & 0 deletions tests/baselines/reference/elementAccessChain.3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//// [elementAccessChain.3.ts]
declare const obj: any;

obj?.["a"]++;
obj?.a["b"]++;
obj?.["a"]--;
obj?.a["b"]--;

++obj?.["a"];
++obj?.a["b"];
--obj?.["a"];
--obj?.a["b"];

obj?.["a"] = 1;
obj?.a["b"] = 1;
obj?.["a"] += 1;
obj?.a["b"] += 1;

for (obj?.["a"] in {});
for (obj?.a["b"] in {});
for (obj?.["a"] of []);
for (obj?.a["b"] of []);

({ a: obj?.["a"] } = { a: 1 });
({ a: obj?.a["b"] } = { a: 1 });
({ ...obj?.["a"] } = { a: 1 });
({ ...obj?.a["b"] } = { a: 1 });
[...obj?.["a"]] = [];
[...obj?.a["b"]] = [];


//// [elementAccessChain.3.js]
"use strict";
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x;
((_a = obj) === null || _a === void 0 ? void 0 : _a["a"])++;
((_b = obj) === null || _b === void 0 ? void 0 : _b.a["b"])++;
((_c = obj) === null || _c === void 0 ? void 0 : _c["a"])--;
((_d = obj) === null || _d === void 0 ? void 0 : _d.a["b"])--;
++((_e = obj) === null || _e === void 0 ? void 0 : _e["a"]);
++((_f = obj) === null || _f === void 0 ? void 0 : _f.a["b"]);
--((_g = obj) === null || _g === void 0 ? void 0 : _g["a"]);
--((_h = obj) === null || _h === void 0 ? void 0 : _h.a["b"]);
(_j = obj) === null || _j === void 0 ? void 0 : _j["a"] = 1;
(_k = obj) === null || _k === void 0 ? void 0 : _k.a["b"] = 1;
(_l = obj) === null || _l === void 0 ? void 0 : _l["a"] += 1;
(_m = obj) === null || _m === void 0 ? void 0 : _m.a["b"] += 1;
for ((_o = obj) === null || _o === void 0 ? void 0 : _o["a"] in {})
;
for ((_p = obj) === null || _p === void 0 ? void 0 : _p.a["b"] in {})
;
for (var _i = 0, _y = []; _i < _y.length; _i++) {
(_q = obj) === null || _q === void 0 ? void 0 : _q["a"] = _y[_i];
;
}
for (var _z = 0, _0 = []; _z < _0.length; _z++) {
(_r = obj) === null || _r === void 0 ? void 0 : _r.a["b"] = _0[_z];
;
}
((_s = obj) === null || _s === void 0 ? void 0 : _s["a"] = { a: 1 }.a);
((_t = obj) === null || _t === void 0 ? void 0 : _t.a["b"] = { a: 1 }.a);
((_u = obj) === null || _u === void 0 ? void 0 : _u["a"] = __rest({ a: 1 }, []));
((_v = obj) === null || _v === void 0 ? void 0 : _v.a["b"] = __rest({ a: 1 }, []));
(_w = obj) === null || _w === void 0 ? void 0 : _w["a"] = [].slice(0);
(_x = obj) === null || _x === void 0 ? void 0 : _x.a["b"] = [].slice(0);
Loading

0 comments on commit e073c05

Please sign in to comment.