Skip to content

Commit 4ac598c

Browse files
authored
Merge pull request #283 from interfaced/no-undefined-types-add-gcc-templates
feat(no-undefined-types): treat GCC generic types as defined
2 parents 5b7da11 + 2289eed commit 4ac598c

File tree

5 files changed

+168
-8
lines changed

5 files changed

+168
-8
lines changed

README.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2784,6 +2784,35 @@ function quux(foo, bar, baz) {
27842784
// Settings: {"jsdoc":{"preferredTypes":{"hertype":{"replacement":false},"histype":"HisType"}}}
27852785
// Options: [{"definedTypes":["MyType"],"preferredTypesDefined":true}]
27862786
// Message: The type 'HerType' is undefined.
2787+
2788+
class Foo {
2789+
/**
2790+
* @return {TEMPLATE_TYPE}
2791+
*/
2792+
bar () {
2793+
}
2794+
}
2795+
// Message: The type 'TEMPLATE_TYPE' is undefined.
2796+
2797+
class Foo {
2798+
/**
2799+
* @return {TEMPLATE_TYPE}
2800+
*/
2801+
invalidTemplateReference () {
2802+
}
2803+
}
2804+
2805+
/**
2806+
* @template TEMPLATE_TYPE
2807+
*/
2808+
class Bar {
2809+
/**
2810+
* @return {TEMPLATE_TYPE}
2811+
*/
2812+
validTemplateReference () {
2813+
}
2814+
}
2815+
// Message: The type 'TEMPLATE_TYPE' is undefined.
27872816
````
27882817

27892818
The following patterns are not considered problems:
@@ -2934,6 +2963,29 @@ function quux(foo, bar, baz) {
29342963
}
29352964
// Settings: {"jsdoc":{"preferredTypes":{"hertype":{"replacement":"HerType<>"},"histype":"HisType.<>"}}}
29362965
// Options: [{"definedTypes":["MyType"],"preferredTypesDefined":true}]
2966+
2967+
/**
2968+
* @template TEMPLATE_TYPE
2969+
*/
2970+
class Foo {
2971+
/**
2972+
* @return {TEMPLATE_TYPE}
2973+
*/
2974+
bar () {
2975+
}
2976+
}
2977+
2978+
/**
2979+
* @template TEMPLATE_TYPE_A, TEMPLATE_TYPE_B
2980+
*/
2981+
class Foo {
2982+
/**
2983+
* @param {TEMPLATE_TYPE_A} baz
2984+
* @return {TEMPLATE_TYPE_B}
2985+
*/
2986+
bar (baz) {
2987+
}
2988+
}
29372989
````
29382990

29392991

src/iterateJsdoc.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -189,20 +189,23 @@ const curryUtils = (
189189
return false;
190190
};
191191

192-
utils.classHasTag = (tagName) => {
192+
utils.getClassJsdoc = () => {
193193
const classNode = utils.getClassNode();
194194
const classJsdocNode = getJSDocComment(sourceCode, classNode);
195195

196196
if (classJsdocNode) {
197197
const indent = _.repeat(' ', classJsdocNode.loc.start.column);
198-
const classJsdoc = parseComment(classJsdocNode, indent);
199198

200-
if (jsdocUtils.hasTag(classJsdoc, tagName)) {
201-
return true;
202-
}
199+
return parseComment(classJsdocNode, indent);
203200
}
204201

205-
return false;
202+
return null;
203+
};
204+
205+
utils.classHasTag = (tagName) => {
206+
const classJsdoc = utils.getClassJsdoc();
207+
208+
return classJsdoc && jsdocUtils.hasTag(classJsdoc, tagName);
206209
};
207210

208211
utils.forEachTag = (tagName, arrayHandler) => {

src/jsdocUtils.js

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,22 @@ const isInlineTag = (tag) => {
461461
};
462462
*/
463463

464+
/**
465+
* Parses GCC Generic/Template types
466+
*
467+
* @see {https://github.com/google/closure-compiler/wiki/Generic-Types}
468+
* @param {JsDocTag} tag
469+
* @returns {Array<string>}
470+
*/
471+
const parseClosureTemplateTag = (tag) => {
472+
return tag.source
473+
.split('@template')[1]
474+
.split(',')
475+
.map((type) => {
476+
return type.trim();
477+
});
478+
};
479+
464480
export default {
465481
getFunctionParameterNames,
466482
getJsdocParameterNames,
@@ -474,5 +490,6 @@ export default {
474490
isNamepathTag,
475491
isPotentiallyEmptyNamepathTag,
476492
isTagWithType,
477-
isValidTag
493+
isValidTag,
494+
parseClosureTemplateTag
478495
};

src/rules/noUndefinedTypes.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'flat-map-polyfill';
33
import _ from 'lodash';
44
import {parse as parseType, traverse} from 'jsdoctypeparser';
55
import iterateJsdoc, {parseComment} from '../iterateJsdoc';
6+
import jsdocUtils from '../jsdocUtils';
67

78
const extraTypes = [
89
'null', 'undefined', 'string', 'boolean', 'object',
@@ -67,6 +68,18 @@ export default iterateJsdoc(({
6768
})
6869
.value();
6970

71+
let closureGenericTypes = [];
72+
const classJsdoc = utils.getClassJsdoc();
73+
if (classJsdoc && classJsdoc.tags) {
74+
closureGenericTypes = classJsdoc.tags
75+
.filter((tag) => {
76+
return tag.tag === 'template';
77+
})
78+
.flatMap((tag) => {
79+
return jsdocUtils.parseClosureTemplateTag(tag);
80+
});
81+
}
82+
7083
const allDefinedTypes = globalScope.variables.map((variable) => {
7184
return variable.name;
7285
})
@@ -89,7 +102,8 @@ export default iterateJsdoc(({
89102
.concat(extraTypes)
90103
.concat(typedefDeclarations)
91104
.concat(definedTypes)
92-
.concat(definedPreferredTypes);
105+
.concat(definedPreferredTypes)
106+
.concat(closureGenericTypes);
93107

94108
const jsdocTags = utils.filterTags((tag) => {
95109
return utils.isTagWithType(tag.tag);

test/rules/assertions/noUndefinedTypes.js

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,51 @@ export default {
154154
}
155155
}
156156
}
157+
},
158+
{
159+
code: `
160+
class Foo {
161+
/**
162+
* @return {TEMPLATE_TYPE}
163+
*/
164+
bar () {
165+
}
166+
}
167+
`,
168+
errors: [
169+
{
170+
line: 4,
171+
message: 'The type \'TEMPLATE_TYPE\' is undefined.'
172+
}
173+
]
174+
},
175+
{
176+
code: `
177+
class Foo {
178+
/**
179+
* @return {TEMPLATE_TYPE}
180+
*/
181+
invalidTemplateReference () {
182+
}
183+
}
184+
185+
/**
186+
* @template TEMPLATE_TYPE
187+
*/
188+
class Bar {
189+
/**
190+
* @return {TEMPLATE_TYPE}
191+
*/
192+
validTemplateReference () {
193+
}
194+
}
195+
`,
196+
errors: [
197+
{
198+
line: 4,
199+
message: 'The type \'TEMPLATE_TYPE\' is undefined.'
200+
}
201+
]
157202
}
158203
],
159204
valid: [
@@ -393,6 +438,35 @@ export default {
393438
}
394439
}
395440
}
441+
},
442+
{
443+
code: `
444+
/**
445+
* @template TEMPLATE_TYPE
446+
*/
447+
class Foo {
448+
/**
449+
* @return {TEMPLATE_TYPE}
450+
*/
451+
bar () {
452+
}
453+
}
454+
`
455+
},
456+
{
457+
code: `
458+
/**
459+
* @template TEMPLATE_TYPE_A, TEMPLATE_TYPE_B
460+
*/
461+
class Foo {
462+
/**
463+
* @param {TEMPLATE_TYPE_A} baz
464+
* @return {TEMPLATE_TYPE_B}
465+
*/
466+
bar (baz) {
467+
}
468+
}
469+
`
396470
}
397471
]
398472
};

0 commit comments

Comments
 (0)