Skip to content

Commit

Permalink
fix(require-template, check-template-names): check @Property tags;
Browse files Browse the repository at this point in the history
…fixes gajus#1269
  • Loading branch information
brettz9 committed Jul 20, 2024
1 parent 736a23b commit 22e3274
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 39 deletions.
16 changes: 16 additions & 0 deletions docs/rules/check-template-names.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,14 @@ export type Extras<D, U> = [D, U | undefined];
* @typedef {[D, U | undefined]} Extras
*/
// Message: @template V not in use

/**
* @template D
* @template V
* @typedef Pairs
* @property {V} foo
*/
// Message: @template D not in use
````


Expand Down Expand Up @@ -130,5 +138,13 @@ export type Extras<D, U, V> = [D, U, V | undefined];
* @typedef Foo
* @prop {string} bar
*/

/**
* @template D
* @template V
* @typedef Pairs
* @property {D} foo
* @property {V} bar
*/
````

16 changes: 16 additions & 0 deletions docs/rules/require-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ export type Pairs<D, V> = [D, V | undefined];
*/
// "jsdoc/require-template": ["error"|"warn", {"requireSeparateTemplates":true}]
// Message: Missing separate @template for V

/**
* @template X
* @typedef {object} Pairs
* @property {D} foo
* @property {X} bar
*/
// Message: Missing @template D
````


Expand Down Expand Up @@ -148,5 +156,13 @@ export type Extras<D, U, V> = [D, U, V | undefined];
* @typedef Foo
* @prop {string} bar
*/

/**
* @template D
* @template V
* @typedef {object} Pairs
* @property {D} foo
* @property {V} bar
*/
````

51 changes: 32 additions & 19 deletions src/rules/checkTemplateNames.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,27 +64,40 @@ export default iterateJsdoc(({
return;
}

const potentialType = typedefTags[0].type;
/**
* @param {string} potentialType
*/
const checkForUsedTypes = (potentialType) => {
let parsedType;
try {
parsedType = mode === 'permissive' ?
tryParseType(/** @type {string} */ (potentialType)) :
parseType(/** @type {string} */ (potentialType), mode);
} catch {
return;
}

let parsedType;
try {
parsedType = mode === 'permissive' ?
tryParseType(/** @type {string} */ (potentialType)) :
parseType(/** @type {string} */ (potentialType), mode)
} catch {
// Todo: Should handle types in @prop/erty
return;
}
traverse(parsedType, (nde) => {
const {
type,
value,
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) {
usedNames.add(value);
}
});
};

traverse(parsedType, (nde) => {
const {
type,
value,
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) {
usedNames.add(value);
}
});
const potentialTypedefType = typedefTags[0].type;
checkForUsedTypes(potentialTypedefType);

const tagName = /** @type {string} */ (utils.getPreferredTagName({
tagName: 'property',
}));
const propertyTags = utils.getTags(tagName);
for (const propertyTag of propertyTags) {
checkForUsedTypes(propertyTag.type);
}

for (const tag of templateTags) {
const {name} = tag;
Expand Down
58 changes: 38 additions & 20 deletions src/rules/requireTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,32 +75,50 @@ export default iterateJsdoc(({
return;
}

const potentialType = typedefTags[0].type;
const usedNameToTag = new Map();

let parsedType;
try {
parsedType = mode === 'permissive' ?
tryParseType(/** @type {string} */ (potentialType)) :
parseType(/** @type {string} */ (potentialType), mode)
} catch {
// Todo: Should handle types in @prop/erty
return;
}

traverse(parsedType, (nde) => {
const {
type,
value,
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) {
usedNames.add(value);
/**
* @param {import('comment-parser').Spec} potentialTag
*/
const checkForUsedTypes = (potentialTag) => {
let parsedType;
try {
parsedType = mode === 'permissive' ?
tryParseType(/** @type {string} */ (potentialTag.type)) :
parseType(/** @type {string} */ (potentialTag.type), mode)
} catch {
return;
}
});

traverse(parsedType, (nde) => {
const {
type,
value,
} = /** @type {import('jsdoc-type-pratt-parser').NameResult} */ (nde);
if (type === 'JsdocTypeName' && (/^[A-Z]$/).test(value)) {
usedNames.add(value);
if (!usedNameToTag.has(value)) {
usedNameToTag.set(value, potentialTag);
}
}
});
};

const potentialTypedef = typedefTags[0];
checkForUsedTypes(potentialTypedef);

const tagName = /** @type {string} */ (utils.getPreferredTagName({
tagName: 'property',
}));
const propertyTags = utils.getTags(tagName);
for (const propertyTag of propertyTags) {
checkForUsedTypes(propertyTag);
}

// Could check against whitelist/blacklist
for (const usedName of usedNames) {
if (!templateNames.includes(usedName)) {
report(`Missing @template ${usedName}`, null, typedefTags[0]);
report(`Missing @template ${usedName}`, null, usedNameToTag.get(usedName));
}
}
}, {
Expand Down
27 changes: 27 additions & 0 deletions test/rules/assertions/checkTemplateNames.js
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,22 @@ export default {
},
],
},
{
code: `
/**
* @template D
* @template V
* @typedef Pairs
* @property {V} foo
*/
`,
errors: [
{
line: 3,
message: '@template D not in use',
},
],
},
],
valid: [
{
Expand Down Expand Up @@ -201,5 +217,16 @@ export default {
*/
`,
},
{
code: `
/**
* @template D
* @template V
* @typedef Pairs
* @property {D} foo
* @property {V} bar
*/
`,
},
],
};
27 changes: 27 additions & 0 deletions test/rules/assertions/requireTemplate.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,22 @@ export default {
}
],
},
{
code: `
/**
* @template X
* @typedef {object} Pairs
* @property {D} foo
* @property {X} bar
*/
`,
errors: [
{
line: 5,
message: 'Missing @template D',
},
],
},
],
valid: [
{
Expand Down Expand Up @@ -213,5 +229,16 @@ export default {
*/
`,
},
{
code: `
/**
* @template D
* @template V
* @typedef {object} Pairs
* @property {D} foo
* @property {V} bar
*/
`,
},
],
};

0 comments on commit 22e3274

Please sign in to comment.