Skip to content
Closed
2 changes: 2 additions & 0 deletions .README/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Finally, enable all of the rules that you would like to use.
"jsdoc/empty-tags": 1, // Recommended
"jsdoc/implements-on-classes": 1, // Recommended
"jsdoc/informative-docs": 1,
"jsdoc/used-types": 1,
"jsdoc/match-description": 1,
"jsdoc/multiline-blocks": 1, // Recommended
"jsdoc/no-bad-blocks": 1,
Expand Down Expand Up @@ -240,4 +241,5 @@ Problems reported by rules which have a wrench :wrench: below can be fixed autom
|||[sort-tags](./docs/rules/sort-tags.md#readme)|Sorts tags by a specified sequence according to tag name, optionally adding line breaks between tag groups|
|:heavy_check_mark:|:wrench:|[tag-lines](./docs/rules/tag-lines.md#readme)|Enforces lines (or no lines) between tags|
||:wrench:|[text-escaping](./docs/rules/text-escaping.md#readme)|This rule can auto-escape certain characters that are input within block and tag descriptions|
|||[used-types](./docs/rules/used-types.md#readme)|Marks all types referenced from JSDoc tags as used.|
|:heavy_check_mark:||[valid-types](./docs/rules/valid-types.md#readme)|Requires all types/namepaths to be valid JSDoc, Closure compiler, or TypeScript types (configurable in settings)|
27 changes: 27 additions & 0 deletions .README/rules/used-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# `used-types`

{"gitdown": "contents", "rootId": "used-types"}

Marks all types referenced from JSDoc tags as used.

## Fixer

Not applicable.

#### Options

|||
|---|---|
|Context|everywhere|
|Tags|N/A|
|Recommended|false|
|Settings||
|Options||

## Failing examples

<!-- assertions-failing usedTypes -->

## Passing examples

<!-- assertions-passing usedTypes -->
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ Finally, enable all of the rules that you would like to use.
"jsdoc/empty-tags": 1, // Recommended
"jsdoc/implements-on-classes": 1, // Recommended
"jsdoc/informative-docs": 1,
"jsdoc/used-types": 1,
"jsdoc/match-description": 1,
"jsdoc/multiline-blocks": 1, // Recommended
"jsdoc/no-bad-blocks": 1,
Expand Down Expand Up @@ -261,4 +262,5 @@ Problems reported by rules which have a wrench :wrench: below can be fixed autom
|||[sort-tags](./docs/rules/sort-tags.md#readme)|Sorts tags by a specified sequence according to tag name, optionally adding line breaks between tag groups|
|:heavy_check_mark:|:wrench:|[tag-lines](./docs/rules/tag-lines.md#readme)|Enforces lines (or no lines) between tags|
||:wrench:|[text-escaping](./docs/rules/text-escaping.md#readme)|This rule can auto-escape certain characters that are input within block and tag descriptions|
|||[used-types](./docs/rules/used-types.md#readme)|Marks all types referenced from JSDoc tags as used.|
|:heavy_check_mark:||[valid-types](./docs/rules/valid-types.md#readme)|Requires all types/namepaths to be valid JSDoc, Closure compiler, or TypeScript types (configurable in settings)|
59 changes: 59 additions & 0 deletions docs/rules/used-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<a name="user-content-used-types"></a>
<a name="used-types"></a>
# <code>used-types</code>

* [Fixer](#user-content-used-types-fixer)
* [Failing examples](#user-content-used-types-failing-examples)
* [Passing examples](#user-content-used-types-passing-examples)


Marks all types referenced from JSDoc tags as used.

<a name="user-content-used-types-fixer"></a>
<a name="used-types-fixer"></a>
## Fixer

Not applicable.

<a name="user-content-used-types-fixer-options"></a>
<a name="used-types-fixer-options"></a>
#### Options

|||
|---|---|
|Context|everywhere|
|Tags|N/A|
|Recommended|false|
|Settings||
|Options||

<a name="user-content-used-types-failing-examples"></a>
<a name="used-types-failing-examples"></a>
## Failing examples

The following patterns are considered problems:

````js

````



<a name="user-content-used-types-passing-examples"></a>
<a name="used-types-passing-examples"></a>
## Passing examples

The following patterns are not considered problems:

````js
class Foo {}
/** @param {Foo} */
function foo() {}
foo();

class Foo {}
/** @returns {Foo} */
function foo() {}
foo();
````

2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"debug": "^4.3.4",
"escape-string-regexp": "^4.0.0",
"esquery": "^1.5.0",
"jsdoc-type-pratt-parser": "^4.0.0",
"semver": "^7.5.0",
"spdx-expression-parse": "^3.0.1"
},
Expand Down Expand Up @@ -41,7 +42,6 @@
"gitdown": "^3.1.5",
"glob": "^10.2.2",
"husky": "^8.0.3",
"jsdoc-type-pratt-parser": "^4.0.0",
"lint-staged": "^13.2.2",
"lodash.defaultsdeep": "^4.6.1",
"mocha": "^10.2.0",
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import requireYieldsCheck from './rules/requireYieldsCheck';
import sortTags from './rules/sortTags';
import tagLines from './rules/tagLines';
import textEscaping from './rules/textEscaping';
import usedTypes from './rules/usedTypes';
import validTypes from './rules/validTypes';

const index = {
Expand Down Expand Up @@ -105,6 +106,7 @@ const index = {
'sort-tags': sortTags,
'tag-lines': tagLines,
'text-escaping': textEscaping,
'used-types': usedTypes,
'valid-types': validTypes,
},
};
Expand Down
100 changes: 100 additions & 0 deletions src/rules/usedTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import {
parse,
} from 'jsdoc-type-pratt-parser';
import iterateJsdoc from '../iterateJsdoc';

/**
* Extracts the type names from parsed type declaration.
*/
const extractTypeNames = (parsed) => { // eslint-disable-line complexity
if (typeof parsed !== 'object') {
return [];
}

switch (parsed.type) {
case 'JsdocTypeName':
return [
parsed.value,
];
case 'JsdocTypeOptional':
case 'JsdocTypeNullable':
case 'JsdocTypeNotNullable':
case 'JsdocTypeTypeof':
case 'JsdocTypeKeyof':
case 'JsdocTypeParenthesis':
case 'JsdocTypeVariadic':
return extractTypeNames(parsed.element);
case 'JsdocTypeUnion':
case 'JsdocTypeObject':
case 'JsdocTypeTuple':
case 'JsdocTypeIntersection':
return parsed.elements.flatMap(extractTypeNames);
case 'JsdocTypeGeneric':
return [
...extractTypeNames(parsed.left),
...parsed.elements.flatMap(extractTypeNames),
];
case 'JsdocTypeFunction':
return [
...parsed.parameters.flatMap(extractTypeNames),
...extractTypeNames(parsed.returnType),
];
case 'JsdocTypeNamePath':
return extractTypeNames(parsed.left);
case 'JsdocTypePredicate':
// We purposefully don't consider the left (subject of the predicate) used
return extractTypeNames(parsed.right);
case 'JsdocTypeObjectField':
return [
...extractTypeNames(parsed.key),
...extractTypeNames(parsed.right),
];
case 'JsdocTypeJsdocObjectField':
return [
...extractTypeNames(parsed.left),
...extractTypeNames(parsed.right),
];
case 'JsdocTypeKeyValue':
case 'JsdocTypeIndexSignature':
case 'JsdocTypeMappedType':
return extractTypeNames(parsed.right);
default:
return [];
}
};

export default iterateJsdoc(({
jsdoc,
jsdocNode,
context,
settings,
}) => {
const {
mode,
} = settings;

const sourceCode = context.getSourceCode();
for (const tag of jsdoc.tags) {
const parsedType = parse(tag.type, mode);
const typeNames = extractTypeNames(parsedType);
for (const typeName of typeNames) {
sourceCode.markVariableAsUsed(typeName, jsdocNode);
}
}
}, {
iterateAllJsdocs: true,
meta: {
docs: {
description: 'Marks all types referenced from JSDoc tags as used.',
url: 'https://github.com/gajus/eslint-plugin-jsdoc#eslint-plugin-jsdoc-rules-used-types',
},
fixable: 'code',
schema: [
{
additionalProperties: false,
properties: {},
},
],
type: 'suggestion',
},
});
68 changes: 68 additions & 0 deletions test/rules/assertions/usedTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
export default {
invalid: [],
valid: [
// {
// code: `
// const foo = "bar";
// /** This thing uses {@link foo} for something */
// `,
// /*
// rules: {
// 'no-unused-vars': 'error',
// },
// */
// },
{
code: `
class Foo {}
/** @param {Foo} */
function foo() {}
foo();
`,
rules: {
'no-unused-vars': 'error',
},
},
{
code: `
class Foo {}
/** @returns {Foo} */
function foo() {}
foo();
`,
rules: {
'no-unused-vars': 'error',
},
},
{
code: `
class Foo {}
class Bar {}
class Baz {}
class Qux {}
/** @type {(!Foo|?Bar|...Baz|Qux[]|foo=|obj["level1"]|{Foo?: Foo}|function(this:Foo))|external:something} */
let foo = null;
`,
ignoreReadme: true,
rules: {
'no-unused-vars': 'error',
},
},
{
code: `
class Foo {}
/** @type {typeof foo|import("some-package")|new(number, string): Foo|foo is Foo|{foo: Foo}} */
let foo = null;
`,
ignoreReadme: true,
rules: {
'no-unused-vars': 'error',
},
settings: {
jsdoc: {
mode: 'typescript',
},
},
},
],
};
1 change: 1 addition & 0 deletions test/rules/ruleNames.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"empty-tags",
"implements-on-classes",
"informative-docs",
"used-types",
"match-description",
"match-name",
"multiline-blocks",
Expand Down