From e0f486218b56a369a98d4eb2ecd8dab652e99ab9 Mon Sep 17 00:00:00 2001 From: Brett Zamir Date: Thu, 19 Jan 2023 18:49:30 -0700 Subject: [PATCH] fix(`require-returns-check`, `require-yields-check`): allow `*` and `any` types to avoid reporting of missing return; fixes #950 --- README.md | 16 ++++++++++++--- src/iterateJsdoc.js | 4 ++-- src/jsdocUtils.js | 21 +++++++++++--------- src/rules/requireReturnsCheck.js | 2 +- src/rules/requireYieldsCheck.js | 4 ++-- test/jsdocUtils.js | 4 ++-- test/rules/assertions/requireReturnsCheck.js | 19 +++++++++++++++--- 7 files changed, 48 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 7e4d27a95..4ab8aaa0e 100644 --- a/README.md +++ b/README.md @@ -17875,7 +17875,7 @@ function quux () { } /** - * @returns {*} Foo. + * @returns {SomeType} Foo. */ const quux = () => foo; @@ -18276,14 +18276,14 @@ function quux (path) { }; /** - * @returns {*} Foo. + * @returns {SomeType} Foo. */ const quux = () => new Promise((resolve) => { resolve(3); }); /** - * @returns {*} Foo. + * @returns {SomeType} Foo. */ const quux = function () { return new Promise((resolve) => { @@ -18420,6 +18420,16 @@ export function f(): string { interface I {} } + +/** + * @param {boolean} bar A fun variable. + * @returns {*} Anything at all! + */ +function foo( bar ) { + if ( bar ) { + return functionWithUnknownReturnType(); + } +} ```` diff --git a/src/iterateJsdoc.js b/src/iterateJsdoc.js index dcca87fc8..7b3ebcdce 100644 --- a/src/iterateJsdoc.js +++ b/src/iterateJsdoc.js @@ -724,8 +724,8 @@ const getUtils = ( return jsdocUtils.getTagStructureForMode(mde, settings.structuredTags); }; - utils.hasDefinedTypeTag = (tag) => { - return jsdocUtils.hasDefinedTypeTag(tag, settings.mode); + utils.mayBeUndefinedTypeTag = (tag) => { + return jsdocUtils.mayBeUndefinedTypeTag(tag, settings.mode); }; utils.hasValueOrExecutorHasNonEmptyResolveValue = (anyPromiseAsReturn, allBranches) => { diff --git a/src/jsdocUtils.js b/src/jsdocUtils.js index 9aa0ce5ac..3873c89e3 100644 --- a/src/jsdocUtils.js +++ b/src/jsdocUtils.js @@ -464,26 +464,29 @@ const hasATag = (jsdoc, targetTagNames) => { }; /** - * Checks if the JSDoc comment declares a defined type. + * Checks if the JSDoc comment has an undefined type. * * @param {JsDocTag} tag * the tag which should be checked. * @param {"jsdoc"|"closure"|"typescript"} mode * @returns {boolean} - * true in case a defined type is declared; otherwise false. + * true in case a defined type is undeclared; otherwise false. */ -const hasDefinedTypeTag = (tag, mode) => { +const mayBeUndefinedTypeTag = (tag, mode) => { // The function should not continue in the event the type is not defined... if (typeof tag === 'undefined' || tag === null) { - return false; + return true; } // .. same applies if it declares an `{undefined}` or `{void}` type const tagType = tag.type.trim(); // Exit early if matching - if (tagType === 'undefined' || tagType === 'void') { - return false; + if ( + tagType === 'undefined' || tagType === 'void' || + tagType === '*' || tagType === 'any' + ) { + return true; } let parsedTypes; @@ -506,11 +509,11 @@ const hasDefinedTypeTag = (tag, mode) => { return elem.type === 'JsdocTypeUndefined' || elem.type === 'JsdocTypeName' && elem.value === 'void'; })) { - return false; + return true; } // In any other case, a type is present - return true; + return false; }; /** @@ -1239,7 +1242,6 @@ export default { getTagsByType, getTagStructureForMode, hasATag, - hasDefinedTypeTag, hasParams, hasReturnValue, hasTag, @@ -1251,6 +1253,7 @@ export default { isNamepathDefiningTag, isSetter, isValidTag, + mayBeUndefinedTypeTag, overrideTagStructure, parseClosureTemplateTag, pathDoesNotBeginWith, diff --git a/src/rules/requireReturnsCheck.js b/src/rules/requireReturnsCheck.js index abc7c6c19..8179cfc15 100755 --- a/src/rules/requireReturnsCheck.js +++ b/src/rules/requireReturnsCheck.js @@ -92,7 +92,7 @@ export default iterateJsdoc(({ !returnNever && ( reportMissingReturnForUndefinedTypes || - utils.hasDefinedTypeTag(tag) + !utils.mayBeUndefinedTypeTag(tag) ) && !utils.hasValueOrExecutorHasNonEmptyResolveValue( exemptAsync, diff --git a/src/rules/requireYieldsCheck.js b/src/rules/requireYieldsCheck.js index 9cc7154d1..f3837b216 100644 --- a/src/rules/requireYieldsCheck.js +++ b/src/rules/requireYieldsCheck.js @@ -90,7 +90,7 @@ export default iterateJsdoc(({ return true; } - return utils.hasDefinedTypeTag(yieldTag) && !utils.hasYieldValue(); + return !utils.mayBeUndefinedTypeTag(yieldTag) && !utils.hasYieldValue(); }; // In case a yield value is declared in JSDoc, we also expect one in the code. @@ -120,7 +120,7 @@ export default iterateJsdoc(({ return true; } - return utils.hasDefinedTypeTag(nextTag) && !utils.hasYieldReturnValue(); + return !utils.mayBeUndefinedTypeTag(nextTag) && !utils.hasYieldReturnValue(); }; if (shouldReportNext()) { diff --git a/test/jsdocUtils.js b/test/jsdocUtils.js index 86c6fbd11..5d5f6722a 100644 --- a/test/jsdocUtils.js +++ b/test/jsdocUtils.js @@ -68,10 +68,10 @@ describe('jsdocUtils', () => { }); }); }); - describe('hasDefinedTypeTag()', () => { + describe('mayBeUndefinedTypeTag()', () => { context('Missing tag', () => { it('should return `false` with a missing tag', () => { - expect(jsdocUtils.hasDefinedTypeTag(null)).to.equal(false); + expect(jsdocUtils.mayBeUndefinedTypeTag(null)).to.equal(true); }); }); }); diff --git a/test/rules/assertions/requireReturnsCheck.js b/test/rules/assertions/requireReturnsCheck.js index fb0135406..ccb2c7418 100755 --- a/test/rules/assertions/requireReturnsCheck.js +++ b/test/rules/assertions/requireReturnsCheck.js @@ -701,7 +701,7 @@ export default { { code: ` /** - * @returns {*} Foo. + * @returns {SomeType} Foo. */ const quux = () => foo; `, @@ -1306,7 +1306,7 @@ export default { { code: ` /** - * @returns {*} Foo. + * @returns {SomeType} Foo. */ const quux = () => new Promise((resolve) => { resolve(3); @@ -1316,7 +1316,7 @@ export default { { code: ` /** - * @returns {*} Foo. + * @returns {SomeType} Foo. */ const quux = function () { return new Promise((resolve) => { @@ -1505,5 +1505,18 @@ export default { `, parser: require.resolve('@typescript-eslint/parser'), }, + { + code: ` + /** + * @param {boolean} bar A fun variable. + * @returns {*} Anything at all! + */ + function foo( bar ) { + if ( bar ) { + return functionWithUnknownReturnType(); + } + } + `, + }, ], };