diff --git a/src/extra/ast.ts b/src/extra/ast.ts index 97cdc0bccc..a41a08f8bd 100644 --- a/src/extra/ast.ts +++ b/src/extra/ast.ts @@ -967,6 +967,7 @@ export class ASTBuilder { this.serializeDecorator(decorators[i]); } } + this.serializeExternalModifiers(node); this.serializeAccessModifiers(node); if (node.name.text.length) { sb.push("function "); diff --git a/src/parser.ts b/src/parser.ts index b833cb682b..ff357f2a56 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -819,18 +819,20 @@ export class Parser extends DiagnosticEmitter { return null; } var members = new Array(); - if (!tn.skip(Token.CLOSEBRACE)) { - do { - let member = this.parseEnumValue(tn, CommonFlags.NONE); - if (!member) return null; - members.push(member); - } while (tn.skip(Token.COMMA)); - if (!tn.skip(Token.CLOSEBRACE)) { - this.error( - DiagnosticCode._0_expected, - tn.range(), "}" - ); - return null; + while (!tn.skip(Token.CLOSEBRACE)) { + let member = this.parseEnumValue(tn, CommonFlags.NONE); + if (!member) return null; + members.push(member); + if (!tn.skip(Token.COMMA)) { + if (tn.skip(Token.CLOSEBRACE)) { + break; + } else { + this.error( + DiagnosticCode._0_expected, + tn.range(), "}" + ); + return null; + } } } var ret = Node.createEnumDeclaration( @@ -899,20 +901,23 @@ export class Parser extends DiagnosticEmitter { // at '<': TypeParameter (',' TypeParameter)* '>' var typeParameters = new Array(); - if (!tn.skip(Token.GREATERTHAN)) { - do { - let typeParameter = this.parseTypeParameter(tn); - if (!typeParameter) return null; - typeParameters.push(typeParameter); - } while (tn.skip(Token.COMMA)); - if (!tn.skip(Token.GREATERTHAN)) { - this.error( - DiagnosticCode._0_expected, - tn.range(), ">" - ); - return null; + while (!tn.skip(Token.GREATERTHAN)) { + let typeParameter = this.parseTypeParameter(tn); + if (!typeParameter) return null; + typeParameters.push(typeParameter); + if (!tn.skip(Token.COMMA)) { + if (tn.skip(Token.GREATERTHAN)) { + break; + } else { + this.error( + DiagnosticCode._0_expected, + tn.range(), ">" + ); + return null; + } } - } else { + } + if (typeParameters.length === 0) { this.error( DiagnosticCode.Type_parameter_list_cannot_be_empty, tn.range() @@ -971,45 +976,47 @@ export class Parser extends DiagnosticEmitter { var seenOptional = false; var reportedRest = false; - if (tn.peek() != Token.CLOSEPAREN) { - do { - let param = this.parseParameter(tn, isConstructor); - if (!param) return null; - if (seenRest && !reportedRest) { + while (!tn.skip(Token.CLOSEPAREN)) { + let param = this.parseParameter(tn, isConstructor); + if (!param) return null; + if (seenRest && !reportedRest) { + this.error( + DiagnosticCode.A_rest_parameter_must_be_last_in_a_parameter_list, + seenRest.name.range + ); + reportedRest = true; + } + switch (param.parameterKind) { + default: { + if (seenOptional) { + this.error( + DiagnosticCode.A_required_parameter_cannot_follow_an_optional_parameter, + param.name.range + ); + } + break; + } + case ParameterKind.OPTIONAL: { + seenOptional = true; + break; + } + case ParameterKind.REST: { + seenRest = param; + break; + } + } + parameters.push(param); + if (!tn.skip(Token.COMMA)) { + if (tn.skip(Token.CLOSEPAREN)) { + break; + } else { this.error( - DiagnosticCode.A_rest_parameter_must_be_last_in_a_parameter_list, - seenRest.name.range + DiagnosticCode._0_expected, + tn.range(), ")" ); - reportedRest = true; - } - switch (param.parameterKind) { - default: { - if (seenOptional) { - this.error( - DiagnosticCode.A_required_parameter_cannot_follow_an_optional_parameter, - param.name.range - ); - } - break; - } - case ParameterKind.OPTIONAL: { - seenOptional = true; - break; - } - case ParameterKind.REST: { - seenRest = param; - break; - } + return null; } - parameters.push(param); - } while (tn.skip(Token.COMMA)); - } - if (!tn.skip(Token.CLOSEPAREN)) { - this.error( - DiagnosticCode._0_expected, - tn.range(), ")" - ); - return null; + } } return parameters; } @@ -2877,23 +2884,24 @@ export class Parser extends DiagnosticEmitter { // ArrayLiteralExpression case Token.OPENBRACKET: { let elementExpressions = new Array(); - if (!tn.skip(Token.CLOSEBRACKET)) { - do { - if (tn.peek() == Token.COMMA) { - expr = null; // omitted + while (!tn.skip(Token.CLOSEBRACKET)) { + if (tn.peek() == Token.COMMA) { + expr = null; // omitted + } else { + expr = this.parseExpression(tn, Precedence.COMMA + 1); + if (!expr) return null; + } + elementExpressions.push(expr); + if (!tn.skip(Token.COMMA)) { + if (tn.skip(Token.CLOSEBRACKET)) { + break; } else { - expr = this.parseExpression(tn, Precedence.COMMA + 1); - if (!expr) return null; + this.error( + DiagnosticCode._0_expected, + tn.range(), "]" + ); + return null; } - elementExpressions.push(expr); - if (tn.peek() == Token.CLOSEBRACKET) break; - } while (tn.skip(Token.COMMA)); - if (!tn.skip(Token.CLOSEBRACKET)) { - this.error( - DiagnosticCode._0_expected, - tn.range(), "]" - ); - return null; } } return Node.createArrayLiteralExpression(elementExpressions, tn.range(startPos, tn.pos)); @@ -2979,6 +2987,9 @@ export class Parser extends DiagnosticEmitter { if (!tn.skip(Token.LESSTHAN)) return null; var typeArguments = new Array(); do { + if (tn.peek() === Token.GREATERTHAN) { + break; + } let type = this.parseType(tn, true, true); if (!type) { tn.reset(state); @@ -3000,18 +3011,20 @@ export class Parser extends DiagnosticEmitter { // at '(': (Expression (',' Expression)*)? ')' var args = new Array(); - if (!tn.skip(Token.CLOSEPAREN)) { - do { - let expr = this.parseExpression(tn, Precedence.COMMA + 1); - if (!expr) return null; - args.push(expr); - } while (tn.skip(Token.COMMA)); - if (!tn.skip(Token.CLOSEPAREN)) { - this.error( - DiagnosticCode._0_expected, - tn.range(), ")" - ); - return null; + while (!tn.skip(Token.CLOSEPAREN)) { + let expr = this.parseExpression(tn, Precedence.COMMA + 1); + if (!expr) return null; + args.push(expr); + if (!tn.skip(Token.COMMA)) { + if (tn.skip(Token.CLOSEPAREN)) { + break; + } else { + this.error( + DiagnosticCode._0_expected, + tn.range(), ")" + ); + return null; + } } } return args; diff --git a/tests/parser/trailing-commas.ts b/tests/parser/trailing-commas.ts new file mode 100644 index 0000000000..a9bac5695d --- /dev/null +++ b/tests/parser/trailing-commas.ts @@ -0,0 +1,33 @@ +enum Foo { + A, + B, +} + +function add( + x: i32, + y: i32, +): i32 { + return x + y; +} + +function parameterized< + A, + B, +>(a: A, b: B): void { +} + +export function compute(): i32 { + const arr: Array = [ + 1, + 2, + ]; + parameterized< + i8, + // @ts-ignore: Waiting on https://github.com/Microsoft/TypeScript/issues/21984 + i32, + >(0, 0); + return add( + 1, + 2, + ); +} diff --git a/tests/parser/trailing-commas.ts.fixture.ts b/tests/parser/trailing-commas.ts.fixture.ts new file mode 100644 index 0000000000..9c3ce3c4a5 --- /dev/null +++ b/tests/parser/trailing-commas.ts.fixture.ts @@ -0,0 +1,13 @@ +enum Foo { + A, + B +} +function add(x: i32, y: i32): i32 { + return x + y; +} +function parameterized(a: A, b: B): void {} +export function compute(): i32 { + const arr: Array = [1, 2]; + parameterized(0, 0); + return add(1, 2); +}