Skip to content

Commit 840a0bf

Browse files
authored
Error on template tag inside callback/overload/typedef tag (#54118)
1 parent 7c378db commit 840a0bf

File tree

7 files changed

+491
-9
lines changed

7 files changed

+491
-9
lines changed

src/compiler/diagnosticMessages.json

+4
Original file line numberDiff line numberDiff line change
@@ -6592,6 +6592,10 @@
65926592
"category": "Error",
65936593
"code": 8038
65946594
},
6595+
"A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag": {
6596+
"category": "Error",
6597+
"code": 8039
6598+
},
65956599

65966600
"Declaration emit for this file requires using private name '{0}'. An explicit type annotation may unblock declaration emit.": {
65976601
"category": "Error",

src/compiler/parser.ts

+20-9
Original file line numberDiff line numberDiff line change
@@ -9140,12 +9140,15 @@ namespace Parser {
91409140
function parseNestedTypeLiteral(typeExpression: JSDocTypeExpression | undefined, name: EntityName, target: PropertyLikeParse, indent: number) {
91419141
if (typeExpression && isObjectOrObjectArrayTypeReference(typeExpression.type)) {
91429142
const pos = getNodePos();
9143-
let child: JSDocPropertyLikeTag | JSDocTypeTag | false;
9143+
let child: JSDocPropertyLikeTag | JSDocTypeTag | JSDocTemplateTag | false;
91449144
let children: JSDocPropertyLikeTag[] | undefined;
91459145
while (child = tryParse(() => parseChildParameterOrPropertyTag(target, indent, name))) {
91469146
if (child.kind === SyntaxKind.JSDocParameterTag || child.kind === SyntaxKind.JSDocPropertyTag) {
91479147
children = append(children, child);
91489148
}
9149+
else if (child.kind === SyntaxKind.JSDocTemplateTag) {
9150+
parseErrorAtRange(child.tagName, Diagnostics.A_JSDoc_template_tag_may_not_follow_a_typedef_callback_or_overload_tag);
9151+
}
91499152
}
91509153
if (children) {
91519154
const literal = finishNode(factory.createJSDocTypeLiteral(children, typeExpression.type.kind === SyntaxKind.ArrayType), pos);
@@ -9291,11 +9294,14 @@ namespace Parser {
92919294

92929295
let end: number | undefined;
92939296
if (!typeExpression || isObjectOrObjectArrayTypeReference(typeExpression.type)) {
9294-
let child: JSDocTypeTag | JSDocPropertyTag | false;
9297+
let child: JSDocTypeTag | JSDocPropertyTag | JSDocTemplateTag | false;
92959298
let childTypeTag: JSDocTypeTag | undefined;
92969299
let jsDocPropertyTags: JSDocPropertyTag[] | undefined;
92979300
let hasChildren = false;
92989301
while (child = tryParse(() => parseChildPropertyTag(indent))) {
9302+
if (child.kind === SyntaxKind.JSDocTemplateTag) {
9303+
break;
9304+
}
92999305
hasChildren = true;
93009306
if (child.kind === SyntaxKind.JSDocTypeTag) {
93019307
if (childTypeTag) {
@@ -9359,12 +9365,15 @@ namespace Parser {
93599365
return typeNameOrNamespaceName;
93609366
}
93619367

9362-
93639368
function parseCallbackTagParameters(indent: number) {
93649369
const pos = getNodePos();
9365-
let child: JSDocParameterTag | false;
9370+
let child: JSDocParameterTag | JSDocTemplateTag | false;
93669371
let parameters;
9367-
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag)) {
9372+
while (child = tryParse(() => parseChildParameterOrPropertyTag(PropertyLikeParse.CallbackParameter, indent) as JSDocParameterTag | JSDocTemplateTag)) {
9373+
if (child.kind === SyntaxKind.JSDocTemplateTag) {
9374+
parseErrorAtRange(child.tagName, Diagnostics.A_JSDoc_template_tag_may_not_follow_a_typedef_callback_or_overload_tag);
9375+
break;
9376+
}
93689377
parameters = append(parameters, child);
93699378
}
93709379
return createNodeArray(parameters || [], pos);
@@ -9420,10 +9429,10 @@ namespace Parser {
94209429
}
94219430

94229431
function parseChildPropertyTag(indent: number) {
9423-
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | false;
9432+
return parseChildParameterOrPropertyTag(PropertyLikeParse.Property, indent) as JSDocTypeTag | JSDocPropertyTag | JSDocTemplateTag | false;
94249433
}
94259434

9426-
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
9435+
function parseChildParameterOrPropertyTag(target: PropertyLikeParse, indent: number, name?: EntityName): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | false {
94279436
let canParseTag = true;
94289437
let seenAsterisk = false;
94299438
while (true) {
@@ -9459,13 +9468,13 @@ namespace Parser {
94599468
}
94609469
}
94619470

9462-
function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | false {
9471+
function tryParseChildTag(target: PropertyLikeParse, indent: number): JSDocTypeTag | JSDocPropertyTag | JSDocParameterTag | JSDocTemplateTag | false {
94639472
Debug.assert(token() === SyntaxKind.AtToken);
94649473
const start = scanner.getTokenFullStart();
94659474
nextTokenJSDoc();
94669475

94679476
const tagName = parseJSDocIdentifierName();
9468-
skipWhitespace();
9477+
const indentText = skipWhitespaceOrAsterisk();
94699478
let t: PropertyLikeParse;
94709479
switch (tagName.escapedText) {
94719480
case "type":
@@ -9479,6 +9488,8 @@ namespace Parser {
94799488
case "param":
94809489
t = PropertyLikeParse.Parameter | PropertyLikeParse.CallbackParameter;
94819490
break;
9491+
case "template":
9492+
return parseTemplateTag(start, tagName, indent, indentText);
94829493
default:
94839494
return false;
94849495
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
error TS-1: Pre-emit (11) and post-emit (13) diagnostic counts do not match! This can indicate that a semantic _error_ was added by the emit resolver - such an error may not be reflected on the command line or in the editor, but may be captured in a baseline here!
2+
tests/cases/conformance/jsdoc/templateInsideCallback.js(2,13): error TS8021: JSDoc '@typedef' tag should either have a type annotation or be followed by '@property' or '@member' tags.
3+
tests/cases/conformance/jsdoc/templateInsideCallback.js(9,5): error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
4+
tests/cases/conformance/jsdoc/templateInsideCallback.js(10,12): error TS2304: Cannot find name 'T'.
5+
tests/cases/conformance/jsdoc/templateInsideCallback.js(15,11): error TS2315: Type 'Call' is not generic.
6+
tests/cases/conformance/jsdoc/templateInsideCallback.js(17,18): error TS7006: Parameter 'x' implicitly has an 'any' type.
7+
tests/cases/conformance/jsdoc/templateInsideCallback.js(23,5): error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
8+
tests/cases/conformance/jsdoc/templateInsideCallback.js(30,5): error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
9+
tests/cases/conformance/jsdoc/templateInsideCallback.js(32,12): error TS2304: Cannot find name 'T'.
10+
tests/cases/conformance/jsdoc/templateInsideCallback.js(33,16): error TS2304: Cannot find name 'T'.
11+
tests/cases/conformance/jsdoc/templateInsideCallback.js(38,5): error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
12+
tests/cases/conformance/jsdoc/templateInsideCallback.js(39,12): error TS2304: Cannot find name 'T'.
13+
14+
15+
!!! error TS-1: Pre-emit (11) and post-emit (13) diagnostic counts do not match! This can indicate that a semantic _error_ was added by the emit resolver - such an error may not be reflected on the command line or in the editor, but may be captured in a baseline here!
16+
!!! related TS-1: The excess diagnostics are:
17+
!!! related TS7012 tests/cases/conformance/jsdoc/templateInsideCallback.js:29:5: This overload implicitly returns the type 'any' because it lacks a return type annotation.
18+
!!! related TS7012 tests/cases/conformance/jsdoc/templateInsideCallback.js:37:5: This overload implicitly returns the type 'any' because it lacks a return type annotation.
19+
==== tests/cases/conformance/jsdoc/templateInsideCallback.js (11 errors) ====
20+
/**
21+
* @typedef Oops
22+
~~~~
23+
!!! error TS8021: JSDoc '@typedef' tag should either have a type annotation or be followed by '@property' or '@member' tags.
24+
* @template T
25+
* @property {T} a
26+
* @property {T} b
27+
*/
28+
/**
29+
* @callback Call
30+
* @template T
31+
~~~~~~~~
32+
!!! error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
33+
* @param {T} x
34+
~
35+
!!! error TS2304: Cannot find name 'T'.
36+
* @returns {T}
37+
*/
38+
/**
39+
* @template T
40+
* @type {Call<T>}
41+
~~~~~~~
42+
!!! error TS2315: Type 'Call' is not generic.
43+
*/
44+
const identity = x => x;
45+
~
46+
!!! error TS7006: Parameter 'x' implicitly has an 'any' type.
47+
48+
/**
49+
* @typedef Nested
50+
* @property {Object} oh
51+
* @property {number} oh.no
52+
* @template T
53+
~~~~~~~~
54+
!!! error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
55+
* @property {string} oh.noooooo
56+
*/
57+
58+
59+
/**
60+
* @overload
61+
* @template T
62+
~~~~~~~~
63+
!!! error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
64+
* @template U
65+
* @param {T[]} array
66+
~
67+
!!! error TS2304: Cannot find name 'T'.
68+
* @param {(x: T) => U[]} iterable
69+
~
70+
!!! error TS2304: Cannot find name 'T'.
71+
* @returns {U[]}
72+
*/
73+
/**
74+
* @overload
75+
* @template T
76+
~~~~~~~~
77+
!!! error TS8039: A JSDoc '@template' tag may not follow a '@typedef', '@callback', or '@overload' tag
78+
* @param {T[][]} array
79+
~
80+
!!! error TS2304: Cannot find name 'T'.
81+
* @returns {T[]}
82+
*/
83+
/**
84+
* @param {unknown[]} array
85+
* @param {(x: unknown) => unknown} iterable
86+
* @returns {unknown[]}
87+
*/
88+
function flatMap(array, iterable = identity) {
89+
/** @type {unknown[]} */
90+
const result = [];
91+
for (let i = 0; i < array.length; i += 1) {
92+
result.push(.../** @type {unknown[]} */(iterable(array[i])));
93+
}
94+
return result;
95+
}
96+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
//// [templateInsideCallback.js]
2+
/**
3+
* @typedef Oops
4+
* @template T
5+
* @property {T} a
6+
* @property {T} b
7+
*/
8+
/**
9+
* @callback Call
10+
* @template T
11+
* @param {T} x
12+
* @returns {T}
13+
*/
14+
/**
15+
* @template T
16+
* @type {Call<T>}
17+
*/
18+
const identity = x => x;
19+
20+
/**
21+
* @typedef Nested
22+
* @property {Object} oh
23+
* @property {number} oh.no
24+
* @template T
25+
* @property {string} oh.noooooo
26+
*/
27+
28+
29+
/**
30+
* @overload
31+
* @template T
32+
* @template U
33+
* @param {T[]} array
34+
* @param {(x: T) => U[]} iterable
35+
* @returns {U[]}
36+
*/
37+
/**
38+
* @overload
39+
* @template T
40+
* @param {T[][]} array
41+
* @returns {T[]}
42+
*/
43+
/**
44+
* @param {unknown[]} array
45+
* @param {(x: unknown) => unknown} iterable
46+
* @returns {unknown[]}
47+
*/
48+
function flatMap(array, iterable = identity) {
49+
/** @type {unknown[]} */
50+
const result = [];
51+
for (let i = 0; i < array.length; i += 1) {
52+
result.push(.../** @type {unknown[]} */(iterable(array[i])));
53+
}
54+
return result;
55+
}
56+
57+
58+
//// [templateInsideCallback.js]
59+
"use strict";
60+
/**
61+
* @typedef Oops
62+
* @template T
63+
* @property {T} a
64+
* @property {T} b
65+
*/
66+
/**
67+
* @callback Call
68+
* @template T
69+
* @param {T} x
70+
* @returns {T}
71+
*/
72+
/**
73+
* @template T
74+
* @type {Call<T>}
75+
*/
76+
var identity = function (x) { return x; };
77+
/**
78+
* @typedef Nested
79+
* @property {Object} oh
80+
* @property {number} oh.no
81+
* @template T
82+
* @property {string} oh.noooooo
83+
*/
84+
/**
85+
* @overload
86+
* @template T
87+
* @template U
88+
* @param {T[]} array
89+
* @param {(x: T) => U[]} iterable
90+
* @returns {U[]}
91+
*/
92+
/**
93+
* @overload
94+
* @template T
95+
* @param {T[][]} array
96+
* @returns {T[]}
97+
*/
98+
/**
99+
* @param {unknown[]} array
100+
* @param {(x: unknown) => unknown} iterable
101+
* @returns {unknown[]}
102+
*/
103+
function flatMap(array, iterable) {
104+
if (iterable === void 0) { iterable = identity; }
105+
/** @type {unknown[]} */
106+
var result = [];
107+
for (var i = 0; i < array.length; i += 1) {
108+
result.push.apply(result, /** @type {unknown[]} */ (iterable(array[i])));
109+
}
110+
return result;
111+
}
112+
113+
114+
//// [templateInsideCallback.d.ts]
115+
declare function flatMap<U>(): any;
116+
declare function flatMap(): any;
117+
/**
118+
* @typedef Oops
119+
* @template T
120+
* @property {T} a
121+
* @property {T} b
122+
*/
123+
/**
124+
* @callback Call
125+
* @template T
126+
* @param {T} x
127+
* @returns {T}
128+
*/
129+
/**
130+
* @template T
131+
* @type {Call<T>}
132+
*/
133+
declare const identity: any;
134+
type Nested = {
135+
oh: {
136+
no: number;
137+
noooooo: string;
138+
};
139+
};
140+
type Oops = any;
141+
type Call = () => any;

0 commit comments

Comments
 (0)