From 6c8f97e0e87baafe981b1e5e58496458672b19b6 Mon Sep 17 00:00:00 2001 From: fisker Cheung Date: Tue, 14 Jun 2022 12:33:44 +0800 Subject: [PATCH] Throw errors for invalid `TSEmptyBodyFunctionExpression` node (#12982) --- changelog_unreleased/typescript/12982.md | 15 ++ src/language-js/parse/postprocess/index.js | 15 ++ .../parse/postprocess/throw-syntax-error.js | 12 ++ .../parse/postprocess/typescript.js | 11 +- .../invalid/__snapshots__/jsfmt.spec.js.snap | 144 ++++++++++++++++++ .../format/misc/errors/invalid/jsfmt.spec.js | 8 +- 6 files changed, 195 insertions(+), 10 deletions(-) create mode 100644 changelog_unreleased/typescript/12982.md create mode 100644 src/language-js/parse/postprocess/throw-syntax-error.js diff --git a/changelog_unreleased/typescript/12982.md b/changelog_unreleased/typescript/12982.md new file mode 100644 index 000000000000..5be97d595de7 --- /dev/null +++ b/changelog_unreleased/typescript/12982.md @@ -0,0 +1,15 @@ +#### Stop parsing invalid code (#12982 by @fisker) + + +```tsx +// Input +const object = ({ methodName() }); + +// Prettier stable +const object = { methodName(); }; + +// Prettier main +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 3b3260bc63ae..62959af5b16e 100644 --- a/src/language-js/parse/postprocess/index.js +++ b/src/language-js/parse/postprocess/index.js @@ -6,6 +6,7 @@ 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 throwSyntaxError = require("./throw-syntax-error.js"); function postprocess(ast, options) { if ( @@ -102,6 +103,20 @@ function postprocess(ast, options) { }; } break; + case "ObjectExpression": + // #12963 + if (options.parser === "typescript") { + const invalidProperty = node.properties.find( + (property) => + property.type === "Property" && + property.value.type === "TSEmptyBodyFunctionExpression" + ); + if (invalidProperty) { + throwSyntaxError(invalidProperty.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/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 6817084abd77..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 @@ -73,6 +65,7 @@ function throwErrorForInvalidNodes(ast, options) { if (esTreeNode !== node) { return; } + throwErrorForInvalidDecorator(tsNode, esTreeNode, tsNodeToESTreeNodeMap); throwErrorForInvalidAbstractProperty(tsNode, esTreeNode); }); 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 b2c981a4b24c..851e810b27d3 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",