From 59a8b4a90c56d5e8841ac8f04dfe45087af96677 Mon Sep 17 00:00:00 2001 From: James Garbutt <43081j@users.noreply.github.com> Date: Wed, 1 Jan 2025 13:03:48 +0000 Subject: [PATCH] chore(eslint-plugin): no-confusing-void-expression `getConstraintInfo` Moves to using `getConstraintInfo` internally in the `no-confusing-void-expression` rule. Also fixes a bug this uncovered, where `AwaitExpression` nodes were actually untested and not supported in the type constraint checks. This is because we passed `node` (the `AwaitExpression` itself), rather than the thing being awaited (`node.argument`). By passing the `argument`, we check its type instead. --- .../src/rules/no-confusing-void-expression.ts | 29 +++++++++++++++---- .../no-confusing-void-expression.test.ts | 22 ++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts index e638afc8b944..a7a8f81f2470 100644 --- a/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts +++ b/packages/eslint-plugin/src/rules/no-confusing-void-expression.ts @@ -8,11 +8,12 @@ import type { MakeRequired } from '../util'; import { createRule, - getConstrainedTypeAtLocation, + getConstraintInfo, getParserServices, isClosingParenToken, isOpeningParenToken, isParenthesized, + isAwaitExpression, nullThrows, NullThrowsReasons, } from '../util'; @@ -116,8 +117,19 @@ export default createRule({ | TSESTree.CallExpression | TSESTree.TaggedTemplateExpression, ): void { - const type = getConstrainedTypeAtLocation(services, node); - if (!tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike)) { + const checker = services.program.getTypeChecker(); + const { constraintType } = getConstraintInfo( + checker, + services.getTypeAtLocation( + isAwaitExpression(node) ? node.argument : node, + ), + ); + + if (constraintType == null) { + return; + } + + if (!tsutils.isTypeFlagSet(constraintType, ts.TypeFlags.VoidLike)) { // not a void expression return; } @@ -420,8 +432,15 @@ export default createRule({ ? node.argument : node.body; - const type = getConstrainedTypeAtLocation(services, targetNode); - return tsutils.isTypeFlagSet(type, ts.TypeFlags.VoidLike); + const checker = services.program.getTypeChecker(); + const { constraintType } = getConstraintInfo( + checker, + services.getTypeAtLocation(targetNode), + ); + return ( + constraintType != null && + tsutils.isTypeFlagSet(constraintType, ts.TypeFlags.VoidLike) + ); } function isFunctionReturnTypeIncludesVoid(functionType: ts.Type): boolean { diff --git a/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts b/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts index 4fcb1b8b6865..2ff53bce2c7c 100644 --- a/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts +++ b/packages/eslint-plugin/tests/rules/no-confusing-void-expression.test.ts @@ -410,6 +410,11 @@ test((() => { `, options: [{ ignoreVoidReturningFunctions: true }], }, + ` + async function f(input: T) { + await input; + } + `, ], invalid: [ @@ -1208,5 +1213,22 @@ function test(arg?: string): any | void { } `, }, + { + code: ` + declare const a: void; + const b = await a; + `, + errors: [{ column: 19, messageId: 'invalidVoidExpr' }], + output: null, + }, + { + code: ` + function fn(a: void) { + const b = await a; + } + `, + errors: [{ column: 21, messageId: 'invalidVoidExpr' }], + output: null, + }, ], });