From d2239a1fdec452e24ede04e990d16d42516fa538 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Thu, 19 Nov 2020 00:18:44 +0200 Subject: [PATCH] Fix: no-useless-constructor crash on bodyless constructor (fixes #13830) (#13842) --- lib/rules/no-useless-constructor.js | 8 + .../typescript-parsers/declare-class.js | 418 ++++++++++++++++++ tests/lib/rules/no-useless-constructor.js | 6 +- 3 files changed, 431 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/parsers/typescript-parsers/declare-class.js diff --git a/lib/rules/no-useless-constructor.js b/lib/rules/no-useless-constructor.js index 4c34aeda715..baabe7ec80f 100644 --- a/lib/rules/no-useless-constructor.js +++ b/lib/rules/no-useless-constructor.js @@ -162,6 +162,14 @@ module.exports = { return; } + /* + * Prevent crashing on parsers which do not require class constructor + * to have a body, e.g. typescript and flow + */ + if (!node.value.body) { + return; + } + const body = node.value.body.body; const ctorParams = node.value.params; const superClass = node.parent.parent.superClass; diff --git a/tests/fixtures/parsers/typescript-parsers/declare-class.js b/tests/fixtures/parsers/typescript-parsers/declare-class.js new file mode 100644 index 00000000000..4cd91b4182d --- /dev/null +++ b/tests/fixtures/parsers/typescript-parsers/declare-class.js @@ -0,0 +1,418 @@ +"use strict"; + +/* + * Parsed on astexplorer.net using @typescript-eslint/parser@4.1.0 + * + * Source: + * declare class A { constructor(options: any); } + */ + +exports.parse = () => ({ + "type": "Program", + "body": [ + { + "type": "ClassDeclaration", + "id": { + "type": "Identifier", + "name": "A", + "range": [ + 14, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + "body": { + "type": "ClassBody", + "body": [ + { + "type": "MethodDefinition", + "key": { + "type": "Identifier", + "name": "constructor", + "range": [ + 18, + 29 + ], + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 29 + } + } + }, + "value": { + "type": "TSEmptyBodyFunctionExpression", + "id": null, + "params": [ + { + "type": "Identifier", + "name": "options", + "range": [ + 30, + 42 + ], + "loc": { + "start": { + "line": 1, + "column": 30 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "typeAnnotation": { + "type": "TSTypeAnnotation", + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 42 + } + }, + "range": [ + 37, + 42 + ], + "typeAnnotation": { + "type": "TSAnyKeyword", + "range": [ + 39, + 42 + ], + "loc": { + "start": { + "line": 1, + "column": 39 + }, + "end": { + "line": 1, + "column": 42 + } + } + } + } + } + ], + "generator": false, + "expression": false, + "async": false, + "body": null, + "range": [ + 29, + 44 + ], + "loc": { + "start": { + "line": 1, + "column": 29 + }, + "end": { + "line": 1, + "column": 44 + } + } + }, + "computed": false, + "static": false, + "kind": "constructor", + "range": [ + 18, + 44 + ], + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 44 + } + } + } + ], + "range": [ + 16, + 46 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 46 + } + } + }, + "superClass": null, + "range": [ + 0, + 46 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 46 + } + }, + "declare": true + } + ], + "sourceType": "module", + "range": [ + 0, + 46 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 46 + } + }, + "tokens": [ + { + "type": "Identifier", + "value": "declare", + "range": [ + 0, + 7 + ], + "loc": { + "start": { + "line": 1, + "column": 0 + }, + "end": { + "line": 1, + "column": 7 + } + } + }, + { + "type": "Keyword", + "value": "class", + "range": [ + 8, + 13 + ], + "loc": { + "start": { + "line": 1, + "column": 8 + }, + "end": { + "line": 1, + "column": 13 + } + } + }, + { + "type": "Identifier", + "value": "A", + "range": [ + 14, + 15 + ], + "loc": { + "start": { + "line": 1, + "column": 14 + }, + "end": { + "line": 1, + "column": 15 + } + } + }, + { + "type": "Punctuator", + "value": "{", + "range": [ + 16, + 17 + ], + "loc": { + "start": { + "line": 1, + "column": 16 + }, + "end": { + "line": 1, + "column": 17 + } + } + }, + { + "type": "Identifier", + "value": "constructor", + "range": [ + 18, + 29 + ], + "loc": { + "start": { + "line": 1, + "column": 18 + }, + "end": { + "line": 1, + "column": 29 + } + } + }, + { + "type": "Punctuator", + "value": "(", + "range": [ + 29, + 30 + ], + "loc": { + "start": { + "line": 1, + "column": 29 + }, + "end": { + "line": 1, + "column": 30 + } + } + }, + { + "type": "Identifier", + "value": "options", + "range": [ + 30, + 37 + ], + "loc": { + "start": { + "line": 1, + "column": 30 + }, + "end": { + "line": 1, + "column": 37 + } + } + }, + { + "type": "Punctuator", + "value": ":", + "range": [ + 37, + 38 + ], + "loc": { + "start": { + "line": 1, + "column": 37 + }, + "end": { + "line": 1, + "column": 38 + } + } + }, + { + "type": "Identifier", + "value": "any", + "range": [ + 39, + 42 + ], + "loc": { + "start": { + "line": 1, + "column": 39 + }, + "end": { + "line": 1, + "column": 42 + } + } + }, + { + "type": "Punctuator", + "value": ")", + "range": [ + 42, + 43 + ], + "loc": { + "start": { + "line": 1, + "column": 42 + }, + "end": { + "line": 1, + "column": 43 + } + } + }, + { + "type": "Punctuator", + "value": ";", + "range": [ + 43, + 44 + ], + "loc": { + "start": { + "line": 1, + "column": 43 + }, + "end": { + "line": 1, + "column": 44 + } + } + }, + { + "type": "Punctuator", + "value": "}", + "range": [ + 45, + 46 + ], + "loc": { + "start": { + "line": 1, + "column": 45 + }, + "end": { + "line": 1, + "column": 46 + } + } + } + ], + "comments": [] +}); \ No newline at end of file diff --git a/tests/lib/rules/no-useless-constructor.js b/tests/lib/rules/no-useless-constructor.js index 039a40efb5a..e42a6b267e8 100644 --- a/tests/lib/rules/no-useless-constructor.js +++ b/tests/lib/rules/no-useless-constructor.js @@ -36,7 +36,11 @@ ruleTester.run("no-useless-constructor", rule, { "class A extends B { constructor(foo, bar){ super(foo); } }", "class A extends B { constructor(test) { super(); } }", "class A extends B { constructor() { foo; } }", - "class A extends B { constructor(foo, bar) { super(bar); } }" + "class A extends B { constructor(foo, bar) { super(bar); } }", + { + code: "declare class A { constructor(options: any); }", + parser: require.resolve("../../fixtures/parsers/typescript-parsers/declare-class") + } ], invalid: [ {