From 46a26e373757de446264c8165ee4e9a68fdc0ef1 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Tue, 24 Oct 2023 22:56:48 -0400 Subject: [PATCH 01/12] feat: Add suggestions to no-console Fixes #17493 --- lib/rules/no-console.js | 28 ++++++++++++++++++++++++++-- tests/lib/rules/no-console.js | 35 ++++++++++++++++++++++------------- 2 files changed, 48 insertions(+), 15 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index f257098d38b..f3671258727 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -43,8 +43,11 @@ module.exports = { } ], + hasSuggestions: true, + messages: { - unexpected: "Unexpected console statement." + unexpected: "Unexpected console statement.", + removeConsole: "Remove the console.{{ propertyName }}()." } }, @@ -102,10 +105,31 @@ module.exports = { function report(reference) { const node = reference.identifier.parent; + const propertyName = astUtils.getStaticPropertyName(node); + context.report({ node, loc: node.loc, - messageId: "unexpected" + messageId: "unexpected", + suggest: [ + { + messageId: "removeConsole", + data: { propertyName }, + fix(fixer) { + const copyOfStatementListParents = astUtils.STATEMENT_LIST_PARENTS; + + copyOfStatementListParents.delete("Program"); + + const isNotFixable = copyOfStatementListParents.has(node.parent.parent.parent.type); + + if (isNotFixable) { + return null; + } + + return fixer.removeRange(node.parent.parent.range); + } + } + ] }); } diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index d1c9176d0ff..72da99eb2af 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -40,24 +40,33 @@ ruleTester.run("no-console", rule, { invalid: [ // no options - { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.error(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.info(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.error(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.info(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "switch (a) { case 1: console.log(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "if (a) { console.warn(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "class A { static { console.info(foo) } }", parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, // one option - { code: "console.log(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.error(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.info(foo)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.warn(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "console.log(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.error(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.info(foo)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.warn(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "switch (a) { case 1: console.log(foo) }", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "if (a) { console.info(foo) }", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "class A { static { console.error(foo) } }", options: [{ allow: ["log"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, // multiple options - { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.error(foo)", options: [{ allow: ["warn", "info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.info(foo)", options: [{ allow: ["warn", "error", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.error(foo)", options: [{ allow: ["warn", "info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.info(foo)", options: [{ allow: ["warn", "error", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, + { code: "switch (a) { case 1: console.error(foo) }", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "if (a) { console.log(foo) }", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "class A { static { console.info(foo) } }", options: [{ allow: ["log", "error", "warn"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, // In case that implicit global variable of 'console' exists - { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] } + { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] } ] }); From f6c12cc8e3549100dcb59d7260390402ab320ed5 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Thu, 26 Oct 2023 16:12:25 -0400 Subject: [PATCH 02/12] Fix method of when to show safe suggestions and fix tests. --- lib/rules/no-console.js | 34 ++++++++++++++---------- tests/lib/rules/no-console.js | 50 ++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index f3671258727..af8b4e7c139 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -97,6 +97,22 @@ module.exports = { ); } + /** + * Checks if it is safe to provide suggestions for the specific node. + * @param {ASTNode} node The MemberExpression node to check. + * @returns {boolean} `true` if the node is safe to be fixed via a suggestion + */ + function isSafeToProvideSuggestions(node) { + const isInStatementListParents = astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type); + + return ( + isInStatementListParents && + node.parent.type === "CallExpression" && + node.parent.callee === node && + node.parent.parent.type === "ExpressionStatement" + ); + } + /** * Reports the given reference as a violation. * @param {eslint-scope.Reference} reference The reference to report. @@ -111,25 +127,15 @@ module.exports = { node, loc: node.loc, messageId: "unexpected", - suggest: [ - { + suggest: isSafeToProvideSuggestions(node) + ? [{ messageId: "removeConsole", data: { propertyName }, fix(fixer) { - const copyOfStatementListParents = astUtils.STATEMENT_LIST_PARENTS; - - copyOfStatementListParents.delete("Program"); - - const isNotFixable = copyOfStatementListParents.has(node.parent.parent.parent.type); - - if (isNotFixable) { - return null; - } - return fixer.removeRange(node.parent.parent.range); } - } - ] + }] + : [] }); } diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index 72da99eb2af..d30ae79c9dc 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -40,33 +40,39 @@ ruleTester.run("no-console", rule, { invalid: [ // no options - { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.error(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.info(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "switch (a) { case 1: console.log(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "if (a) { console.warn(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "class A { static { console.info(foo) } }", parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] }, + { code: "console.error(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "" }] }] }, + { code: "console.info(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "" }] }] }, + { code: "console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "" }] }] }, + { code: "switch (a) { case 1: console.log(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "switch (a) { case 1: }" }] }] }, + { code: "if (a) { console.warn(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "if (a) { }" }] }] }, + { code: "if (a) console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "foo(console.log)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "class A { static { console.info(foo) } }", parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "class A { static { } }" }] }] }, // one option - { code: "console.log(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.error(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.info(foo)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.warn(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "switch (a) { case 1: console.log(foo) }", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "if (a) { console.info(foo) }", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "class A { static { console.error(foo) } }", options: [{ allow: ["log"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "console.log(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] }, + { code: "console.error(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "" }] }] }, + { code: "console.info(foo)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "" }] }] }, + { code: "console.warn(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "" }] }] }, + { code: "switch (a) { case 1: console.log(foo) }", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "switch (a) { case 1: }" }] }] }, + { code: "if (a) { console.info(foo) }", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "if (a) { }" }] }] }, + { code: "if (a) console.info(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "foo(console.warn)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "class A { static { console.error(foo) } }", options: [{ allow: ["log"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "class A { static { } }" }] }] }, // multiple options - { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.error(foo)", options: [{ allow: ["warn", "info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.info(foo)", options: [{ allow: ["warn", "error", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] }, - { code: "switch (a) { case 1: console.error(foo) }", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "if (a) { console.log(foo) }", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "class A { static { console.info(foo) } }", options: [{ allow: ["log", "error", "warn"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] }, + { code: "console.error(foo)", options: [{ allow: ["warn", "info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "" }] }] }, + { code: "console.info(foo)", options: [{ allow: ["warn", "error", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "" }] }] }, + { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "" }] }] }, + { code: "switch (a) { case 1: console.error(foo) }", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "switch (a) { case 1: }" }] }] }, + { code: "if (a) { console.log(foo) }", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "if (a) { }" }] }] }, + { code: "if (a) console.log(foo)", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "foo(console.info)", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, + { code: "class A { static { console.info(foo) } }", options: [{ allow: ["log", "error", "warn"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "class A { static { } }" }] }] }, // In case that implicit global variable of 'console' exists - { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", output: "" }] }] } + { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] } ] }); From 1d7dc3b16891c95bbe277a4ae677b3ef5afb3904 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Fri, 27 Oct 2023 03:01:22 -0400 Subject: [PATCH 03/12] improved formatting of tests with suggestions by breaking them into multiple lines --- tests/lib/rules/no-console.js | 356 +++++++++++++++++++++++++++++++--- 1 file changed, 328 insertions(+), 28 deletions(-) diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index d30ae79c9dc..dea3cf74fd5 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -40,39 +40,339 @@ ruleTester.run("no-console", rule, { invalid: [ // no options - { code: "console.log(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] }, - { code: "console.error(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "" }] }] }, - { code: "console.info(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "" }] }] }, - { code: "console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "" }] }] }, - { code: "switch (a) { case 1: console.log(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "switch (a) { case 1: }" }] }] }, - { code: "if (a) { console.warn(foo) }", errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "if (a) { }" }] }] }, - { code: "if (a) console.warn(foo)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "foo(console.log)", errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "class A { static { console.info(foo) } }", parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "class A { static { } }" }] }] }, + { + code: "console.log(foo)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "" + }] + }] + }, + { + code: "console.error(foo)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "error" }, + output: "" + }] + }] + }, + { + code: "console.info(foo)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "" + }] + }] + }, + { + code: "console.warn(foo)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "warn" }, + output: "" + }] + }] + }, + { + code: "switch (a) { case 1: console.log(foo) }", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "switch (a) { case 1: }" + }] + }] + }, + { + code: "if (a) { console.warn(foo) }", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "warn" }, + output: "if (a) { }" + }] + }] + }, + { + code: "if (a) console.warn(foo)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression" + }] + }, + { + code: "foo(console.log)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression" + }] + }, + { + code: "class A { static { console.info(foo) } }", + parserOptions: { ecmaVersion: "latest" }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "class A { static { } }" + }] + }] + }, // one option - { code: "console.log(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] }, - { code: "console.error(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "" }] }] }, - { code: "console.info(foo)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "" }] }] }, - { code: "console.warn(foo)", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "" }] }] }, - { code: "switch (a) { case 1: console.log(foo) }", options: [{ allow: ["error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "switch (a) { case 1: }" }] }] }, - { code: "if (a) { console.info(foo) }", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "if (a) { }" }] }] }, - { code: "if (a) console.info(foo)", options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "foo(console.warn)", options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "class A { static { console.error(foo) } }", options: [{ allow: ["log"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "class A { static { } }" }] }] }, + { + code: "console.log(foo)", + options: [{ allow: ["error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "" + }] + }] + }, + { + code: "console.error(foo)", + options: [{ allow: ["warn"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "error" }, + output: "" + }] + }] + }, + { + code: "console.info(foo)", + options: [{ allow: ["log"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "" + }] + }] + }, + { + code: "console.warn(foo)", + options: [{ allow: ["error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "warn" }, + output: "" + }] + }] + }, + { + code: "switch (a) { case 1: console.log(foo) }", + options: [{ allow: ["error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "switch (a) { case 1: }" + }] + }] + }, + { + code: "if (a) { console.info(foo) }", + options: [{ allow: ["warn"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "if (a) { }" + }] + }] + }, + { + code: "if (a) console.info(foo)", + options: [{ allow: ["warn"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression" + }] + }, + { + code: "foo(console.warn)", + options: [{ allow: ["log"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression" + }] + }, + { + code: "class A { static { console.error(foo) } }", + options: [{ allow: ["log"] }], + parserOptions: { ecmaVersion: "latest" }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "error" }, + output: "class A { static { } }" + }] + }] + }, // multiple options - { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] }, - { code: "console.error(foo)", options: [{ allow: ["warn", "info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "" }] }] }, - { code: "console.info(foo)", options: [{ allow: ["warn", "error", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "" }] }] }, - { code: "console.warn(foo)", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "warn" }, output: "" }] }] }, - { code: "switch (a) { case 1: console.error(foo) }", options: [{ allow: ["info", "log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "error" }, output: "switch (a) { case 1: }" }] }] }, - { code: "if (a) { console.log(foo) }", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "if (a) { }" }] }] }, - { code: "if (a) console.log(foo)", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "foo(console.info)", options: [{ allow: ["warn", "error"] }], errors: [{ messageId: "unexpected", type: "MemberExpression" }] }, - { code: "class A { static { console.info(foo) } }", options: [{ allow: ["log", "error", "warn"] }], parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "info" }, output: "class A { static { } }" }] }] }, + { + code: "console.log(foo)", + options: [{ allow: ["warn", "info"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "" + }] + }] + }, + { + code: "console.error(foo)", + options: [{ allow: ["warn", "info", "log"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "error" }, + output: "" + }] + }] + }, + { + code: "console.info(foo)", + options: [{ allow: ["warn", "error", "log"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "" + }] + }] + }, + { + code: "console.warn(foo)", + options: [{ allow: ["info", "log"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "warn" }, + output: "" + }] + }] + }, + { + code: "switch (a) { case 1: console.error(foo) }", + options: [{ allow: ["info", "log"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "error" }, + output: "switch (a) { case 1: }" + }] + }] + }, + { + code: "if (a) { console.log(foo) }", + options: [{ allow: ["warn", "error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "if (a) { }" + }] + }] + }, + { + code: "if (a) console.log(foo)", + options: [{ allow: ["warn", "error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression" + }] + }, + { + code: "foo(console.info)", + options: [{ allow: ["warn", "error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression" + }] + }, + { + code: "class A { static { console.info(foo) } }", + options: [{ allow: ["log", "error", "warn"] }], + parserOptions: { ecmaVersion: "latest" }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "class A { static { } }" + }] + }] + }, // In case that implicit global variable of 'console' exists - { code: "console.log(foo)", env: { node: true }, errors: [{ messageId: "unexpected", type: "MemberExpression", suggestions: [{ messageId: "removeConsole", data: { propertyName: "log" }, output: "" }] }] } + { + code: "console.log(foo)", + env: { node: true }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "" + }] + }] + } ] }); From 384c3811c8e746f09fc020d1d108fce73005d362 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Fri, 27 Oct 2023 03:04:00 -0400 Subject: [PATCH 04/12] Updated function name to canProvideSuggestions. Added better description for the function. --- lib/rules/no-console.js | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index af8b4e7c139..78193f5b079 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -98,11 +98,22 @@ module.exports = { } /** - * Checks if it is safe to provide suggestions for the specific node. + * Checks if the MemberExpression node's parent.parent.parent is a + * Program, BlockStatement, StaticBlock, or SwitchCase node. This check + * is necessary to avoid providing a suggestion that might cause a syntax error. + * + * eg. if (a) console.log(b), removing console.log() here will lead to a + * syntax error. + * if (a) { console.log(b) }, removing console.log() here is acceptable. + * + * Additionally, it checks if the callee of the CallExpression node is + * the node itself. + * + * eg. foo(console.log), cannot provide a suggestion here. * @param {ASTNode} node The MemberExpression node to check. - * @returns {boolean} `true` if the node is safe to be fixed via a suggestion + * @returns {boolean} `true` if a suggestion can be provided for a node. */ - function isSafeToProvideSuggestions(node) { + function canProvideSuggestions(node) { const isInStatementListParents = astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type); return ( @@ -127,7 +138,7 @@ module.exports = { node, loc: node.loc, messageId: "unexpected", - suggest: isSafeToProvideSuggestions(node) + suggest: canProvideSuggestions(node) ? [{ messageId: "removeConsole", data: { propertyName }, From bd8de82c588801997e992a170e9cd9f9aa233d14 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Fri, 27 Oct 2023 13:00:45 -0400 Subject: [PATCH 05/12] Fixed code that fails when AST is not deep enough. --- lib/rules/no-console.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index 78193f5b079..50b2e3fc81e 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -114,13 +114,11 @@ module.exports = { * @returns {boolean} `true` if a suggestion can be provided for a node. */ function canProvideSuggestions(node) { - const isInStatementListParents = astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type); - return ( - isInStatementListParents && node.parent.type === "CallExpression" && node.parent.callee === node && - node.parent.parent.type === "ExpressionStatement" + node.parent.parent.type === "ExpressionStatement" && + astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type) ); } @@ -143,7 +141,7 @@ module.exports = { messageId: "removeConsole", data: { propertyName }, fix(fixer) { - return fixer.removeRange(node.parent.parent.range); + return fixer.remove(node.parent.parent); } }] : [] From c79833d2190311f618fe9e27ed7ab18ec62f461c Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Fri, 27 Oct 2023 13:18:52 -0400 Subject: [PATCH 06/12] Added suggestions:null for test cases that will not provide a suggestion. --- tests/lib/rules/no-console.js | 96 +++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index dea3cf74fd5..0ea02926793 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -40,6 +40,22 @@ ruleTester.run("no-console", rule, { invalid: [ // no options + { + code: "if (a) console.warn(foo)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: null + }] + }, + { + code: "foo(console.log)", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: null + }] + }, { code: "console.log(foo)", errors: [{ @@ -113,34 +129,38 @@ ruleTester.run("no-console", rule, { }] }, { - code: "if (a) console.warn(foo)", + code: "class A { static { console.info(foo) } }", + parserOptions: { ecmaVersion: "latest" }, errors: [{ messageId: "unexpected", - type: "MemberExpression" + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "info" }, + output: "class A { static { } }" + }] }] }, + + // one option { - code: "foo(console.log)", + code: "if (a) console.info(foo)", + options: [{ allow: ["warn"] }], errors: [{ messageId: "unexpected", - type: "MemberExpression" + type: "MemberExpression", + suggestions: null }] }, { - code: "class A { static { console.info(foo) } }", - parserOptions: { ecmaVersion: "latest" }, + code: "foo(console.warn)", + options: [{ allow: ["log"] }], errors: [{ messageId: "unexpected", type: "MemberExpression", - suggestions: [{ - messageId: "removeConsole", - data: { propertyName: "info" }, - output: "class A { static { } }" - }] + suggestions: null }] }, - - // one option { code: "console.log(foo)", options: [{ allow: ["error"] }], @@ -219,22 +239,6 @@ ruleTester.run("no-console", rule, { }] }] }, - { - code: "if (a) console.info(foo)", - options: [{ allow: ["warn"] }], - errors: [{ - messageId: "unexpected", - type: "MemberExpression" - }] - }, - { - code: "foo(console.warn)", - options: [{ allow: ["log"] }], - errors: [{ - messageId: "unexpected", - type: "MemberExpression" - }] - }, { code: "class A { static { console.error(foo) } }", options: [{ allow: ["log"] }], @@ -251,6 +255,24 @@ ruleTester.run("no-console", rule, { }, // multiple options + { + code: "if (a) console.log(foo)", + options: [{ allow: ["warn", "error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: null + }] + }, + { + code: "foo(console.info)", + options: [{ allow: ["warn", "error"] }], + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: null + }] + }, { code: "console.log(foo)", options: [{ allow: ["warn", "info"] }], @@ -329,22 +351,6 @@ ruleTester.run("no-console", rule, { }] }] }, - { - code: "if (a) console.log(foo)", - options: [{ allow: ["warn", "error"] }], - errors: [{ - messageId: "unexpected", - type: "MemberExpression" - }] - }, - { - code: "foo(console.info)", - options: [{ allow: ["warn", "error"] }], - errors: [{ - messageId: "unexpected", - type: "MemberExpression" - }] - }, { code: "class A { static { console.info(foo) } }", options: [{ allow: ["log", "error", "warn"] }], From a74dc8756f44f05233705fdbc7ffb4b590d520d7 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Sat, 4 Nov 2023 00:31:01 -0400 Subject: [PATCH 07/12] test to make sure console statement with semicolon is removed via suggestion --- tests/lib/rules/no-console.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index 0ea02926793..484524154ac 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -128,6 +128,18 @@ ruleTester.run("no-console", rule, { }] }] }, + { + code: "a();\nconsole.log(foo);\nb();", + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "a();\n\nb();" + }] + }] + }, { code: "class A { static { console.info(foo) } }", parserOptions: { ecmaVersion: "latest" }, From f1128039691907e91e62c06f24152b4a4df8d61b Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Mon, 6 Nov 2023 21:36:12 -0500 Subject: [PATCH 08/12] dont provide suggestions if removing console.log() will lead to ASI breaking --- lib/rules/no-console.js | 36 ++++++++++++++++++++++++++++++++++- tests/lib/rules/no-console.js | 22 +++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index 50b2e3fc81e..6baf57e5120 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -97,6 +97,39 @@ module.exports = { ); } + /** + * Checks if removing the MemberExpression node will cause ASI to + * break. + * eg. + * foo() + * console.log(); + * [1, 2, 3].forEach(a => doSomething(a)) + * + * Removing the console.log(); statement should leave two statements, but + * here the two statements will become one because [ causes continuation after + * foo(). + * @param {ASTNode} node The MemberExpression node to check. + * @returns {boolean} `true` if ASI will break after removing the MemberExpression + * node + */ + function maybeAsiHazard(node) { + const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-` + const CONTINUATION_PATTERN = /^[:;{]/u; // One of :;{ + const tokenBefore = sourceCode.getTokenBefore(node.parent.parent); + const tokenAfter = sourceCode.getTokenAfter(node.parent.parent); + + return ( + Boolean(tokenAfter) && + OPT_OUT_PATTERN.test(tokenAfter.value) && + tokenAfter.value !== "++" && + tokenAfter.value !== "--" && + Boolean(tokenBefore) && + !CONTINUATION_PATTERN.test(tokenBefore.value) && + tokenBefore.value !== "++" && + tokenBefore.value !== "--" + ); + } + /** * Checks if the MemberExpression node's parent.parent.parent is a * Program, BlockStatement, StaticBlock, or SwitchCase node. This check @@ -118,7 +151,8 @@ module.exports = { node.parent.type === "CallExpression" && node.parent.callee === node && node.parent.parent.type === "ExpressionStatement" && - astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type) + astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type) && + !maybeAsiHazard(node) ); } diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index 484524154ac..648173836d2 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -153,6 +153,28 @@ ruleTester.run("no-console", rule, { }] }] }, + { + code: "a()\nconsole.log(foo);\n[1, 2, 3].forEach(a => doSomething(a))", + parserOptions: { ecmaVersion: "latest" }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: null + }] + }, + { + code: "a();\nconsole.log(foo);\n[1, 2, 3].forEach(a => doSomething(a));", + parserOptions: { ecmaVersion: "latest" }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: [{ + messageId: "removeConsole", + data: { propertyName: "log" }, + output: "a();\n\n[1, 2, 3].forEach(a => doSomething(a));" + }] + }] + }, // one option { From 1b1dea560e521aa5b7eda63fdeeb75d6197de766 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Tue, 7 Nov 2023 01:01:28 -0500 Subject: [PATCH 09/12] missing period --- lib/rules/no-console.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index 6baf57e5120..9f24fe51830 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -110,7 +110,7 @@ module.exports = { * foo(). * @param {ASTNode} node The MemberExpression node to check. * @returns {boolean} `true` if ASI will break after removing the MemberExpression - * node + * node. */ function maybeAsiHazard(node) { const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-` From b997d5894193a60e362578112bdcd0e9df49431b Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Thu, 16 Nov 2023 19:26:09 -0500 Subject: [PATCH 10/12] renamed regexps variable names for better understanding --- lib/rules/no-console.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index 9f24fe51830..4499102f5dc 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -113,18 +113,19 @@ module.exports = { * node. */ function maybeAsiHazard(node) { - const OPT_OUT_PATTERN = /^[-[(/+`]/u; // One of [(/+-` - const CONTINUATION_PATTERN = /^[:;{]/u; // One of :;{ + const SAFE_TOKENS_BEFORE = /^[:;{]$/u; // One of :;{ + const UNSAFE_CHARS_AFTER = /^[-[(/+`]/u; // One of [(/+-` + const tokenBefore = sourceCode.getTokenBefore(node.parent.parent); const tokenAfter = sourceCode.getTokenAfter(node.parent.parent); return ( Boolean(tokenAfter) && - OPT_OUT_PATTERN.test(tokenAfter.value) && + UNSAFE_CHARS_AFTER.test(tokenAfter.value) && tokenAfter.value !== "++" && tokenAfter.value !== "--" && Boolean(tokenBefore) && - !CONTINUATION_PATTERN.test(tokenBefore.value) && + !SAFE_TOKENS_BEFORE.test(tokenBefore.value) && tokenBefore.value !== "++" && tokenBefore.value !== "--" ); From f4e4d9a7613728b06c8534538b642294fca7d69a Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Thu, 16 Nov 2023 19:43:33 -0500 Subject: [PATCH 11/12] updated passing in expressionstatement node instead of memberexpression node to maybeAsiHazard --- lib/rules/no-console.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index 4499102f5dc..24f2fa1ae09 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -98,7 +98,7 @@ module.exports = { } /** - * Checks if removing the MemberExpression node will cause ASI to + * Checks if removing the ExpressionStatement node will cause ASI to * break. * eg. * foo() @@ -108,16 +108,16 @@ module.exports = { * Removing the console.log(); statement should leave two statements, but * here the two statements will become one because [ causes continuation after * foo(). - * @param {ASTNode} node The MemberExpression node to check. - * @returns {boolean} `true` if ASI will break after removing the MemberExpression + * @param {ASTNode} node The ExpressionStatement node to check. + * @returns {boolean} `true` if ASI will break after removing the ExpressionStatement * node. */ function maybeAsiHazard(node) { const SAFE_TOKENS_BEFORE = /^[:;{]$/u; // One of :;{ const UNSAFE_CHARS_AFTER = /^[-[(/+`]/u; // One of [(/+-` - const tokenBefore = sourceCode.getTokenBefore(node.parent.parent); - const tokenAfter = sourceCode.getTokenAfter(node.parent.parent); + const tokenBefore = sourceCode.getTokenBefore(node); + const tokenAfter = sourceCode.getTokenAfter(node); return ( Boolean(tokenAfter) && @@ -153,7 +153,7 @@ module.exports = { node.parent.callee === node && node.parent.parent.type === "ExpressionStatement" && astUtils.STATEMENT_LIST_PARENTS.has(node.parent.parent.parent.type) && - !maybeAsiHazard(node) + !maybeAsiHazard(node.parent.parent) ); } From e12d3bca44fed8f4103cbdcaaff6c289be2a6d65 Mon Sep 17 00:00:00 2001 From: Joel Mathew Koshy Date: Thu, 16 Nov 2023 19:56:11 -0500 Subject: [PATCH 12/12] ++ or -- in the token before is not always safe. --- lib/rules/no-console.js | 4 +--- tests/lib/rules/no-console.js | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/rules/no-console.js b/lib/rules/no-console.js index 24f2fa1ae09..d20477c5d9a 100644 --- a/lib/rules/no-console.js +++ b/lib/rules/no-console.js @@ -125,9 +125,7 @@ module.exports = { tokenAfter.value !== "++" && tokenAfter.value !== "--" && Boolean(tokenBefore) && - !SAFE_TOKENS_BEFORE.test(tokenBefore.value) && - tokenBefore.value !== "++" && - tokenBefore.value !== "--" + !SAFE_TOKENS_BEFORE.test(tokenBefore.value) ); } diff --git a/tests/lib/rules/no-console.js b/tests/lib/rules/no-console.js index 648173836d2..d55cf5c2d58 100644 --- a/tests/lib/rules/no-console.js +++ b/tests/lib/rules/no-console.js @@ -162,6 +162,15 @@ ruleTester.run("no-console", rule, { suggestions: null }] }, + { + code: "a++\nconsole.log();\n/b/", + parserOptions: { ecmaVersion: "latest" }, + errors: [{ + messageId: "unexpected", + type: "MemberExpression", + suggestions: null + }] + }, { code: "a();\nconsole.log(foo);\n[1, 2, 3].forEach(a => doSomething(a));", parserOptions: { ecmaVersion: "latest" },