Skip to content

Commit

Permalink
feat(ecmascript/codegen): fix and use omit_trailing_semi (#1298)
Browse files Browse the repository at this point in the history
swc_ecma_codegen:
- Use omit_trailing_semi in 'assert_min' tests, to identify bugs. Before this,
  no code was using this method - and it had some serious bugs.
- Omit semicolons when writing punctuation, as it's permitted to do so.
- Use two macros ('semi', 'formatting_semi') to distinguish between required
  semicolons, as used in for(...), and optional ones that are used between
  statements.

This commit does not enforce the use of omit_trailing_semi when cfg.minify is
used (except in tests using assert_min), and as such should not modify the
behavior of existing apps. This is confirmed by the Test262 suite passing
without changes.

Co-authored-by: 강동윤 <kdy1@dudy.kr>
  • Loading branch information
punkeel and kdy1 authored Dec 29, 2020
1 parent bc7ac45 commit 9063908
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 162 deletions.
2 changes: 1 addition & 1 deletion ecmascript/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_codegen"
repository = "https://github.com/swc-project/swc.git"
version = "0.42.1"
version = "0.42.2"

[dependencies]
bitflags = "1"
Expand Down
4 changes: 2 additions & 2 deletions ecmascript/codegen/macros/src/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ impl Fold for InjectSelf {
"unimplemented" => i,

//TODO: Collect expect and give that list to unexpected
"keyword" | "emit" | "punct" | "semi" | "space" | "formatting_space" | "operator"
| "opt" | "opt_leading_space" => {
"keyword" | "emit" | "punct" | "semi" | "formatting_semi" | "space"
| "formatting_space" | "operator" | "opt" | "opt_leading_space" => {
let tokens = if i.tokens.is_empty() {
quote_spanned!(span => #parser)
} else {
Expand Down
8 changes: 4 additions & 4 deletions ecmascript/codegen/src/decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ impl<'a> Emitter<'a> {

Decl::Var(ref n) => {
emit!(n);
semi!(); // VarDecl is also used for for-loops
formatting_semi!(); // VarDecl is also used for for-loops
}
Decl::TsEnum(ref n) => emit!(n),
Decl::TsInterface(ref n) => emit!(n),
Expand Down Expand Up @@ -113,16 +113,16 @@ mod tests {
"function* foo(){
yield getServiceHosts()
}",
"function*foo(){yield getServiceHosts();}",
"function*foo(){yield getServiceHosts()}",
);
}

#[test]
fn single_argument_arrow_expression() {
assert_min("function* f(){ yield x => x}", "function*f(){yield x=>x;}");
assert_min("function* f(){ yield x => x}", "function*f(){yield x=>x}");
assert_min(
"function* f(){ yield ({x}) => x}",
"function*f(){yield({x})=>x;}",
"function*f(){yield({x})=>x}",
);
}
}
196 changes: 98 additions & 98 deletions ecmascript/codegen/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,142 +7,142 @@ mod tests {

#[test]
fn values() {
assert_min("null", "null;");
assert_min("undefined", "undefined;");
assert_min("true", "true;");
assert_min("false", "false;");
assert_min("42", "42;");
assert_min("3.14", "3.14;");
assert_min(r#" 'foobar' "#, r#"'foobar';"#);
assert_min("null", "null");
assert_min("undefined", "undefined");
assert_min("true", "true");
assert_min("false", "false");
assert_min("42", "42");
assert_min("3.14", "3.14");
assert_min(r#" 'foobar' "#, r#"'foobar'"#);
}

#[test]
fn bin_expr() {
assert_min("1+2+3+4+5", "1+2+3+4+5;");
assert_min("1+2+3+4+5", "1+2+3+4+5");
}

#[test]
fn template_expression() {
assert_min("``", "``;");
assert_min("foo``", "foo``;");
assert_min("`foobar`", "`foobar`;");
assert_min("foo`bar`", "foo`bar`;");
assert_min("`foo${ 10 }bar${ 20 }baz`", "`foo${10}bar${20}baz`;");
assert_min("foo`bar${ 10 }baz`", "foo`bar${10}baz`;");
assert_min("foo`${ 10 }`", "foo`${10}`;");
assert_min("``", "``");
assert_min("foo``", "foo``");
assert_min("`foobar`", "`foobar`");
assert_min("foo`bar`", "foo`bar`");
assert_min("`foo${ 10 }bar${ 20 }baz`", "`foo${10}bar${20}baz`");
assert_min("foo`bar${ 10 }baz`", "foo`bar${10}baz`");
assert_min("foo`${ 10 }`", "foo`${10}`");
}

#[test]
fn sequence_expression() {
assert_min("foo, bar, baz;", "foo,bar,baz;");
assert_min("1, 2, 3;", "1,2,3;");
assert_min("1,2,3+4;", "1,2,3+4;");
assert_min("1+2,3,4;", "1+2,3,4;");
assert_min("1+(2,3,4);", "1+(2,3,4);");
assert_min("(1,2,3)+4;", "(1,2,3)+4;");
assert_min("foo, bar, baz;", "foo,bar,baz");
assert_min("1, 2, 3;", "1,2,3");
assert_min("1,2,3+4;", "1,2,3+4");
assert_min("1+2,3,4;", "1+2,3,4");
assert_min("1+(2,3,4);", "1+(2,3,4)");
assert_min("(1,2,3)+4;", "(1,2,3)+4");
}

#[test]
fn binary_expression() {
assert_min("a = 10", "a=10;");
assert_min("a == 10", "a==10;");
assert_min("a === 10", "a===10;");
assert_min("a != 10", "a!=10;");
assert_min("a !== 10", "a!==10;");
assert_min("a += 10", "a+=10;");
assert_min("a -= 10", "a-=10;");
assert_min("a <<= 10", "a<<=10;");
assert_min("a >>= 10", "a>>=10;");
assert_min("a >>>= 10", "a>>>=10;");
assert_min("2 + 2", "2+2;");
assert_min("2 - 2", "2-2;");
assert_min("2 * 2", "2*2;");
assert_min("2 / 2", "2/2;");
assert_min("2 % 2", "2%2;");
assert_min("2 ** 2", "2**2;");
assert_min("2 << 2", "2<<2;");
assert_min("2 >> 2", "2>>2;");
assert_min("2 >>> 2", "2>>>2;");
assert_min("foo in bar", "foo in bar;");
assert_min("foo instanceof Foo", "foo instanceof Foo;");
assert_min("a = 10", "a=10");
assert_min("a == 10", "a==10");
assert_min("a === 10", "a===10");
assert_min("a != 10", "a!=10");
assert_min("a !== 10", "a!==10");
assert_min("a += 10", "a+=10");
assert_min("a -= 10", "a-=10");
assert_min("a <<= 10", "a<<=10");
assert_min("a >>= 10", "a>>=10");
assert_min("a >>>= 10", "a>>>=10");
assert_min("2 + 2", "2+2");
assert_min("2 - 2", "2-2");
assert_min("2 * 2", "2*2");
assert_min("2 / 2", "2/2");
assert_min("2 % 2", "2%2");
assert_min("2 ** 2", "2**2");
assert_min("2 << 2", "2<<2");
assert_min("2 >> 2", "2>>2");
assert_min("2 >>> 2", "2>>>2");
assert_min("foo in bar", "foo in bar");
assert_min("foo instanceof Foo", "foo instanceof Foo");
}

#[test]
fn prefix_expression() {
assert_min("+foo", "+foo;");
assert_min("-foo", "-foo;");
assert_min("!foo", "!foo;");
assert_min("~foo", "~foo;");
assert_min("++foo", "++foo;");
assert_min("--foo", "--foo;");
assert_min("new foo", "new foo;");
assert_min("void foo", "void foo;");
assert_min("typeof foo", "typeof foo;");
assert_min("+foo", "+foo");
assert_min("-foo", "-foo");
assert_min("!foo", "!foo");
assert_min("~foo", "~foo");
assert_min("++foo", "++foo");
assert_min("--foo", "--foo");
assert_min("new foo", "new foo");
assert_min("void foo", "void foo");
assert_min("typeof foo", "typeof foo");
}

#[test]
fn postfix_expression() {
assert_min("foo++", "foo++;");
assert_min("foo--", "foo--;");
assert_min("foo++", "foo++");
assert_min("foo--", "foo--");
}

#[test]
fn conditional_expression() {
assert_min("true ? foo : bar", "true?foo:bar;")
assert_min("true ? foo : bar", "true?foo:bar")
}

#[test]
fn function_expression() {
assert_min("(function () {})", "(function(){});");
assert_min("(function foo() {})", "(function foo(){});");
assert_min("(function () {})", "(function(){})");
assert_min("(function foo() {})", "(function foo(){})");
}

#[test]
#[ignore]
fn class_expression() {
assert_min("(class {})", "(class{});");
assert_min("(class Foo {})", "(class Foo{});");
assert_min("(class extends Foo {})", "(class extends Foo{});");
assert_min("(class Foo extends Bar {})", "(class Foo extends Bar{});");
assert_min("(class {})", "(class{})");
assert_min("(class Foo {})", "(class Foo{})");
assert_min("(class extends Foo {})", "(class extends Foo{})");
assert_min("(class Foo extends Bar {})", "(class Foo extends Bar{})");
}

#[test]
fn call_expression() {
assert_min("foobar();", "foobar();");
assert_min("foobar(1, 2, 3);", "foobar(1,2,3);");
assert_min("foobar();", "foobar()");
assert_min("foobar(1, 2, 3);", "foobar(1,2,3)");
}

#[test]
fn member_expression() {
assert_min("foo.bar", "foo.bar;");
assert_min("this.bar", "this.bar;");
assert_min("10..fooz", "10..fooz;");
assert_min("foo[10]", "foo[10];");
assert_min(r#"foo["bar"]"#, r#"foo["bar"];"#);
assert_min("foo.bar", "foo.bar");
assert_min("this.bar", "this.bar");
assert_min("10..fooz", "10..fooz");
assert_min("foo[10]", "foo[10]");
assert_min(r#"foo["bar"]"#, r#"foo["bar"]"#);
}

#[test]
fn array_expression() {
assert_min("[]", "[];");
assert_min("[foo]", "[foo];");
assert_min("[foo,bar]", "[foo,bar];");
assert_min("[foo,bar,baz]", "[foo,bar,baz];");
assert_min("[]", "[]");
assert_min("[foo]", "[foo]");
assert_min("[foo,bar]", "[foo,bar]");
assert_min("[foo,bar,baz]", "[foo,bar,baz]");
}

#[test]
fn array_spread() {
assert_min("[...foo,...bar]", "[...foo,...bar];");
assert_min("[...foo,...bar]", "[...foo,...bar]");
}

#[test]
fn sparse_array_expression() {
assert_min("[]", "[];");
assert_min("[,]", "[,];");
assert_min("[1,]", "[1,];");
assert_min("[,1]", "[,1];");
assert_min("[,,];", "[,,];");
assert_min("[1,,];", "[1,,];");
assert_min("[,,1];", "[,,1];");
assert_min("[]", "[]");
assert_min("[,]", "[,]");
assert_min("[1,]", "[1,]");
assert_min("[,1]", "[,1]");
assert_min("[,,];", "[,,]");
assert_min("[1,,];", "[1,,]");
assert_min("[,,1];", "[,,1]");
}

// #[test]
Expand All @@ -158,41 +158,41 @@ mod tests {

#[test]
fn object_expression() {
assert_min("({});", "({});");
assert_min("({ foo });", "({foo});");
assert_min("({ foo: 10 });", "({foo:10});");
assert_min("({ foo, bar });", "({foo,bar});");
assert_min("({ foo: 10, bar: 20 });", "({foo:10,bar:20});");
assert_min("({ foo: 10, bar() {} });", "({foo:10,bar(){}});");
assert_min("({ foo(bar, baz) {} });", "({foo(bar,baz){}});");
assert_min("({});", "({})");
assert_min("({ foo });", "({foo})");
assert_min("({ foo: 10 });", "({foo:10})");
assert_min("({ foo, bar });", "({foo,bar})");
assert_min("({ foo: 10, bar: 20 });", "({foo:10,bar:20})");
assert_min("({ foo: 10, bar() {} });", "({foo:10,bar(){}})");
assert_min("({ foo(bar, baz) {} });", "({foo(bar,baz){}})");
// let expected = "({\n foo: true,\n bar: false\n});";
// assert_pretty("({ foo: true, bar: false })", expected);
}

#[test]
fn binding_power() {
assert_min("1 + 2 * 3;", "1+2*3;");
assert_min("1 + 2 * 3;", "1+2*3;");
assert_min("(1 + 2) * 3;", "(1+2)*3;");
assert_min("1 + 2 * 3;", "1+2*3");
assert_min("1 + 2 * 3;", "1+2*3");
assert_min("(1 + 2) * 3;", "(1+2)*3");
assert_min(
"(denominator / divider * 100).toFixed(2);",
"(denominator/divider*100).toFixed(2);",
"(denominator/divider*100).toFixed(2)",
);
assert_min("(1 + 1)[0];", "(1+1)[0];");
assert_min("2 * 2 / 2;", "2*2/2;");
assert_min("2 * (2 / 2);", "2*(2/2);");
assert_min("2 * 2 / 2;", "2*2/2;");
assert_min("(1 + 1)[0];", "(1+1)[0]");
assert_min("2 * 2 / 2;", "2*2/2");
assert_min("2 * (2 / 2);", "2*(2/2)");
assert_min("2 * 2 / 2;", "2*2/2");
}

#[test]
fn regression_increments() {
assert_min("x++ + ++y", "x++ + ++y;");
assert_min("x++ + ++y", "x++ + ++y");
}

#[test]
fn bigint_property_key() {
assert_min("({ 1n: 2 });", "({1n:2});");
assert_min("(class C { 1n = 1 });", "(class C{1n=1;});");
assert_min("(class C { 1n () { } });", "(class C{1n(){}});");
assert_min("({ 1n: 2 });", "({1n:2})");
assert_min("(class C { 1n = 1 });", "(class C{1n=1})");
assert_min("(class C { 1n () { } });", "(class C{1n(){}})");
}
}
Loading

0 comments on commit 9063908

Please sign in to comment.