Addnew sheets (armor, weapons, malefica) and v13 support
This commit is contained in:
549
node_modules/eslint-plugin-jsdoc/src/utils/hasReturnValue.js
generated
vendored
Normal file
549
node_modules/eslint-plugin-jsdoc/src/utils/hasReturnValue.js
generated
vendored
Normal file
@@ -0,0 +1,549 @@
|
||||
/**
|
||||
* @typedef {import('estree').Node|
|
||||
* import('@typescript-eslint/types').TSESTree.Node} ESTreeOrTypeScriptNode
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a node is a promise but has no resolve value or an empty value.
|
||||
* An `undefined` resolve does not count.
|
||||
* @param {ESTreeOrTypeScriptNode|undefined|null} node
|
||||
* @returns {boolean|undefined|null}
|
||||
*/
|
||||
const isNewPromiseExpression = (node) => {
|
||||
return node && node.type === 'NewExpression' && node.callee.type === 'Identifier' &&
|
||||
node.callee.name === 'Promise';
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ESTreeOrTypeScriptNode|null|undefined} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const isVoidPromise = (node) => {
|
||||
return /** @type {import('@typescript-eslint/types').TSESTree.TSTypeReference} */ (node)?.typeArguments?.params?.[0]?.type === 'TSVoidKeyword'
|
||||
/* c8 ignore next */
|
||||
|| /** @type {import('@typescript-eslint/types').TSESTree.TSTypeReference} */ (node)?.typeParameters?.params?.[0]?.type === 'TSVoidKeyword';
|
||||
};
|
||||
|
||||
const undefinedKeywords = new Set([
|
||||
'TSVoidKeyword', 'TSUndefinedKeyword', 'TSNeverKeyword',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Checks if a node has a return statement. Void return does not count.
|
||||
* @param {ESTreeOrTypeScriptNode|undefined|null} node
|
||||
* @param {boolean} [throwOnNullReturn]
|
||||
* @param {PromiseFilter} [promFilter]
|
||||
* @returns {boolean|undefined}
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
const hasReturnValue = (node, throwOnNullReturn, promFilter) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'TSDeclareFunction':
|
||||
case 'TSFunctionType':
|
||||
case 'TSMethodSignature': {
|
||||
const type = node?.returnType?.typeAnnotation?.type;
|
||||
return type && !undefinedKeywords.has(type);
|
||||
}
|
||||
|
||||
case 'MethodDefinition':
|
||||
return hasReturnValue(node.value, throwOnNullReturn, promFilter);
|
||||
case 'FunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'ArrowFunctionExpression': {
|
||||
return 'expression' in node && node.expression && (!isNewPromiseExpression(
|
||||
node.body,
|
||||
) || !isVoidPromise(node.body)) ||
|
||||
hasReturnValue(node.body, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'BlockStatement': {
|
||||
return node.body.some((bodyNode) => {
|
||||
return bodyNode.type !== 'FunctionDeclaration' && hasReturnValue(bodyNode, throwOnNullReturn, promFilter);
|
||||
});
|
||||
}
|
||||
|
||||
case 'LabeledStatement':
|
||||
case 'WhileStatement':
|
||||
case 'DoWhileStatement':
|
||||
case 'ForStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'WithStatement': {
|
||||
return hasReturnValue(node.body, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'IfStatement': {
|
||||
return hasReturnValue(node.consequent, throwOnNullReturn, promFilter) ||
|
||||
hasReturnValue(node.alternate, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'TryStatement': {
|
||||
return hasReturnValue(node.block, throwOnNullReturn, promFilter) ||
|
||||
hasReturnValue(node.handler && node.handler.body, throwOnNullReturn, promFilter) ||
|
||||
hasReturnValue(node.finalizer, throwOnNullReturn, promFilter);
|
||||
}
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return node.cases.some(
|
||||
(someCase) => {
|
||||
return someCase.consequent.some((nde) => {
|
||||
return hasReturnValue(nde, throwOnNullReturn, promFilter);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
case 'ReturnStatement': {
|
||||
// void return does not count.
|
||||
if (node.argument === null) {
|
||||
if (throwOnNullReturn) {
|
||||
throw new Error('Null return');
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (promFilter && isNewPromiseExpression(node.argument)) {
|
||||
// Let caller decide how to filter, but this is, at the least,
|
||||
// a return of sorts and truthy
|
||||
return promFilter(node.argument);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a node has a return statement. Void return does not count.
|
||||
* @param {ESTreeOrTypeScriptNode|null|undefined} node
|
||||
* @param {PromiseFilter} promFilter
|
||||
* @returns {undefined|boolean|ESTreeOrTypeScriptNode}
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
const allBrancheshaveReturnValues = (node, promFilter) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (node.type) {
|
||||
case 'TSDeclareFunction':
|
||||
case 'TSFunctionType':
|
||||
case 'TSMethodSignature': {
|
||||
const type = node?.returnType?.typeAnnotation?.type;
|
||||
return type && !undefinedKeywords.has(type);
|
||||
}
|
||||
|
||||
// case 'MethodDefinition':
|
||||
// return allBrancheshaveReturnValues(node.value, promFilter);
|
||||
case 'FunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'ArrowFunctionExpression': {
|
||||
return 'expression' in node && node.expression && (!isNewPromiseExpression(node.body) || !isVoidPromise(node.body)) ||
|
||||
allBrancheshaveReturnValues(node.body, promFilter) ||
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.BlockStatement} */
|
||||
(node.body).body.some((nde) => {
|
||||
return nde.type === 'ReturnStatement';
|
||||
});
|
||||
}
|
||||
|
||||
case 'BlockStatement': {
|
||||
const lastBodyNode = node.body.slice(-1)[0];
|
||||
return allBrancheshaveReturnValues(lastBodyNode, promFilter);
|
||||
}
|
||||
|
||||
case 'WhileStatement':
|
||||
case 'DoWhileStatement':
|
||||
if (
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.Literal}
|
||||
*/
|
||||
(node.test).value === true
|
||||
) {
|
||||
// If this is an infinite loop, we assume only one branch
|
||||
// is needed to provide a return
|
||||
return hasReturnValue(node.body, false, promFilter);
|
||||
}
|
||||
|
||||
// Fallthrough
|
||||
case 'LabeledStatement':
|
||||
case 'ForStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'WithStatement': {
|
||||
return allBrancheshaveReturnValues(node.body, promFilter);
|
||||
}
|
||||
|
||||
case 'IfStatement': {
|
||||
return allBrancheshaveReturnValues(node.consequent, promFilter) &&
|
||||
allBrancheshaveReturnValues(node.alternate, promFilter);
|
||||
}
|
||||
|
||||
case 'TryStatement': {
|
||||
// If `finally` returns, all return
|
||||
return node.finalizer && allBrancheshaveReturnValues(node.finalizer, promFilter) ||
|
||||
// Return in `try`/`catch` may still occur despite `finally`
|
||||
allBrancheshaveReturnValues(node.block, promFilter) &&
|
||||
(!node.handler ||
|
||||
allBrancheshaveReturnValues(node.handler && node.handler.body, promFilter)) &&
|
||||
(!node.finalizer || (() => {
|
||||
try {
|
||||
hasReturnValue(node.finalizer, true, promFilter);
|
||||
} catch (error) {
|
||||
if (/** @type {Error} */ (error).message === 'Null return') {
|
||||
return false;
|
||||
}
|
||||
/* c8 ignore next 2 */
|
||||
throw error;
|
||||
}
|
||||
|
||||
// As long as not an explicit empty return, then return true
|
||||
return true;
|
||||
})());
|
||||
}
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return /** @type {import('@typescript-eslint/types').TSESTree.SwitchStatement} */ (node).cases.every(
|
||||
(someCase) => {
|
||||
return !someCase.consequent.some((consNode) => {
|
||||
return consNode.type === 'BreakStatement' ||
|
||||
consNode.type === 'ReturnStatement' && consNode.argument === null;
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
case 'ThrowStatement': {
|
||||
return true;
|
||||
}
|
||||
|
||||
case 'ReturnStatement': {
|
||||
// void return does not count.
|
||||
if (node.argument === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (promFilter && isNewPromiseExpression(node.argument)) {
|
||||
// Let caller decide how to filter, but this is, at the least,
|
||||
// a return of sorts and truthy
|
||||
return promFilter(node.argument);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
default: {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @callback PromiseFilter
|
||||
* @param {ESTreeOrTypeScriptNode|undefined} node
|
||||
* @returns {boolean}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Avoids further checking child nodes if a nested function shadows the
|
||||
* resolver, but otherwise, if name is used (by call or passed in as an
|
||||
* argument to another function), will be considered as non-empty.
|
||||
*
|
||||
* This could check for redeclaration of the resolver, but as such is
|
||||
* unlikely, we avoid the performance cost of checking everywhere for
|
||||
* (re)declarations or assignments.
|
||||
* @param {import('@typescript-eslint/types').TSESTree.Node|null|undefined} node
|
||||
* @param {string} resolverName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
// eslint-disable-next-line complexity
|
||||
const hasNonEmptyResolverCall = (node, resolverName) => {
|
||||
if (!node) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Arrow function without block
|
||||
switch (node.type) {
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'OptionalCallExpression':
|
||||
case 'CallExpression':
|
||||
return /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
node.callee
|
||||
).name === resolverName && (
|
||||
|
||||
// Implicit or explicit undefined
|
||||
node.arguments.length > 1 || node.arguments[0] !== undefined
|
||||
) ||
|
||||
node.arguments.some((nde) => {
|
||||
// Being passed in to another function (which might invoke it)
|
||||
return nde.type === 'Identifier' && nde.name === resolverName ||
|
||||
|
||||
// Handle nested items
|
||||
hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
case 'ChainExpression':
|
||||
case 'Decorator':
|
||||
case 'ExpressionStatement':
|
||||
return hasNonEmptyResolverCall(node.expression, resolverName);
|
||||
case 'ClassBody':
|
||||
case 'BlockStatement':
|
||||
return node.body.some((bodyNode) => {
|
||||
return hasNonEmptyResolverCall(bodyNode, resolverName);
|
||||
});
|
||||
case 'FunctionExpression':
|
||||
case 'FunctionDeclaration':
|
||||
case 'ArrowFunctionExpression': {
|
||||
// Shadowing
|
||||
if (/** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
node.params[0]
|
||||
)?.name === resolverName) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasNonEmptyResolverCall(node.body, resolverName);
|
||||
}
|
||||
|
||||
case 'LabeledStatement':
|
||||
case 'WhileStatement':
|
||||
case 'DoWhileStatement':
|
||||
case 'ForStatement':
|
||||
case 'ForInStatement':
|
||||
case 'ForOfStatement':
|
||||
case 'WithStatement': {
|
||||
return hasNonEmptyResolverCall(node.body, resolverName);
|
||||
}
|
||||
|
||||
case 'ConditionalExpression':
|
||||
case 'IfStatement': {
|
||||
return hasNonEmptyResolverCall(node.test, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.consequent, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.alternate, resolverName);
|
||||
}
|
||||
|
||||
case 'TryStatement': {
|
||||
return hasNonEmptyResolverCall(node.block, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.handler && node.handler.body, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.finalizer, resolverName);
|
||||
}
|
||||
|
||||
case 'SwitchStatement': {
|
||||
return node.cases.some(
|
||||
(someCase) => {
|
||||
return someCase.consequent.some((nde) => {
|
||||
return hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
case 'ArrayPattern':
|
||||
case 'ArrayExpression':
|
||||
return node.elements.some((element) => {
|
||||
return hasNonEmptyResolverCall(element, resolverName);
|
||||
});
|
||||
|
||||
case 'AssignmentPattern':
|
||||
return hasNonEmptyResolverCall(node.right, resolverName);
|
||||
|
||||
case 'AssignmentExpression':
|
||||
case 'BinaryExpression':
|
||||
case 'LogicalExpression': {
|
||||
return hasNonEmptyResolverCall(node.left, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.right, resolverName);
|
||||
}
|
||||
|
||||
// Comma
|
||||
case 'SequenceExpression':
|
||||
case 'TemplateLiteral':
|
||||
return node.expressions.some((subExpression) => {
|
||||
return hasNonEmptyResolverCall(subExpression, resolverName);
|
||||
});
|
||||
|
||||
case 'ObjectPattern':
|
||||
case 'ObjectExpression':
|
||||
return node.properties.some((property) => {
|
||||
return hasNonEmptyResolverCall(property, resolverName);
|
||||
});
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ClassMethod':
|
||||
case 'MethodDefinition':
|
||||
return node.decorators && node.decorators.some((decorator) => {
|
||||
return hasNonEmptyResolverCall(decorator, resolverName);
|
||||
}) ||
|
||||
node.computed && hasNonEmptyResolverCall(node.key, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.value, resolverName);
|
||||
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ObjectProperty':
|
||||
/* eslint-disable no-fallthrough */
|
||||
/* c8 ignore next -- In Babel? */
|
||||
case 'PropertyDefinition':
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ClassProperty':
|
||||
case 'Property':
|
||||
/* eslint-enable no-fallthrough */
|
||||
return node.computed && hasNonEmptyResolverCall(node.key, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.value, resolverName);
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'ObjectMethod':
|
||||
/* c8 ignore next 6 -- In Babel? */
|
||||
// @ts-expect-error
|
||||
return node.computed && hasNonEmptyResolverCall(node.key, resolverName) ||
|
||||
// @ts-expect-error
|
||||
node.arguments.some((nde) => {
|
||||
return hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
|
||||
case 'ClassExpression':
|
||||
case 'ClassDeclaration':
|
||||
return hasNonEmptyResolverCall(node.body, resolverName);
|
||||
|
||||
case 'AwaitExpression':
|
||||
case 'SpreadElement':
|
||||
case 'UnaryExpression':
|
||||
case 'YieldExpression':
|
||||
return hasNonEmptyResolverCall(node.argument, resolverName);
|
||||
|
||||
case 'VariableDeclaration': {
|
||||
return node.declarations.some((nde) => {
|
||||
return hasNonEmptyResolverCall(nde, resolverName);
|
||||
});
|
||||
}
|
||||
|
||||
case 'VariableDeclarator': {
|
||||
return hasNonEmptyResolverCall(node.id, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.init, resolverName);
|
||||
}
|
||||
|
||||
case 'TaggedTemplateExpression':
|
||||
return hasNonEmptyResolverCall(node.quasi, resolverName);
|
||||
|
||||
// ?.
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'OptionalMemberExpression':
|
||||
case 'MemberExpression':
|
||||
return hasNonEmptyResolverCall(node.object, resolverName) ||
|
||||
hasNonEmptyResolverCall(node.property, resolverName);
|
||||
|
||||
/* c8 ignore next 2 -- In Babel? */
|
||||
// @ts-expect-error Babel?
|
||||
case 'Import':
|
||||
case 'ImportExpression':
|
||||
return hasNonEmptyResolverCall(node.source, resolverName);
|
||||
|
||||
case 'ReturnStatement': {
|
||||
if (node.argument === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasNonEmptyResolverCall(node.argument, resolverName);
|
||||
}
|
||||
|
||||
/*
|
||||
// Shouldn't need to parse literals/literal components, etc.
|
||||
|
||||
case 'Identifier':
|
||||
case 'TemplateElement':
|
||||
case 'Super':
|
||||
// Exports not relevant in this context
|
||||
*/
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks if a Promise executor has no resolve value or an empty value.
|
||||
* An `undefined` resolve does not count.
|
||||
* @param {ESTreeOrTypeScriptNode} node
|
||||
* @param {boolean} anyPromiseAsReturn
|
||||
* @param {boolean} [allBranches]
|
||||
* @returns {boolean}
|
||||
*/
|
||||
const hasValueOrExecutorHasNonEmptyResolveValue = (node, anyPromiseAsReturn, allBranches) => {
|
||||
const hasReturnMethod = allBranches ?
|
||||
/**
|
||||
* @param {ESTreeOrTypeScriptNode} nde
|
||||
* @param {PromiseFilter} promiseFilter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
(nde, promiseFilter) => {
|
||||
let hasReturn;
|
||||
try {
|
||||
hasReturn = hasReturnValue(nde, true, promiseFilter);
|
||||
} catch (error) {
|
||||
// c8 ignore else
|
||||
if (/** @type {Error} */ (error).message === 'Null return') {
|
||||
return false;
|
||||
}
|
||||
/* c8 ignore next 2 */
|
||||
throw error;
|
||||
}
|
||||
|
||||
// `hasReturn` check needed since `throw` treated as valid return by
|
||||
// `allBrancheshaveReturnValues`
|
||||
return Boolean(hasReturn && allBrancheshaveReturnValues(nde, promiseFilter));
|
||||
} :
|
||||
/**
|
||||
* @param {ESTreeOrTypeScriptNode} nde
|
||||
* @param {PromiseFilter} promiseFilter
|
||||
* @returns {boolean}
|
||||
*/
|
||||
(nde, promiseFilter) => {
|
||||
return Boolean(hasReturnValue(nde, false, promiseFilter));
|
||||
};
|
||||
|
||||
return hasReturnMethod(node, (prom) => {
|
||||
if (anyPromiseAsReturn) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isVoidPromise(prom)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
params,
|
||||
body,
|
||||
} =
|
||||
/**
|
||||
* @type {import('@typescript-eslint/types').TSESTree.FunctionExpression|
|
||||
* import('@typescript-eslint/types').TSESTree.ArrowFunctionExpression}
|
||||
*/ (
|
||||
/** @type {import('@typescript-eslint/types').TSESTree.NewExpression} */ (
|
||||
prom
|
||||
).arguments[0]
|
||||
) || {};
|
||||
|
||||
if (!params?.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {
|
||||
name: resolverName,
|
||||
} = /** @type {import('@typescript-eslint/types').TSESTree.Identifier} */ (
|
||||
params[0]
|
||||
);
|
||||
|
||||
return hasNonEmptyResolverCall(body, resolverName);
|
||||
});
|
||||
};
|
||||
|
||||
export {
|
||||
hasReturnValue,
|
||||
hasValueOrExecutorHasNonEmptyResolveValue,
|
||||
};
|
||||
Reference in New Issue
Block a user