diff --git a/crates/swc_ecma_parser/src/error.rs b/crates/swc_ecma_parser/src/error.rs index 7e350a74cf3..60fb5a89cef 100644 --- a/crates/swc_ecma_parser/src/error.rs +++ b/crates/swc_ecma_parser/src/error.rs @@ -169,6 +169,7 @@ pub enum SyntaxError { PrivateNameModifier(JsWord), ReadOnlyMethod, GeneratorConstructor, + DuplicateConstructor, TsBindingPatCannotBeOptional, SuperCallOptional, @@ -422,6 +423,7 @@ impl SyntaxError { SyntaxError::PropertyNamedConstructor => { "Classes may not have a non-static field named 'constructor'".into() } + SyntaxError::DuplicateConstructor => "A class can only have one constructor".into(), SyntaxError::PrivateNameModifier(modifier) => format!( "'{}' modifier cannot be used with a private identifier", modifier 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 60988282f02..51827412444 100644 --- a/crates/swc_ecma_parser/src/parser/class_and_fn.rs +++ b/crates/swc_ecma_parser/src/parser/class_and_fn.rs @@ -316,6 +316,7 @@ impl<'a, I: Tokens> Parser { fn parse_class_body(&mut self) -> PResult> { let mut elems = vec![]; + let mut has_constructor_with_body = false; while !eof!(self) && !is!(self, '}') { if eat_exact!(self, ';') { let span = self.input.prev_span(); @@ -328,7 +329,22 @@ impl<'a, I: Tokens> Parser { allow_direct_super: true, ..self.ctx() }); - elems.push(p.parse_class_member()?); + let elem = p.parse_class_member()?; + + if !p.ctx().in_declare { + if let ClassMember::Constructor(Constructor { + body: Some(..), + span, + .. + }) = elem + { + if has_constructor_with_body { + p.emit_err(span, SyntaxError::DuplicateConstructor); + } + has_constructor_with_body = true; + } + } + elems.push(elem); } Ok(elems) } @@ -706,6 +722,14 @@ impl<'a, I: Tokens> Parser { let body: Option<_> = self.parse_fn_body(false, false, params.is_simple_parameter_list())?; + if body.is_none() { + for param in params.iter() { + if param.is_ts_param_prop() { + self.emit_err(param.span(), SyntaxError::TS2369) + } + } + } + if self.syntax().typescript() && body.is_none() { // Declare constructors cannot have assignment pattern in parameters for p in ¶ms { diff --git a/crates/swc_ecma_parser/tests/typescript-errors/class/override-parameter-property/input.ts.stderr b/crates/swc_ecma_parser/tests/typescript-errors/class/override-parameter-property/input.ts.stderr index 65b29dfe5ba..a9692ea89c1 100644 --- a/crates/swc_ecma_parser/tests/typescript-errors/class/override-parameter-property/input.ts.stderr +++ b/crates/swc_ecma_parser/tests/typescript-errors/class/override-parameter-property/input.ts.stderr @@ -10,3 +10,9 @@ 3 | constructor(override public v: string) : ^^^^^^^^^^^^^^^^ `---- + + x A parameter property is only allowed in a constructor implementation + ,-[$DIR/tests/typescript-errors/class/override-parameter-property/input.ts:3:3] + 3 | constructor(override public v: string) + : ^^^^^^^^^^^^^^^^^^^^^^^^^ + `---- diff --git a/crates/swc_node_bundler/tests/pass/deno-001/full/input/textproto/mod.ts b/crates/swc_node_bundler/tests/pass/deno-001/full/input/textproto/mod.ts index c8b18bfc18b..e78e610bb27 100644 --- a/crates/swc_node_bundler/tests/pass/deno-001/full/input/textproto/mod.ts +++ b/crates/swc_node_bundler/tests/pass/deno-001/full/input/textproto/mod.ts @@ -24,7 +24,6 @@ function charCode(s: string): number { export class TextProtoReader { constructor(readonly r: BufReader) { } - constructor(readonly r: BufReader) { } /** readLine() reads a single line from the TextProtoReader, * eliding the final \n or \r\n from the returned string. diff --git a/crates/swc_node_bundler/tests/pass/deno-001/full/output/entry.js b/crates/swc_node_bundler/tests/pass/deno-001/full/output/entry.js index d083f75d406..44102f6dc2f 100644 --- a/crates/swc_node_bundler/tests/pass/deno-001/full/output/entry.js +++ b/crates/swc_node_bundler/tests/pass/deno-001/full/output/entry.js @@ -354,9 +354,6 @@ function charCode(s) { return s.charCodeAt(0); } class TextProtoReader { - constructor(r){ - this.r = r; - } constructor(r){ this.r = r; }