From 62f3ccd9c00f2da4cc995ef0e66c4dfb91eec8bf Mon Sep 17 00:00:00 2001 From: Wenlu Wang Date: Thu, 1 Apr 2021 06:57:25 +0800 Subject: [PATCH] Error if assignment after block (#41115) * Error if assignment after block * Update src/compiler/diagnosticMessages.json Co-authored-by: Daniel Rosenwasser * Fix diags * Error after block Co-authored-by: Daniel Rosenwasser Co-authored-by: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> --- src/compiler/diagnosticMessages.json | 4 ++ src/compiler/parser.ts | 11 +++-- .../reference/assignmentLHSIsValue.errors.txt | 15 +++--- .../reference/assignmentLHSIsValue.types | 2 +- .../destructionAssignmentError.errors.txt | 27 +++++++++++ .../reference/destructionAssignmentError.js | 28 +++++++++++ .../destructionAssignmentError.symbols | 36 ++++++++++++++ .../destructionAssignmentError.types | 48 +++++++++++++++++++ .../compiler/destructionAssignmentError.ts | 12 +++++ 9 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 tests/baselines/reference/destructionAssignmentError.errors.txt create mode 100644 tests/baselines/reference/destructionAssignmentError.js create mode 100644 tests/baselines/reference/destructionAssignmentError.symbols create mode 100644 tests/baselines/reference/destructionAssignmentError.types create mode 100644 tests/cases/compiler/destructionAssignmentError.ts diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 9a275dd586b..b0d3edd52bd 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3308,6 +3308,10 @@ "category": "Error", "code": 2808 }, + "Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses.": { + "category": "Error", + "code": 2809 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e951178c1a8..1d42346123b 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -2109,8 +2109,7 @@ namespace ts { while (!isListTerminator(kind)) { if (isListElement(kind, /*inErrorRecovery*/ false)) { - const element = parseListElement(kind, parseElement); - list.push(element); + list.push(parseListElement(kind, parseElement)); continue; } @@ -5602,7 +5601,13 @@ namespace ts { const multiLine = scanner.hasPrecedingLineBreak(); const statements = parseList(ParsingContext.BlockStatements, parseStatement); parseExpectedMatchingBrackets(SyntaxKind.OpenBraceToken, SyntaxKind.CloseBraceToken, openBracePosition); - return finishNode(factory.createBlock(statements, multiLine), pos); + const result = finishNode(factory.createBlock(statements, multiLine), pos); + if (token() === SyntaxKind.EqualsToken) { + parseErrorAtCurrentToken(Diagnostics.Declaration_or_statement_expected_This_follows_a_block_of_statements_so_if_you_intended_to_write_a_destructuring_assignment_you_might_need_to_wrap_the_the_whole_assignment_in_parentheses); + nextToken(); + } + + return result; } else { const statements = createMissingList(); diff --git a/tests/baselines/reference/assignmentLHSIsValue.errors.txt b/tests/baselines/reference/assignmentLHSIsValue.errors.txt index 1cf07b81622..71809b4605f 100644 --- a/tests/baselines/reference/assignmentLHSIsValue.errors.txt +++ b/tests/baselines/reference/assignmentLHSIsValue.errors.txt @@ -13,14 +13,15 @@ tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(2 tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(30,1): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(31,1): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(32,1): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. -tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(35,9): error TS1128: Declaration or statement expected. +tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(35,9): error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(38,2): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(38,6): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(42,36): error TS1034: 'super' must be followed by an argument list or member access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(44,19): error TS1034: 'super' must be followed by an argument list or member access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(46,27): error TS1034: 'super' must be followed by an argument list or member access. -tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(50,20): error TS1128: Declaration or statement expected. -tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(51,11): error TS1005: ';' expected. +tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(50,20): error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. +tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(51,11): error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. +tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(51,13): error TS1005: ';' expected. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(54,1): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(57,1): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(58,2): error TS2631: Cannot assign to 'M' because it is a namespace. @@ -38,7 +39,7 @@ tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(6 tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(70,1): error TS2364: The left-hand side of an assignment expression must be a variable or a property access. -==== tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts (38 errors) ==== +==== tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts (39 errors) ==== // expected error for all the LHS of assignments var value: any; @@ -105,7 +106,7 @@ tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(7 // object literals { a: 0} = value; ~ -!!! error TS1128: Declaration or statement expected. +!!! error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. // array literals ['', ''] = value; @@ -132,9 +133,11 @@ tests/cases/conformance/expressions/assignmentOperator/assignmentLHSIsValue.ts(7 // function expression function bar() { } = value; ~ -!!! error TS1128: Declaration or statement expected. +!!! error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. () => { } = value; ~ +!!! error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. + ~~~~~ !!! error TS1005: ';' expected. // function calls diff --git a/tests/baselines/reference/assignmentLHSIsValue.types b/tests/baselines/reference/assignmentLHSIsValue.types index 82dd7f026ad..ad4905acc94 100644 --- a/tests/baselines/reference/assignmentLHSIsValue.types +++ b/tests/baselines/reference/assignmentLHSIsValue.types @@ -146,7 +146,7 @@ function bar() { } = value; >value : any () => { } = value; ->() => { } : () => void +>() => { } = : () => void >value : any // function calls diff --git a/tests/baselines/reference/destructionAssignmentError.errors.txt b/tests/baselines/reference/destructionAssignmentError.errors.txt new file mode 100644 index 00000000000..831f9a5fc6c --- /dev/null +++ b/tests/baselines/reference/destructionAssignmentError.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/destructionAssignmentError.ts(6,3): error TS2695: Left side of comma operator is unused and has no side effects. +tests/cases/compiler/destructionAssignmentError.ts(6,10): error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. +tests/cases/compiler/destructionAssignmentError.ts(11,3): error TS2695: Left side of comma operator is unused and has no side effects. +tests/cases/compiler/destructionAssignmentError.ts(12,1): error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. + + +==== tests/cases/compiler/destructionAssignmentError.ts (4 errors) ==== + declare function fn(): { a: 1, b: 2 } + let a: number; + let b: number; + + ({ a, b } = fn()); + { a, b } = fn(); + ~ +!!! error TS2695: Left side of comma operator is unused and has no side effects. + ~ +!!! error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. + + ({ a, b } = + fn()); + + { a, b } + ~ +!!! error TS2695: Left side of comma operator is unused and has no side effects. + = fn(); + ~ +!!! error TS2809: Declaration or statement expected. This '=' follows a block of statements, so if you intended to write a destructuring assignment, you might need to wrap the the whole assignment in parentheses. \ No newline at end of file diff --git a/tests/baselines/reference/destructionAssignmentError.js b/tests/baselines/reference/destructionAssignmentError.js new file mode 100644 index 00000000000..f188ccc1daa --- /dev/null +++ b/tests/baselines/reference/destructionAssignmentError.js @@ -0,0 +1,28 @@ +//// [destructionAssignmentError.ts] +declare function fn(): { a: 1, b: 2 } +let a: number; +let b: number; + +({ a, b } = fn()); +{ a, b } = fn(); + +({ a, b } = +fn()); + +{ a, b } += fn(); + +//// [destructionAssignmentError.js] +var _a, _b; +var a; +var b; +(_a = fn(), a = _a.a, b = _a.b); +{ + a, b; +} +fn(); +(_b = fn(), a = _b.a, b = _b.b); +{ + a, b; +} +fn(); diff --git a/tests/baselines/reference/destructionAssignmentError.symbols b/tests/baselines/reference/destructionAssignmentError.symbols new file mode 100644 index 00000000000..82650703289 --- /dev/null +++ b/tests/baselines/reference/destructionAssignmentError.symbols @@ -0,0 +1,36 @@ +=== tests/cases/compiler/destructionAssignmentError.ts === +declare function fn(): { a: 1, b: 2 } +>fn : Symbol(fn, Decl(destructionAssignmentError.ts, 0, 0)) +>a : Symbol(a, Decl(destructionAssignmentError.ts, 0, 24)) +>b : Symbol(b, Decl(destructionAssignmentError.ts, 0, 30)) + +let a: number; +>a : Symbol(a, Decl(destructionAssignmentError.ts, 1, 3)) + +let b: number; +>b : Symbol(b, Decl(destructionAssignmentError.ts, 2, 3)) + +({ a, b } = fn()); +>a : Symbol(a, Decl(destructionAssignmentError.ts, 4, 2)) +>b : Symbol(b, Decl(destructionAssignmentError.ts, 4, 5)) +>fn : Symbol(fn, Decl(destructionAssignmentError.ts, 0, 0)) + +{ a, b } = fn(); +>a : Symbol(a, Decl(destructionAssignmentError.ts, 1, 3)) +>b : Symbol(b, Decl(destructionAssignmentError.ts, 2, 3)) +>fn : Symbol(fn, Decl(destructionAssignmentError.ts, 0, 0)) + +({ a, b } = +>a : Symbol(a, Decl(destructionAssignmentError.ts, 7, 2)) +>b : Symbol(b, Decl(destructionAssignmentError.ts, 7, 5)) + +fn()); +>fn : Symbol(fn, Decl(destructionAssignmentError.ts, 0, 0)) + +{ a, b } +>a : Symbol(a, Decl(destructionAssignmentError.ts, 1, 3)) +>b : Symbol(b, Decl(destructionAssignmentError.ts, 2, 3)) + += fn(); +>fn : Symbol(fn, Decl(destructionAssignmentError.ts, 0, 0)) + diff --git a/tests/baselines/reference/destructionAssignmentError.types b/tests/baselines/reference/destructionAssignmentError.types new file mode 100644 index 00000000000..94cbd3f792f --- /dev/null +++ b/tests/baselines/reference/destructionAssignmentError.types @@ -0,0 +1,48 @@ +=== tests/cases/compiler/destructionAssignmentError.ts === +declare function fn(): { a: 1, b: 2 } +>fn : () => { a: 1; b: 2;} +>a : 1 +>b : 2 + +let a: number; +>a : number + +let b: number; +>b : number + +({ a, b } = fn()); +>({ a, b } = fn()) : { a: 1; b: 2; } +>{ a, b } = fn() : { a: 1; b: 2; } +>{ a, b } : { a: number; b: number; } +>a : number +>b : number +>fn() : { a: 1; b: 2; } +>fn : () => { a: 1; b: 2; } + +{ a, b } = fn(); +>a, b : number +>a : number +>b : number +>fn() : { a: 1; b: 2; } +>fn : () => { a: 1; b: 2; } + +({ a, b } = +>({ a, b } =fn()) : { a: 1; b: 2; } +>{ a, b } =fn() : { a: 1; b: 2; } +>{ a, b } : { a: number; b: number; } +>a : number +>b : number + +fn()); +>fn() : { a: 1; b: 2; } +>fn : () => { a: 1; b: 2; } + +{ a, b } +>a, b : number +>a : number +>b : number + += fn(); +>fn() : { a: 1; b: 2; } +>fn : () => { a: 1; b: 2; } + diff --git a/tests/cases/compiler/destructionAssignmentError.ts b/tests/cases/compiler/destructionAssignmentError.ts new file mode 100644 index 00000000000..6845391d659 --- /dev/null +++ b/tests/cases/compiler/destructionAssignmentError.ts @@ -0,0 +1,12 @@ +declare function fn(): { a: 1, b: 2 } +let a: number; +let b: number; + +({ a, b } = fn()); +{ a, b } = fn(); + +({ a, b } = +fn()); + +{ a, b } += fn(); \ No newline at end of file