11import type { TSESLint , TSESTree } from '@typescript-eslint/utils' ;
22import { AST_NODE_TYPES } from '@typescript-eslint/utils' ;
33import * as tsutils from 'tsutils' ;
4- import type * as ts from 'typescript' ;
4+ import * as ts from 'typescript' ;
55
66import * as util from '../util' ;
77
@@ -12,7 +12,11 @@ type Options = [
1212 } ,
1313] ;
1414
15- type MessageId = 'floating' | 'floatingVoid' | 'floatingFixVoid' ;
15+ type MessageId =
16+ | 'floating'
17+ | 'floatingVoid'
18+ | 'floatingFixVoid'
19+ | 'floatingFixAwait' ;
1620
1721export default util . createRule < Options , MessageId > ( {
1822 name : 'no-floating-promises' ,
@@ -27,6 +31,7 @@ export default util.createRule<Options, MessageId>({
2731 messages : {
2832 floating :
2933 'Promises must be awaited, end with a call to .catch, or end with a call to .then with a rejection handler.' ,
34+ floatingFixAwait : 'Add await operator.' ,
3035 floatingVoid :
3136 'Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler' +
3237 ' or be explicitly marked as ignored with the `void` operator.' ,
@@ -95,12 +100,54 @@ export default util.createRule<Options, MessageId>({
95100 context . report ( {
96101 node,
97102 messageId : 'floating' ,
103+ suggest : [
104+ {
105+ messageId : 'floatingFixAwait' ,
106+ fix ( fixer ) : TSESLint . RuleFix | TSESLint . RuleFix [ ] {
107+ if (
108+ expression . type === AST_NODE_TYPES . UnaryExpression &&
109+ expression . operator === 'void'
110+ ) {
111+ return fixer . replaceTextRange (
112+ [ expression . range [ 0 ] , expression . range [ 0 ] + 4 ] ,
113+ 'await' ,
114+ ) ;
115+ }
116+ const tsNode = parserServices . esTreeNodeToTSNodeMap . get (
117+ node . expression ,
118+ ) ;
119+ if ( isHigherPrecedenceThanAwait ( tsNode ) ) {
120+ return fixer . insertTextBefore ( node , 'await ' ) ;
121+ } else {
122+ return [
123+ fixer . insertTextBefore ( node , 'await (' ) ,
124+ fixer . insertTextAfterRange (
125+ [ expression . range [ 1 ] , expression . range [ 1 ] ] ,
126+ ')' ,
127+ ) ,
128+ ] ;
129+ }
130+ } ,
131+ } ,
132+ ] ,
98133 } ) ;
99134 }
100135 }
101136 } ,
102137 } ;
103138
139+ function isHigherPrecedenceThanAwait ( node : ts . Node ) : boolean {
140+ const operator = tsutils . isBinaryExpression ( node )
141+ ? node . operatorToken . kind
142+ : ts . SyntaxKind . Unknown ;
143+ const nodePrecedence = util . getOperatorPrecedence ( node . kind , operator ) ;
144+ const awaitPrecedence = util . getOperatorPrecedence (
145+ ts . SyntaxKind . AwaitExpression ,
146+ ts . SyntaxKind . Unknown ,
147+ ) ;
148+ return nodePrecedence > awaitPrecedence ;
149+ }
150+
104151 function isAsyncIife ( node : TSESTree . ExpressionStatement ) : boolean {
105152 if ( node . expression . type !== AST_NODE_TYPES . CallExpression ) {
106153 return false ;
0 commit comments