Skip to content

Commit 7f5c439

Browse files
committed
Skip whitespace or asterisk in JSDoc param type and name
1 parent e2a8f99 commit 7f5c439

File tree

6 files changed

+189
-3
lines changed

6 files changed

+189
-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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
tests/cases/conformance/jsdoc/a.js(15,11): error TS1003: Identifier expected.
2+
tests/cases/conformance/jsdoc/a.js(15,11): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
3+
tests/cases/conformance/jsdoc/a.js(18,4): error TS1003: Identifier expected.
4+
tests/cases/conformance/jsdoc/a.js(18,4): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
5+
tests/cases/conformance/jsdoc/a.js(19,19): error TS1003: Identifier expected.
6+
tests/cases/conformance/jsdoc/a.js(19,19): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
7+
tests/cases/conformance/jsdoc/a.js(22,14): error TS7006: Parameter 'x' implicitly has an 'any' type.
8+
tests/cases/conformance/jsdoc/a.js(22,17): error TS7006: Parameter 'y' implicitly has an 'any' type.
9+
tests/cases/conformance/jsdoc/a.js(22,20): error TS7006: Parameter 'z' implicitly has an 'any' type.
10+
11+
12+
==== tests/cases/conformance/jsdoc/a.js (9 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+
good(1, 2, 3)
24+
25+
26+
/**
27+
* @param *
28+
29+
!!! error TS1003: Identifier expected.
30+
31+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
32+
* {number} x Arg x.
33+
* @param {number}
34+
* * y Arg y.
35+
36+
!!! error TS1003: Identifier expected.
37+
38+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
39+
* @param {number} * z
40+
41+
!!! error TS1003: Identifier expected.
42+
43+
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
44+
* Arg z.
45+
*/
46+
function bad(x, y, z) {
47+
~
48+
!!! error TS7006: Parameter 'x' implicitly has an 'any' type.
49+
~
50+
!!! error TS7006: Parameter 'y' implicitly has an 'any' type.
51+
~
52+
!!! error TS7006: Parameter 'z' implicitly has an 'any' type.
53+
}
54+
bad(1, 2, 3)
55+
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
=== tests/cases/conformance/jsdoc/a.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(a.js, 0, 0))
12+
>x : Symbol(x, Decl(a.js, 8, 14))
13+
>y : Symbol(y, Decl(a.js, 8, 16))
14+
>z : Symbol(z, Decl(a.js, 8, 19))
15+
}
16+
good(1, 2, 3)
17+
>good : Symbol(good, Decl(a.js, 0, 0))
18+
19+
20+
/**
21+
* @param *
22+
* {number} x Arg x.
23+
* @param {number}
24+
* * y Arg y.
25+
* @param {number} * z
26+
* Arg z.
27+
*/
28+
function bad(x, y, z) {
29+
>bad : Symbol(bad, Decl(a.js, 10, 13))
30+
>x : Symbol(x, Decl(a.js, 21, 13))
31+
>y : Symbol(y, Decl(a.js, 21, 15))
32+
>z : Symbol(z, Decl(a.js, 21, 18))
33+
}
34+
bad(1, 2, 3)
35+
>bad : Symbol(bad, Decl(a.js, 10, 13))
36+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/conformance/jsdoc/a.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+
good(1, 2, 3)
17+
>good(1, 2, 3) : void
18+
>good : (x: number, y: number, z: number) => void
19+
>1 : 1
20+
>2 : 2
21+
>3 : 3
22+
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+
>bad : (x: any, y: any, z: any) => void
34+
>x : any
35+
>y : any
36+
>z : any
37+
}
38+
bad(1, 2, 3)
39+
>bad(1, 2, 3) : void
40+
>bad : (x: any, y: any, z: any) => void
41+
>1 : 1
42+
>2 : 2
43+
>3 : 3
44+
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// @noEmit: true
2+
// @allowJs: true
3+
// @checkJs: true
4+
// @strict: true
5+
// @Filename: a.js
6+
7+
/**
8+
* @param
9+
* {number} x Arg x.
10+
* @param {number}
11+
* y Arg y.
12+
* @param {number} z
13+
* Arg z.
14+
*/
15+
function good(x, y, z) {
16+
}
17+
good(1, 2, 3)
18+
19+
20+
/**
21+
* @param *
22+
* {number} x Arg x.
23+
* @param {number}
24+
* * y Arg y.
25+
* @param {number} * z
26+
* Arg z.
27+
*/
28+
function bad(x, y, z) {
29+
}
30+
bad(1, 2, 3)

0 commit comments

Comments
 (0)