diff --git a/src/parser.ts b/src/parser.ts index cade050161..27f0af6320 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -939,7 +939,7 @@ export class Parser extends DiagnosticEmitter { } while (tn.skip(Token.Comma)); let ret = Node.createVariableStatement(decorators, declarations, tn.range(startPos, tn.pos)); - tn.skip(Token.Semicolon); + if (!tn.skip(Token.Semicolon) && !isFor) this.checkASI(tn); return ret; } @@ -1122,7 +1122,7 @@ export class Parser extends DiagnosticEmitter { } let ret = Node.createReturnStatement(expr, tn.range(startPos, tn.pos)); - tn.skip(Token.Semicolon); + if (!tn.skip(Token.Semicolon)) this.checkASI(tn); return ret; } @@ -3009,7 +3009,7 @@ export class Parser extends DiagnosticEmitter { } } let ret = Node.createBlockStatement(statements, tn.range(startPos, tn.pos)); - tn.skip(Token.Semicolon); + if (topLevel) tn.skip(Token.Semicolon); return ret; } @@ -3417,7 +3417,7 @@ export class Parser extends DiagnosticEmitter { let expression = this.parseExpression(tn); if (!expression) return null; let ret = Node.createThrowStatement(expression, tn.range(startPos, tn.pos)); - tn.skip(Token.Semicolon); + if (!tn.skip(Token.Semicolon)) this.checkASI(tn); return ret; } @@ -4401,6 +4401,18 @@ export class Parser extends DiagnosticEmitter { return expr; } + private checkASI( + tn: Tokenizer + ): void { + // see: https://tc39.es/ecma262/#sec-automatic-semicolon-insertion + let token = tn.peek(true); + if (tn.nextTokenOnNewLine || token == Token.EndOfFile || token == Token.CloseBrace) return; + this.error( + DiagnosticCode.Unexpected_token, + tn.range(tn.nextTokenPos) + ); + } + /** Skips over a statement on errors in an attempt to reduce unnecessary diagnostic noise. */ skipStatement(tn: Tokenizer): void { tn.peek(true); diff --git a/tests/parser/asi.ts b/tests/parser/asi.ts new file mode 100644 index 0000000000..542d063520 --- /dev/null +++ b/tests/parser/asi.ts @@ -0,0 +1,19 @@ +function failLet(y: i32): i32 { + let x = y 234; + return x + y; +} + +function failReturn(): i32 { + return 123 456; +} + +function failThrow(): i32 { + throw 123 456; +} + +function successCloseBrace(): i32 { + return 123 } + +function successCloseParen(): i32 { + return ( 123 ) +} \ No newline at end of file diff --git a/tests/parser/asi.ts.fixture.ts b/tests/parser/asi.ts.fixture.ts new file mode 100644 index 0000000000..d6d8e567b8 --- /dev/null +++ b/tests/parser/asi.ts.fixture.ts @@ -0,0 +1,22 @@ +function failLet(y: i32): i32 { + let x = y; + 234; + return x + y; +} +function failReturn(): i32 { + return 123; + 456; +} +function failThrow(): i32 { + throw 123; + 456; +} +function successCloseBrace(): i32 { + return 123; +} +function successCloseParen(): i32 { + return (123); +} +// ERROR 1012: "Unexpected token." in asi.ts(2,13+0) +// ERROR 1012: "Unexpected token." in asi.ts(7,14+0) +// ERROR 1012: "Unexpected token." in asi.ts(11,13+0)