From 075ef2cf315e75b51b671c40ce9a97c66b2e4b50 Mon Sep 17 00:00:00 2001 From: Daniel Bartholomae Date: Fri, 16 Dec 2022 22:07:48 +0100 Subject: [PATCH] feat: add suggestion for no-return-await (#16637) * refactor: extract error list into a function Suggestions will be different for different errors, so a constant object won't work anymore * feat: suggest removing await in no-return-await * Fix suggestion message Co-authored-by: Milos Djermanovic * refactor: remove unused data * fix: only trim after await if there is a space * refactor: allow expecting no suggestion * refactor: move code for fixing into fix function * fix: do not suggest a fix if await and awaited expression are not on th same line * Simplify suggestion message Co-authored-by: Milos Djermanovic * Simplify single-character access Co-authored-by: Milos Djermanovic * Simplify removing a range Co-authored-by: Milos Djermanovic * feat: suggest removing await when expression starts with ( * refactor: use awaitToken to determine await range * fix: use more performant way to identify tokens Co-authored-by: Milos Djermanovic --- lib/rules/no-return-await.js | 29 ++++- tests/lib/rules/no-return-await.js | 171 +++++++++++++++++++++++------ 2 files changed, 168 insertions(+), 32 deletions(-) diff --git a/lib/rules/no-return-await.js b/lib/rules/no-return-await.js index 3007c8c877d..ecfd6697a13 100644 --- a/lib/rules/no-return-await.js +++ b/lib/rules/no-return-await.js @@ -13,6 +13,7 @@ const astUtils = require("./utils/ast-utils"); /** @type {import('../shared/types').Rule} */ module.exports = { meta: { + hasSuggestions: true, type: "suggestion", docs: { @@ -29,6 +30,7 @@ module.exports = { ], messages: { + removeAwait: "Remove redundant `await`.", redundantUseOfAwait: "Redundant use of `await` on a return value." } }, @@ -44,7 +46,32 @@ module.exports = { context.report({ node: context.getSourceCode().getFirstToken(node), loc: node.loc, - messageId: "redundantUseOfAwait" + messageId: "redundantUseOfAwait", + suggest: [ + { + messageId: "removeAwait", + fix(fixer) { + const sourceCode = context.getSourceCode(); + const [awaitToken, tokenAfterAwait] = sourceCode.getFirstTokens(node, 2); + + const areAwaitAndAwaitedExpressionOnTheSameLine = awaitToken.loc.start.line === tokenAfterAwait.loc.start.line; + + if (!areAwaitAndAwaitedExpressionOnTheSameLine) { + return null; + } + + const [startOfAwait, endOfAwait] = awaitToken.range; + + const characterAfterAwait = sourceCode.text[endOfAwait]; + const trimLength = characterAfterAwait === " " ? 1 : 0; + + const range = [startOfAwait, endOfAwait + trimLength]; + + return fixer.removeRange(range); + } + } + ] + }); } diff --git a/tests/lib/rules/no-return-await.js b/tests/lib/rules/no-return-await.js index 508d7cb8528..b9184a0a532 100644 --- a/tests/lib/rules/no-return-await.js +++ b/tests/lib/rules/no-return-await.js @@ -16,8 +16,24 @@ const { RuleTester } = require("../../../lib/rule-tester"); // Tests //------------------------------------------------------------------------------ -// pending https://github.com/eslint/espree/issues/304, the type should be "Keyword" -const errors = [{ messageId: "redundantUseOfAwait", type: "Identifier" }]; +/** + * Creates the list of errors that should be found by this rule + * @param {Object} options Options for creating errors + * @param {string} options.suggestionOutput The suggested output + * @returns {Array} the list of errors + */ +function createErrorList({ suggestionOutput: output } = {}) { + + // pending https://github.com/eslint/espree/issues/304, the type should be "Keyword" + return [{ + messageId: "redundantUseOfAwait", + type: "Identifier", + suggestions: output ? [{ + messageId: "removeAwait", output + }] : [] + }]; +} + const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 2017 } }); @@ -138,99 +154,103 @@ ruleTester.run("no-return-await", rule, { invalid: [ { code: "\nasync function foo() {\n\treturn await bar();\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn bar();\n}\n" }) + }, + { + code: "\nasync function foo() {\n\treturn await(bar());\n}\n", + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a, await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a, bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a, b, await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a, b, bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a && await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a && bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a && b && await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a && b && bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a || await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a || bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a, b, (c, d, await bar()));\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a, b, (c, d, bar()));\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (a, b, (c && await bar()));\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (a, b, (c && bar()));\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (await baz(), b, await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (await baz(), b, bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (baz() ? await bar() : b);\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (baz() ? bar() : b);\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (baz() ? a : await bar());\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (baz() ? a : bar());\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (baz() ? (a, await bar()) : b);\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (baz() ? (a, bar()) : b);\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (baz() ? a : (b, await bar()));\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (baz() ? a : (b, bar()));\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (baz() ? (a && await bar()) : b);\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (baz() ? (a && bar()) : b);\n}\n" }) }, { code: "\nasync function foo() {\n\treturn (baz() ? a : (b && await bar()));\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\n\treturn (baz() ? a : (b && bar()));\n}\n" }) }, { code: "\nasync () => { return await bar(); }\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => { return bar(); }\n" }) }, { code: "\nasync () => await bar()\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => bar()\n" }) }, { code: "\nasync () => (a, b, await bar())\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => (a, b, bar())\n" }) }, { code: "\nasync () => (a && await bar())\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => (a && bar())\n" }) }, { code: "\nasync () => (baz() ? await bar() : b)\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => (baz() ? bar() : b)\n" }) }, { code: "\nasync () => (baz() ? a : (b, await bar()))\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => (baz() ? a : (b, bar()))\n" }) }, { code: "\nasync () => (baz() ? a : (b && await bar()))\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => (baz() ? a : (b && bar()))\n" }) }, { code: "\nasync function foo() {\nif (a) {\n\t\tif (b) {\n\t\t\treturn await bar();\n\t\t}\n\t}\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync function foo() {\nif (a) {\n\t\tif (b) {\n\t\t\treturn bar();\n\t\t}\n\t}\n}\n" }) }, { code: "\nasync () => {\nif (a) {\n\t\tif (b) {\n\t\t\treturn await bar();\n\t\t}\n\t}\n}\n", - errors + errors: createErrorList({ suggestionOutput: "\nasync () => {\nif (a) {\n\t\tif (b) {\n\t\t\treturn bar();\n\t\t}\n\t}\n}\n" }) }, { code: ` @@ -241,7 +261,16 @@ ruleTester.run("no-return-await", rule, { } } `, - errors + errors: createErrorList({ + suggestionOutput: ` + async function foo() { + try {} + finally { + return bar(); + } + } + ` + }) }, { code: ` @@ -252,7 +281,16 @@ ruleTester.run("no-return-await", rule, { } } `, - errors + errors: createErrorList({ + suggestionOutput: ` + async function foo() { + try {} + catch (e) { + return bar(); + } + } + ` + }) }, { code: ` @@ -262,7 +300,15 @@ ruleTester.run("no-return-await", rule, { } } catch (e) {} `, - errors + errors: createErrorList({ + suggestionOutput: ` + try { + async function foo() { + return bar(); + } + } catch (e) {} + ` + }) }, { code: ` @@ -270,7 +316,13 @@ ruleTester.run("no-return-await", rule, { async () => await bar(); } catch (e) {} `, - errors + errors: createErrorList({ + suggestionOutput: ` + try { + async () => bar(); + } catch (e) {} + ` + }) }, { code: ` @@ -284,7 +336,64 @@ ruleTester.run("no-return-await", rule, { } } `, - errors + errors: createErrorList({ + suggestionOutput: ` + async function foo() { + try {} + catch (e) { + try {} + catch (e) { + return bar(); + } + } + } + ` + }) + }, + { + code: ` + async function foo() { + return await new Promise(resolve => { + resolve(5); + }); + } + `, + errors: createErrorList({ + suggestionOutput: ` + async function foo() { + return new Promise(resolve => { + resolve(5); + }); + } + ` + }) + }, + { + code: ` + async () => { + return await ( + foo() + ) + }; + `, + errors: createErrorList({ + suggestionOutput: ` + async () => { + return ( + foo() + ) + }; + ` + }) + }, + { + code: ` + async function foo() { + return await // Test + 5; + } + `, + errors: createErrorList() } ] });