From e9764f3e2fe3f7b6341c9a4381f0dcd23548338e Mon Sep 17 00:00:00 2001 From: Milos Djermanovic Date: Thu, 2 Sep 2021 18:14:16 +0200 Subject: [PATCH] Fix: no-undef-init should not apply to class fields (refs #14857) (#14994) * Fix: no-undef-init should not apply to class fields (refs #14857) * update docs --- docs/rules/no-undef-init.md | 25 +++++++++-- lib/rules/no-undef-init.js | 72 ++++---------------------------- tests/lib/rules/no-undef-init.js | 32 +++----------- 3 files changed, 35 insertions(+), 94 deletions(-) diff --git a/docs/rules/no-undef-init.md b/docs/rules/no-undef-init.md index 10db58c17b0..0cf99c19296 100644 --- a/docs/rules/no-undef-init.md +++ b/docs/rules/no-undef-init.md @@ -19,13 +19,12 @@ It's considered a best practice to avoid initializing variables to `undefined`. ## Rule Details -This rule aims to eliminate variable declarations that initialize to `undefined`. +This rule aims to eliminate `var` and `let` variable declarations that initialize to `undefined`. Examples of **incorrect** code for this rule: ```js /*eslint no-undef-init: "error"*/ -/*eslint-env es6*/ var foo = undefined; let bar = undefined; @@ -35,11 +34,29 @@ Examples of **correct** code for this rule: ```js /*eslint no-undef-init: "error"*/ -/*eslint-env es6*/ var foo; let bar; -const baz = undefined; +``` + +Please note that this rule does not check `const` declarations, destructuring patterns, function parameters, and class fields. + +Examples of additional **correct** code for this rule: + +```js +/*eslint no-undef-init: "error"*/ + +const foo = undefined; + +let { bar = undefined } = baz; + +[quux = undefined] = quuux; + +(foo = undefined) => {}; + +class Foo { + bar = undefined; +} ``` ## When Not To Use It diff --git a/lib/rules/no-undef-init.js b/lib/rules/no-undef-init.js index 3252fbb250f..7298d344941 100644 --- a/lib/rules/no-undef-init.js +++ b/lib/rules/no-undef-init.js @@ -33,91 +33,37 @@ module.exports = { const sourceCode = context.getSourceCode(); - /** - * Get the node of init target. - * @param {ASTNode} node The node to get. - * @throws {Error} (Unreachable.) - * @returns {ASTNode} The node of init target. - */ - function getIdNode(node) { - switch (node.type) { - case "VariableDeclarator": - return node.id; - case "PropertyDefinition": - return node.key; - default: - throw new Error("unreachable"); - } - } - - /** - * Get the node of init value. - * @param {ASTNode} node The node to get. - * @throws {Error} (Unreachable.) - * @returns {ASTNode} The node of init value. - */ - function getInitNode(node) { - switch (node.type) { - case "VariableDeclarator": - return node.init; - case "PropertyDefinition": - return node.value; - default: - throw new Error("unreachable"); - } - } - - /** - * Get the parent kind of the node. - * @param {ASTNode} node The node to get. - * @throws {Error} (Unreachable.) - * @returns {string} The parent kind. - */ - function getParentKind(node) { - switch (node.type) { - case "VariableDeclarator": - return node.parent.kind; - case "PropertyDefinition": - return "field"; - default: - throw new Error("unreachable"); - } - } - return { - "VariableDeclarator, PropertyDefinition"(node) { - const idNode = getIdNode(node), - name = sourceCode.getText(idNode), - initNode = getInitNode(node), - initIsUndefined = initNode && initNode.type === "Identifier" && initNode.name === "undefined", - parentKind = getParentKind(node), + VariableDeclarator(node) { + const name = sourceCode.getText(node.id), + init = node.init && node.init.name, scope = context.getScope(), undefinedVar = astUtils.getVariableByName(scope, "undefined"), shadowed = undefinedVar && undefinedVar.defs.length > 0, - lastToken = sourceCode.getLastToken(node, astUtils.isNotSemicolonToken); + lastToken = sourceCode.getLastToken(node); - if (initIsUndefined && parentKind !== "const" && !shadowed) { + if (init === "undefined" && node.parent.kind !== "const" && !shadowed) { context.report({ node, messageId: "unnecessaryUndefinedInit", data: { name }, fix(fixer) { - if (parentKind === "var") { + if (node.parent.kind === "var") { return null; } - if (idNode.type === "ArrayPattern" || idNode.type === "ObjectPattern") { + if (node.id.type === "ArrayPattern" || node.id.type === "ObjectPattern") { // Don't fix destructuring assignment to `undefined`. return null; } - if (sourceCode.commentsExistBetween(idNode, lastToken)) { + if (sourceCode.commentsExistBetween(node.id, lastToken)) { return null; } - return fixer.removeRange([idNode.range[1], lastToken.range[1]]); + return fixer.removeRange([node.id.range[1], node.range[1]]); } }); } diff --git a/tests/lib/rules/no-undef-init.js b/tests/lib/rules/no-undef-init.js index 69d0566caf8..7f134ba0b3a 100644 --- a/tests/lib/rules/no-undef-init.js +++ b/tests/lib/rules/no-undef-init.js @@ -22,7 +22,11 @@ ruleTester.run("no-undef-init", rule, { valid: [ "var a;", { code: "const foo = undefined", parserOptions: { ecmaVersion: 6 } }, - "var undefined = 5; var foo = undefined;" + "var undefined = 5; var foo = undefined;", + + // doesn't apply to class fields + { code: "class C { field = undefined; }", parserOptions: { ecmaVersion: 2022 } } + ], invalid: [ { @@ -148,32 +152,6 @@ ruleTester.run("no-undef-init", rule, { output: "let a//comment\n, b;", parserOptions: { ecmaVersion: 6 }, errors: [{ messageId: "unnecessaryUndefinedInit", data: { name: "a" }, type: "VariableDeclarator" }] - }, - - // Class fields - { - code: "class C { field = undefined; }", - output: "class C { field; }", - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: "unnecessaryUndefinedInit", data: { name: "field" }, type: "PropertyDefinition" }] - }, - { - code: "class C { field = undefined }", - output: "class C { field }", - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: "unnecessaryUndefinedInit", data: { name: "field" }, type: "PropertyDefinition" }] - }, - { - code: "class C { #field = undefined; }", - output: "class C { #field; }", - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: "unnecessaryUndefinedInit", data: { name: "#field" }, type: "PropertyDefinition" }] - }, - { - code: "class C { '#field' = undefined; }", - output: "class C { '#field'; }", - parserOptions: { ecmaVersion: 2022 }, - errors: [{ messageId: "unnecessaryUndefinedInit", data: { name: "'#field'" }, type: "PropertyDefinition" }] } ] });