Skip to content

Commit c91e49c

Browse files
committed
feat(valid-types): require whitelist of types and prevent names with suppress tag
1 parent 3a27c39 commit c91e49c

File tree

4 files changed

+184
-1
lines changed

4 files changed

+184
-1
lines changed

README.md

+28
Original file line numberDiff line numberDiff line change
@@ -20561,6 +20561,27 @@ parseArray = function(parser) {
2056120561
};
2056220562
// Settings: {"jsdoc":{"mode":"closure"}}
2056320563
// Message: Syntax error in namepath: R<~
20564+
20565+
/**
20566+
* @suppress
20567+
*/
20568+
function quux () {}
20569+
// Settings: {"jsdoc":{"mode":"closure"}}
20570+
// Message: Tag @suppress must have a type in "closure" mode.
20571+
20572+
/**
20573+
* @suppress {visibility} sth
20574+
*/
20575+
function quux () {}
20576+
// Settings: {"jsdoc":{"mode":"closure"}}
20577+
// Message: @suppress should not have a name in "closure" mode.
20578+
20579+
/**
20580+
* @suppress {visibility|blah}
20581+
*/
20582+
function quux () {}
20583+
// Settings: {"jsdoc":{"mode":"closure"}}
20584+
// Message: Syntax error in supresss type: blah
2056420585
````
2056520586

2056620587
The following patterns are not considered problems:
@@ -20876,6 +20897,13 @@ type ComplicatedType<T, U, V, W, X> = never
2087620897
*/
2087720898
class quux {}
2087820899
// Settings: {"jsdoc":{"mode":"typescript"}}
20900+
20901+
/**
20902+
* @suppress {visibility|underscore}
20903+
*/
20904+
function quux() {
20905+
}
20906+
// Settings: {"jsdoc":{"mode":"closure"}}
2087920907
````
2088020908

2088120909

src/getDefaultTagStructureForMode.js

+5
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,11 @@ const getDefaultTagStructureForMode = (mode) => {
396396
['typeAllowed', isClosureOrPermissive],
397397
])],
398398

399+
['suppress', new Map([
400+
['nameContents', !isClosure],
401+
['typeRequired', isClosure],
402+
])],
403+
399404
['template', new Map([
400405
['nameContents', isJsdoc ? 'text' : 'namepath-referencing'],
401406

src/rules/validTypes.js

+77-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,67 @@
11
import {
2-
tryParse, parse,
2+
tryParse, parse, traverse,
33
} from 'jsdoc-type-pratt-parser';
44
import iterateJsdoc from '../iterateJsdoc';
55

66
const asExpression = /as\s+/u;
77

8+
const suppressTypes = new Set([
9+
// https://github.com/google/closure-compiler/wiki/@suppress-annotations
10+
// https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/parsing/ParserConfig.properties#L154
11+
'accessControls',
12+
'checkDebuggerStatement',
13+
'checkPrototypalTypes',
14+
'checkRegExp',
15+
'checkTypes',
16+
'checkVars',
17+
'closureDepMethodUsageChecks',
18+
'const',
19+
'constantProperty',
20+
'deprecated',
21+
'duplicate',
22+
'es5Strict',
23+
'externsValidation',
24+
'extraProvide',
25+
'extraRequire',
26+
'globalThis',
27+
'invalidCasts',
28+
'lateProvide',
29+
'legacyGoogScopeRequire',
30+
'lintChecks',
31+
'messageConventions',
32+
'misplacedTypeAnnotation',
33+
'missingOverride',
34+
'missingPolyfill',
35+
'missingProperties',
36+
'missingProvide',
37+
'missingRequire',
38+
'missingSourcesWarnings',
39+
'moduleLoad',
40+
'nonStandardJsDocs',
41+
'partialAlias',
42+
'polymer',
43+
'reportUnknownTypes',
44+
'strictMissingProperties',
45+
'strictModuleDepCheck',
46+
'strictPrimitiveOperators',
47+
'suspiciousCode',
48+
49+
// Not documented in enum
50+
'switch',
51+
'transitionalSuspiciousCodeWarnings',
52+
'undefinedNames',
53+
'undefinedVars',
54+
'underscore',
55+
'unknownDefines',
56+
'untranspilableFeatures',
57+
'unusedLocalVariables',
58+
'unusedPrivateMembers',
59+
'useOfGoogProvide',
60+
'uselessCode',
61+
'visibility',
62+
'with',
63+
]);
64+
865
const tryParsePathIgnoreError = (path) => {
966
try {
1067
tryParse(path);
@@ -111,6 +168,25 @@ export default iterateJsdoc(({
111168
continue;
112169
}
113170

171+
if (tag.tag === 'suppress' && mode === 'closure') {
172+
let parsedTypes;
173+
174+
try {
175+
parsedTypes = tryParse(tag.type);
176+
} catch {
177+
// Ignore
178+
}
179+
180+
if (parsedTypes) {
181+
traverse(parsedTypes, (node) => {
182+
const {value: type} = node;
183+
if (type !== undefined && !suppressTypes.has(type)) {
184+
report(`Syntax error in supresss type: ${type}`, null, tag);
185+
}
186+
});
187+
}
188+
}
189+
114190
const otherModeMaps = ['jsdoc', 'typescript', 'closure', 'permissive'].filter(
115191
(mde) => {
116192
return mde !== mode;

test/rules/assertions/validTypes.js

+74
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,66 @@ export default {
721721
},
722722
},
723723
},
724+
{
725+
code: `
726+
/**
727+
* @suppress
728+
*/
729+
function quux () {}
730+
731+
`,
732+
errors: [
733+
{
734+
line: 3,
735+
message: 'Tag @suppress must have a type in "closure" mode.',
736+
},
737+
],
738+
settings: {
739+
jsdoc: {
740+
mode: 'closure',
741+
},
742+
},
743+
},
744+
{
745+
code: `
746+
/**
747+
* @suppress {visibility} sth
748+
*/
749+
function quux () {}
750+
751+
`,
752+
errors: [
753+
{
754+
line: 3,
755+
message: '@suppress should not have a name in "closure" mode.',
756+
},
757+
],
758+
settings: {
759+
jsdoc: {
760+
mode: 'closure',
761+
},
762+
},
763+
},
764+
{
765+
code: `
766+
/**
767+
* @suppress {visibility|blah}
768+
*/
769+
function quux () {}
770+
771+
`,
772+
errors: [
773+
{
774+
line: 3,
775+
message: 'Syntax error in supresss type: blah',
776+
},
777+
],
778+
settings: {
779+
jsdoc: {
780+
mode: 'closure',
781+
},
782+
},
783+
},
724784
],
725785
valid: [
726786
{
@@ -1278,5 +1338,19 @@ export default {
12781338
},
12791339
},
12801340
},
1341+
{
1342+
code: `
1343+
/**
1344+
* @suppress {visibility|underscore}
1345+
*/
1346+
function quux() {
1347+
}
1348+
`,
1349+
settings: {
1350+
jsdoc: {
1351+
mode: 'closure',
1352+
},
1353+
},
1354+
},
12811355
],
12821356
};

0 commit comments

Comments
 (0)