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:
parent
9681796785
commit
735a46f838
|
@ -4,7 +4,6 @@ namespace ts {
|
|||
Yield = 1 << 0,
|
||||
Await = 1 << 1,
|
||||
Type = 1 << 2,
|
||||
RequireCompleteParameterList = 1 << 3,
|
||||
IgnoreMissingOpenBrace = 1 << 4,
|
||||
JSDoc = 1 << 5,
|
||||
}
|
||||
|
@ -1257,8 +1256,8 @@ namespace ts {
|
|||
new TokenConstructor(kind, p, p);
|
||||
}
|
||||
|
||||
function createNodeWithJSDoc(kind: SyntaxKind): Node {
|
||||
const node = createNode(kind);
|
||||
function createNodeWithJSDoc(kind: SyntaxKind, pos?: number): Node {
|
||||
const node = createNode(kind, pos);
|
||||
if (scanner.getTokenFlags() & TokenFlags.PrecedingJSDocComment) {
|
||||
addJSDocComment(<HasJSDoc>node);
|
||||
}
|
||||
|
@ -2257,6 +2256,24 @@ namespace ts {
|
|||
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 {
|
||||
nextToken();
|
||||
const node = createNode(SyntaxKind.TypePredicate, lhs.pos) as TypePredicateNode;
|
||||
|
@ -2343,7 +2360,7 @@ namespace ts {
|
|||
return finishNode(parameter);
|
||||
}
|
||||
|
||||
function parseJSDocType() {
|
||||
function parseJSDocType(): TypeNode {
|
||||
const dotdotdot = parseOptionalToken(SyntaxKind.DotDotDotToken);
|
||||
let type = parseType();
|
||||
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
|
||||
*/
|
||||
function fillSignature(
|
||||
|
@ -2460,12 +2478,12 @@ namespace ts {
|
|||
if (!(flags & SignatureFlags.JSDoc)) {
|
||||
signature.typeParameters = parseTypeParameters();
|
||||
}
|
||||
signature.parameters = parseParameterList(flags)!; // TODO: GH#18217
|
||||
const parametersParsedSuccessfully = parseParameterList(signature, flags);
|
||||
if (shouldParseReturnType(returnToken, !!(flags & SignatureFlags.Type))) {
|
||||
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 {
|
||||
|
@ -2485,7 +2503,8 @@ namespace ts {
|
|||
return false;
|
||||
}
|
||||
|
||||
function parseParameterList(flags: SignatureFlags) {
|
||||
// Returns true on success.
|
||||
function parseParameterList(signature: SignatureDeclaration, flags: SignatureFlags): boolean {
|
||||
// FormalParameters [Yield,Await]: (modified)
|
||||
// [empty]
|
||||
// FormalParameterList[?Yield,Await]
|
||||
|
@ -2499,31 +2518,23 @@ namespace ts {
|
|||
//
|
||||
// SingleNameBinding [Yield,Await]:
|
||||
// BindingIdentifier[?Yield,?Await]Initializer [In, ?Yield,?Await] opt
|
||||
if (parseExpected(SyntaxKind.OpenParenToken)) {
|
||||
const savedYieldContext = inYieldContext();
|
||||
const savedAwaitContext = inAwaitContext();
|
||||
|
||||
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;
|
||||
if (!parseExpected(SyntaxKind.OpenParenToken)) {
|
||||
signature.parameters = createMissingList<ParameterDeclaration>();
|
||||
return false;
|
||||
}
|
||||
|
||||
// We didn't even have an open paren. If the caller requires a complete parameter list,
|
||||
// we definitely can't provide that. However, if they're ok with an incomplete one,
|
||||
// then just return an empty set of parameters.
|
||||
return (flags & SignatureFlags.RequireCompleteParameterList) ? undefined : createMissingList<ParameterDeclaration>();
|
||||
const savedYieldContext = inYieldContext();
|
||||
const savedAwaitContext = inAwaitContext();
|
||||
|
||||
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() {
|
||||
|
@ -2772,28 +2783,19 @@ namespace ts {
|
|||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseParenthesizedType(): ParenthesizedTypeNode {
|
||||
function parseParenthesizedType(): TypeNode {
|
||||
const node = <ParenthesizedTypeNode>createNode(SyntaxKind.ParenthesizedType);
|
||||
parseExpected(SyntaxKind.OpenParenToken);
|
||||
node.type = parseType();
|
||||
if (!node.type) {
|
||||
return undefined!; // TODO: GH#18217
|
||||
}
|
||||
parseExpected(SyntaxKind.CloseParenToken);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
function parseFunctionOrConstructorType(kind: SyntaxKind): FunctionOrConstructorTypeNode | undefined {
|
||||
const node = <FunctionOrConstructorTypeNode>createNodeWithJSDoc(kind);
|
||||
if (kind === SyntaxKind.ConstructorType) {
|
||||
parseExpected(SyntaxKind.NewKeyword);
|
||||
}
|
||||
if (!fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type | (sourceFile.languageVariant === LanguageVariant.JSX ? SignatureFlags.RequireCompleteParameterList : 0), node)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!node.parameters) {
|
||||
return undefined;
|
||||
}
|
||||
function parseFunctionOrConstructorType(): TypeNode {
|
||||
const pos = getNodePos();
|
||||
const kind = parseOptional(SyntaxKind.NewKeyword) ? SyntaxKind.ConstructorType : SyntaxKind.FunctionType;
|
||||
const node = <FunctionOrConstructorTypeNode>createNodeWithJSDoc(kind, pos);
|
||||
fillSignature(SyntaxKind.EqualsGreaterThanToken, SignatureFlags.Type, node);
|
||||
return finishNode(node);
|
||||
}
|
||||
|
||||
|
@ -3134,11 +3136,8 @@ namespace ts {
|
|||
}
|
||||
|
||||
function parseTypeWorker(noConditionalTypes?: boolean): TypeNode {
|
||||
if (isStartOfFunctionType()) {
|
||||
return parseFunctionOrConstructorType(SyntaxKind.FunctionType)!; // TODO: GH#18217
|
||||
}
|
||||
if (token() === SyntaxKind.NewKeyword) {
|
||||
return parseFunctionOrConstructorType(SyntaxKind.ConstructorType)!;
|
||||
if (isStartOfFunctionType() || token() === SyntaxKind.NewKeyword) {
|
||||
return parseFunctionOrConstructorType();
|
||||
}
|
||||
const type = parseUnionTypeOrHigher();
|
||||
if (!noConditionalTypes && !scanner.hasPrecedingLineBreak() && parseOptional(SyntaxKind.ExtendsKeyword)) {
|
||||
|
@ -3626,12 +3625,7 @@ namespace ts {
|
|||
// a => (b => c)
|
||||
// And think that "(b =>" was actually a parenthesized arrow function with a missing
|
||||
// close paren.
|
||||
if (!fillSignature(SyntaxKind.ColonToken, isAsync | (allowAmbiguity ? SignatureFlags.None : SignatureFlags.RequireCompleteParameterList), node)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If we couldn't get parameters, we definitely could not parse out an arrow function.
|
||||
if (!node.parameters) {
|
||||
if (!fillSignature(SyntaxKind.ColonToken, isAsync, node) && !allowAmbiguity) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
|
@ -1098,11 +1098,16 @@ namespace ts {
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
export interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase {
|
||||
export interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase {
|
||||
kind: SyntaxKind.ConstructorType;
|
||||
}
|
||||
|
||||
|
|
|
@ -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): => {
|
||||
~
|
||||
!!! error TS2304: Cannot find name 'a'.
|
||||
~
|
||||
!!! error TS1005: ',' expected.
|
||||
~~
|
||||
!!! error TS1110: Type expected.
|
||||
!!! error TS1005: ';' expected.
|
||||
|
||||
};
|
|
@ -4,5 +4,7 @@ var v = (a): => {
|
|||
};
|
||||
|
||||
//// [ArrowFunction3.js]
|
||||
var v = function (a) {
|
||||
};
|
||||
var v = (a);
|
||||
{
|
||||
}
|
||||
;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts ===
|
||||
var v = (a): => {
|
||||
>v : Symbol(v, Decl(ArrowFunction3.ts, 0, 3))
|
||||
>a : Symbol(a, Decl(ArrowFunction3.ts, 0, 9))
|
||||
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/ArrowFunction3.ts ===
|
||||
var v = (a): => {
|
||||
>v : (a: any) => any
|
||||
>(a): => { } : (a: any) => any
|
||||
>v : any
|
||||
>(a) : any
|
||||
>a : any
|
||||
> : No type information available!
|
||||
|
||||
};
|
||||
|
|
|
@ -715,10 +715,14 @@ declare namespace ts {
|
|||
kind: SyntaxKind.ThisType;
|
||||
}
|
||||
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;
|
||||
}
|
||||
interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase {
|
||||
interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase {
|
||||
kind: SyntaxKind.ConstructorType;
|
||||
}
|
||||
interface NodeWithTypeArguments extends TypeNode {
|
||||
|
|
|
@ -715,10 +715,14 @@ declare namespace ts {
|
|||
kind: SyntaxKind.ThisType;
|
||||
}
|
||||
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;
|
||||
}
|
||||
interface ConstructorTypeNode extends TypeNode, SignatureDeclarationBase {
|
||||
interface ConstructorTypeNode extends FunctionOrConstructorTypeNodeBase {
|
||||
kind: SyntaxKind.ConstructorType;
|
||||
}
|
||||
interface NodeWithTypeArguments extends TypeNode {
|
||||
|
|
|
@ -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) {}
|
||||
|
|
@ -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))
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
=== /a.js ===
|
||||
/** @param {<} x */
|
||||
function f(x) {}
|
||||
>f : (x: any) => void
|
||||
>x : any
|
||||
|
|
@ -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): => {
|
||||
~
|
||||
!!! error TS2304: Cannot find name 'a'.
|
||||
~
|
||||
!!! error TS1005: ',' expected.
|
||||
~~
|
||||
!!! error TS1110: Type expected.
|
||||
!!! error TS1005: ';' expected.
|
||||
|
||||
};
|
|
@ -4,5 +4,7 @@ var v = (a): => {
|
|||
};
|
||||
|
||||
//// [parserX_ArrowFunction3.js]
|
||||
var v = function (a) {
|
||||
};
|
||||
var v = (a);
|
||||
{
|
||||
}
|
||||
;
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts ===
|
||||
var v = (a): => {
|
||||
>v : Symbol(v, Decl(parserX_ArrowFunction3.ts, 0, 3))
|
||||
>a : Symbol(a, Decl(parserX_ArrowFunction3.ts, 0, 9))
|
||||
|
||||
};
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
=== tests/cases/conformance/parser/ecmascript5/ErrorRecovery/ArrowFunctions/parserX_ArrowFunction3.ts ===
|
||||
var v = (a): => {
|
||||
>v : (a: any) => any
|
||||
>(a): => { } : (a: any) => any
|
||||
>v : any
|
||||
>(a) : any
|
||||
>a : any
|
||||
> : No type information available!
|
||||
|
||||
};
|
||||
|
|
7
tests/cases/compiler/jsdocFunctionTypeFalsePositive.ts
Normal file
7
tests/cases/compiler/jsdocFunctionTypeFalsePositive.ts
Normal file
|
@ -0,0 +1,7 @@
|
|||
// @allowJs: true
|
||||
// @checkJs: true
|
||||
// @noEmit: true
|
||||
|
||||
// @Filename: /a.js
|
||||
/** @param {<} x */
|
||||
function f(x) {}
|
|
@ -1,13 +1,7 @@
|
|||
/// <reference path='fourslash.ts' />
|
||||
|
||||
////var x : (s/*1*/
|
||||
|
||||
////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");
|
||||
verify.completionListIsEmpty(); // Parameter name
|
||||
verify.completionListAllowsNewIdentifier();
|
Loading…
Reference in a new issue