From 359e0527c44d1922423e346029612e3ce05e4359 Mon Sep 17 00:00:00 2001 From: gyumong Date: Sun, 27 Oct 2024 17:30:53 +0900 Subject: [PATCH 01/14] fix(eslint-plugin): correctly report errors for async methods returning Promise where void is expected --- .../src/rules/no-misused-promises.ts | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index c80b253c650e..d07645905bb5 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -6,6 +6,7 @@ import * as ts from 'typescript'; import { createRule, + getFunctionHeadLoc, getParserServices, isArrayMethodCallWithPredicate, isFunction, @@ -490,10 +491,32 @@ export default createRule({ ); if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { - context.report({ - node: node.value, - messageId: 'voidReturnProperty', - }); + const signature = checker.getSignatureFromDeclaration(tsNode); + if (signature) { + const returnType = checker.getReturnTypeOfSignature(signature); + if (tsutils.isThenableType(checker, tsNode, returnType)) { + const functionNode = node.value; + if (isFunction(functionNode)) { + if (functionNode.returnType) { + context.report({ + node: functionNode.returnType, + messageId: 'voidReturnProperty', + }); + } else { + context.report({ + loc: getFunctionHeadLoc(functionNode, context.sourceCode), + node: functionNode, + messageId: 'voidReturnProperty', + }); + } + } else { + context.report({ + node: node.value, + messageId: 'voidReturnProperty', + }); + } + } + } } return; } From d8c6635a9a1e8e415bdb4e4b993ba24e7347f868 Mon Sep 17 00:00:00 2001 From: gyumong Date: Sun, 27 Oct 2024 18:49:53 +0900 Subject: [PATCH 02/14] test add coverage --- .../tests/rules/no-misused-promises.test.ts | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index ed685906ee99..c6c9e23cdbe5 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -2429,5 +2429,43 @@ arrayFn<() => void>( }, ], }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; + +const o: HasVoidMethod = { + async f() { + return 3; + }, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; + +const o: HasVoidMethod = { + async f(): Promise { + return 3; + }, +}; + `, + errors: [ + { + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, ], }); From 3d26e12a0cbd6b18a28a443e70ef2354be93197e Mon Sep 17 00:00:00 2001 From: gyumong Date: Sun, 27 Oct 2024 20:23:13 +0900 Subject: [PATCH 03/14] fix: remove unreachable code --- packages/eslint-plugin/src/rules/no-misused-promises.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index d07645905bb5..d52f4b03a438 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -509,11 +509,6 @@ export default createRule({ messageId: 'voidReturnProperty', }); } - } else { - context.report({ - node: node.value, - messageId: 'voidReturnProperty', - }); } } } From a5dbd8761d018d5cc1620a3b3a06d9ed7975eff7 Mon Sep 17 00:00:00 2001 From: gyumong Date: Mon, 28 Oct 2024 03:45:55 +0900 Subject: [PATCH 04/14] fix: simplify redundant if checks in context.report logic --- .../src/rules/no-misused-promises.ts | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index d52f4b03a438..80d377a9b25d 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -7,6 +7,7 @@ import * as ts from 'typescript'; import { createRule, getFunctionHeadLoc, + getFunctionHeadLocation, getParserServices, isArrayMethodCallWithPredicate, isFunction, @@ -43,6 +44,19 @@ type MessageId = | 'voidReturnReturnValue' | 'voidReturnVariable'; +function findFunctionNode( + node: TSESTree.Node, +): + | TSESTree.FunctionDeclaration + | TSESTree.FunctionExpression + | TSESTree.ArrowFunctionExpression { + let current: TSESTree.Node | undefined = node; + while (current && !isFunction(current)) { + current = current.parent; + } + return nullThrows(current, NullThrowsReasons.MissingParent); +} + function parseChecksVoidReturn( checksVoidReturn: boolean | ChecksVoidReturnOptions | undefined, ): ChecksVoidReturnOptions | false { @@ -491,26 +505,19 @@ export default createRule({ ); if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { - const signature = checker.getSignatureFromDeclaration(tsNode); - if (signature) { - const returnType = checker.getReturnTypeOfSignature(signature); - if (tsutils.isThenableType(checker, tsNode, returnType)) { - const functionNode = node.value; - if (isFunction(functionNode)) { - if (functionNode.returnType) { - context.report({ - node: functionNode.returnType, - messageId: 'voidReturnProperty', - }); - } else { - context.report({ - loc: getFunctionHeadLoc(functionNode, context.sourceCode), - node: functionNode, - messageId: 'voidReturnProperty', - }); - } - } - } + const functionNode = findFunctionNode(node.value); + + if (functionNode.returnType) { + context.report({ + node: functionNode.returnType, + messageId: 'voidReturnProperty', + }); + } else { + context.report({ + loc: getFunctionHeadLoc(functionNode, context.sourceCode), + node: functionNode, + messageId: 'voidReturnProperty', + }); } } return; @@ -524,13 +531,8 @@ export default createRule({ } // syntactically ignore some known-good cases to avoid touching type info - const functionNode = (() => { - let current: TSESTree.Node | undefined = node.parent; - while (current && !isFunction(current)) { - current = current.parent; - } - return nullThrows(current, NullThrowsReasons.MissingParent); - })(); + const functionNode = findFunctionNode(node); + if ( functionNode.returnType && !isPossiblyFunctionType(functionNode.returnType) From a762f40e1b7d4ee01567c78942fddded4050208e Mon Sep 17 00:00:00 2001 From: gyumong Date: Mon, 28 Oct 2024 03:47:10 +0900 Subject: [PATCH 05/14] test: specify error location in voidReturnProperty test cases --- .../eslint-plugin/tests/rules/no-misused-promises.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index c6c9e23cdbe5..ccb5803ab4b9 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -2443,6 +2443,9 @@ const o: HasVoidMethod = { `, errors: [ { + column: 3, + endColumn: 10, + endLine: 7, line: 7, messageId: 'voidReturnProperty', }, @@ -2462,6 +2465,9 @@ const o: HasVoidMethod = { `, errors: [ { + column: 12, + endColumn: 29, + endLine: 7, line: 7, messageId: 'voidReturnProperty', }, From 432b792003a8ff98120a3140ef1e48b5d1169852 Mon Sep 17 00:00:00 2001 From: gyumong Date: Mon, 28 Oct 2024 04:28:05 +0900 Subject: [PATCH 06/14] fix lint --- packages/eslint-plugin/src/rules/no-misused-promises.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 80d377a9b25d..7cd53204b033 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -7,7 +7,6 @@ import * as ts from 'typescript'; import { createRule, getFunctionHeadLoc, - getFunctionHeadLocation, getParserServices, isArrayMethodCallWithPredicate, isFunction, @@ -47,9 +46,9 @@ type MessageId = function findFunctionNode( node: TSESTree.Node, ): + | TSESTree.ArrowFunctionExpression | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression - | TSESTree.ArrowFunctionExpression { + | TSESTree.FunctionExpression { let current: TSESTree.Node | undefined = node; while (current && !isFunction(current)) { current = current.parent; From 4c7d78741ac7ba9789c7239eb45c6b417fe90058 Mon Sep 17 00:00:00 2001 From: gyumong Date: Thu, 31 Oct 2024 20:18:20 +0900 Subject: [PATCH 07/14] =?UTF-8?q?test:=20add=20full=20loc=20(line,=20endLi?= =?UTF-8?q?ne,=20column,=20endColumn)=20to=20existing=20test=20cases?= =?UTF-8?q?=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tests/rules/no-misused-promises.test.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index ccb5803ab4b9..5c51e0c367a2 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -1451,6 +1451,9 @@ const obj: O = { `, errors: [ { + column: 3, + endColumn: 10, + endLine: 4, line: 4, messageId: 'voidReturnProperty', }, @@ -1472,14 +1475,23 @@ function f(): O { `, errors: [ { + column: 5, + endColumn: 12, + endLine: 6, line: 6, messageId: 'voidReturnProperty', }, { + column: 8, + endColumn: 21, + endLine: 9, line: 9, messageId: 'voidReturnProperty', }, { + column: 5, + endColumn: 6, + endLine: 10, line: 10, messageId: 'voidReturnProperty', }, @@ -2465,7 +2477,7 @@ const o: HasVoidMethod = { `, errors: [ { - column: 12, + column: 14, endColumn: 29, endLine: 7, line: 7, From 65ae6285f95193c83a33625b73f96de7fda449e5 Mon Sep 17 00:00:00 2001 From: gyumong Date: Thu, 31 Oct 2024 20:24:41 +0900 Subject: [PATCH 08/14] test: add new invalid test cases for promise-returning methods without async --- .../tests/rules/no-misused-promises.test.ts | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 5c51e0c367a2..4d2d525f40db 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -2485,5 +2485,47 @@ const o: HasVoidMethod = { }, ], }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; +const obj: HasVoidMethod = { + f() { + return Promise.resolve('foo'); + }, +}; + `, + errors: [ + { + column: 2, + endColumn: 3, + endLine: 6, + line: 6, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; +const obj: HasVoidMethod = { + f(): Promise { + throw new Error(); + }, +}; + `, + errors: [ + { + column: 8, + endColumn: 21, + endLine: 6, + line: 6, + messageId: 'voidReturnProperty', + }, + ], + }, ], }); From 801bf4c6d12eb948e5ff0ac9e5d08af364b386d1 Mon Sep 17 00:00:00 2001 From: gyumong Date: Thu, 31 Oct 2024 20:30:37 +0900 Subject: [PATCH 09/14] fix : test col --- .../eslint-plugin/tests/rules/no-misused-promises.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 4d2d525f40db..8c0ddbe42074 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -2498,8 +2498,8 @@ const obj: HasVoidMethod = { `, errors: [ { - column: 2, - endColumn: 3, + column: 3, + endColumn: 4, endLine: 6, line: 6, messageId: 'voidReturnProperty', From f367c202d94b1d2084309a4091786dc0c8641540 Mon Sep 17 00:00:00 2001 From: gyumong Date: Thu, 31 Oct 2024 20:33:09 +0900 Subject: [PATCH 10/14] fix: log --- .../src/rules/no-misused-promises.ts | 25 +++++++------------ 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 7cd53204b033..db00b2786fcd 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -43,19 +43,6 @@ type MessageId = | 'voidReturnReturnValue' | 'voidReturnVariable'; -function findFunctionNode( - node: TSESTree.Node, -): - | TSESTree.ArrowFunctionExpression - | TSESTree.FunctionDeclaration - | TSESTree.FunctionExpression { - let current: TSESTree.Node | undefined = node; - while (current && !isFunction(current)) { - current = current.parent; - } - return nullThrows(current, NullThrowsReasons.MissingParent); -} - function parseChecksVoidReturn( checksVoidReturn: boolean | ChecksVoidReturnOptions | undefined, ): ChecksVoidReturnOptions | false { @@ -504,11 +491,11 @@ export default createRule({ ); if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { - const functionNode = findFunctionNode(node.value); + const functionNode = node.value as TSESTree.FunctionExpression; if (functionNode.returnType) { context.report({ - node: functionNode.returnType, + node: functionNode.returnType.typeAnnotation, messageId: 'voidReturnProperty', }); } else { @@ -530,7 +517,13 @@ export default createRule({ } // syntactically ignore some known-good cases to avoid touching type info - const functionNode = findFunctionNode(node); + const functionNode = (() => { + let current: TSESTree.Node | undefined = node.parent; + while (current && !isFunction(current)) { + current = current.parent; + } + return nullThrows(current, NullThrowsReasons.MissingParent); + })(); if ( functionNode.returnType && From e79283cfb9e6f4a7e0e3d060a68636fb37984731 Mon Sep 17 00:00:00 2001 From: Gyumong <60845910+Gyumong@users.noreply.github.com> Date: Fri, 1 Nov 2024 21:29:49 +0900 Subject: [PATCH 11/14] Update packages/eslint-plugin/src/rules/no-misused-promises.ts Co-authored-by: Kirk Waiblinger --- packages/eslint-plugin/src/rules/no-misused-promises.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index db00b2786fcd..199777bddb98 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -501,7 +501,6 @@ export default createRule({ } else { context.report({ loc: getFunctionHeadLoc(functionNode, context.sourceCode), - node: functionNode, messageId: 'voidReturnProperty', }); } From 2bc76666733cec2db3ffe2953b6d87ef41ee9996 Mon Sep 17 00:00:00 2001 From: gyumong Date: Mon, 4 Nov 2024 16:07:01 +0900 Subject: [PATCH 12/14] refactor(no-misused-promises): improve function type checking in void return validation --- .../src/rules/no-misused-promises.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 199777bddb98..f21870ea97cf 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -437,10 +437,25 @@ export default createRule({ ) && returnsThenable(checker, tsNode.initializer) ) { - context.report({ - node: node.value, - messageId: 'voidReturnProperty', - }); + if (isFunction(node.value)) { + const functionNode = node.value; + if (functionNode.returnType) { + context.report({ + node: functionNode.returnType.typeAnnotation, + messageId: 'voidReturnProperty', + }); + } else { + context.report({ + loc: getFunctionHeadLoc(functionNode, context.sourceCode), + messageId: 'voidReturnProperty', + }); + } + } else { + context.report({ + node: node.value, + messageId: 'voidReturnProperty', + }); + } } } else if (ts.isShorthandPropertyAssignment(tsNode)) { const contextualType = checker.getContextualType(tsNode.name); From 2da0b5cce529eab70d38b54fa3987b165ec56790 Mon Sep 17 00:00:00 2001 From: gyumong Date: Mon, 4 Nov 2024 16:33:25 +0900 Subject: [PATCH 13/14] test(no-misused-promises): add test cases for isFunction utility usage --- .../tests/rules/no-misused-promises.test.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 8c0ddbe42074..9cc0e46fc878 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -1405,6 +1405,9 @@ const obj: O = { `, errors: [ { + column: 3, + endColumn: 12, + endLine: 4, line: 4, messageId: 'voidReturnProperty', }, @@ -1419,6 +1422,9 @@ const obj: O = { `, errors: [ { + column: 3, + endColumn: 12, + endLine: 4, line: 4, messageId: 'voidReturnProperty', }, @@ -1482,8 +1488,8 @@ function f(): O { messageId: 'voidReturnProperty', }, { - column: 8, - endColumn: 21, + column: 5, + endColumn: 14, endLine: 9, line: 9, messageId: 'voidReturnProperty', @@ -1795,7 +1801,15 @@ const test: ReturnsRecord = () => { return { asynchronous: async () => {} }; }; `, - errors: [{ line: 5, messageId: 'voidReturnProperty' }], + errors: [ + { + column: 12, + endColumn: 32, + endLine: 5, + line: 5, + messageId: 'voidReturnProperty', + }, + ], }, { code: ` From c7ac4e67a0bf9890ed9a3056b915d63189e9992e Mon Sep 17 00:00:00 2001 From: gyumong Date: Tue, 5 Nov 2024 22:01:44 +0900 Subject: [PATCH 14/14] test(no-misused-promises): add test cases --- .../tests/rules/no-misused-promises.test.ts | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts index 9cc0e46fc878..16ee46b5f01d 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -2541,5 +2541,40 @@ const obj: HasVoidMethod = { }, ], }, + { + code: ` +type O = { f: () => void }; +const asyncFunction = async () => 'foo'; +const obj: O = { + f: asyncFunction, +}; + `, + errors: [ + { + column: 6, + endColumn: 19, + endLine: 5, + line: 5, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type O = { f: () => void }; +const obj: O = { + f: async (): Promise => 'foo', +}; + `, + errors: [ + { + column: 16, + endColumn: 31, + endLine: 4, + line: 4, + messageId: 'voidReturnProperty', + }, + ], + }, ], }); pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy