Skip to content

Commit 7e4369b

Browse files
AndrewLeedhambrettz9
authored andcommitted
feat(require-returns): add forceReturnsWithAsync
Async functions implicitly returning Promise<void> are now behind the forceReturnsWithAsync (or forceRequireReturn) setting.
1 parent 0f54957 commit 7e4369b

File tree

7 files changed

+184
-15
lines changed

7 files changed

+184
-15
lines changed

.README/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,10 @@ only (e.g., to match `Array` if the type is `Array` vs. `Array.<string>`).
296296
`@returns` documentation regardless of implicit or explicit `return`'s
297297
in the function. May be desired to flag that a project is aware of an
298298
`undefined`/`void` return.
299+
* `settings.jsdoc.forceReturnsWithAsync` - Set to `true` to always insist on
300+
`@returns` documentation regardless of implicit or explicit `return`'s
301+
in an async function. May be desired to flag that a project is aware of a
302+
`Promise<void>` return.
299303

300304
### Settings to Configure `require-example`
301305

.README/rules/require-returns.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ Requires returns are documented.
77
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
88
|Tags|`returns`|
99
|Aliases|`return`|
10-
|Settings|`forceRequireReturn`|
10+
|Settings|`forceRequireReturn`, `forceReturnsWithAsync`|
1111

1212
<!-- assertions requireReturns -->

README.md

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,10 @@ only (e.g., to match `Array` if the type is `Array` vs. `Array.<string>`).
351351
`@returns` documentation regardless of implicit or explicit `return`'s
352352
in the function. May be desired to flag that a project is aware of an
353353
`undefined`/`void` return.
354+
* `settings.jsdoc.forceReturnsWithAsync` - Set to `true` to always insist on
355+
`@returns` documentation regardless of implicit or explicit `return`'s
356+
in an async function. May be desired to flag that a project is aware of a
357+
`Promise<void>` return.
354358

355359
<a name="eslint-plugin-jsdoc-settings-settings-to-configure-require-example"></a>
356360
### Settings to Configure <code>require-example</code>
@@ -4569,7 +4573,7 @@ Requires returns are documented.
45694573
|Context|`ArrowFunctionExpression`, `FunctionDeclaration`, `FunctionExpression`|
45704574
|Tags|`returns`|
45714575
|Aliases|`return`|
4572-
|Settings|`forceRequireReturn`|
4576+
|Settings|`forceRequireReturn`, `forceReturnsWithAsync`|
45734577

45744578
The following patterns are considered problems:
45754579

@@ -4616,19 +4620,23 @@ function quux (foo) {
46164620
/**
46174621
*
46184622
*/
4619-
async function quux() {}
4623+
async function quux() {
4624+
}
4625+
// Settings: {"jsdoc":{"forceRequireReturn":true}}
46204626
// Message: Missing JSDoc @returns declaration.
46214627

46224628
/**
46234629
*
46244630
*/
46254631
const quux = async function () {}
4632+
// Settings: {"jsdoc":{"forceRequireReturn":true}}
46264633
// Message: Missing JSDoc @returns declaration.
46274634

46284635
/**
46294636
*
46304637
*/
46314638
const quux = async () => {}
4639+
// Settings: {"jsdoc":{"forceRequireReturn":true}}
46324640
// Message: Missing JSDoc @returns declaration.
46334641

46344642
/**
@@ -4648,6 +4656,14 @@ const language = {
46484656
}
46494657
}
46504658
// Message: Missing JSDoc @returns declaration.
4659+
4660+
/**
4661+
*
4662+
*/
4663+
async function quux () {
4664+
}
4665+
// Settings: {"jsdoc":{"forceReturnsWithAsync":true}}
4666+
// Message: Missing JSDoc @returns declaration.
46514667
````
46524668

46534669
The following patterns are not considered problems:
@@ -4857,6 +4873,35 @@ function quux () {
48574873
}
48584874
// Settings: {"jsdoc":{"forceRequireReturn":true}}
48594875

4876+
/**
4877+
* @returns {Promise}
4878+
*/
4879+
async function quux () {
4880+
}
4881+
// Settings: {"jsdoc":{"forceRequireReturn":true}}
4882+
4883+
/**
4884+
* @returns {Promise}
4885+
*/
4886+
async function quux () {
4887+
}
4888+
// Settings: {"jsdoc":{"forceReturnsWithAsync":true}}
4889+
4890+
/**
4891+
*
4892+
*/
4893+
async function quux () {}
4894+
4895+
/**
4896+
*
4897+
*/
4898+
const quux = async function () {}
4899+
4900+
/**
4901+
*
4902+
*/
4903+
const quux = async () => {}
4904+
48604905
/** foo class */
48614906
class foo {
48624907
/** foo constructor */

src/iterateJsdoc.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ const curryUtils = (
4646
allowAugmentsExtendsWithoutParam,
4747
checkSeesForNamepaths,
4848
forceRequireReturn,
49+
forceReturnsWithAsync,
4950
avoidExampleOnConstructors,
5051
ancestors,
5152
sourceCode,
@@ -199,8 +200,12 @@ const curryUtils = (
199200
return jsdocUtils.hasDefinedTypeReturnTag(tag);
200201
};
201202

202-
utils.hasReturnValue = () => {
203-
return jsdocUtils.hasReturnValue(node, context);
203+
utils.hasReturnValue = (ignoreAsync = false) => {
204+
return jsdocUtils.hasReturnValue(node, context, ignoreAsync);
205+
};
206+
207+
utils.isAsync = () => {
208+
return node.async;
204209
};
205210

206211
utils.getTags = (tagName) => {
@@ -213,6 +218,10 @@ const curryUtils = (
213218
return forceRequireReturn;
214219
};
215220

221+
utils.isForceReturnsWithAsync = () => {
222+
return forceReturnsWithAsync;
223+
};
224+
216225
utils.filterTags = (filter) => {
217226
return (jsdoc.tags || []).filter(filter);
218227
};
@@ -312,6 +321,7 @@ export default (iterator, opts = {}) => {
312321

313322
// `require-returns` only
314323
const forceRequireReturn = Boolean(_.get(context, 'settings.jsdoc.forceRequireReturn'));
324+
const forceReturnsWithAsync = Boolean(_.get(context, 'settings.jsdoc.forceReturnsWithAsync'));
315325

316326
// `require-example` only
317327
const avoidExampleOnConstructors = Boolean(_.get(context, 'settings.jsdoc.avoidExampleOnConstructors'));
@@ -388,6 +398,7 @@ export default (iterator, opts = {}) => {
388398
allowAugmentsExtendsWithoutParam,
389399
checkSeesForNamepaths,
390400
forceRequireReturn,
401+
forceReturnsWithAsync,
391402
avoidExampleOnConstructors,
392403
ancestors,
393404
sourceCode

src/jsdocUtils.js

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -372,27 +372,27 @@ const lookupTable = {
372372
is (node) {
373373
return node.type === 'FunctionExpression';
374374
},
375-
check (node, context) {
376-
return node.async || lookupTable.BlockStatement.check(node.body, context);
375+
check (node, context, ignoreAsync) {
376+
return !ignoreAsync && node.async || lookupTable.BlockStatement.check(node.body, context);
377377
}
378378
},
379379
ArrowFunctionExpression: {
380380
is (node) {
381381
return node.type === 'ArrowFunctionExpression';
382382
},
383-
check (node, context) {
383+
check (node, context, ignoreAsync) {
384384
// An expression always has a return value.
385385
return node.expression ||
386-
node.async ||
386+
!ignoreAsync && node.async ||
387387
lookupTable.BlockStatement.check(node.body, context);
388388
}
389389
},
390390
FunctionDeclaration: {
391391
is (node) {
392392
return node.type === 'FunctionDeclaration';
393393
},
394-
check (node, context) {
395-
return node.async || lookupTable.BlockStatement.check(node.body, context);
394+
check (node, context, ignoreAsync) {
395+
return !ignoreAsync && node.async || lookupTable.BlockStatement.check(node.body, context);
396396
}
397397
},
398398
'@default': {
@@ -431,14 +431,17 @@ const lookupTable = {
431431
*
432432
* @param {Object} node
433433
* the node which should be checked.
434+
* @param {Object} context
435+
* @param {boolean} ignoreAsync
436+
* ignore implicit async return.
434437
* @returns {boolean}
435438
* true in case the code returns a return value
436439
*/
437-
const hasReturnValue = (node, context) => {
440+
const hasReturnValue = (node, context, ignoreAsync) => {
438441
// Loop through all of our entry points
439442
for (const item of ENTRY_POINTS) {
440443
if (lookupTable[item].is(node)) {
441-
return lookupTable[item].check(node, context);
444+
return lookupTable[item].check(node, context, ignoreAsync);
442445
}
443446
}
444447

src/rules/requireReturns.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export default iterateJsdoc(({
5858
const [tag] = tags;
5959
const missingReturnTag = typeof tag === 'undefined' || tag === null;
6060
if (missingReturnTag &&
61-
(utils.hasReturnValue() || utils.isForceRequireReturn())
61+
((utils.isAsync() && !utils.hasReturnValue(true) ? utils.isForceReturnsWithAsync() : utils.hasReturnValue()) || utils.isForceRequireReturn())
6262
) {
6363
report('Missing JSDoc @' + tagName + ' declaration.');
6464
}

test/rules/assertions/requireReturns.js

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ export default {
9090
/**
9191
*
9292
*/
93-
async function quux() {}
93+
async function quux() {
94+
}
9495
`,
9596
errors: [
9697
{
@@ -100,6 +101,11 @@ export default {
100101
],
101102
parserOptions: {
102103
ecmaVersion: 8
104+
},
105+
settings: {
106+
jsdoc: {
107+
forceRequireReturn: true
108+
}
103109
}
104110
},
105111
{
@@ -117,6 +123,11 @@ export default {
117123
],
118124
parserOptions: {
119125
ecmaVersion: 8
126+
},
127+
settings: {
128+
jsdoc: {
129+
forceRequireReturn: true
130+
}
120131
}
121132
},
122133
{
@@ -134,6 +145,11 @@ export default {
134145
],
135146
parserOptions: {
136147
ecmaVersion: 8
148+
},
149+
settings: {
150+
jsdoc: {
151+
forceRequireReturn: true
152+
}
137153
}
138154
},
139155
{
@@ -173,6 +189,29 @@ export default {
173189
message: 'Missing JSDoc @returns declaration.'
174190
}
175191
]
192+
},
193+
{
194+
code: `
195+
/**
196+
*
197+
*/
198+
async function quux () {
199+
}
200+
`,
201+
errors: [
202+
{
203+
line: 2,
204+
message: 'Missing JSDoc @returns declaration.'
205+
}
206+
],
207+
parserOptions: {
208+
ecmaVersion: 8
209+
},
210+
settings: {
211+
jsdoc: {
212+
forceReturnsWithAsync: true
213+
}
214+
}
176215
}
177216
],
178217
valid: [
@@ -487,6 +526,73 @@ export default {
487526
}
488527
}
489528
},
529+
{
530+
code: `
531+
/**
532+
* @returns {Promise}
533+
*/
534+
async function quux () {
535+
}
536+
`,
537+
parserOptions: {
538+
ecmaVersion: 8
539+
},
540+
settings: {
541+
jsdoc: {
542+
forceRequireReturn: true
543+
}
544+
}
545+
},
546+
{
547+
code: `
548+
/**
549+
* @returns {Promise}
550+
*/
551+
async function quux () {
552+
}
553+
`,
554+
parserOptions: {
555+
ecmaVersion: 8
556+
},
557+
settings: {
558+
jsdoc: {
559+
forceReturnsWithAsync: true
560+
}
561+
}
562+
},
563+
{
564+
code: `
565+
/**
566+
*
567+
*/
568+
async function quux () {}
569+
`,
570+
parserOptions: {
571+
ecmaVersion: 8
572+
}
573+
},
574+
{
575+
code: `
576+
/**
577+
*
578+
*/
579+
const quux = async function () {}
580+
`,
581+
parserOptions: {
582+
ecmaVersion: 8
583+
}
584+
},
585+
{
586+
code: `
587+
/**
588+
*
589+
*/
590+
const quux = async () => {}
591+
`,
592+
parserOptions: {
593+
ecmaVersion: 8
594+
}
595+
},
490596
{
491597
code: `
492598
/** foo class */

0 commit comments

Comments
 (0)