From f56c52d4fc9d5d0c2c39d954bf68973d9458d72c Mon Sep 17 00:00:00 2001 From: fisker Date: Thu, 9 Mar 2023 13:57:20 +0800 Subject: [PATCH 1/3] feat: throw errors for invalid object properties --- .../_error_/missing-getter-body/fixture.ts | 1 + .../snapshots/1-TSESTree-Error.shot | 8 ++++++ .../snapshots/2-Babel-Error.shot | 3 ++ .../snapshots/3-Alignment-Error.shot | 3 ++ .../_error_/missing-method-body/fixture.ts | 1 + .../snapshots/1-TSESTree-Error.shot | 8 ++++++ .../snapshots/2-Babel-Error.shot | 3 ++ .../snapshots/3-Alignment-Error.shot | 3 ++ .../_error_/missing-setter-body/fixture.ts | 1 + .../snapshots/1-TSESTree-Error.shot | 8 ++++++ .../snapshots/2-Babel-Error.shot | 3 ++ .../snapshots/3-Alignment-Error.shot | 3 ++ packages/typescript-estree/src/convert.ts | 28 +++++++++++++++---- 13 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/fixture.ts create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/3-Alignment-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/fixture.ts create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/3-Alignment-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/fixture.ts create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/2-Babel-Error.shot create mode 100644 packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/3-Alignment-Error.shot diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/fixture.ts b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/fixture.ts new file mode 100644 index 00000000000..876119b3990 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/fixture.ts @@ -0,0 +1 @@ +({get foo();}) diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot new file mode 100644 index 00000000000..d0f902cbffe --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-getter-body TSESTree - Error 1`] = ` +"TSError +> 1 | ({get foo();}) + | ^^^^^^^^^^ Unexpected object property value. + 2 |" +`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/2-Babel-Error.shot new file mode 100644 index 00000000000..71aabcc7954 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-getter-body Babel - Error 1`] = `[SyntaxError: Unexpected token, expected "{" (1:11)]`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/3-Alignment-Error.shot new file mode 100644 index 00000000000..bc5201f9cee --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-getter-body Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/fixture.ts b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/fixture.ts new file mode 100644 index 00000000000..433743dbdf0 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/fixture.ts @@ -0,0 +1 @@ +({method();}) diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot new file mode 100644 index 00000000000..66adc7dbc52 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-method-body TSESTree - Error 1`] = ` +"TSError +> 1 | ({method();}) + | ^^^^^^^^^ Unexpected object property value. + 2 |" +`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/2-Babel-Error.shot new file mode 100644 index 00000000000..83750d60313 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-method-body Babel - Error 1`] = `[SyntaxError: Unexpected token, expected "{" (1:10)]`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/3-Alignment-Error.shot new file mode 100644 index 00000000000..37626ca0238 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-method-body Error Alignment 1`] = `"Both errored"`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/fixture.ts b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/fixture.ts new file mode 100644 index 00000000000..7f9318e62fd --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/fixture.ts @@ -0,0 +1 @@ +({set foo(value);}) diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot new file mode 100644 index 00000000000..0b8d488766d --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-setter-body TSESTree - Error 1`] = ` +"TSError +> 1 | ({set foo(value);}) + | ^^^^^^^^^^^^^^^ Unexpected object property value. + 2 |" +`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/2-Babel-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/2-Babel-Error.shot new file mode 100644 index 00000000000..340728334fd --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/2-Babel-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-setter-body Babel - Error 1`] = `[SyntaxError: Unexpected token, expected "{" (1:16)]`; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/3-Alignment-Error.shot new file mode 100644 index 00000000000..21d1ea67346 --- /dev/null +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/3-Alignment-Error.shot @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`AST Fixtures expression ObjectExpression _error_ missing-setter-body Error Alignment 1`] = `"Both errored"`; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 43f16db875d..faa7e7fb374 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1027,12 +1027,30 @@ export class Converter { properties: node.properties.map(el => this.convertPattern(el)), typeAnnotation: undefined, }); - } else { - return this.createNode(node, { - type: AST_NODE_TYPES.ObjectExpression, - properties: node.properties.map(el => this.convertChild(el)), - }); } + + const properties: TSESTree.Property[] = []; + for (const property of node.properties) { + if ( + (property.kind === SyntaxKind.GetAccessor || + property.kind === SyntaxKind.SetAccessor || + property.kind === SyntaxKind.MethodDeclaration) && + !property.body + ) { + // TypeScript throws `'{' expected.` + this.#throwUnlessAllowInvalidAST( + property, + 'Unexpected object property value.', + ); + } + + properties.push(this.convertChild(property) as TSESTree.Property); + } + + return this.createNode(node, { + type: AST_NODE_TYPES.ObjectExpression, + properties, + }); } case SyntaxKind.PropertyAssignment: { From d3847e9fb881a0e3d7ed9babac7c714e01574fd7 Mon Sep 17 00:00:00 2001 From: fisker Date: Thu, 9 Mar 2023 14:12:22 +0800 Subject: [PATCH 2/3] Throw same error as typescript --- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- .../snapshots/1-TSESTree-Error.shot | 2 +- packages/typescript-estree/src/convert.ts | 21 ++++++++++++------- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot index d0f902cbffe..16431b24f4d 100644 --- a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-getter-body/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures expression ObjectExpression _error_ missing-getter-body TSESTree - Error 1`] = ` "TSError > 1 | ({get foo();}) - | ^^^^^^^^^^ Unexpected object property value. + | ^ '{' expected. 2 |" `; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot index 66adc7dbc52..d5072a95357 100644 --- a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-method-body/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures expression ObjectExpression _error_ missing-method-body TSESTree - Error 1`] = ` "TSError > 1 | ({method();}) - | ^^^^^^^^^ Unexpected object property value. + | ^ '{' expected. 2 |" `; diff --git a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot index 0b8d488766d..b775e8717cb 100644 --- a/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/expression/ObjectExpression/fixtures/_error_/missing-setter-body/snapshots/1-TSESTree-Error.shot @@ -3,6 +3,6 @@ exports[`AST Fixtures expression ObjectExpression _error_ missing-setter-body TSESTree - Error 1`] = ` "TSError > 1 | ({set foo(value);}) - | ^^^^^^^^^^^^^^^ Unexpected object property value. + | ^ '{' expected. 2 |" `; diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index faa7e7fb374..fd6aec2a392 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1037,11 +1037,7 @@ export class Converter { property.kind === SyntaxKind.MethodDeclaration) && !property.body ) { - // TypeScript throws `'{' expected.` - this.#throwUnlessAllowInvalidAST( - property, - 'Unexpected object property value.', - ); + this.#throwUnlessAllowInvalidAST(property.end - 1, "'{' expected."); } properties.push(this.convertChild(property) as TSESTree.Property); @@ -3093,7 +3089,7 @@ export class Converter { } #throwUnlessAllowInvalidAST( - node: ts.Node, + node: ts.Node | number, message: string, ): asserts node is never { if (!this.options.allowInvalidAST) { @@ -3101,7 +3097,16 @@ export class Converter { } } - #throwError(node: ts.Node, message: string): asserts node is never { - throw createError(message, this.ast, node.getStart(), node.getEnd()); + #throwError(node: ts.Node | number, message: string): asserts node is never { + let start; + let end; + if (typeof node === 'number') { + start = end = node; + } else { + start = node.getStart(this.ast); + end = node.getEnd(); + } + + throw createError(message, this.ast, start, end); } } From eed22052f290398d8bad5d3fd6c0273368e29320 Mon Sep 17 00:00:00 2001 From: fisker Date: Thu, 9 Mar 2023 20:27:51 +0800 Subject: [PATCH 3/3] Fix tests --- .../eslint-plugin/tests/rules/no-unsafe-assignment.test.ts | 2 +- .../tests/rules/prefer-readonly-parameter-types.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts index 8b08ec79ff9..f0d32d2ed47 100644 --- a/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unsafe-assignment.test.ts @@ -113,7 +113,7 @@ class Foo { 'const x = new Set();', 'const x = { y: 1 };', 'const x = { y = 1 };', - noFormat`const x = { y(); };`, + noFormat`const x = { y(){} };`, 'const x: { y: number } = { y: 1 };', 'const x = [...[1, 2, 3]];', 'const [{ [`x${1}`]: x }] = [{ [`x`]: 1 }] as [{ [`x`]: any }];', diff --git a/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts b/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts index 44919f60d79..649b4970050 100644 --- a/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts @@ -290,7 +290,7 @@ ruleTester.run('prefer-readonly-parameter-types', rule, { new (arg: readonly string[]): void; } `, // TSConstructSignatureDeclaration - noFormat`const x = { foo(arg: readonly string[]): void; };`, // TSEmptyBodyFunctionExpression + noFormat`class Foo { foo(arg: readonly string[]): void; };`, // TSEmptyBodyFunctionExpression 'function foo(arg: readonly string[]);', // TSDeclareFunction 'type Foo = (arg: readonly string[]) => void;', // TSFunctionType ` @@ -667,7 +667,7 @@ ruleTester.run('prefer-readonly-parameter-types', rule, { }, { // TSEmptyBodyFunctionExpression - code: noFormat`const x = { foo(arg: string[]): void; };`, + code: noFormat`class Foo { foo(arg: string[]): void; };`, errors: [ { messageId: 'shouldBeReadonly',