diff --git a/docs/rules/require-param.md b/docs/rules/require-param.md index 4918198ec..43aa20a8e 100644 --- a/docs/rules/require-param.md +++ b/docs/rules/require-param.md @@ -629,6 +629,15 @@ function quux ({ foo, bar: { baz }}) { } // Message: Missing JSDoc @param "root0" declaration. +/** + * @param root0 + * @param root0.foo + * @param root0.bar.baz + */ +function quux ({ foo, bar: { baz }}) { +} +// Message: Missing JSDoc @param "root0.bar" declaration. + /** * */ diff --git a/src/rules/requireParam.js b/src/rules/requireParam.js index 8c02470d1..2855f8ef0 100644 --- a/src/rules/requireParam.js +++ b/src/rules/requireParam.js @@ -181,27 +181,36 @@ export default iterateJsdoc(({ * }} */ const findExpectedIndex = (jsdocTags, indexAtFunctionParams) => { - const remainingRoots = functionParameterNames.slice(indexAtFunctionParams || 0); + // Get the parameters that come after the current index in the flattened order + const remainingFlattenedRoots = flattenedRoots.slice((indexAtFunctionParams || 0) + 1); + + // Find the first existing tag that comes after the current parameter in the flattened order const foundIndex = jsdocTags.findIndex(({ name, newAdd, }) => { - return !newAdd && remainingRoots.some((remainingRoot) => { - if (Array.isArray(remainingRoot)) { - return ( - /** - * @type {import('../jsdocUtils.js').FlattendRootInfo & { - * annotationParamName?: string|undefined; - * }} - */ (remainingRoot[1]).names.includes(name) - ); + if (newAdd) { + return false; + } + + // Check if the tag name matches any of the remaining flattened roots + return remainingFlattenedRoots.some((flattenedRoot) => { + // The flattened roots don't have the root prefix (e.g., "bar", "bar.baz") + // but JSDoc tags do (e.g., "root0", "root0.bar", "root0.bar.baz") + // So we need to check if the tag name ends with the flattened root + + // Check if tag name ends with "." + if (name.endsWith(`.${flattenedRoot}`)) { + return true; } - if (typeof remainingRoot === 'object') { - return name === remainingRoot.name; + // Also check if tag name exactly matches the flattenedRoot + // (for single-level params) + if (name === flattenedRoot) { + return true; } - return name === remainingRoot; + return false; }); }); diff --git a/test/rules/assertions/requireParam.js b/test/rules/assertions/requireParam.js index 95b8fdd37..0daf84524 100644 --- a/test/rules/assertions/requireParam.js +++ b/test/rules/assertions/requireParam.js @@ -339,6 +339,33 @@ export default /** @type {import('../index.js').TestCases} */ ({ } `, }, + { + code: ` + /** + * @param root0 + * @param root0.foo + * @param root0.bar.baz + */ + function quux ({ foo, bar: { baz }}) { + } + `, + errors: [ + { + line: 2, + message: 'Missing JSDoc @param "root0.bar" declaration.', + }, + ], + output: ` + /** + * @param root0 + * @param root0.foo + * @param root0.bar + * @param root0.bar.baz + */ + function quux ({ foo, bar: { baz }}) { + } + `, + }, { code: ` /** @@ -2244,8 +2271,8 @@ export default /** @type {import('../index.js').TestCases} */ ({ /** * Description. * @param {Object} options - * @param options.foo * @param {FooBar} foo + * @param options.foo * @param options.foo.bar */ function quux ({ foo: { bar } }) {}