Skip to content

Commit

Permalink
fix(require-yields): avoid checking nested generators (as with func…
Browse files Browse the repository at this point in the history
…tions)
  • Loading branch information
brettz9 committed Jan 31, 2021
1 parent a8d09eb commit b9058e4
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 28 deletions.
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16621,6 +16621,15 @@ function * quux () {
yield;
}
// Options: [{"next":true}]

/**
*
*/
function * quux (foo) {
const a = function * bar () {
yield foo;
}
}
````


Expand Down
64 changes: 36 additions & 28 deletions src/jsdocUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -794,33 +794,25 @@ const hasValueOrExecutorHasNonEmptyResolveValue = (node, anyPromiseAsReturn) =>
});
};

/**
* Checks if a node has a return statement. Void return does not count.
*
* @param {object} node
* @returns {boolean}
*/
// eslint-disable-next-line complexity
const hasYieldValue = (node, checkYieldReturnValue) => {
const hasNonFunctionYield = (node, checkYieldReturnValue) => {
if (!node) {
return false;
}
switch (node.type) {
case 'FunctionExpression':
case 'FunctionDeclaration': {
return node.generator && (
node.expression || hasYieldValue(node.body, checkYieldReturnValue)
);
}
case 'BlockStatement': {
return node.body.some((bodyNode) => {
return bodyNode.type !== 'FunctionDeclaration' && hasYieldValue(
return ![
'ArrowFunctionExpression',
'FunctionDeclaration',
'FunctionExpression',
].includes(bodyNode.type) && hasNonFunctionYield(
bodyNode, checkYieldReturnValue,
);
});
}
case 'ExpressionStatement': {
return hasYieldValue(node.expression, checkYieldReturnValue);
return hasNonFunctionYield(node.expression, checkYieldReturnValue);
}
case 'LabeledStatement':
case 'WhileStatement':
Expand All @@ -829,44 +821,44 @@ const hasYieldValue = (node, checkYieldReturnValue) => {
case 'ForInStatement':
case 'ForOfStatement':
case 'WithStatement': {
return hasYieldValue(node.body, checkYieldReturnValue);
return hasNonFunctionYield(node.body, checkYieldReturnValue);
}
case 'ConditionalExpression':
case 'IfStatement': {
return hasYieldValue(node.test, checkYieldReturnValue) ||
hasYieldValue(node.consequent, checkYieldReturnValue) ||
hasYieldValue(node.alternate, checkYieldReturnValue);
return hasNonFunctionYield(node.test, checkYieldReturnValue) ||
hasNonFunctionYield(node.consequent, checkYieldReturnValue) ||
hasNonFunctionYield(node.alternate, checkYieldReturnValue);
}
case 'TryStatement': {
return hasYieldValue(node.block, checkYieldReturnValue) ||
hasYieldValue(node.handler && node.handler.body, checkYieldReturnValue) ||
hasYieldValue(node.finalizer, checkYieldReturnValue);
return hasNonFunctionYield(node.block, checkYieldReturnValue) ||
hasNonFunctionYield(node.handler && node.handler.body, checkYieldReturnValue) ||
hasNonFunctionYield(node.finalizer, checkYieldReturnValue);
}
case 'SwitchStatement': {
return node.cases.some(
(someCase) => {
return someCase.consequent.some((nde) => {
return hasYieldValue(nde, checkYieldReturnValue);
return hasNonFunctionYield(nde, checkYieldReturnValue);
});
},
);
}
case 'ArrayPattern':
case 'ArrayExpression':
return node.elements.some((element) => {
return hasYieldValue(element, checkYieldReturnValue);
return hasNonFunctionYield(element, checkYieldReturnValue);
});
case 'AssignmentPattern':
return hasYieldValue(node.right, checkYieldReturnValue);
return hasNonFunctionYield(node.right, checkYieldReturnValue);

case 'VariableDeclaration': {
return node.declarations.some((nde) => {
return hasYieldValue(nde, checkYieldReturnValue);
return hasNonFunctionYield(nde, checkYieldReturnValue);
});
}
case 'VariableDeclarator': {
return hasYieldValue(node.id, checkYieldReturnValue) ||
hasYieldValue(node.init, checkYieldReturnValue);
return hasNonFunctionYield(node.id, checkYieldReturnValue) ||
hasNonFunctionYield(node.init, checkYieldReturnValue);
}
case 'YieldExpression': {
if (checkYieldReturnValue) {
Expand All @@ -890,6 +882,18 @@ const hasYieldValue = (node, checkYieldReturnValue) => {
}
};

/**
* Checks if a node has a return statement. Void return does not count.
*
* @param {object} node
* @returns {boolean}
*/
const hasYieldValue = (node, checkYieldReturnValue) => {
return node.generator && (
node.expression || hasNonFunctionYield(node.body, checkYieldReturnValue)
);
};

/**
* Checks if a node has a throws statement.
*
Expand All @@ -902,6 +906,10 @@ const hasThrowValue = (node, innerFunction) => {
if (!node) {
return false;
}

// There are cases where a function may execute its inner function which
// throws, but we're treating functions atomically rather than trying to
// follow them
switch (node.type) {
case 'FunctionExpression':
case 'FunctionDeclaration':
Expand Down
12 changes: 12 additions & 0 deletions test/rules/assertions/requireYields.js
Original file line number Diff line number Diff line change
Expand Up @@ -1446,5 +1446,17 @@ export default {
next: true,
}],
},
{
code: `
/**
*
*/
function * quux (foo) {
const a = function * bar () {
yield foo;
}
}
`,
},
],
};

0 comments on commit b9058e4

Please sign in to comment.