Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(eslint-plugin): [no-floating-promises] error on logical expressi…
…on (#6356)

* feat(eslint-plugin) [no-floating-promises] Error on logical expression

* Update packages/eslint-plugin/src/rules/no-floating-promises.ts

Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>

* remove sourceCode Variable

---------

Co-authored-by: Josh Goldberg <git@joshuakgoldberg.com>
  • Loading branch information
islandryu and JoshuaKGoldberg committed Feb 3, 2023
1 parent fc24a57 commit f330e06
Show file tree
Hide file tree
Showing 2 changed files with 334 additions and 12 deletions.
36 changes: 24 additions & 12 deletions packages/eslint-plugin/src/rules/no-floating-promises.ts
Expand Up @@ -4,6 +4,7 @@ import * as tsutils from 'tsutils';
import * as ts from 'typescript';

import * as util from '../util';
import { OperatorPrecedence } from '../util';

type Options = [
{
Expand Down Expand Up @@ -66,7 +67,6 @@ export default util.createRule<Options, MessageId>({
create(context, [options]) {
const parserServices = util.getParserServices(context);
const checker = parserServices.program.getTypeChecker();
const sourceCode = context.getSourceCode();

return {
ExpressionStatement(node): void {
Expand All @@ -88,10 +88,21 @@ export default util.createRule<Options, MessageId>({
suggest: [
{
messageId: 'floatingFixVoid',
fix(fixer): TSESLint.RuleFix {
let code = sourceCode.getText(node);
code = `void ${code}`;
return fixer.replaceText(node, code);
fix(fixer): TSESLint.RuleFix | TSESLint.RuleFix[] {
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(
node.expression,
);
if (isHigherPrecedenceThanUnary(tsNode)) {
return fixer.insertTextBefore(node, 'void ');
} else {
return [
fixer.insertTextBefore(node, 'void ('),
fixer.insertTextAfterRange(
[expression.range[1], expression.range[1]],
')',
),
];
}
},
},
],
Expand All @@ -116,7 +127,7 @@ export default util.createRule<Options, MessageId>({
const tsNode = parserServices.esTreeNodeToTSNodeMap.get(
node.expression,
);
if (isHigherPrecedenceThanAwait(tsNode)) {
if (isHigherPrecedenceThanUnary(tsNode)) {
return fixer.insertTextBefore(node, 'await ');
} else {
return [
Expand All @@ -136,16 +147,12 @@ export default util.createRule<Options, MessageId>({
},
};

function isHigherPrecedenceThanAwait(node: ts.Node): boolean {
function isHigherPrecedenceThanUnary(node: ts.Node): boolean {
const operator = tsutils.isBinaryExpression(node)
? node.operatorToken.kind
: ts.SyntaxKind.Unknown;
const nodePrecedence = util.getOperatorPrecedence(node.kind, operator);
const awaitPrecedence = util.getOperatorPrecedence(
ts.SyntaxKind.AwaitExpression,
ts.SyntaxKind.Unknown,
);
return nodePrecedence > awaitPrecedence;
return nodePrecedence > OperatorPrecedence.Unary;
}

function isAsyncIife(node: TSESTree.ExpressionStatement): boolean {
Expand Down Expand Up @@ -214,6 +221,11 @@ export default util.createRule<Options, MessageId>({
// `new Promise()`), the promise is not handled because it doesn't have the
// necessary then/catch call at the end of the chain.
return true;
} else if (node.type === AST_NODE_TYPES.LogicalExpression) {
return (
isUnhandledPromise(checker, node.left) ||
isUnhandledPromise(checker, node.right)
);
}

// We conservatively return false for all other types of expressions because
Expand Down

0 comments on commit f330e06

Please sign in to comment.