Skip to content

Commit

Permalink
feat(require-returns-check): add exemptAsync option
Browse files Browse the repository at this point in the history
  • Loading branch information
brettz9 committed Jan 31, 2021
1 parent 445b020 commit 0ed24c0
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 15 deletions.
14 changes: 14 additions & 0 deletions .README/rules/require-returns-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,25 @@ in jsdoc comment.

Will also report if multiple `@returns` tags are present.

#### Options

- `exemptAsync` - By default, functions which return a `Promise` that are not
detected as resolving with a non-`undefined` value and `async` functions
(even ones that do not explicitly return a value, as these are returning a
`Promise` implicitly) will be exempted from reporting by this rule.
If you wish to insist that only `Promise`'s which resolve to
non-`undefined` values or `async` functions with explicit `return`'s will
be exempted from reporting (i.e., that `async` functions can be reported
if they lack an explicit (non-`undefined`) `return` when a `@returns` is
present), you can set `exemptAsync` to `false` on the options object.
Defaults to `true`.

|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Tags|`returns`|
|Aliases|`return`|
|Options|`exemptAsync`|
|Recommended|true|

<!-- assertions requireReturnsCheck -->
82 changes: 73 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13737,11 +13737,26 @@ in jsdoc comment.

Will also report if multiple `@returns` tags are present.

<a name="eslint-plugin-jsdoc-rules-require-returns-check-options-27"></a>
#### Options

- `exemptAsync` - By default, functions which return a `Promise` that are not
detected as resolving with a non-`undefined` value and `async` functions
(even ones that do not explicitly return a value, as these are returning a
`Promise` implicitly) will be exempted from reporting by this rule.
If you wish to insist that only `Promise`'s which resolve to
non-`undefined` values or `async` functions with explicit `return`'s will
be exempted from reporting (i.e., that `async` functions can be reported
if they lack an explicit (non-`undefined`) `return` when a `@returns` is
present), you can set `exemptAsync` to `false` on the options object.
Defaults to `true`.

|||
|---|---|
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
|Tags|`returns`|
|Aliases|`return`|
|Options|`exemptAsync`|
|Recommended|true|

The following patterns are considered problems:
Expand Down Expand Up @@ -13822,6 +13837,35 @@ function f () {
}
}
// Message: JSDoc @returns declaration present but return expression not available in function.

/**
* @returns {Promise<void>}
*/
async function quux() {}
// Options: [{"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.

/**
* @returns {Promise<void>}
*/
function quux() {
return new Promise((resolve, reject) => {})
}
// Options: [{"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.

/**
* @returns {Promise<void>}
*/
function quux() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
});
})
}
// Options: [{"exemptAsync":false}]
// Message: JSDoc @returns declaration present but return expression not available in function.
````

The following patterns are not considered problems:
Expand Down Expand Up @@ -14127,6 +14171,26 @@ function quux () {
}
return;
}

/**
* @returns {Promise<void>}
*/
async function quux() {
return 5;
}
// Options: [{"exemptAsync":false}]

/**
* @returns {Promise<void>}
*/
function quux() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
});
})
}
// Options: [{"exemptAsync":false}]
````


Expand All @@ -14137,10 +14201,10 @@ Requires that the `@returns` tag has a `description` value. The error
will not be reported if the return value is `void` or `undefined`
or if it is `Promise<void>` or `Promise<undefined>`.

<a name="eslint-plugin-jsdoc-rules-require-returns-description-options-27"></a>
<a name="eslint-plugin-jsdoc-rules-require-returns-description-options-28"></a>
#### Options

<a name="eslint-plugin-jsdoc-rules-require-returns-description-options-27-contexts-10"></a>
<a name="eslint-plugin-jsdoc-rules-require-returns-description-options-28-contexts-10"></a>
##### <code>contexts</code>

Set this to an array of strings representing the AST context
Expand Down Expand Up @@ -14293,10 +14357,10 @@ function quux () {

Requires that `@returns` tag has `type` value.

<a name="eslint-plugin-jsdoc-rules-require-returns-type-options-28"></a>
<a name="eslint-plugin-jsdoc-rules-require-returns-type-options-29"></a>
#### Options

<a name="eslint-plugin-jsdoc-rules-require-returns-type-options-28-contexts-11"></a>
<a name="eslint-plugin-jsdoc-rules-require-returns-type-options-29-contexts-11"></a>
##### <code>contexts</code>

Set this to an array of strings representing the AST context
Expand Down Expand Up @@ -14416,7 +14480,7 @@ Requires that returns are documented.

Will also report if multiple `@returns` tags are present.

<a name="eslint-plugin-jsdoc-rules-require-returns-options-29"></a>
<a name="eslint-plugin-jsdoc-rules-require-returns-options-30"></a>
#### Options

- `checkConstructors` - A value indicating whether `constructor`s should
Expand Down Expand Up @@ -15436,7 +15500,7 @@ async function foo() {

Requires that throw statements are documented.

<a name="eslint-plugin-jsdoc-rules-require-throws-options-30"></a>
<a name="eslint-plugin-jsdoc-rules-require-throws-options-31"></a>
#### Options

- `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the
Expand Down Expand Up @@ -15711,7 +15775,7 @@ Will also report if multiple `@yields` tags are present.
See the `next`, `forceRequireNext`, and `nextWithGeneratorTag` options for an
option to expect a non-standard `@next` tag.

<a name="eslint-plugin-jsdoc-rules-require-yields-options-31"></a>
<a name="eslint-plugin-jsdoc-rules-require-yields-options-32"></a>
#### Options

- `exemptedBy` - Array of tags (e.g., `['type']`) whose presence on the
Expand Down Expand Up @@ -16511,7 +16575,7 @@ function bodies.

Will also report if multiple `@yields` tags are present.

<a name="eslint-plugin-jsdoc-rules-require-yields-check-options-32"></a>
<a name="eslint-plugin-jsdoc-rules-require-yields-check-options-33"></a>
#### Options

- `checkGeneratorsOnly` - Avoids checking the function body and merely insists
Expand Down Expand Up @@ -17064,7 +17128,7 @@ for valid types (based on the tag's `type` value), and either portion checked
for presence (based on `false` `name` or `type` values or their `required`
value). See the setting for more details.

<a name="eslint-plugin-jsdoc-rules-valid-types-options-33"></a>
<a name="eslint-plugin-jsdoc-rules-valid-types-options-34"></a>
#### Options

- `allowEmptyNamepaths` (default: true) - Set to `false` to bulk disallow
Expand Down
4 changes: 0 additions & 4 deletions src/iterateJsdoc.js
Original file line number Diff line number Diff line change
Expand Up @@ -457,10 +457,6 @@ const getUtils = (
return jsdocUtils.hasDefinedTypeTag(tag);
};

utils.hasReturnValue = () => {
return jsdocUtils.hasReturnValue(node);
};

utils.hasValueOrExecutorHasNonEmptyResolveValue = (anyPromiseAsReturn) => {
return jsdocUtils.hasValueOrExecutorHasNonEmptyResolveValue(node, anyPromiseAsReturn);
};
Expand Down
21 changes: 19 additions & 2 deletions src/rules/requireReturnsCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,20 @@ const canSkip = (utils, settings) => {
};

export default iterateJsdoc(({
context,
report,
settings,
utils,
}) => {
const {
exemptAsync = true,
} = context.options[0] || {};

if (canSkip(utils, settings)) {
return;
}

if (utils.isAsync()) {
if (exemptAsync && utils.isAsync()) {
return;
}

Expand All @@ -58,7 +63,7 @@ export default iterateJsdoc(({
}

// In case a return value is declared in JSDoc, we also expect one in the code.
if (utils.hasDefinedTypeTag(tags[0]) && !utils.hasReturnValue()) {
if (utils.hasDefinedTypeTag(tags[0]) && !utils.hasValueOrExecutorHasNonEmptyResolveValue(exemptAsync)) {
report(`JSDoc @${tagName} declaration present but return expression not available in function.`);
}
}, {
Expand All @@ -67,6 +72,18 @@ export default iterateJsdoc(({
description: 'Requires a return statement in function body if a `@returns` tag is specified in jsdoc comment.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-require-returns-check',
},
schema: [
{
additionalProperties: false,
properties: {
exemptAsync: {
default: true,
type: 'boolean',
},
},
type: 'object',
},
],
type: 'suggestion',
},
});
95 changes: 95 additions & 0 deletions test/rules/assertions/requireReturnsCheck.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,68 @@ export default {
},
],
},
{
code: `
/**
* @returns {Promise<void>}
*/
async function quux() {}
`,
errors: [
{
line: 2,
message: 'JSDoc @returns declaration present but return expression not available in function.',
},
],
options: [{
exemptAsync: false,
}],
parserOptions: {
ecmaVersion: 8,
},
},
{
code: `
/**
* @returns {Promise<void>}
*/
function quux() {
return new Promise((resolve, reject) => {})
}
`,
errors: [
{
line: 2,
message: 'JSDoc @returns declaration present but return expression not available in function.',
},
],
options: [{
exemptAsync: false,
}],
},
{
code: `
/**
* @returns {Promise<void>}
*/
function quux() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
});
})
}
`,
errors: [
{
line: 2,
message: 'JSDoc @returns declaration present but return expression not available in function.',
},
],
options: [{
exemptAsync: false,
}],
},
],
valid: [
{
Expand Down Expand Up @@ -575,5 +637,38 @@ export default {
}
`,
},
{
code: `
/**
* @returns {Promise<void>}
*/
async function quux() {
return 5;
}
`,
options: [{
exemptAsync: false,
}],
parserOptions: {
ecmaVersion: 8,
},
},
{
code: `
/**
* @returns {Promise<void>}
*/
function quux() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(true);
});
})
}
`,
options: [{
exemptAsync: false,
}],
},
],
};

0 comments on commit 0ed24c0

Please sign in to comment.