Fix parsing of parenthesized JSDoc parameters (#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
This commit is contained in:
Nathan Shively-Sanders
2018-07-19 12:50:36 -07:00
committed by GitHub
parent 46827f4a76
commit 1cedab18be
7 changed files with 69 additions and 5 deletions
+15 -5
View File
@@ -1509,7 +1509,9 @@ namespace ts {
case ParsingContext.ArgumentExpressions:
return token() === SyntaxKind.DotDotDotToken || isStartOfExpression();
case ParsingContext.Parameters:
return isStartOfParameter();
return isStartOfParameter(/*isJSDocParameter*/ false);
case ParsingContext.JSDocParameters:
return isStartOfParameter(/*isJSDocParameter*/ true);
case ParsingContext.TypeArguments:
case ParsingContext.TupleElementTypes:
return token() === SyntaxKind.CommaToken || isStartOfType();
@@ -1612,6 +1614,7 @@ namespace ts {
case ParsingContext.TupleElementTypes:
case ParsingContext.ArrayBindingElements:
return token() === SyntaxKind.CloseBracketToken;
case ParsingContext.JSDocParameters:
case ParsingContext.Parameters:
case ParsingContext.RestProperties:
// Tokens other than ')' and ']' (the latter for index signatures) are here for better error recovery
@@ -1795,6 +1798,7 @@ namespace ts {
case ParsingContext.VariableDeclarations:
return isReusableVariableDeclaration(node);
case ParsingContext.JSDocParameters:
case ParsingContext.Parameters:
return isReusableParameter(node);
@@ -2009,6 +2013,7 @@ namespace ts {
case ParsingContext.ArgumentExpressions: return Diagnostics.Argument_expression_expected;
case ParsingContext.ObjectLiteralMembers: return Diagnostics.Property_assignment_expected;
case ParsingContext.ArrayLiteralMembers: return Diagnostics.Expression_or_comma_expected;
case ParsingContext.JSDocParameters: return Diagnostics.Parameter_declaration_expected;
case ParsingContext.Parameters: return Diagnostics.Parameter_declaration_expected;
case ParsingContext.TypeParameters: return Diagnostics.Type_parameter_declaration_expected;
case ParsingContext.TypeArguments: return Diagnostics.Type_argument_expected;
@@ -2430,12 +2435,12 @@ namespace ts {
return undefined;
}
function isStartOfParameter(): boolean {
function isStartOfParameter(isJSDocParameter: boolean): boolean {
return token() === SyntaxKind.DotDotDotToken ||
isIdentifierOrPattern() ||
isModifierKind(token()) ||
token() === SyntaxKind.AtToken ||
isStartOfType(/*inStartOfParameter*/ true);
isStartOfType(/*inStartOfParameter*/ !isJSDocParameter);
}
function parseParameter(): ParameterDeclaration {
@@ -2534,7 +2539,9 @@ namespace ts {
setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));
signature.parameters = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter);
signature.parameters = flags & SignatureFlags.JSDoc ?
parseDelimitedList(ParsingContext.JSDocParameters, parseJSDocParameter) :
parseDelimitedList(ParsingContext.Parameters, parseParameter);
setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);
@@ -2960,6 +2967,8 @@ namespace ts {
case SyntaxKind.InferKeyword:
case SyntaxKind.ImportKeyword:
return true;
case SyntaxKind.FunctionKeyword:
return !inStartOfParameter;
case SyntaxKind.MinusToken:
return !inStartOfParameter && lookAhead(nextTokenIsNumericLiteral);
case SyntaxKind.OpenParenToken:
@@ -2973,7 +2982,7 @@ namespace ts {
function isStartOfParenthesizedOrFunctionType() {
nextToken();
return token() === SyntaxKind.CloseParenToken || isStartOfParameter() || isStartOfType();
return token() === SyntaxKind.CloseParenToken || isStartOfParameter(/*isJSDocParameter*/ false) || isStartOfType();
}
function parsePostfixTypeOrHigher(): TypeNode {
@@ -6255,6 +6264,7 @@ namespace ts {
JsxChildren, // Things between opening and closing JSX tags
ArrayLiteralMembers, // Members in array literal
Parameters, // Parameters in parameter list
JSDocParameters, // JSDoc parameters in parameter list of JSDoc function type
RestProperties, // Property names in a rest type list
TypeParameters, // Type parameters in type parameter list
TypeArguments, // Type arguments in type argument list
@@ -0,0 +1,9 @@
=== tests/cases/conformance/jsdoc/paren.js ===
/** @type {function((string), function((string)): string): string} */
var x = (s, id) => id(s)
>x : Symbol(x, Decl(paren.js, 1, 3))
>s : Symbol(s, Decl(paren.js, 1, 9))
>id : Symbol(id, Decl(paren.js, 1, 11))
>id : Symbol(id, Decl(paren.js, 1, 11))
>s : Symbol(s, Decl(paren.js, 1, 9))
@@ -0,0 +1,11 @@
=== tests/cases/conformance/jsdoc/paren.js ===
/** @type {function((string), function((string)): string): string} */
var x = (s, id) => id(s)
>x : (arg0: string, arg1: (arg0: string) => string) => string
>(s, id) => id(s) : (s: string, id: (arg0: string) => string) => string
>s : string
>id : (arg0: string) => string
>id(s) : string
>id : (arg0: string) => string
>s : string
@@ -0,0 +1,9 @@
=== tests/cases/conformance/jsdoc/paren.js ===
/** @type {function((string)): string} */
var x = s => s.toString()
>x : Symbol(x, Decl(paren.js, 1, 3))
>s : Symbol(s, Decl(paren.js, 1, 7))
>s.toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --))
>s : Symbol(s, Decl(paren.js, 1, 7))
>toString : Symbol(String.toString, Decl(lib.es5.d.ts, --, --))
@@ -0,0 +1,11 @@
=== tests/cases/conformance/jsdoc/paren.js ===
/** @type {function((string)): string} */
var x = s => s.toString()
>x : (arg0: string) => string
>s => s.toString() : (s: string) => string
>s : string
>s.toString() : string
>s.toString : () => string
>s : string
>toString : () => string
@@ -0,0 +1,7 @@
// @noemit: true
// @allowjs: true
// @checkjs: true
// @strict: true
// @Filename: paren.js
/** @type {function((string), function((string)): string): string} */
var x = (s, id) => id(s)
@@ -0,0 +1,7 @@
// @noemit: true
// @allowjs: true
// @checkjs: true
// @strict: true
// @Filename: paren.js
/** @type {function((string)): string} */
var x = s => s.toString()