diff --git a/crates/swc_ecma_parser/src/parser/class_and_fn.rs b/crates/swc_ecma_parser/src/parser/class_and_fn.rs index 52e72fd4b09..3b3120070a0 100644 --- a/crates/swc_ecma_parser/src/parser/class_and_fn.rs +++ b/crates/swc_ecma_parser/src/parser/class_and_fn.rs @@ -157,7 +157,16 @@ impl<'a, I: Tokens> Parser { ..p.ctx() }) .parse_class_body()?; - expect!(p, '}'); + + if p.input.cur().is_none() { + let eof_text = p.input.dump_cur(); + p.emit_err( + p.input.cur_span(), + SyntaxError::Expected(&Token::RBrace, eof_text), + ); + } else { + expect!(p, '}'); + } let end = last_pos!(p); Ok(( diff --git a/crates/swc_ecma_parser/src/parser/stmt.rs b/crates/swc_ecma_parser/src/parser/stmt.rs index 6ef0c890537..6d984c72e4c 100644 --- a/crates/swc_ecma_parser/src/parser/stmt.rs +++ b/crates/swc_ecma_parser/src/parser/stmt.rs @@ -16,7 +16,7 @@ impl<'a, I: Tokens> Parser { &mut self, mut allow_directives: bool, top_level: bool, - end: Option<&Token>, + end: Option<&'static Token>, ) -> PResult> where Self: StmtLikeParser<'a, Type>, @@ -28,8 +28,17 @@ impl<'a, I: Tokens> Parser { let stmts = Arena::new(); while { - let c = cur!(self, false).ok(); - c != end + if self.input.cur().is_none() && end.is_some() { + let eof_text = self.input.dump_cur(); + self.emit_err( + self.input.cur_span(), + SyntaxError::Expected(end.unwrap(), eof_text), + ); + false + } else { + let c = cur!(self, false).ok(); + c != end + } } { let stmt = self.parse_stmt_like(true, top_level)?; if allow_directives { @@ -54,7 +63,7 @@ impl<'a, I: Tokens> Parser { stmts.alloc(stmt); } - if end.is_some() { + if self.input.cur().is_some() && end.is_some() { bump!(self); } diff --git a/crates/swc_ecma_parser/tests/span/js/decl/class-no-close-brace.js b/crates/swc_ecma_parser/tests/span/js/decl/class-no-close-brace.js new file mode 100644 index 00000000000..f84903de472 --- /dev/null +++ b/crates/swc_ecma_parser/tests/span/js/decl/class-no-close-brace.js @@ -0,0 +1,3 @@ +class Test { + + method() {} \ No newline at end of file diff --git a/crates/swc_ecma_parser/tests/span/js/decl/class-no-close-brace.js.spans b/crates/swc_ecma_parser/tests/span/js/decl/class-no-close-brace.js.spans new file mode 100644 index 00000000000..f58a05f061d --- /dev/null +++ b/crates/swc_ecma_parser/tests/span/js/decl/class-no-close-brace.js.spans @@ -0,0 +1,84 @@ + + x Module + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | ,-> class Test { + 2 | | + 3 | `-> method() {} + `---- + + x ModuleItem + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | ,-> class Test { + 2 | | + 3 | `-> method() {} + `---- + + x Stmt + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | ,-> class Test { + 2 | | + 3 | `-> method() {} + `---- + + x Decl + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | ,-> class Test { + 2 | | + 3 | `-> method() {} + `---- + + x ClassDecl + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | ,-> class Test { + 2 | | + 3 | `-> method() {} + `---- + + x Ident + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | class Test { + : ^^^^ + `---- + + x Class + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:1:1] + 1 | ,-> class Test { + 2 | | + 3 | `-> method() {} + `---- + + x ClassMember + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:3:3] + 3 | method() {} + : ^^^^^^^^^^^ + `---- + + x ClassMethod + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:3:3] + 3 | method() {} + : ^^^^^^^^^^^ + `---- + + x PropName + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:3:3] + 3 | method() {} + : ^^^^^^ + `---- + + x Ident + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:3:3] + 3 | method() {} + : ^^^^^^ + `---- + + x Function + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:3:3] + 3 | method() {} + : ^^^^^^^^^^^ + `---- + + x BlockStmt + ,-[$DIR/tests/span/js/decl/class-no-close-brace.js:3:3] + 3 | method() {} + : ^^ + `---- diff --git a/crates/swc_ecma_parser/tests/test262-error-references/fail/021fb596db81e6d0.js.stderr b/crates/swc_ecma_parser/tests/test262-error-references/fail/021fb596db81e6d0.js.stderr index d4484d32863..852ca0d13b9 100644 --- a/crates/swc_ecma_parser/tests/test262-error-references/fail/021fb596db81e6d0.js.stderr +++ b/crates/swc_ecma_parser/tests/test262-error-references/fail/021fb596db81e6d0.js.stderr @@ -1,6 +1,6 @@ - x Unexpected eof + x Expected '}', got '' ,-[$DIR/tests/test262-parser/fail/021fb596db81e6d0.js:1:1] 1 | { - : ^ + : ^ `---- diff --git a/crates/swc_ecma_parser/tests/test262-error-references/fail/4d579849c75cfef9.js.stderr b/crates/swc_ecma_parser/tests/test262-error-references/fail/4d579849c75cfef9.js.stderr index 62a3be1e545..63029e1995b 100644 --- a/crates/swc_ecma_parser/tests/test262-error-references/fail/4d579849c75cfef9.js.stderr +++ b/crates/swc_ecma_parser/tests/test262-error-references/fail/4d579849c75cfef9.js.stderr @@ -1,6 +1,6 @@ - x Unexpected eof + x Expected '}', got '' ,-[$DIR/tests/test262-parser/fail/4d579849c75cfef9.js:4:1] 4 | { - : ^ + : ^ `---- diff --git a/crates/swc_ecma_parser/tests/test262-error-references/fail/50efa1f220e37136.js.stderr b/crates/swc_ecma_parser/tests/test262-error-references/fail/50efa1f220e37136.js.stderr index 54d685e78c3..307aecfdd16 100644 --- a/crates/swc_ecma_parser/tests/test262-error-references/fail/50efa1f220e37136.js.stderr +++ b/crates/swc_ecma_parser/tests/test262-error-references/fail/50efa1f220e37136.js.stderr @@ -1,6 +1,6 @@ - x Unexpected eof + x Expected '}', got '' ,-[$DIR/tests/test262-parser/fail/50efa1f220e37136.js:1:1] 1 | { ; ; - : ^ + : ^ `---- diff --git a/crates/swc_ecma_parser/tests/test262-error-references/fail/77fe5a8d6ae33dd1.js.stderr b/crates/swc_ecma_parser/tests/test262-error-references/fail/77fe5a8d6ae33dd1.js.stderr index c4407995e68..71607b45bd6 100644 --- a/crates/swc_ecma_parser/tests/test262-error-references/fail/77fe5a8d6ae33dd1.js.stderr +++ b/crates/swc_ecma_parser/tests/test262-error-references/fail/77fe5a8d6ae33dd1.js.stderr @@ -1,6 +1,6 @@ - x Unexpected eof + x Expected '}', got '' ,-[$DIR/tests/test262-parser/fail/77fe5a8d6ae33dd1.js:1:1] 1 | function t() { ; ; - : ^ + : ^ `---- diff --git a/crates/swc_ecma_parser/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts b/crates/swc_ecma_parser/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts new file mode 100644 index 00000000000..c558d01e1f1 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts @@ -0,0 +1,2 @@ +const t = () => { + console.log(5); \ No newline at end of file diff --git a/crates/swc_ecma_parser/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts.stderr b/crates/swc_ecma_parser/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts.stderr new file mode 100644 index 00000000000..16f6f22dc44 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts.stderr @@ -0,0 +1,6 @@ + + x Expected '}', got '' + ,-[$DIR/tests/typescript-errors/arrow-function/missing-closing-brace/input.ts:2:3] + 2 | console.log(5); + : ^ + `---- diff --git a/crates/swc_ecma_parser/tests/typescript-errors/class/missing-closing-brace/input.ts b/crates/swc_ecma_parser/tests/typescript-errors/class/missing-closing-brace/input.ts new file mode 100644 index 00000000000..2c98acb236c --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/class/missing-closing-brace/input.ts @@ -0,0 +1,2 @@ +class Class { + prop: string; \ No newline at end of file diff --git a/crates/swc_ecma_parser/tests/typescript-errors/class/missing-closing-brace/input.ts.stderr b/crates/swc_ecma_parser/tests/typescript-errors/class/missing-closing-brace/input.ts.stderr new file mode 100644 index 00000000000..9403e3d1b75 --- /dev/null +++ b/crates/swc_ecma_parser/tests/typescript-errors/class/missing-closing-brace/input.ts.stderr @@ -0,0 +1,6 @@ + + x Expected '}', got '' + ,-[$DIR/tests/typescript-errors/class/missing-closing-brace/input.ts:2:3] + 2 | prop: string; + : ^ + `----