diff --git a/packages/eslint-plugin/src/rules/require-await.ts b/packages/eslint-plugin/src/rules/require-await.ts index e7b6a5f5959a..99c455cd0f6a 100644 --- a/packages/eslint-plugin/src/rules/require-await.ts +++ b/packages/eslint-plugin/src/rules/require-await.ts @@ -11,6 +11,8 @@ interface ScopeInfo { upper: ScopeInfo | null; hasAwait: boolean; hasAsync: boolean; + isGen: boolean; + isAsyncYield: boolean; } type FunctionNode = | TSESTree.FunctionDeclaration @@ -49,6 +51,8 @@ export default util.createRule({ upper: scopeInfo, hasAwait: false, hasAsync: node.async, + isGen: node.generator || false, + isAsyncYield: false, }; } @@ -62,7 +66,12 @@ export default util.createRule({ return; } - if (node.async && !scopeInfo.hasAwait && !isEmptyFunction(node)) { + if ( + node.async && + !scopeInfo.hasAwait && + !isEmptyFunction(node) && + !(scopeInfo.isGen && scopeInfo.isAsyncYield) + ) { context.report({ node, loc: getFunctionHeadLoc(node, sourceCode), @@ -92,10 +101,34 @@ export default util.createRule({ if (!scopeInfo) { return; } - scopeInfo.hasAwait = true; } + /** + * mark `scopeInfo.isAsyncYield` to `true` if its a generator + * function and the delegate is `true` + */ + function markAsHasDelegateGen(node: TSESTree.YieldExpression): void { + if (!scopeInfo || !scopeInfo.isGen || !node.argument) { + return; + } + + if (node?.argument?.type === AST_NODE_TYPES.Literal) { + // making this `false` as for literals we don't need to check the definition + // eg : async function* run() { yield* 1 } + scopeInfo.isAsyncYield = false; + } + + const tsNode = parserServices.esTreeNodeToTSNodeMap.get(node?.argument); + const type = checker.getTypeAtLocation(tsNode); + const symbol = type.getSymbol(); + + // async function* test1() {yield* asyncGenerator() } + if (symbol?.getName() === 'AsyncGenerator') { + scopeInfo.isAsyncYield = true; + } + } + return { FunctionDeclaration: enterFunction, FunctionExpression: enterFunction, @@ -106,6 +139,7 @@ export default util.createRule({ AwaitExpression: markAsHasAwait, 'ForOfStatement[await = true]': markAsHasAwait, + 'YieldExpression[delegate = true]': markAsHasDelegateGen, // check body-less async arrow function. // ignore `async () => await foo` because it's obviously correct diff --git a/packages/eslint-plugin/tests/rules/require-await.test.ts b/packages/eslint-plugin/tests/rules/require-await.test.ts index 66db54979806..caea93fbfb95 100644 --- a/packages/eslint-plugin/tests/rules/require-await.test.ts +++ b/packages/eslint-plugin/tests/rules/require-await.test.ts @@ -16,102 +16,130 @@ ruleTester.run('require-await', rule, { valid: [ // Non-async function declaration `function numberOne(): number { - return 1; - }`, + return 1; + }`, // Non-async function expression `const numberOne = function(): number { - return 1; - }`, + return 1; + }`, // Non-async arrow function expression (concise-body) `const numberOne = (): number => 1;`, // Non-async arrow function expression (block-body) `const numberOne = (): number => { - return 1; - };`, + return 1; + };`, // Non-async function that returns a promise // https://github.com/typescript-eslint/typescript-eslint/issues/1226 ` -function delay() { - return Promise.resolve(); -} - `, + function delay() { + return Promise.resolve(); + } + `, ` -const delay = () => { - return Promise.resolve(); -} - `, + const delay = () => { + return Promise.resolve(); + } + `, `const delay = () => Promise.resolve();`, // Async function declaration with await `async function numberOne(): Promise { - return await 1; - }`, + return await 1; + }`, // Async function expression with await `const numberOne = async function(): Promise { - return await 1; - }`, + return await 1; + }`, // Async arrow function expression with await (concise-body) `const numberOne = async (): Promise => await 1;`, // Async arrow function expression with await (block-body) `const numberOne = async (): Promise => { - return await 1; - };`, + return await 1; + };`, // Async function declaration with promise return `async function numberOne(): Promise { - return Promise.resolve(1); - }`, + return Promise.resolve(1); + }`, // Async function expression with promise return `const numberOne = async function(): Promise { - return Promise.resolve(1); - }`, + return Promise.resolve(1); + }`, // Async arrow function with promise return (concise-body) `const numberOne = async (): Promise => Promise.resolve(1);`, // Async arrow function with promise return (block-body) `const numberOne = async (): Promise => { - return Promise.resolve(1); - };`, + return Promise.resolve(1); + };`, // Async function declaration with async function return `async function numberOne(): Promise { - return getAsyncNumber(1); - } - async function getAsyncNumber(x: number): Promise { - return Promise.resolve(x); - }`, + return getAsyncNumber(1); + } + async function getAsyncNumber(x: number): Promise { + return Promise.resolve(x); + }`, // Async function expression with async function return `const numberOne = async function(): Promise { - return getAsyncNumber(1); - } - const getAsyncNumber = async function(x: number): Promise { - return Promise.resolve(x); - }`, + return getAsyncNumber(1); + } + const getAsyncNumber = async function(x: number): Promise { + return Promise.resolve(x); + }`, // Async arrow function with async function return (concise-body) `const numberOne = async (): Promise => getAsyncNumber(1); - const getAsyncNumber = async function(x: number): Promise { - return Promise.resolve(x); - }`, + const getAsyncNumber = async function(x: number): Promise { + return Promise.resolve(x); + }`, // Async arrow function with async function return (block-body) `const numberOne = async (): Promise => { - return getAsyncNumber(1); - }; - const getAsyncNumber = async function(x: number): Promise { - return Promise.resolve(x); - }`, + return getAsyncNumber(1); + }; + const getAsyncNumber = async function(x: number): Promise { + return Promise.resolve(x); + }`, // https://github.com/typescript-eslint/typescript-eslint/issues/1188 ` -async function testFunction(): Promise { - await Promise.all([1, 2, 3].map( - // this should not trigger an error on the parent function - async value => Promise.resolve(value) - )) -} + async function testFunction(): Promise { + await Promise.all([1, 2, 3].map( + // this should not trigger an error on the parent function + async value => Promise.resolve(value) + )) + } + `, + ` + async function* run() { + await new Promise(resolve => setTimeout(resolve, 100)); + yield 'Hello'; + console.log('World'); + } + `, + 'async function* run() { }', + 'async function* asyncGenerator() { await Promise.resolve(); yield 1 }', + 'function* test6() { yield* syncGenerator() }', + 'function* test8() { yield syncGenerator() }', + 'function* syncGenerator() { yield 1 }', + ` + async function* asyncGenerator() { + await Promise.resolve() + yield 1 + } + async function* test1() {yield* asyncGenerator() } `, + `async function* foo() { + await Promise.resolve() + yield 1 + } + async function* bar() { + yield* foo() + }`, + 'const foo : () => void = async function *(){}', + 'async function* foo() : Promise { return new Promise((res) => res(`hello`)) }', ], invalid: [ { // Async function declaration with no await code: `async function numberOne(): Promise { - return 1; - }`, + return 1; + }`, errors: [ { messageId: 'missingAwait', @@ -124,8 +152,8 @@ async function testFunction(): Promise { { // Async function expression with no await code: `const numberOne = async function(): Promise { - return 1; - }`, + return 1; + }`, errors: [ { messageId: 'missingAwait', @@ -160,9 +188,85 @@ async function testFunction(): Promise { }, ], }, + { + code: 'async function* foo() : void { doSomething() }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'foo'", + }, + }, + ], + }, + { + code: 'async function* foo() { yield 1 }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'foo'", + }, + }, + ], + }, + { + code: 'async function* run() { console.log("bar") }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'run'", + }, + }, + ], + }, + { + code: 'const foo = async function *(){ console.log("bar") }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: 'Async generator function', + }, + }, + ], + }, + { + code: 'async function* syncGenerator() { yield 1 }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'syncGenerator'", + }, + }, + ], + }, + { + code: 'async function* test3() { yield asyncGenerator() }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'test3'", + }, + }, + ], + }, + { + code: 'async function* test7() { yield syncGenerator() }', + errors: [ + { + messageId: 'missingAwait', + data: { + name: "Async generator function 'test7'", + }, + }, + ], + }, ], }); - // base eslint tests // https://github.com/eslint/eslint/blob/03a69dbe86d5b5768a310105416ae726822e3c1c/tests/lib/rules/require-await.js#L25-L132 ruleTester.run('require-await', rule, { @@ -175,27 +279,23 @@ ruleTester.run('require-await', rule, { 'class A { async foo() { await doSomething() } }', '(class { async foo() { await doSomething() } })', 'async function foo() { await (async () => { await doSomething() }) }', - // empty functions are ok. 'async function foo() {}', 'async () => {}', - // normal functions are ok. 'function foo() { doSomething() }', - // for-await-of 'async function foo() { for await (x of xs); }', - // global await { code: 'await foo()', }, { code: ` - for await (let num of asyncIterable) { - console.log(num); - } - `, + for await (let num of asyncIterable) { + console.log(num); + } + `, }, ], invalid: [ @@ -289,5 +389,23 @@ ruleTester.run('require-await', rule, { }, ], }, + { + code: 'async function* run() { yield * anotherAsyncGenerator() }', + errors: [ + { + messageId: 'missingAwait', + data: { name: "Async generator function 'run'" }, + }, + ], + }, + { + code: 'async function* run() { yield* 1 }', + errors: [ + { + messageId: 'missingAwait', + data: { name: "Async generator function 'run'" }, + }, + ], + }, ], }); 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