Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(eslint-plugin): [require-await] handle async generators #1782

Merged
merged 9 commits into from Mar 31, 2020
38 changes: 36 additions & 2 deletions packages/eslint-plugin/src/rules/require-await.ts
Expand Up @@ -11,6 +11,8 @@ interface ScopeInfo {
upper: ScopeInfo | null;
hasAwait: boolean;
hasAsync: boolean;
isGen: boolean;
isAsyncYield: boolean;
}
type FunctionNode =
| TSESTree.FunctionDeclaration
Expand Down Expand Up @@ -49,6 +51,8 @@ export default util.createRule({
upper: scopeInfo,
hasAwait: false,
hasAsync: node.async,
isGen: node.generator || false,
isAsyncYield: false,
};
}

Expand All @@ -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),
Expand Down Expand Up @@ -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 `true` as for literals we dont need to check the defination
// eg : async function* run() { yield* 1 }
scopeInfo.isAsyncYield = true;
anikethsaha marked this conversation as resolved.
Show resolved Hide resolved
}

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,
Expand All @@ -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
Expand Down