Skip to content

Commit 1cedab1

Browse files
authored
Fix parsing of parenthesized JSDoc parameters (microsoft#25799)
* Fix parsing of parenthesized JSDoc parameters Parenthesis can start a jsdoc function parameter since it is just a type, and parenthesis can start a type: ```js /** @type {function(((string))): void} */ ``` However, this is not legal in other parameter lists: ```ts function x((((a))): string) { } ``` This change makes jsdoc function parameter lists parse differently than normal parameter lists by allowing parenthesis as a start character of jsdoc parameters. * Parse nested uses of jsdoc function types * Fix test
1 parent 46827f4 commit 1cedab1

7 files changed

+69
-5
lines changed

src/compiler/parser.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -1509,7 +1509,9 @@ namespace ts {
15091509
case ParsingContext.ArgumentExpressions:
15101510
return token() === SyntaxKind.DotDotDotToken || isStartOfExpression();
15111511
case ParsingContext.Parameters:
1512-
return isStartOfParameter();
1512+
return isStartOfParameter(/*isJSDocParameter*/ false);
1513+
case ParsingContext.JSDocParameters:
1514+
return isStartOfParameter(/*isJSDocParameter*/ true);
15131515
case ParsingContext.TypeArguments:
15141516
case ParsingContext.TupleElementTypes:
15151517
return token() === SyntaxKind.CommaToken || isStartOfType();
@@ -1612,6 +1614,7 @@ namespace ts {
16121614
case ParsingContext.TupleElementTypes:
16131615
case ParsingContext.ArrayBindingElements:
16141616
return token() === SyntaxKind.CloseBracketToken;
1617+
case ParsingContext.JSDocParameters:
16151618
case ParsingContext.Parameters:
16161619
case ParsingContext.RestProperties:
16171620
// Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery
@@ -1795,6 +1798,7 @@ namespace ts {
17951798
case ParsingContext.VariableDeclarations:
17961799
return isReusableVariableDeclaration(node);
17971800

1801+
case ParsingContext.JSDocParameters:
17981802
case ParsingContext.Parameters:
17991803
return isReusableParameter(node);
18001804

@@ -2009,6 +2013,7 @@ namespace ts {
20092013
case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected;
20102014
case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected;
20112015
case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected;
2016+
case ParsingContext.JSDocParameters: return Diagnostics.Parameter_declaration_expected;
20122017
case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected;
20132018
case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected;
20142019
case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected;
@@ -2430,12 +2435,12 @@ namespace ts {
24302435
return undefined;
24312436
}
24322437

2433-
function isStartOfParameter(): boolean {
2438+
function isStartOfParameter(isJSDocParameter: boolean): boolean {
24342439
return token() === SyntaxKind.DotDotDotToken ||
24352440
isIdentifierOrPattern() ||
24362441
isModifierKind(token()) ||
24372442
token() === SyntaxKind.AtToken ||
2438-
isStartOfType(/*inStartOfParameter*/ true);
2443+
isStartOfType(/*inStartOfParameter*/ !isJSDocParameter);
24392444
}
24402445

24412446
function parseParameter(): ParameterDeclaration {
@@ -2534,7 +2539,9 @@ namespace ts {
25342539
setYieldContext(!!(flags & SignatureFlags.Yield));
25352540
setAwaitContext(!!(flags & SignatureFlags.Await));
25362541

2537-
signature.parameters = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter);
2542+
signature.parameters = flags & SignatureFlags.JSDoc ?
2543+
parseDelimitedList(ParsingContext.JSDocParameters, parseJSDocParameter) :
2544+
parseDelimitedList(ParsingContext.Parameters, parseParameter);
25382545

25392546
setYieldContext(savedYieldContext);
25402547
setAwaitContext(savedAwaitContext);
@@ -2960,6 +2967,8 @@ namespace ts {
29602967
case SyntaxKind.InferKeyword:
29612968
case SyntaxKind.ImportKeyword:
29622969
return true;
2970+
case SyntaxKind.FunctionKeyword:
2971+
return !inStartOfParameter;
29632972
case SyntaxKind.MinusToken:
29642973
return !inStartOfParameter && lookAhead(nextTokenIsNumericLiteral);
29652974
case SyntaxKind.OpenParenToken:
@@ -2973,7 +2982,7 @@ namespace ts {
29732982

29742983
function isStartOfParenthesizedOrFunctionType() {
29752984
nextToken();
2976-
return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
2985+
return token() === SyntaxKind.CloseParenToken || isStartOfParameter(/*isJSDocParameter*/ false) || isStartOfType();
29772986
}
29782987

29792988
function parsePostfixTypeOrHigher(): TypeNode {
@@ -6255,6 +6264,7 @@ namespace ts {
62556264
JsxChildren, // Things between opening and closing JSX tags
62566265
ArrayLiteralMembers, // Members in array literal
62576266
Parameters, // Parameters in parameter list
6267+
JSDocParameters, // JSDoc parameters in parameter list of JSDoc function type
62586268
RestProperties, // Property names in a rest type list
62596269
TypeParameters, // Type parameters in type parameter list
62606270
TypeArguments, // Type arguments in type argument list
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/jsdoc/paren.js ===
2+
/** @type {function((string), function((string)): string): string} */
3+
var x = (s, id) => id(s)
4+
>x : Symbol(x, Decl(paren.js, 1, 3))
5+
>s : Symbol(s, Decl(paren.js, 1, 9))
6+
>id : Symbol(id, Decl(paren.js, 1, 11))
7+
>id : Symbol(id, Decl(paren.js, 1, 11))
8+
>s : Symbol(s, Decl(paren.js, 1, 9))
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/jsdoc/paren.js ===
2+
/** @type {function((string), function((string)): string): string} */
3+
var x = (s, id) => id(s)
4+
>x : (arg0: string, arg1: (arg0: string) => string) => string
5+
>(s, id) => id(s) : (s: string, id: (arg0: string) => string) => string
6+
>s : string
7+
>id : (arg0: string) => string
8+
>id(s) : string
9+
>id : (arg0: string) => string
10+
>s : string
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
=== tests/cases/conformance/jsdoc/paren.js ===
2+
/** @type {function((string)): string} */
3+
var x = s => s.toString()
4+
>x : Symbol(x, Decl(paren.js, 1, 3))
5+
>s : Symbol(s, Decl(paren.js, 1, 7))
6+
>s.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --))
7+
>s : Symbol(s, Decl(paren.js, 1, 7))
8+
>toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --))
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
=== tests/cases/conformance/jsdoc/paren.js ===
2+
/** @type {function((string)): string} */
3+
var x = s => s.toString()
4+
>x : (arg0: string) => string
5+
>s => s.toString() : (s: string) => string
6+
>s : string
7+
>s.toString() : string
8+
>s.toString : () => string
9+
>s : string
10+
>toString : () => string
11+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @noemit: true
2+
// @allowjs: true
3+
// @checkjs: true
4+
// @strict: true
5+
// @Filename: paren.js
6+
/** @type {function((string), function((string)): string): string} */
7+
var x = (s, id) => id(s)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @noemit: true
2+
// @allowjs: true
3+
// @checkjs: true
4+
// @strict: true
5+
// @Filename: paren.js
6+
/** @type {function((string)): string} */
7+
var x = s => s.toString()

0 commit comments

Comments
 (0)