Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(parser) Throw syntax error when const not inititialized in strict mode #3742

Merged
merged 12 commits into from Feb 27, 2022
2 changes: 0 additions & 2 deletions crates/swc_ecma_codegen/src/stmt.rs
Expand Up @@ -42,13 +42,11 @@ mod tests {
fn declaration_statement() {
assert_min("var foo;", "var foo");
assert_min("let foo;", "let foo");
assert_min("const foo;", "const foo");
kdy1 marked this conversation as resolved.
Show resolved Hide resolved
assert_min("var foo = 10;", "var foo=10");
assert_min("let foo = 10;", "let foo=10");
assert_min("const foo = 10;", "const foo=10");
assert_min("var foo, bar;", "var foo,bar");
assert_min("let foo, bar;", "let foo,bar");
assert_min("const foo, bar;", "const foo,bar");
assert_min("var foo = 10, bar = 20;", "var foo=10,bar=20");
assert_min("let foo = 10, bar = 20;", "let foo=10,bar=20");
assert_min("const foo = 10, bar = 20;", "const foo=10,bar=20");
Expand Down
6 changes: 6 additions & 0 deletions crates/swc_ecma_parser/src/error.rs
Expand Up @@ -183,6 +183,8 @@ pub enum SyntaxError {
ImportBindingIsString(JsWord),
ExportBindingIsString,

ConstDeclarationsRequireInitialization,

TS1003,
TS1005,
TS1009,
Expand Down Expand Up @@ -487,6 +489,10 @@ impl SyntaxError {
"A string literal cannot be used as an exported binding without `from`.".into()
}

SyntaxError::ConstDeclarationsRequireInitialization => {
"'const' declarations must be initialized".into()
}

SyntaxError::TS1003 => "Expected an identifier".into(),
SyntaxError::TS1005 => "Expected a semicolon".into(),
SyntaxError::TS1009 => "Trailing comma is not allowed".into(),
Expand Down
47 changes: 45 additions & 2 deletions crates/swc_ecma_parser/src/parser/stmt.rs
Expand Up @@ -727,7 +727,7 @@ impl<'a, I: Tokens> Parser<I> {
break;
}

decls.push(self.with_ctx(ctx).parse_var_declarator(for_loop)?);
decls.push(self.with_ctx(ctx).parse_var_declarator(for_loop, &kind)?);
}

if !for_loop && !eat!(self, ';') {
Expand All @@ -748,7 +748,11 @@ impl<'a, I: Tokens> Parser<I> {
})
}

fn parse_var_declarator(&mut self, for_loop: bool) -> PResult<VarDeclarator> {
fn parse_var_declarator(
&mut self,
for_loop: bool,
kind: &VarDeclKind,
williamtetlow marked this conversation as resolved.
Show resolved Hide resolved
) -> PResult<VarDeclarator> {
let start = cur_pos!(self);

let mut name = self.parse_binding_pat_or_ident()?;
Expand Down Expand Up @@ -798,6 +802,13 @@ impl<'a, I: Tokens> Parser<I> {
// Destructuring bindings require initializers, but
// typescript allows `declare` vars not to have initializers.
if self.ctx().in_declare {
None
} else if *kind == VarDeclKind::Const && self.ctx().strict {
self.emit_err(
span!(self, start),
SyntaxError::ConstDeclarationsRequireInitialization,
);

None
} else {
match name {
Expand Down Expand Up @@ -2214,4 +2225,36 @@ export default function waitUntil(callback, options = {}) {
let src = "export { 'foo' };";
test_parser(src, Syntax::Es(Default::default()), |p| p.parse_module());
}

#[test]
#[should_panic(expected = "'const' declarations must be initialized")]
fn ts_error_for_const_declaration_not_initialized() {
let src = r#"
"use strict";
const foo;"#;

test_parser(
src,
Syntax::Typescript(TsConfig {
..Default::default()
}),
|p| p.parse_script(),
);
}

#[test]
#[should_panic(expected = "'const' declarations must be initialized")]
fn es_error_for_const_declaration_not_initialized() {
let src = r#"
"use strict";
const foo;"#;

test_parser(
src,
Syntax::Es(EsConfig {
..Default::default()
}),
|p| p.parse_script(),
);
}
}