diff --git a/packages/eslint-plugin/src/rules/no-floating-promises.ts b/packages/eslint-plugin/src/rules/no-floating-promises.ts index fdc93cbed3f..466e29bbb9f 100644 --- a/packages/eslint-plugin/src/rules/no-floating-promises.ts +++ b/packages/eslint-plugin/src/rules/no-floating-promises.ts @@ -1,5 +1,6 @@ import * as tsutils from 'tsutils'; import * as ts from 'typescript'; +import { TSESLint } from '@typescript-eslint/experimental-utils'; import * as util from '../util'; @@ -9,7 +10,9 @@ type Options = [ }, ]; -export default util.createRule({ +type MessageId = 'floating' | 'floatingVoid' | 'floatingFixVoid'; + +export default util.createRule({ name: 'no-floating-promises', meta: { docs: { @@ -20,6 +23,10 @@ export default util.createRule({ }, messages: { floating: 'Promises must be handled appropriately', + floatingVoid: + 'Promises must be handled appropriately' + + ' or explicitly marked as ignored with the `void` operator', + floatingFixVoid: 'Add void operator to ignore', }, schema: [ { @@ -41,16 +48,34 @@ export default util.createRule({ create(context, [options]) { const parserServices = util.getParserServices(context); const checker = parserServices.program.getTypeChecker(); + const sourceCode = context.getSourceCode(); return { ExpressionStatement(node): void { const { expression } = parserServices.esTreeNodeToTSNodeMap.get(node); if (isUnhandledPromise(checker, expression)) { - context.report({ - messageId: 'floating', - node, - }); + if (options.ignoreVoid) { + context.report({ + node, + messageId: 'floatingVoid', + suggest: [ + { + messageId: 'floatingFixVoid', + fix(fixer): TSESLint.RuleFix { + let code = sourceCode.getText(node); + code = `void ${code}`; + return fixer.replaceText(node, code); + }, + }, + ], + }); + } else { + context.report({ + node, + messageId: 'floating', + }); + } } }, }; diff --git a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts index d381fb4ba7c..a6fcf777754 100644 --- a/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-floating-promises.test.ts @@ -2,7 +2,6 @@ import rule from '../../src/rules/no-floating-promises'; import { RuleTester, getFixturesRootDir } from '../RuleTester'; const rootDir = getFixturesRootDir(); -const messageId = 'floating'; const ruleTester = new RuleTester({ parserOptions: { @@ -245,15 +244,39 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, { line: 4, - messageId, + messageId: 'floating', }, { line: 5, - messageId, + messageId: 'floating', + }, + ], + }, + { + options: [{ ignoreVoid: true }], + code: ` +async function test() { + Promise.resolve("value"); +} +`, + errors: [ + { + line: 3, + messageId: 'floatingVoid', + suggestions: [ + { + messageId: 'floatingFixVoid', + output: ` +async function test() { + void Promise.resolve("value"); +} +`, + }, + ], }, ], }, @@ -268,15 +291,15 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, { line: 4, - messageId, + messageId: 'floating', }, { line: 5, - messageId, + messageId: 'floating', }, ], }, @@ -291,15 +314,15 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, { line: 4, - messageId, + messageId: 'floating', }, { line: 5, - messageId, + messageId: 'floating', }, ], }, @@ -316,15 +339,15 @@ async function test() { errors: [ { line: 5, - messageId, + messageId: 'floating', }, { line: 6, - messageId, + messageId: 'floating', }, { line: 7, - messageId, + messageId: 'floating', }, ], }, @@ -338,11 +361,11 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, { line: 4, - messageId, + messageId: 'floating', }, ], }, @@ -357,15 +380,15 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, { line: 4, - messageId, + messageId: 'floating', }, { line: 5, - messageId, + messageId: 'floating', }, ], }, @@ -378,7 +401,7 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, ], }, @@ -392,7 +415,7 @@ async function test() { errors: [ { line: 4, - messageId, + messageId: 'floating', }, ], }, @@ -405,7 +428,7 @@ async function test() { errors: [ { line: 3, - messageId, + messageId: 'floating', }, ], }, @@ -422,15 +445,15 @@ async function test() { errors: [ { line: 5, - messageId, + messageId: 'floating', }, { line: 6, - messageId, + messageId: 'floating', }, { line: 7, - messageId, + messageId: 'floating', }, ], }, @@ -445,7 +468,7 @@ async function test() { errors: [ { line: 5, - messageId, + messageId: 'floating', }, ], }, @@ -462,15 +485,15 @@ async function test() { errors: [ { line: 5, - messageId, + messageId: 'floating', }, { line: 6, - messageId, + messageId: 'floating', }, { line: 7, - messageId, + messageId: 'floating', }, ], }, @@ -488,15 +511,15 @@ async function test() { errors: [ { line: 6, - messageId, + messageId: 'floating', }, { line: 7, - messageId, + messageId: 'floating', }, { line: 8, - messageId, + messageId: 'floating', }, ], }, @@ -517,11 +540,11 @@ async function test() { errors: [ { line: 10, - messageId, + messageId: 'floating', }, { line: 11, - messageId, + messageId: 'floating', }, ], }, @@ -551,15 +574,15 @@ async function test() { errors: [ { line: 18, - messageId, + messageId: 'floating', }, { line: 19, - messageId, + messageId: 'floating', }, { line: 20, - messageId, + messageId: 'floating', }, ], },