Skip to content

Commit

Permalink
feat: add suggestion for no-return-await (#16637)
Browse files Browse the repository at this point in the history
* 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 <milos.djermanovic@gmail.com>

* 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 <milos.djermanovic@gmail.com>

* Simplify single-character access

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* Simplify removing a range

Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>

* 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 <milos.djermanovic@gmail.com>
  • Loading branch information
dbartholomae and mdjermanovic committed Dec 16, 2022
1 parent ba74253 commit 075ef2c
Show file tree
Hide file tree
Showing 2 changed files with 168 additions and 32 deletions.
29 changes: 28 additions & 1 deletion lib/rules/no-return-await.js
Expand Up @@ -13,6 +13,7 @@ const astUtils = require("./utils/ast-utils");
/** @type {import('../shared/types').Rule} */
module.exports = {
meta: {
hasSuggestions: true,
type: "suggestion",

docs: {
Expand All @@ -29,6 +30,7 @@ module.exports = {
],

messages: {
removeAwait: "Remove redundant `await`.",
redundantUseOfAwait: "Redundant use of `await` on a return value."
}
},
Expand All @@ -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);
}
}
]

});
}

Expand Down
171 changes: 140 additions & 31 deletions tests/lib/rules/no-return-await.js
Expand Up @@ -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 } });

Expand Down Expand Up @@ -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: `
Expand All @@ -241,7 +261,16 @@ ruleTester.run("no-return-await", rule, {
}
}
`,
errors
errors: createErrorList({
suggestionOutput: `
async function foo() {
try {}
finally {
return bar();
}
}
`
})
},
{
code: `
Expand All @@ -252,7 +281,16 @@ ruleTester.run("no-return-await", rule, {
}
}
`,
errors
errors: createErrorList({
suggestionOutput: `
async function foo() {
try {}
catch (e) {
return bar();
}
}
`
})
},
{
code: `
Expand All @@ -262,15 +300,29 @@ ruleTester.run("no-return-await", rule, {
}
} catch (e) {}
`,
errors
errors: createErrorList({
suggestionOutput: `
try {
async function foo() {
return bar();
}
} catch (e) {}
`
})
},
{
code: `
try {
async () => await bar();
} catch (e) {}
`,
errors
errors: createErrorList({
suggestionOutput: `
try {
async () => bar();
} catch (e) {}
`
})
},
{
code: `
Expand All @@ -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()
}
]
});

0 comments on commit 075ef2c

Please sign in to comment.