Skip to content

Commit 6fd725f

Browse files
tschaubsandersn
authored andcommitted
Skip whitespace or asterisk in JSDoc param type and name (#26067)
1 parent 746e39e commit 6fd725f

File tree

6 files changed

+203
-3
lines changed

6 files changed

+203
-3
lines changed

src/compiler/parser.ts

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6507,14 +6507,33 @@ namespace ts {
65076507
}
65086508
}
65096509

6510+
function skipWhitespaceOrAsterisk(): void {
6511+
if (token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) {
6512+
if (lookAhead(isNextNonwhitespaceTokenEndOfFile)) {
6513+
return; // Don't skip whitespace prior to EoF (or end of comment) - that shouldn't be included in any node's range
6514+
}
6515+
}
6516+
6517+
let precedingLineBreak = scanner.hasPrecedingLineBreak();
6518+
while ((precedingLineBreak && token() === SyntaxKind.AsteriskToken) || token() === SyntaxKind.WhitespaceTrivia || token() === SyntaxKind.NewLineTrivia) {
6519+
if (token() === SyntaxKind.NewLineTrivia) {
6520+
precedingLineBreak = true;
6521+
}
6522+
else if (token() === SyntaxKind.AsteriskToken) {
6523+
precedingLineBreak = false;
6524+
}
6525+
nextJSDocToken();
6526+
}
6527+
}
6528+
65106529
function parseTag(indent: number) {
65116530
Debug.assert(token() === SyntaxKind.AtToken);
65126531
const atToken = <AtToken>createNode(SyntaxKind.AtToken, scanner.getTokenPos());
65136532
atToken.end = scanner.getTextPos();
65146533
nextJSDocToken();
65156534

65166535
const tagName = parseJSDocIdentifierName();
6517-
skipWhitespace();
6536+
skipWhitespaceOrAsterisk();
65186537

65196538
let tag: JSDocTag | undefined;
65206539
switch (tagName.escapedText) {
@@ -6658,7 +6677,7 @@ namespace ts {
66586677
}
66596678

66606679
function tryParseTypeExpression(): JSDocTypeExpression | undefined {
6661-
skipWhitespace();
6680+
skipWhitespaceOrAsterisk();
66626681
return token() === SyntaxKind.OpenBraceToken ? parseJSDocTypeExpression() : undefined;
66636682
}
66646683

@@ -6698,7 +6717,7 @@ namespace ts {
66986717
function parseParameterOrPropertyTag(atToken: AtToken, tagName: Identifier, target: PropertyLikeParse, indent: number | undefined): JSDocParameterTag | JSDocPropertyTag {
66996718
let typeExpression = tryParseTypeExpression();
67006719
let isNameFirst = !typeExpression;
6701-
skipWhitespace();
6720+
skipWhitespaceOrAsterisk();
67026721

67036722
const { name, isBracketed } = parseBracketNameInPropertyAndParamTag();
67046723
skipWhitespace();

src/compiler/scanner.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,6 +1933,7 @@ namespace ts {
19331933

19341934
function scanJSDocToken(): JsDocSyntaxKind {
19351935
startPos = tokenPos = pos;
1936+
tokenFlags = 0;
19361937
if (pos >= end) {
19371938
return token = SyntaxKind.EndOfFileToken;
19381939
}
@@ -1952,6 +1953,7 @@ namespace ts {
19521953
return token = SyntaxKind.AtToken;
19531954
case CharacterCodes.lineFeed:
19541955
case CharacterCodes.carriageReturn:
1956+
tokenFlags |= TokenFlags.PrecedingLineBreak;
19551957
return token = SyntaxKind.NewLineTrivia;
19561958
case CharacterCodes.asterisk:
19571959
return token = SyntaxKind.AsteriskToken;
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
tests/cases/conformance/jsdoc/bad.js(2,11): error TS1003: Identifier expected.
2+
tests/cases/conformance/jsdoc/bad.js(2,11): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
3+
tests/cases/conformance/jsdoc/bad.js(5,4): error TS1003: Identifier expected.
4+
tests/cases/conformance/jsdoc/bad.js(5,4): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
5+
tests/cases/conformance/jsdoc/bad.js(6,19): error TS1003: Identifier expected.
6+
tests/cases/conformance/jsdoc/bad.js(6,19): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
7+
tests/cases/conformance/jsdoc/bad.js(9,14): error TS7006: Parameter 'x' implicitly has an 'any' type.
8+
tests/cases/conformance/jsdoc/bad.js(9,17): error TS7006: Parameter 'y' implicitly has an 'any' type.
9+
tests/cases/conformance/jsdoc/bad.js(9,20): error TS7006: Parameter 'z' implicitly has an 'any' type.
10+
11+
12+
==== tests/cases/conformance/jsdoc/good.js (0 errors) ====
13+
/**
14+
* @param
15+
* {number} x Arg x.
16+
* @param {number}
17+
* y Arg y.
18+
* @param {number} z
19+
* Arg z.
20+
*/
21+
function good(x, y, z) {
22+
}
23+
24+
good(1, 2, 3)
25+
26+
27+
==== tests/cases/conformance/jsdoc/bad.js (9 errors) ====
28+
/**
29+
* @param *
30+
31+
!!! error TS1003: Identifier expected.
32+
33+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
34+
* {number} x Arg x.
35+
* @param {number}
36+
* * y Arg y.
37+
38+
!!! error TS1003: Identifier expected.
39+
40+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
41+
* @param {number} * z
42+
43+
!!! error TS1003: Identifier expected.
44+
45+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
46+
* Arg z.
47+
*/
48+
function bad(x, y, z) {
49+
~
50+
!!! error TS7006: Parameter 'x' implicitly has an 'any' type.
51+
~
52+
!!! error TS7006: Parameter 'y' implicitly has an 'any' type.
53+
~
54+
!!! error TS7006: Parameter 'z' implicitly has an 'any' type.
55+
}
56+
57+
bad(1, 2, 3)
58+
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/conformance/jsdoc/good.js ===
2+
/**
3+
* @param
4+
* {number} x Arg x.
5+
* @param {number}
6+
* y Arg y.
7+
* @param {number} z
8+
* Arg z.
9+
*/
10+
function good(x, y, z) {
11+
>good : Symbol(good, Decl(good.js, 0, 0))
12+
>x : Symbol(x, Decl(good.js, 8, 14))
13+
>y : Symbol(y, Decl(good.js, 8, 16))
14+
>z : Symbol(z, Decl(good.js, 8, 19))
15+
}
16+
17+
good(1, 2, 3)
18+
>good : Symbol(good, Decl(good.js, 0, 0))
19+
20+
21+
=== tests/cases/conformance/jsdoc/bad.js ===
22+
/**
23+
* @param *
24+
* {number} x Arg x.
25+
* @param {number}
26+
* * y Arg y.
27+
* @param {number} * z
28+
* Arg z.
29+
*/
30+
function bad(x, y, z) {
31+
>bad : Symbol(bad, Decl(bad.js, 0, 0))
32+
>x : Symbol(x, Decl(bad.js, 8, 13))
33+
>y : Symbol(y, Decl(bad.js, 8, 15))
34+
>z : Symbol(z, Decl(bad.js, 8, 18))
35+
}
36+
37+
bad(1, 2, 3)
38+
>bad : Symbol(bad, Decl(bad.js, 0, 0))
39+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/conformance/jsdoc/good.js ===
2+
/**
3+
* @param
4+
* {number} x Arg x.
5+
* @param {number}
6+
* y Arg y.
7+
* @param {number} z
8+
* Arg z.
9+
*/
10+
function good(x, y, z) {
11+
>good : (x: number, y: number, z: number) => void
12+
>x : number
13+
>y : number
14+
>z : number
15+
}
16+
17+
good(1, 2, 3)
18+
>good(1, 2, 3) : void
19+
>good : (x: number, y: number, z: number) => void
20+
>1 : 1
21+
>2 : 2
22+
>3 : 3
23+
24+
25+
=== tests/cases/conformance/jsdoc/bad.js ===
26+
/**
27+
* @param *
28+
* {number} x Arg x.
29+
* @param {number}
30+
* * y Arg y.
31+
* @param {number} * z
32+
* Arg z.
33+
*/
34+
function bad(x, y, z) {
35+
>bad : (x: any, y: any, z: any) => void
36+
>x : any
37+
>y : any
38+
>z : any
39+
}
40+
41+
bad(1, 2, 3)
42+
>bad(1, 2, 3) : void
43+
>bad : (x: any, y: any, z: any) => void
44+
>1 : 1
45+
>2 : 2
46+
>3 : 3
47+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @strict: true
5+
6+
// @Filename: good.js
7+
8+
/**
9+
* @param
10+
* {number} x Arg x.
11+
* @param {number}
12+
* y Arg y.
13+
* @param {number} z
14+
* Arg z.
15+
*/
16+
function good(x, y, z) {
17+
}
18+
19+
good(1, 2, 3)
20+
21+
22+
// @Filename: bad.js
23+
24+
/**
25+
* @param *
26+
* {number} x Arg x.
27+
* @param {number}
28+
* * y Arg y.
29+
* @param {number} * z
30+
* Arg z.
31+
*/
32+
function bad(x, y, z) {
33+
}
34+
35+
bad(1, 2, 3)

0 commit comments

Comments
 (0)