diff --git a/packages/eslint-plugin/src/rules/no-misused-promises.ts b/packages/eslint-plugin/src/rules/no-misused-promises.ts index c80b253c650e..f21870ea97cf 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, @@ -436,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); @@ -490,10 +506,19 @@ export default createRule({ ); if (isVoidReturningFunctionType(checker, tsNode.name, contextualType)) { - context.report({ - node: node.value, - messageId: 'voidReturnProperty', - }); + const functionNode = node.value as TSESTree.FunctionExpression; + + if (functionNode.returnType) { + context.report({ + node: functionNode.returnType.typeAnnotation, + messageId: 'voidReturnProperty', + }); + } else { + context.report({ + loc: getFunctionHeadLoc(functionNode, context.sourceCode), + messageId: 'voidReturnProperty', + }); + } } return; } @@ -513,6 +538,7 @@ export default createRule({ } return nullThrows(current, NullThrowsReasons.MissingParent); })(); + if ( functionNode.returnType && !isPossiblyFunctionType(functionNode.returnType) 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..16ee46b5f01d 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', }, @@ -1451,6 +1457,9 @@ const obj: O = { `, errors: [ { + column: 3, + endColumn: 10, + endLine: 4, line: 4, messageId: 'voidReturnProperty', }, @@ -1472,14 +1481,23 @@ function f(): O { `, errors: [ { + column: 5, + endColumn: 12, + endLine: 6, line: 6, messageId: 'voidReturnProperty', }, { + column: 5, + endColumn: 14, + endLine: 9, line: 9, messageId: 'voidReturnProperty', }, { + column: 5, + endColumn: 6, + endLine: 10, line: 10, messageId: 'voidReturnProperty', }, @@ -1783,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: ` @@ -2429,5 +2455,126 @@ arrayFn<() => void>( }, ], }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; + +const o: HasVoidMethod = { + async f() { + return 3; + }, +}; + `, + errors: [ + { + column: 3, + endColumn: 10, + endLine: 7, + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; + +const o: HasVoidMethod = { + async f(): Promise { + return 3; + }, +}; + `, + errors: [ + { + column: 14, + endColumn: 29, + endLine: 7, + line: 7, + messageId: 'voidReturnProperty', + }, + ], + }, + { + code: ` +type HasVoidMethod = { + f(): void; +}; +const obj: HasVoidMethod = { + f() { + return Promise.resolve('foo'); + }, +}; + `, + errors: [ + { + column: 3, + endColumn: 4, + 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', + }, + ], + }, + { + 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', + }, + ], + }, ], });