From 71fe49a333de1181a4eb79d965e2523db6b7bdd5 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Sat, 18 Dec 2021 13:28:22 +0800 Subject: [PATCH] feat(`require-returns-check`, `require-yields-check`, `require-throws`): confirm that a "never" value is indeed present --- README.md | 61 +++++++++++++++++--- src/rules/requireReturnsCheck.js | 10 +++- src/rules/requireThrows.js | 4 ++ src/rules/requireYieldsCheck.js | 8 +++ test/rules/assertions/requireReturnsCheck.js | 42 ++++++++++---- test/rules/assertions/requireThrows.js | 35 +++++++++++ test/rules/assertions/requireYieldsCheck.js | 31 ++++++++++ 7 files changed, 173 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index e18fa76f2..ad6aed6b8 100644 --- a/README.md +++ b/README.md @@ -16507,6 +16507,22 @@ async function foo() { function quux () {} // "jsdoc/require-returns-check": ["error"|"warn", {"reportMissingReturnForUndefinedTypes":true}] // Message: JSDoc @returns declaration present but return expression not available in function. + +/** + * @returns {never} Foo. + */ +function quux () { + return undefined; +} +// Message: JSDoc @returns declaration set with "never" but return expression is present in function. + +/** + * @returns {never} + */ +function quux (foo) { + return foo; +} +// Message: JSDoc @returns declaration set with "never" but return expression is present in function. ```` The following patterns are not considered problems: @@ -16643,13 +16659,6 @@ function quux () { function quux () { } -/** - * @returns {never} Foo. - */ -function quux () { - return undefined; -} - /** * @returns {void} Foo. */ @@ -18445,6 +18454,14 @@ const directThrowAfterArrow = (b) => { return a; }; // Message: Missing JSDoc @throws declaration. + +/** + * @throws {never} + */ +function quux (foo) { + throw new Error('err') +} +// Message: JSDoc @throws declaration set to "never" but throw value found. ```` The following patterns are not considered problems: @@ -18466,6 +18483,13 @@ function quux (foo) { } catch(e) {} } +/** + * @throws {object} + */ +function quux (foo) { + throw new Error('err') +} + /** * @inheritdoc */ @@ -18509,6 +18533,12 @@ const nested = () => () => {throw new Error('oops');}; async function foo() { throw Error("bar"); } + +/** + * @throws {never} + */ +function quux (foo) { +} ```` @@ -19493,6 +19523,23 @@ async function * quux() {} */ const quux = async function * () {} // Message: JSDoc @yields declaration present but yield expression not available in function. + +/** + * @yields {never} Foo. + */ +function * quux () { + yield 5; +} +// Message: JSDoc @yields declaration set with "never" but yield expression is present in function. + +/** + * @next {never} + */ +function * quux (foo) { + const a = yield; +} +// "jsdoc/require-yields-check": ["error"|"warn", {"next":true}] +// Message: JSDoc @next declaration set with "never" but yield expression with return value is present in function. ```` The following patterns are not considered problems: diff --git a/src/rules/requireReturnsCheck.js b/src/rules/requireReturnsCheck.js index e7243343d..9bbab41b1 100755 --- a/src/rules/requireReturnsCheck.js +++ b/src/rules/requireReturnsCheck.js @@ -68,9 +68,17 @@ export default iterateJsdoc(({ const [tag] = tags; + const returnNever = tag.type.trim() === 'never'; + + if (returnNever && utils.hasValueOrExecutorHasNonEmptyResolveValue(false)) { + report(`JSDoc @${tagName} declaration set with "never" but return expression is present in function.`); + + return; + } + // In case a return value is declared in JSDoc, we also expect one in the code. if ( - tag.type.trim() !== 'never' && + !returnNever && ( reportMissingReturnForUndefinedTypes || utils.hasDefinedTypeTag(tag) diff --git a/src/rules/requireThrows.js b/src/rules/requireThrows.js index e09a07d8f..80432e8b9 100644 --- a/src/rules/requireThrows.js +++ b/src/rules/requireThrows.js @@ -47,6 +47,10 @@ export default iterateJsdoc(({ const shouldReport = () => { if (!missingThrowsTag) { + if (tag.type.trim() === 'never' && iteratingFunction && utils.hasThrowValue()) { + report(`JSDoc @${tagName} declaration set to "never" but throw value found.`); + } + return false; } diff --git a/src/rules/requireYieldsCheck.js b/src/rules/requireYieldsCheck.js index f7b3972be..11f334fb7 100644 --- a/src/rules/requireYieldsCheck.js +++ b/src/rules/requireYieldsCheck.js @@ -72,6 +72,10 @@ export default iterateJsdoc(({ if (preferredYieldTagName) { const shouldReportYields = () => { if (yieldTag.type.trim() === 'never') { + if (utils.hasYieldValue()) { + report(`JSDoc @${preferredYieldTagName} declaration set with "never" but yield expression is present in function.`); + } + return false; } @@ -95,6 +99,10 @@ export default iterateJsdoc(({ if (preferredNextTagName) { const shouldReportNext = () => { if (nextTag.type.trim() === 'never') { + if (utils.hasYieldReturnValue()) { + report(`JSDoc @${preferredNextTagName} declaration set with "never" but yield expression with return value is present in function.`); + } + return false; } diff --git a/test/rules/assertions/requireReturnsCheck.js b/test/rules/assertions/requireReturnsCheck.js index 882a8728a..8d2ecb75a 100755 --- a/test/rules/assertions/requireReturnsCheck.js +++ b/test/rules/assertions/requireReturnsCheck.js @@ -325,6 +325,38 @@ export default { reportMissingReturnForUndefinedTypes: true, }], }, + { + code: ` + /** + * @returns {never} Foo. + */ + function quux () { + return undefined; + } + `, + errors: [ + { + line: 2, + message: 'JSDoc @returns declaration set with "never" but return expression is present in function.', + }, + ], + }, + { + code: ` + /** + * @returns {never} + */ + function quux (foo) { + return foo; + } + `, + errors: [ + { + line: 2, + message: 'JSDoc @returns declaration set with "never" but return expression is present in function.', + }, + ], + }, ], valid: [ { @@ -528,16 +560,6 @@ export default { } `, }, - { - code: ` - /** - * @returns {never} Foo. - */ - function quux () { - return undefined; - } - `, - }, { code: ` /** diff --git a/test/rules/assertions/requireThrows.js b/test/rules/assertions/requireThrows.js index d4f0e062e..d6fcd66d6 100644 --- a/test/rules/assertions/requireThrows.js +++ b/test/rules/assertions/requireThrows.js @@ -334,6 +334,22 @@ export default { }, ], }, + { + code: ` + /** + * @throws {never} + */ + function quux (foo) { + throw new Error('err') + } + `, + errors: [ + { + line: 2, + message: 'JSDoc @throws declaration set to "never" but throw value found.', + }, + ], + }, ], valid: [ { @@ -358,6 +374,16 @@ export default { } `, }, + { + code: ` + /** + * @throws {object} + */ + function quux (foo) { + throw new Error('err') + } + `, + }, { code: ` /** @@ -430,5 +456,14 @@ export default { ecmaVersion: 8, }, }, + { + code: ` + /** + * @throws {never} + */ + function quux (foo) { + } + `, + }, ], }; diff --git a/test/rules/assertions/requireYieldsCheck.js b/test/rules/assertions/requireYieldsCheck.js index c495e2617..c7d2fbfdd 100644 --- a/test/rules/assertions/requireYieldsCheck.js +++ b/test/rules/assertions/requireYieldsCheck.js @@ -297,6 +297,37 @@ export default { ecmaVersion: 2_018, }, }, + { + code: ` + /** + * @yields {never} Foo. + */ + function * quux () { + yield 5; + } + `, + errors: [{ + line: 2, + message: 'JSDoc @yields declaration set with "never" but yield expression is present in function.', + }], + }, + { + code: ` + /** + * @next {never} + */ + function * quux (foo) { + const a = yield; + } + `, + errors: [{ + line: 2, + message: 'JSDoc @next declaration set with "never" but yield expression with return value is present in function.', + }], + options: [{ + next: true, + }], + }, ], valid: [ {