Skip to content
7 changes: 7 additions & 0 deletions .README/rules/require-returns.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,18 @@

Requires returns are documented.

By default `async` functions that do not explicitly return a value pass this rule. You can force all `async` functions to require return statements by setting `forceReturnsWithAsync` as true on the options object. This may be useful as an `async` function will always return a Promise, even if the Promise returns void.

```js
'jsdoc/require-jsdoc': ['error', {forceReturnsWithAsync: true}]
```

|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Tags|`returns`|
|Aliases|`return`|
|Settings|`forceRequireReturn`|
|Options|`forceReturnsWithAsync`|

<!-- assertions requireReturns -->
64 changes: 63 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4564,12 +4564,19 @@ function quux () {

Requires returns are documented.

By default `async` functions that do not explicitly return a value pass this rule. You can force all `async` functions to require return statements by setting `forceReturnsWithAsync` as true on the options object. This may be useful as an `async` function will always return a Promise, even if the Promise returns void.

```js
'jsdoc/require-jsdoc': ['error', {forceReturnsWithAsync: true}]
```

|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Tags|`returns`|
|Aliases|`return`|
|Settings|`forceRequireReturn`|
|Options|`forceReturnsWithAsync`|

The following patterns are considered problems:

Expand Down Expand Up @@ -4616,19 +4623,30 @@ function quux (foo) {
/**
*
*/
async function quux() {}
async function quux() {
}
// Settings: {"jsdoc":{"forceRequireReturn":true}}
// Message: Missing JSDoc @returns declaration.

/**
*
*/
const quux = async function () {}
// Settings: {"jsdoc":{"forceRequireReturn":true}}
// Message: Missing JSDoc @returns declaration.

/**
*
*/
const quux = async () => {}
// Settings: {"jsdoc":{"forceRequireReturn":true}}
// Message: Missing JSDoc @returns declaration.

/**
*
*/
async function quux () {}
// Settings: {"jsdoc":{"forceRequireReturn":true}}
// Message: Missing JSDoc @returns declaration.

/**
Expand All @@ -4648,6 +4666,14 @@ const language = {
}
}
// Message: Missing JSDoc @returns declaration.

/**
*
*/
async function quux () {
}
// Options: [{"forceReturnsWithAsync":true}]
// Message: Missing JSDoc @returns declaration.
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -4857,6 +4883,35 @@ function quux () {
}
// Settings: {"jsdoc":{"forceRequireReturn":true}}

/**
* @returns {Promise}
*/
async function quux () {
}
// Settings: {"jsdoc":{"forceRequireReturn":true}}

/**
* @returns {Promise}
*/
async function quux () {
}
// Options: [{"forceReturnsWithAsync":true}]

/**
*
*/
async function quux () {}

/**
*
*/
const quux = async function () {}

/**
*
*/
const quux = async () => {}

/** foo class */
class foo {
/** foo constructor */
Expand All @@ -4867,6 +4922,13 @@ class foo {
}

export default foo;

/**
*
*/
function quux () {
}
// Options: [{"forceReturnsWithAsync":true}]
````


Expand Down
8 changes: 6 additions & 2 deletions src/iterateJsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,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) => {
Expand Down
19 changes: 11 additions & 8 deletions src/jsdocUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -372,27 +372,27 @@ 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);
}
},
FunctionDeclaration: {
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': {
Expand Down Expand Up @@ -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);
}
}

Expand Down
7 changes: 5 additions & 2 deletions src/rules/requireReturns.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,17 @@ const canSkip = (utils) => {

export default iterateJsdoc(({
report,
utils
utils,
context
}) => {
// A preflight check. We do not need to run a deep check
// in case the @returns comment is optional or undefined.
if (canSkip(utils)) {
return;
}

const options = context.options[0] || {};

const tagName = utils.getPreferredTagName('returns');
const tags = utils.getTags(tagName);

Expand All @@ -58,7 +61,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) ? Boolean(options.forceReturnsWithAsync) : utils.hasReturnValue()) || utils.isForceRequireReturn())
) {
report('Missing JSDoc @' + tagName + ' declaration.');
}
Expand Down
Loading