From d565879f73a45bc94b4b72f5d70507825ee5c46e Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 6 Jun 2022 23:18:47 +0800 Subject: [PATCH 1/6] Forbid `TSEmptyBodyFunctionExpression` in `ObjectExpression` --- src/language-js/parse/postprocess/index.js | 17 ++- .../parse/postprocess/typescript.js | 3 +- .../invalid/__snapshots__/jsfmt.spec.js.snap | 144 ++++++++++++++++++ .../format/misc/errors/invalid/jsfmt.spec.js | 8 +- 4 files changed, 169 insertions(+), 3 deletions(-) diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index 3b3260bc63ae..d63e23d4e2f1 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -5,7 +5,10 @@ const isTsKeywordType = require("../../utils/is-ts-keyword-type.js"); const isTypeCastComment = require("../../utils/is-type-cast-comment.js"); const getLast = require("../../../utils/get-last.js"); const visitNode = require("./visit-node.js"); -const { throwErrorForInvalidNodes } = require("./typescript.js"); +const { + throwErrorForInvalidNodes, + throwSyntaxError, +} = require("./typescript.js"); function postprocess(ast, options) { if ( @@ -102,6 +105,18 @@ function postprocess(ast, options) { }; } break; + case "ObjectExpression": + // #12963 + if (options.parser === "typescript") { + const invalidNode = node.properties.find( + (property) => + property.type === "Property" && + property.value.type === "TSEmptyBodyFunctionExpression" + ); + throwSyntaxError(invalidNode.value, "Unexpected token."); + } + break; + case "SequenceExpression": { // Babel (unlike other parsers) includes spaces and comments in the range. Let's unify this. const lastExpression = getLast(node.expressions); diff --git a/src/language-js/parse/postprocess/typescript.js b/src/language-js/parse/postprocess/typescript.js index 6817084abd77..d7439d475e75 100644 --- a/src/language-js/parse/postprocess/typescript.js +++ b/src/language-js/parse/postprocess/typescript.js @@ -73,9 +73,10 @@ function throwErrorForInvalidNodes(ast, options) { if (esTreeNode !== node) { return; } + throwErrorForInvalidDecorator(tsNode, esTreeNode, tsNodeToESTreeNodeMap); throwErrorForInvalidAbstractProperty(tsNode, esTreeNode); }); } -module.exports = { throwErrorForInvalidNodes }; +module.exports = { throwErrorForInvalidNodes, throwSyntaxError }; diff --git a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap index 62bb784b9f3a..47d58e9269b8 100644 --- a/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap +++ b/tests/format/misc/errors/invalid/__snapshots__/jsfmt.spec.js.snap @@ -156,92 +156,236 @@ exports[`snippet: #1 [typescript] format 1`] = ` | ^" `; +exports[`snippet: #2 [acorn] format 1`] = ` +"Unexpected token (1:13) +> 1 | ({ method() }) + | ^" +`; + exports[`snippet: #2 [babel] format 1`] = ` +"Unexpected token, expected "{" (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [babel] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a = 1) = 2 | ^" `; exports[`snippet: #2 [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [babel-flow] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a = 1) = 2 | ^" `; exports[`snippet: #2 [babel-ts] format 1`] = ` +"Unexpected token, expected "{" (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [babel-ts] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a = 1) = 2 | ^" `; +exports[`snippet: #2 [espree] format 1`] = ` +"Unexpected token } (1:13) +> 1 | ({ method() }) + | ^" +`; + exports[`snippet: #2 [flow] format 1`] = ` +"Unexpected token \`}\`, expected the token \`{\` (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [flow] format 2`] = ` "Invalid left-hand side in assignment (1:2) > 1 | (a = 1) = 2 | ^^^^^" `; exports[`snippet: #2 [meriyah] format 1`] = ` +"Expected '{' (1:13) +> 1 | ({ method() }) + | ^" +`; + +exports[`snippet: #2 [meriyah] format 2`] = ` "Invalid left-hand side in assignment (1:9) > 1 | (a = 1) = 2 | ^" `; +exports[`snippet: #2 [typescript] format 1`] = ` +"Unexpected token. (1:10) +> 1 | ({ method() }) + | ^^" +`; + +exports[`snippet: #3 [acorn] format 1`] = ` +"Unexpected token (1:15) +> 1 | ({ method({}) }) + | ^" +`; + exports[`snippet: #3 [babel] format 1`] = ` +"Unexpected token, expected "{" (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [babel] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a += b) = 1 | ^" `; exports[`snippet: #3 [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [babel-flow] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a += b) = 1 | ^" `; exports[`snippet: #3 [babel-ts] format 1`] = ` +"Unexpected token, expected "{" (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [babel-ts] format 2`] = ` "Invalid parenthesized assignment pattern. (1:1) > 1 | (a += b) = 1 | ^" `; +exports[`snippet: #3 [espree] format 1`] = ` +"Unexpected token } (1:15) +> 1 | ({ method({}) }) + | ^" +`; + exports[`snippet: #3 [flow] format 1`] = ` +"Unexpected token \`}\`, expected the token \`{\` (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [flow] format 2`] = ` "Invalid left-hand side in assignment (1:2) > 1 | (a += b) = 1 | ^^^^^^" `; exports[`snippet: #3 [meriyah] format 1`] = ` +"Expected '{' (1:15) +> 1 | ({ method({}) }) + | ^" +`; + +exports[`snippet: #3 [meriyah] format 2`] = ` "Invalid left-hand side in assignment (1:10) > 1 | (a += b) = 1 | ^" `; +exports[`snippet: #3 [typescript] format 1`] = ` +"Unexpected token. (1:10) +> 1 | ({ method({}) }) + | ^^^^" +`; + +exports[`snippet: #4 [acorn] format 1`] = ` +"Unexpected token (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + exports[`snippet: #4 [babel] format 1`] = ` +"Unexpected token, expected "{" (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [babel] format 2`] = ` "Invalid left-hand side in parenthesized expression. (1:2) > 1 | (a = b) += 1 | ^" `; exports[`snippet: #4 [babel-flow] format 1`] = ` +"Unexpected token, expected "{" (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [babel-flow] format 2`] = ` "Invalid left-hand side in parenthesized expression. (1:2) > 1 | (a = b) += 1 | ^" `; exports[`snippet: #4 [babel-ts] format 1`] = ` +"Unexpected token, expected "{" (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [babel-ts] format 2`] = ` "Invalid left-hand side in parenthesized expression. (1:2) > 1 | (a = b) += 1 | ^" `; +exports[`snippet: #4 [espree] format 1`] = ` +"Unexpected token } (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + exports[`snippet: #4 [flow] format 1`] = ` +"Unexpected token \`}\`, expected the token \`{\` (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [flow] format 2`] = ` "Invalid left-hand side in assignment (1:2) > 1 | (a = b) += 1 | ^^^^^" `; exports[`snippet: #4 [meriyah] format 1`] = ` +"Expected '{' (1:23) +> 1 | ({ method(parameter,) }) + | ^" +`; + +exports[`snippet: #4 [meriyah] format 2`] = ` "Invalid left-hand side in assignment (1:10) > 1 | (a = b) += 1 | ^" `; + +exports[`snippet: #4 [typescript] format 1`] = ` +"Unexpected token. (1:10) +> 1 | ({ method(parameter,) }) + | ^^^^^^^^^^^^" +`; diff --git a/tests/format/misc/errors/invalid/jsfmt.spec.js b/tests/format/misc/errors/invalid/jsfmt.spec.js index bdc6df385284..fa83db0112cb 100644 --- a/tests/format/misc/errors/invalid/jsfmt.spec.js +++ b/tests/format/misc/errors/invalid/jsfmt.spec.js @@ -1,7 +1,13 @@ run_spec( { dirname: __dirname, - snippets: ["for each (a in b) {}", "class switch() {}"], + snippets: [ + "for each (a in b) {}", + "class switch() {}", + "({ method() })", + "({ method({}) })", + "({ method(parameter,) })", + ], }, [ "babel", From 0ac5ac1c2511f4f740ff24cbff0b087300209f74 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 6 Jun 2022 23:28:50 +0800 Subject: [PATCH 2/6] Rename variable --- src/language-js/parse/postprocess/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index d63e23d4e2f1..c3c476fe0257 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -108,12 +108,12 @@ function postprocess(ast, options) { case "ObjectExpression": // #12963 if (options.parser === "typescript") { - const invalidNode = node.properties.find( + const invalidProperty = node.properties.find( (property) => property.type === "Property" && property.value.type === "TSEmptyBodyFunctionExpression" ); - throwSyntaxError(invalidNode.value, "Unexpected token."); + throwSyntaxError(invalidProperty.value, "Unexpected token."); } break; From bbc6afb066053781f08d0e175613803c382ed50d Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 6 Jun 2022 23:35:17 +0800 Subject: [PATCH 3/6] Add changelog --- changelog_unreleased/typescript/12982.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 changelog_unreleased/typescript/12982.md diff --git a/changelog_unreleased/typescript/12982.md b/changelog_unreleased/typescript/12982.md new file mode 100644 index 000000000000..444de10470b1 --- /dev/null +++ b/changelog_unreleased/typescript/12982.md @@ -0,0 +1,15 @@ +#### Stop parsing invalid code (#12980 by @fisker) + + +```tsx +// Input +const object = ({ methodName() }); + +// Prettier stable +const object = { methodName(); }; + +// Prettier main +SyntaxError: Unexpected token. (1:28) +> 1 | const object = { methodName(); }; + | ^^^ +``` From 55d3a99e2b4d61b7576478ebe01658d64006e3b4 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 6 Jun 2022 23:37:47 +0800 Subject: [PATCH 4/6] Fix --- changelog_unreleased/typescript/12982.md | 6 +++--- src/language-js/parse/postprocess/index.js | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/changelog_unreleased/typescript/12982.md b/changelog_unreleased/typescript/12982.md index 444de10470b1..9fb75032bcfe 100644 --- a/changelog_unreleased/typescript/12982.md +++ b/changelog_unreleased/typescript/12982.md @@ -9,7 +9,7 @@ const object = ({ methodName() }); const object = { methodName(); }; // Prettier main -SyntaxError: Unexpected token. (1:28) -> 1 | const object = { methodName(); }; - | ^^^ +SyntaxError: Unexpected token. (1:29) +> 1 | const object = ({ methodName() }); + | ^^ ``` diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index c3c476fe0257..cf4231d998c2 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -113,7 +113,9 @@ function postprocess(ast, options) { property.type === "Property" && property.value.type === "TSEmptyBodyFunctionExpression" ); - throwSyntaxError(invalidProperty.value, "Unexpected token."); + if (invalidProperty) { + throwSyntaxError(invalidProperty.value, "Unexpected token."); + } } break; From 7e34a701a92d8497ac2a0a98de5c488eb47f08fa Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Mon, 6 Jun 2022 23:41:12 +0800 Subject: [PATCH 5/6] Fix PR number --- changelog_unreleased/typescript/12982.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog_unreleased/typescript/12982.md b/changelog_unreleased/typescript/12982.md index 9fb75032bcfe..5be97d595de7 100644 --- a/changelog_unreleased/typescript/12982.md +++ b/changelog_unreleased/typescript/12982.md @@ -1,4 +1,4 @@ -#### Stop parsing invalid code (#12980 by @fisker) +#### Stop parsing invalid code (#12982 by @fisker) ```tsx From 498edd37ed81b8d7f4d3563121f4a79bb5543612 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 12 Jun 2022 12:18:26 +0800 Subject: [PATCH 6/6] Extract `throwSyntaxError` --- src/language-js/parse/postprocess/index.js | 6 ++---- .../parse/postprocess/throw-syntax-error.js | 12 ++++++++++++ src/language-js/parse/postprocess/typescript.js | 12 ++---------- 3 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 src/language-js/parse/postprocess/throw-syntax-error.js diff --git a/src/language-js/parse/postprocess/index.js b/src/language-js/parse/postprocess/index.js index cf4231d998c2..62959af5b16e 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -5,10 +5,8 @@ const isTsKeywordType = require("../../utils/is-ts-keyword-type.js"); const isTypeCastComment = require("../../utils/is-type-cast-comment.js"); const getLast = require("../../../utils/get-last.js"); const visitNode = require("./visit-node.js"); -const { - throwErrorForInvalidNodes, - throwSyntaxError, -} = require("./typescript.js"); +const { throwErrorForInvalidNodes } = require("./typescript.js"); +const throwSyntaxError = require("./throw-syntax-error.js"); function postprocess(ast, options) { if ( diff --git a/src/language-js/parse/postprocess/throw-syntax-error.js b/src/language-js/parse/postprocess/throw-syntax-error.js new file mode 100644 index 000000000000..0505aba379c4 --- /dev/null +++ b/src/language-js/parse/postprocess/throw-syntax-error.js @@ -0,0 +1,12 @@ +"use strict"; +const createError = require("../../../common/parser-create-error.js"); + +function throwSyntaxError(node, message) { + const { start, end } = node.loc; + throw createError(message, { + start: { line: start.line, column: start.column + 1 }, + end: { line: end.line, column: end.column + 1 }, + }); +} + +module.exports = throwSyntaxError; diff --git a/src/language-js/parse/postprocess/typescript.js b/src/language-js/parse/postprocess/typescript.js index d7439d475e75..aea24d9dde46 100644 --- a/src/language-js/parse/postprocess/typescript.js +++ b/src/language-js/parse/postprocess/typescript.js @@ -1,15 +1,7 @@ "use strict"; -const createError = require("../../../common/parser-create-error.js"); const visitNode = require("./visit-node.js"); - -function throwSyntaxError(node, message) { - const { start, end } = node.loc; - throw createError(message, { - start: { line: start.line, column: start.column + 1 }, - end: { line: end.line, column: end.column + 1 }, - }); -} +const throwSyntaxError = require("./throw-syntax-error.js"); // Invalid decorators are removed since `@typescript-eslint/typescript-estree` v4 // https://github.com/typescript-eslint/typescript-eslint/pull/2375 @@ -79,4 +71,4 @@ function throwErrorForInvalidNodes(ast, options) { }); } -module.exports = { throwErrorForInvalidNodes, throwSyntaxError }; +module.exports = { throwErrorForInvalidNodes };