From 5310996be006a97ddb72439f25a5fb1881d6f23c Mon Sep 17 00:00:00 2001 From: Jason Tarver Date: Fri, 9 Dec 2022 14:25:51 -0600 Subject: [PATCH 1/3] avoid unnecessary calls to getContextualType --- .../src/rules/no-misused-promises.ts | 36 ++++++++++--------- 1 file changed, 20 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 b6914ae2c39..9daac8da931 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -269,6 +269,9 @@ export default util.createRule({ function checkProperty(node: TSESTree.Property): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); if (ts.isPropertyAssignment(tsNode)) { + if (!returnsThenable(checker, tsNode.initializer)) { + return; + } const contextualType = checker.getContextualType(tsNode.initializer); if ( contextualType !== undefined && @@ -276,8 +279,7 @@ export default util.createRule({ checker, tsNode.initializer, contextualType, - ) && - returnsThenable(checker, tsNode.initializer) + ) ) { context.report({ messageId: 'voidReturnProperty', @@ -285,11 +287,13 @@ export default util.createRule({ }); } } else if (ts.isShorthandPropertyAssignment(tsNode)) { + if (!returnsThenable(checker, tsNode.name)) { + return; + } const contextualType = checker.getContextualType(tsNode.name); if ( contextualType !== undefined && - isVoidReturningFunctionType(checker, tsNode.name, contextualType) && - returnsThenable(checker, tsNode.name) + isVoidReturningFunctionType(checker, tsNode.name, contextualType) ) { context.report({ messageId: 'voidReturnProperty', @@ -312,6 +316,9 @@ export default util.createRule({ return; } + if (!returnsThenable(checker, tsNode)) { + return; + } const objType = checker.getContextualType(obj); if (objType === undefined) { return; @@ -329,10 +336,7 @@ export default util.createRule({ tsNode.name, ); - if ( - isVoidReturningFunctionType(checker, tsNode.name, contextualType) && - returnsThenable(checker, tsNode) - ) { + if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { context.report({ messageId: 'voidReturnProperty', node: node.value, @@ -347,15 +351,13 @@ export default util.createRule({ if (tsNode.expression === undefined || node.argument === null) { return; } + if (!returnsThenable(checker, tsNode.expression)) { + return; + } const contextualType = checker.getContextualType(tsNode.expression); if ( contextualType !== undefined && - isVoidReturningFunctionType( - checker, - tsNode.expression, - contextualType, - ) && - returnsThenable(checker, tsNode.expression) + isVoidReturningFunctionType(checker, tsNode.expression, contextualType) ) { context.report({ messageId: 'voidReturnReturnValue', @@ -375,11 +377,13 @@ export default util.createRule({ ) { return; } + if (!returnsThenable(checker, value.expression)) { + return; + } const contextualType = checker.getContextualType(value); if ( contextualType !== undefined && - isVoidReturningFunctionType(checker, value, contextualType) && - returnsThenable(checker, value.expression) + isVoidReturningFunctionType(checker, value, contextualType) ) { context.report({ messageId: 'voidReturnAttribute', From d25c078f60d35625938f3e5f9f61bf3219e1d3ec Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 13 Mar 2023 00:54:25 -0400 Subject: [PATCH 2/3] Removed a few more --- .../src/rules/no-misused-promises.ts | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index 9daac8da931..f157cbb24ec 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -240,12 +240,10 @@ export default util.createRule({ return; } - if (returnsThenable(checker, tsNode.right)) { - context.report({ - messageId: 'voidReturnVariable', - node: node.right, - }); - } + context.report({ + messageId: 'voidReturnVariable', + node: node.right, + }); } function checkVariableDeclaration(node: TSESTree.VariableDeclarator): void { @@ -269,9 +267,6 @@ export default util.createRule({ function checkProperty(node: TSESTree.Property): void { const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node); if (ts.isPropertyAssignment(tsNode)) { - if (!returnsThenable(checker, tsNode.initializer)) { - return; - } const contextualType = checker.getContextualType(tsNode.initializer); if ( contextualType !== undefined && @@ -287,9 +282,6 @@ export default util.createRule({ }); } } else if (ts.isShorthandPropertyAssignment(tsNode)) { - if (!returnsThenable(checker, tsNode.name)) { - return; - } const contextualType = checker.getContextualType(tsNode.name); if ( contextualType !== undefined && @@ -351,9 +343,6 @@ export default util.createRule({ if (tsNode.expression === undefined || node.argument === null) { return; } - if (!returnsThenable(checker, tsNode.expression)) { - return; - } const contextualType = checker.getContextualType(tsNode.expression); if ( contextualType !== undefined && @@ -377,9 +366,6 @@ export default util.createRule({ ) { return; } - if (!returnsThenable(checker, value.expression)) { - return; - } const contextualType = checker.getContextualType(value); if ( contextualType !== undefined && From 8797d467f98bafd1bff7a46ccc50a2651088b84a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Mon, 13 Mar 2023 16:42:01 -0400 Subject: [PATCH 3/3] Added back necessary untested logic, with tests --- .../src/rules/no-misused-promises.ts | 23 ++++--- .../tests/rules/no-misused-promises.test.ts | 62 +++++++++++++++++++ 2 files changed, 78 insertions(+), 7 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index e924bb87dd2..07fe430c175 100644 --- a/packages/eslint-plugin/src/rules/no-misused-promises.ts +++ b/packages/eslint-plugin/src/rules/no-misused-promises.ts @@ -240,10 +240,12 @@ export default util.createRule({ return; } - context.report({ - messageId: 'voidReturnVariable', - node: node.right, - }); + if (returnsThenable(checker, tsNode.right)) { + context.report({ + messageId: 'voidReturnVariable', + node: node.right, + }); + } } function checkVariableDeclaration(node: TSESTree.VariableDeclarator): void { @@ -274,7 +276,8 @@ export default util.createRule({ checker, tsNode.initializer, contextualType, - ) + ) && + returnsThenable(checker, tsNode.initializer) ) { context.report({ messageId: 'voidReturnProperty', @@ -285,7 +288,8 @@ export default util.createRule({ const contextualType = checker.getContextualType(tsNode.name); if ( contextualType !== undefined && - isVoidReturningFunctionType(checker, tsNode.name, contextualType) + isVoidReturningFunctionType(checker, tsNode.name, contextualType) && + returnsThenable(checker, tsNode.name) ) { context.report({ messageId: 'voidReturnProperty', @@ -346,7 +350,12 @@ export default util.createRule({ const contextualType = checker.getContextualType(tsNode.expression); if ( contextualType !== undefined && - isVoidReturningFunctionType(checker, tsNode.expression, contextualType) + isVoidReturningFunctionType( + checker, + tsNode.expression, + contextualType, + ) && + returnsThenable(checker, tsNode.expression) ) { context.report({ messageId: 'voidReturnReturnValue', 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 c962761717d..025abd43f6e 100644 --- a/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts +++ b/packages/eslint-plugin/tests/rules/no-misused-promises.test.ts @@ -427,6 +427,39 @@ function restTuple(..._args: string[]): void {} restTuple(); restTuple('Hello'); `, + ` + let value: Record void>; + value.sync = () => {}; + `, + ` + type ReturnsRecord = () => Record void>; + + const test: ReturnsRecord = () => { + return { sync: () => {} }; + }; + `, + ` + type ReturnsRecord = () => Record void>; + + function sync() {} + + const test: ReturnsRecord = () => { + return { sync }; + }; + `, + ` + function withTextRecurser( + recurser: (text: Text) => void, + ): (text: Text) => void { + return (text: Text): void => { + if (text.length) { + return; + } + + return recurser(node); + }; + } + `, ], invalid: [ @@ -1118,5 +1151,34 @@ restTuple(true, () => Promise.resolve(1)); `, errors: [{ line: 7, messageId: 'voidReturnArgument' }], }, + { + code: ` +type ReturnsRecord = () => Record void>; + +const test: ReturnsRecord = () => { + return { asynchronous: async () => {} }; +}; + `, + errors: [{ line: 5, messageId: 'voidReturnProperty' }], + }, + { + code: ` +let value: Record void>; +value.asynchronous = async () => {}; + `, + errors: [{ line: 3, messageId: 'voidReturnVariable' }], + }, + { + code: ` +type ReturnsRecord = () => Record void>; + +async function asynchronous() {} + +const test: ReturnsRecord = () => { + return { asynchronous }; +}; + `, + errors: [{ line: 7, messageId: 'voidReturnProperty' }], + }, ], });