diff --git a/.README/README.md b/.README/README.md index ff66e1524..bb5f60a51 100644 --- a/.README/README.md +++ b/.README/README.md @@ -296,6 +296,10 @@ only (e.g., to match `Array` if the type is `Array` vs. `Array.`). `@returns` documentation regardless of implicit or explicit `return`'s in the function. May be desired to flag that a project is aware of an `undefined`/`void` return. +* `settings.jsdoc.forceReturnsWithAsync` - Set to `true` to always insist on + `@returns` documentation regardless of implicit or explicit `return`'s + in an async function. May be desired to flag that a project is aware of an + `Promise` return. ### Settings to Configure `require-example` diff --git a/.README/rules/require-returns.md b/.README/rules/require-returns.md index ebbb3ab9b..ed4503cef 100644 --- a/.README/rules/require-returns.md +++ b/.README/rules/require-returns.md @@ -7,6 +7,6 @@ Requires returns are documented. |Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`| |Tags|`returns`| |Aliases|`return`| -|Settings|`forceRequireReturn`| +|Settings|`forceRequireReturn`, `forceReturnsWithAsync`| diff --git a/README.md b/README.md index ee4f880ed..1b50735e5 100644 --- a/README.md +++ b/README.md @@ -351,6 +351,10 @@ only (e.g., to match `Array` if the type is `Array` vs. `Array.`). `@returns` documentation regardless of implicit or explicit `return`'s in the function. May be desired to flag that a project is aware of an `undefined`/`void` return. +* `settings.jsdoc.forceReturnsWithAsync` - Set to `true` to always insist on + `@returns` documentation regardless of implicit or explicit `return`'s + in an async function. May be desired to flag that a project is aware of an + `Promise` return. ### Settings to Configure require-example @@ -4569,7 +4573,7 @@ Requires returns are documented. |Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`| |Tags|`returns`| |Aliases|`return`| -|Settings|`forceRequireReturn`| +|Settings|`forceRequireReturn`, `forceReturnsWithAsync`| The following patterns are considered problems: @@ -4616,27 +4620,17 @@ function quux (foo) { /** * */ -async function quux() {} -// Message: Missing JSDoc @returns declaration. - -/** - * - */ -const quux = async function () {} -// Message: Missing JSDoc @returns declaration. - -/** - * - */ -const quux = async () => {} +function quux () { +} +// Settings: {"jsdoc":{"forceRequireReturn":true}} // Message: Missing JSDoc @returns declaration. /** * */ -function quux () { +async function quux () { } -// Settings: {"jsdoc":{"forceRequireReturn":true}} +// Settings: {"jsdoc":{"forceReturnsWithAsync":true}} // Message: Missing JSDoc @returns declaration. const language = { @@ -4857,6 +4851,35 @@ function quux () { } // Settings: {"jsdoc":{"forceRequireReturn":true}} +/** + * @returns {Promise} + */ +async function quux () { +} +// Settings: {"jsdoc":{"forceRequireReturn":true}} + +/** + * @returns {Promise} + */ +async function quux () { +} +// Settings: {"jsdoc":{"forceReturnsWithAsync":true}} + +/** + * + */ +async function quux () {} + +/** + * + */ +const quux = async function () {} + +/** + * + */ +const quux = async () => {} + /** foo class */ class foo { /** foo constructor */ diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index 1a3d790ee..b29471975 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -46,6 +46,7 @@ const curryUtils = ( allowAugmentsExtendsWithoutParam, checkSeesForNamepaths, forceRequireReturn, + forceReturnsWithAsync, avoidExampleOnConstructors, ancestors, sourceCode, @@ -199,8 +200,12 @@ const curryUtils = ( return jsdocUtils.hasDefinedTypeReturnTag(tag); }; - utils.hasReturnValue = () => { - return jsdocUtils.hasReturnValue(node, context); + utils.hasReturnValue = (ignoreAsync = false) => { + return jsdocUtils.hasReturnValue(node, context, ignoreAsync); + }; + + utils.isAsync = () => { + return node.async; }; utils.getTags = (tagName) => { @@ -213,6 +218,10 @@ const curryUtils = ( return forceRequireReturn; }; + utils.isForceReturnsWithAsync = () => { + return forceReturnsWithAsync; + }; + utils.filterTags = (filter) => { return (jsdoc.tags || []).filter(filter); }; @@ -312,6 +321,7 @@ export default (iterator, opts = {}) => { // `require-returns` only const forceRequireReturn = Boolean(_.get(context, 'settings.jsdoc.forceRequireReturn')); + const forceReturnsWithAsync = Boolean(_.get(context, 'settings.jsdoc.forceReturnsWithAsync')); // `require-example` only const avoidExampleOnConstructors = Boolean(_.get(context, 'settings.jsdoc.avoidExampleOnConstructors')); @@ -388,6 +398,7 @@ export default (iterator, opts = {}) => { allowAugmentsExtendsWithoutParam, checkSeesForNamepaths, forceRequireReturn, + forceReturnsWithAsync, avoidExampleOnConstructors, ancestors, sourceCode diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js index 016533486..7f4fae18b 100644 --- a/src/jsdocUtils.js +++ b/src/jsdocUtils.js @@ -372,18 +372,18 @@ const lookupTable = { is (node) { return node.type === 'FunctionExpression'; }, - check (node, context) { - return node.async || lookupTable.BlockStatement.check(node.body, context); + check (node, context, ignoreAsync) { + return !ignoreAsync && node.async || lookupTable.BlockStatement.check(node.body, context); } }, ArrowFunctionExpression: { is (node) { return node.type === 'ArrowFunctionExpression'; }, - check (node, context) { + check (node, context, ignoreAsync) { // An expression always has a return value. return node.expression || - node.async || + !ignoreAsync && node.async || lookupTable.BlockStatement.check(node.body, context); } }, @@ -391,8 +391,8 @@ const lookupTable = { is (node) { return node.type === 'FunctionDeclaration'; }, - check (node, context) { - return node.async || lookupTable.BlockStatement.check(node.body, context); + check (node, context, ignoreAsync) { + return !ignoreAsync && node.async || lookupTable.BlockStatement.check(node.body, context); } }, '@default': { @@ -431,14 +431,17 @@ const lookupTable = { * * @param {Object} node * the node which should be checked. + * @param {Object} context + * @param {boolean} ignoreAsync + * ignore implicit async return. * @returns {boolean} * true in case the code returns a return value */ -const hasReturnValue = (node, context) => { +const hasReturnValue = (node, context, ignoreAsync) => { // Loop through all of our entry points for (const item of ENTRY_POINTS) { if (lookupTable[item].is(node)) { - return lookupTable[item].check(node, context); + return lookupTable[item].check(node, context, ignoreAsync); } } diff --git a/src/rules/requireReturns.js b/src/rules/requireReturns.js index 54a7e2a04..7ad5e11be 100644 --- a/src/rules/requireReturns.js +++ b/src/rules/requireReturns.js @@ -58,7 +58,7 @@ export default iterateJsdoc(({ const [tag] = tags; const missingReturnTag = typeof tag === 'undefined' || tag === null; if (missingReturnTag && - (utils.hasReturnValue() || utils.isForceRequireReturn()) + ((utils.isAsync() && !utils.hasReturnValue(true) ? utils.isForceReturnsWithAsync() : utils.hasReturnValue()) || utils.isForceRequireReturn()) ) { report('Missing JSDoc @' + tagName + ' declaration.'); } diff --git a/test/rules/assertions/requireReturns.js b/test/rules/assertions/requireReturns.js index 267c67b2d..2b7217a60 100644 --- a/test/rules/assertions/requireReturns.js +++ b/test/rules/assertions/requireReturns.js @@ -90,7 +90,8 @@ export default { /** * */ - async function quux() {} + function quux () { + } `, errors: [ { @@ -98,25 +99,10 @@ export default { message: 'Missing JSDoc @returns declaration.' } ], - parserOptions: { - ecmaVersion: 8 - } - }, - { - code: ` - /** - * - */ - const quux = async function () {} - `, - errors: [ - { - line: 2, - message: 'Missing JSDoc @returns declaration.' + settings: { + jsdoc: { + forceRequireReturn: true } - ], - parserOptions: { - ecmaVersion: 8 } }, { @@ -124,7 +110,8 @@ export default { /** * */ - const quux = async () => {} + async function quux () { + } `, errors: [ { @@ -134,25 +121,10 @@ export default { ], parserOptions: { ecmaVersion: 8 - } - }, - { - code: ` - /** - * - */ - function quux () { - } - `, - errors: [ - { - line: 2, - message: 'Missing JSDoc @returns declaration.' - } - ], + }, settings: { jsdoc: { - forceRequireReturn: true + forceReturnsWithAsync: true } } }, @@ -487,6 +459,73 @@ export default { } } }, + { + code: ` + /** + * @returns {Promise} + */ + async function quux () { + } + `, + parserOptions: { + ecmaVersion: 8 + }, + settings: { + jsdoc: { + forceRequireReturn: true + } + } + }, + { + code: ` + /** + * @returns {Promise} + */ + async function quux () { + } + `, + parserOptions: { + ecmaVersion: 8 + }, + settings: { + jsdoc: { + forceReturnsWithAsync: true + } + } + }, + { + code: ` + /** + * + */ + async function quux () {} + `, + parserOptions: { + ecmaVersion: 8 + } + }, + { + code: ` + /** + * + */ + const quux = async function () {} + `, + parserOptions: { + ecmaVersion: 8 + } + }, + { + code: ` + /** + * + */ + const quux = async () => {} + `, + parserOptions: { + ecmaVersion: 8 + } + }, { code: ` /** foo class */