If parsing a function type fails, parseTypeReference() to ensure something is returned (#24567)

* If parsing a function type fails, parseTypeReference() to ensure something is returned

* Avoid tryParse

* Add missing semicolon

* Don't check for undefined, check for missing type

* Don't set parameters undefined, set to missingList and return false

* Update API baselines

* Code review
This commit is contained in:
Andy 2018-06-05 10:24:37 -07:00 committed by GitHub
parent 9681796785
commit 735a46f838
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 143 additions and 87 deletions

View file

@ -4,7 +4,6 @@ namespace ts {
Yield = 1 << 0, Yield = 1 << 0,
Await = 1 << 1, Await = 1 << 1,
Type = 1 << 2, Type = 1 << 2,
RequireCompleteParameterList = 1 << 3,
IgnoreMissingOpenBrace = 1 << 4, IgnoreMissingOpenBrace = 1 << 4,
JSDoc = 1 << 5, JSDoc = 1 << 5,
} }
@ -1257,8 +1256,8 @@ namespace ts {
new TokenConstructor(kind, p, p); new TokenConstructor(kind, p, p);
} }
function createNodeWithJSDoc(kind: SyntaxKind): Node { function createNodeWithJSDoc(kind: SyntaxKind, pos?: number): Node {
const node = createNode(kind); const node = createNode(kind, pos);
if (scanner.getTokenFlags() & TokenFlags.PrecedingJSDocComment) { if (scanner.getTokenFlags() & TokenFlags.PrecedingJSDocComment) {
addJSDocComment(<HasJSDoc>node); addJSDocComment(<HasJSDoc>node);
} }
@ -2257,6 +2256,24 @@ namespace ts {
return finishNode(node); return finishNode(node);
} }
// If true, we should abort parsing an error function.
function typeHasArrowFunctionBlockingParseError(node: TypeNode): boolean {
switch (node.kind) {
case SyntaxKind.TypeReference:
return nodeIsMissing((node as TypeReferenceNode).typeName);
case SyntaxKind.FunctionType:
case SyntaxKind.ConstructorType: {
const { parameters, type } = node as FunctionOrConstructorTypeNode;
// parameters.pos === parameters.end only if we used parseMissingList, else should contain at least `()`
return parameters.pos === parameters.end || typeHasArrowFunctionBlockingParseError(type);
}
case SyntaxKind.ParenthesizedType:
return typeHasArrowFunctionBlockingParseError((node as ParenthesizedTypeNode).type);
default:
return false;
}
}
function parseThisTypePredicate(lhs: ThisTypeNode): TypePredicateNode { function parseThisTypePredicate(lhs: ThisTypeNode): TypePredicateNode {
nextToken(); nextToken();
const node = createNode(SyntaxKind.TypePredicate, lhs.pos) as TypePredicateNode; const node = createNode(SyntaxKind.TypePredicate, lhs.pos) as TypePredicateNode;
@ -2343,7 +2360,7 @@ namespace ts {
return finishNode(parameter); return finishNode(parameter);
} }
function parseJSDocType() { function parseJSDocType(): TypeNode {
const dotdotdot = parseOptionalToken(SyntaxKind.DotDotDotToken); const dotdotdot = parseOptionalToken(SyntaxKind.DotDotDotToken);
let type = parseType(); let type = parseType();
if (dotdotdot) { if (dotdotdot) {
@ -2451,6 +2468,7 @@ namespace ts {
} }
/** /**
* Note: If returnToken is EqualsGreaterThanToken, `signature.type` will always be defined.
* @returns If return type parsing succeeds * @returns If return type parsing succeeds
*/ */
function fillSignature( function fillSignature(
@ -2460,12 +2478,12 @@ namespace ts {
if (!(flags & SignatureFlags.JSDoc)) { if (!(flags & SignatureFlags.JSDoc)) {
signature.typeParameters = parseTypeParameters(); signature.typeParameters = parseTypeParameters();
} }
signature.parameters = parseParameterList(flags)!; // TODO: GH#18217 const parametersParsedSuccessfully = parseParameterList(signature, flags);
if (shouldParseReturnType(returnToken, !!(flags & SignatureFlags.Type))) { if (shouldParseReturnType(returnToken, !!(flags & SignatureFlags.Type))) {
signature.type = parseTypeOrTypePredicate(); signature.type = parseTypeOrTypePredicate();
return signature.type !== undefined; if (typeHasArrowFunctionBlockingParseError(signature.type)) return false;
} }
return true; return parametersParsedSuccessfully;
} }
function shouldParseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): boolean { function shouldParseReturnType(returnToken: SyntaxKind.ColonToken | SyntaxKind.EqualsGreaterThanToken, isType: boolean): boolean {
@ -2485,7 +2503,8 @@ namespace ts {
return false; return false;
} }
function parseParameterList(flags: SignatureFlags) { // Returns true on success.
function parseParameterList(signature: SignatureDeclaration, flags: SignatureFlags): boolean {
// FormalParameters [Yield,Await]: (modified) // FormalParameters [Yield,Await]: (modified)
// [empty] // [empty]
// FormalParameterList[?Yield,Await] // FormalParameterList[?Yield,Await]
@ -2499,31 +2518,23 @@ namespace ts {
// //
// SingleNameBinding [Yield,Await]: // SingleNameBinding [Yield,Await]:
// BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt // BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt
if (parseExpected(SyntaxKind.OpenParenToken)) { if (!parseExpected(SyntaxKind.OpenParenToken)) {
const savedYieldContext = inYieldContext(); signature.parameters = createMissingList<ParameterDeclaration>();
const savedAwaitContext = inAwaitContext(); return false;
setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));
const result = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter);
setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);
if (!parseExpected(SyntaxKind.CloseParenToken) && (flags & SignatureFlags.RequireCompleteParameterList)) {
// Caller insisted that we had to end with a ) We didn't. So just return
// undefined here.
return undefined;
}
return result;
} }
// We didn't even have an open paren. If the caller requires a complete parameter list, const savedYieldContext = inYieldContext();
// we definitely can't provide that. However, if they're ok with an incomplete one, const savedAwaitContext = inAwaitContext();
// then just return an empty set of parameters.
return (flags & SignatureFlags.RequireCompleteParameterList) ? undefined : createMissingList<ParameterDeclaration>(); setYieldContext(!!(flags & SignatureFlags.Yield));
setAwaitContext(!!(flags & SignatureFlags.Await));
signature.parameters = parseDelimitedList(ParsingContext.Parameters, flags & SignatureFlags.JSDoc ? parseJSDocParameter : parseParameter);
setYieldContext(savedYieldContext);
setAwaitContext(savedAwaitContext);
return parseExpected(SyntaxKind.CloseParenToken);
} }
function parseTypeMemberSemicolon() { function parseTypeMemberSemicolon() {
@ -2772,28 +2783,19 @@ namespace ts {
return finishNode(node); return finishNode(node);
} }
function parseParenthesizedType(): ParenthesizedTypeNode { function parseParenthesizedType(): TypeNode {
const node = <ParenthesizedTypeNode>createNode(SyntaxKind.ParenthesizedType); const node = <ParenthesizedTypeNode>createNode(SyntaxKind.ParenthesizedType);
parseExpected(SyntaxKind.OpenParenToken); parseExpected(SyntaxKind.OpenParenToken);
node.type = parseType(); node.type = parseType();
if (!node.type) {
return undefined!; // TODO: GH#18217
}
parseExpected(SyntaxKind.CloseParenToken); parseExpected(SyntaxKind.CloseParenToken);
return finishNode(node); return finishNode(node);
} }
function parseFunctionOrConstructorType(kind: SyntaxKind): FunctionOrConstructorTypeNode | undefined { function parseFunctionOrConstructorType(): TypeNode {
const node = <FunctionOrConstructorTypeNode>createNodeWithJSDoc(kind); const pos = getNodePos();
if (kind === SyntaxKind.ConstructorType) { const kind = parseOptional(SyntaxKind.NewKeyword) ? SyntaxKind.ConstructorType : SyntaxKind.FunctionType;
parseExpected(SyntaxKind.NewKeyword); const node = <FunctionOrConstructorTypeNode>createNodeWithJSDoc(kind, pos);
} fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node);
if (!fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type | (sourceFile.languageVariant === LanguageVariant.JSX ? SignatureFlags.RequireCompleteParameterList : 0), node)) {
return undefined;
}
if (!node.parameters) {
return undefined;
}
return finishNode(node); return finishNode(node);
} }
@ -3134,11 +3136,8 @@ namespace ts {
} }
function parseTypeWorker(noConditionalTypes?: boolean): TypeNode { function parseTypeWorker(noConditionalTypes?: boolean): TypeNode {
if (isStartOfFunctionType()) { if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) {
return parseFunctionOrConstructorType(SyntaxKind.FunctionType)!; // TODO: GH#18217 return parseFunctionOrConstructorType();
}
if (token() === SyntaxKind.NewKeyword) {
return parseFunctionOrConstructorType(SyntaxKind.ConstructorType)!;
} }
const type = parseUnionTypeOrHigher(); const type = parseUnionTypeOrHigher();
if (!noConditionalTypes && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) { if (!noConditionalTypes && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) {
@ -3626,12 +3625,7 @@ namespace ts {
// a => (b => c) // a => (b => c)
// And think that "(b =>" was actually a parenthesized arrow function with a missing // And think that "(b =>" was actually a parenthesized arrow function with a missing
// close paren. // close paren.
if (!fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node)) { if (!fillSignature(SyntaxKind.ColonToken, isAsync, node) && !allowAmbiguity) {
return undefined;
}
// If we couldn't get parameters, we definitely could not parse out an arrow function.
if (!node.parameters) {
return undefined; return undefined;
} }

View file

@ -1098,11 +1098,16 @@ namespace ts {
export type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode; export type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode;
export interface FunctionTypeNode extends TypeNode, SignatureDeclarationBase { export interface FunctionOrConstructorTypeNodeBase extends TypeNode, SignatureDeclarationBase {
kind: SyntaxKind.FunctionType | SyntaxKind.ConstructorType;
type: TypeNode;
}
export interface FunctionTypeNode extends FunctionOrConstructorTypeNodeBase {
kind: SyntaxKind.FunctionType; kind: SyntaxKind.FunctionType;
} }
export interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase { export interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase {
kind: SyntaxKind.ConstructorType; kind: SyntaxKind.ConstructorType;
} }

View file

@ -1,9 +1,15 @@
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,14): error TS1110: Type expected. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,10): error TS2304: Cannot find name 'a'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,12): error TS1005: ',' expected.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts(1,14): error TS1005: ';' expected.
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts (1 errors) ==== ==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts (3 errors) ====
var v = (a): => { var v = (a): => {
~
!!! error TS2304: Cannot find name 'a'.
~
!!! error TS1005: ',' expected.
~~ ~~
!!! error TS1110: Type expected. !!! error TS1005: ';' expected.
}; };

View file

@ -4,5 +4,7 @@ var v = (a): => {
}; };
//// [ArrowFunction3.js] //// [ArrowFunction3.js]
var v = function (a) { var v = (a);
}; {
}
;

View file

@ -1,6 +1,5 @@
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts === === tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts ===
var v = (a): => { var v = (a): => {
>v : Symbol(v, Decl(ArrowFunction3.ts, 0, 3)) >v : Symbol(v, Decl(ArrowFunction3.ts, 0, 3))
>a : Symbol(a, Decl(ArrowFunction3.ts, 0, 9))
}; };

View file

@ -1,8 +1,7 @@
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts === === tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts ===
var v = (a): => { var v = (a): => {
>v : (a: any) => any >v : any
>(a): => { } : (a: any) => any >(a) : any
>a : any >a : any
> : No type information available!
}; };

View file

@ -715,10 +715,14 @@ declare namespace ts {
kind: SyntaxKind.ThisType; kind: SyntaxKind.ThisType;
} }
type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode; type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode;
interface FunctionTypeNode extends TypeNode, SignatureDeclarationBase { interface FunctionOrConstructorTypeNodeBase extends TypeNode, SignatureDeclarationBase {
kind: SyntaxKind.FunctionType | SyntaxKind.ConstructorType;
type: TypeNode;
}
interface FunctionTypeNode extends FunctionOrConstructorTypeNodeBase {
kind: SyntaxKind.FunctionType; kind: SyntaxKind.FunctionType;
} }
interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase { interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase {
kind: SyntaxKind.ConstructorType; kind: SyntaxKind.ConstructorType;
} }
interface NodeWithTypeArguments extends TypeNode { interface NodeWithTypeArguments extends TypeNode {

View file

@ -715,10 +715,14 @@ declare namespace ts {
kind: SyntaxKind.ThisType; kind: SyntaxKind.ThisType;
} }
type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode; type FunctionOrConstructorTypeNode = FunctionTypeNode | ConstructorTypeNode;
interface FunctionTypeNode extends TypeNode, SignatureDeclarationBase { interface FunctionOrConstructorTypeNodeBase extends TypeNode, SignatureDeclarationBase {
kind: SyntaxKind.FunctionType | SyntaxKind.ConstructorType;
type: TypeNode;
}
interface FunctionTypeNode extends FunctionOrConstructorTypeNodeBase {
kind: SyntaxKind.FunctionType; kind: SyntaxKind.FunctionType;
} }
interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase { interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase {
kind: SyntaxKind.ConstructorType; kind: SyntaxKind.ConstructorType;
} }
interface NodeWithTypeArguments extends TypeNode { interface NodeWithTypeArguments extends TypeNode {

View file

@ -0,0 +1,24 @@
/a.js(1,14): error TS1139: Type parameter declaration expected.
/a.js(1,17): error TS1003: Identifier expected.
/a.js(1,17): error TS1110: Type expected.
/a.js(1,17): error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
/a.js(1,18): error TS1005: '>' expected.
/a.js(1,18): error TS1005: '}' expected.
==== /a.js (6 errors) ====
/** @param {<} x */
~
!!! error TS1139: Type parameter declaration expected.
!!! error TS1003: Identifier expected.
!!! error TS1110: Type expected.
!!! error TS8024: JSDoc '@param' tag has name '', but there is no parameter with that name.
!!! error TS1005: '>' expected.
!!! error TS1005: '}' expected.
function f(x) {}

View file

@ -0,0 +1,6 @@
=== /a.js ===
/** @param {<} x */
function f(x) {}
>f : Symbol(f, Decl(a.js, 0, 0))
>x : Symbol(x, Decl(a.js, 1, 11))

View file

@ -0,0 +1,6 @@
=== /a.js ===
/** @param {<} x */
function f(x) {}
>f : (x: any) => void
>x : any

View file

@ -1,9 +1,15 @@
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,14): error TS1110: Type expected. tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,10): error TS2304: Cannot find name 'a'.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,12): error TS1005: ',' expected.
tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts(1,14): error TS1005: ';' expected.
==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts (1 errors) ==== ==== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts (3 errors) ====
var v = (a): => { var v = (a): => {
~
!!! error TS2304: Cannot find name 'a'.
~
!!! error TS1005: ',' expected.
~~ ~~
!!! error TS1110: Type expected. !!! error TS1005: ';' expected.
}; };

View file

@ -4,5 +4,7 @@ var v = (a): => {
}; };
//// [parserX_ArrowFunction3.js] //// [parserX_ArrowFunction3.js]
var v = function (a) { var v = (a);
}; {
}
;

View file

@ -1,6 +1,5 @@
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts === === tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts ===
var v = (a): => { var v = (a): => {
>v : Symbol(v, Decl(parserX_ArrowFunction3.ts, 0, 3)) >v : Symbol(v, Decl(parserX_ArrowFunction3.ts, 0, 3))
>a : Symbol(a, Decl(parserX_ArrowFunction3.ts, 0, 9))
}; };

View file

@ -1,8 +1,7 @@
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts === === tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts ===
var v = (a): => { var v = (a): => {
>v : (a: any) => any >v : any
>(a): => { } : (a: any) => any >(a) : any
>a : any >a : any
> : No type information available!
}; };

View file

@ -0,0 +1,7 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @Filename: /a.js
/** @param {<} x */
function f(x) {}

View file

@ -1,13 +1,7 @@
/// <reference path='fourslash.ts' /> /// <reference path='fourslash.ts' />
////var x : (s/*1*/
////var y : (s:string, list/*2*/ ////var y : (s:string, list/*2*/
goTo.marker("1");
verify.not.completionListIsEmpty();// As this can either be type or become arrow function parameter
verify.completionListAllowsNewIdentifier();
goTo.marker("2"); goTo.marker("2");
verify.completionListIsEmpty(); // Parameter name verify.completionListIsEmpty(); // Parameter name
verify.completionListAllowsNewIdentifier(); verify.completionListAllowsNewIdentifier();