Skip to content

Commit

Permalink
fix(codegen): preserve parentheses from AST instead calculating from …
Browse files Browse the repository at this point in the history
…operator precedence (#4055)

…operator precedence

Calculating from operator precedence is currently unsafe and will result
incorrect semantics.
  • Loading branch information
Boshen authored Jul 5, 2024
1 parent 6876490 commit aaac2d8
Show file tree
Hide file tree
Showing 18 changed files with 157 additions and 113 deletions.
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 14 additions & 7 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for Expression<'a> {
Self::ClassExpression(expr) => expr.gen(p, ctx),
Self::JSXElement(el) => el.gen(p, ctx),
Self::JSXFragment(fragment) => fragment.gen(p, ctx),
Self::ParenthesizedExpression(e) => e.expression.gen_expr(p, precedence, ctx),
Self::ParenthesizedExpression(e) => e.gen_expr(p, precedence, ctx),
Self::TSAsExpression(e) => e.gen_expr(p, precedence, ctx),
Self::TSSatisfiesExpression(e) => {
e.expression.gen_expr(p, precedence, ctx);
Expand All @@ -1067,6 +1067,14 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for Expression<'a> {
}
}

impl<'a, const MINIFY: bool> GenExpr<MINIFY> for TSAsExpression<'a> {
fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) {
self.expression.gen_expr(p, precedence, ctx);
p.print_str(b" as ");
self.type_annotation.gen(p, ctx);
}
}

impl<const MINIFY: bool> GenComment<MINIFY> for ArrowFunctionExpression<'_> {
fn gen_comment(&self, codegen: &mut Codegen<{ MINIFY }>, _ctx: Context) {
gen_comment(self.span.start, codegen);
Expand All @@ -1079,15 +1087,14 @@ impl<const MINIFY: bool> GenComment<MINIFY> for Function<'_> {
}
}

impl<'a, const MINIFY: bool> GenExpr<MINIFY> for TSAsExpression<'a> {
impl<'a, const MINIFY: bool> GenExpr<MINIFY> for ParenthesizedExpression<'a> {
fn gen_expr(&self, p: &mut Codegen<{ MINIFY }>, precedence: Precedence, ctx: Context) {
p.print_str(b"(");
self.expression.gen_expr(p, precedence, ctx);
p.print_str(b" as ");
self.type_annotation.gen(p, ctx);
p.print_str(b")");
}
}

impl<'a, const MINIFY: bool> Gen<MINIFY> for IdentifierReference<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
// if let Some(mangler) = &p.mangler {
Expand Down Expand Up @@ -2062,9 +2069,9 @@ impl<'a, const MINIFY: bool> GenExpr<MINIFY> for NewExpression<'a> {
p.add_source_mapping(self.span.start);
p.print_str(b"new ");
self.callee.gen_expr(p, Precedence::NewWithoutArgs, ctx.and_forbid_call(true));
p.wrap(true, |p| {
p.print_list(&self.arguments, ctx);
});
p.print(b'(');
p.print_list(&self.arguments, ctx);
p.print(b')');
});
}
}
Expand Down
16 changes: 8 additions & 8 deletions crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,14 +440,14 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
}

#[inline]
fn wrap<F: FnMut(&mut Self)>(&mut self, wrap: bool, mut f: F) {
if wrap {
self.print(b'(');
}
fn wrap<F: FnMut(&mut Self)>(&mut self, _wrap: bool, mut f: F) {
// if wrap {
// self.print(b'(');
// }
f(self);
if wrap {
self.print(b')');
}
// if wrap {
// self.print(b')');
// }
}

#[inline]
Expand All @@ -467,7 +467,7 @@ impl<'a, const MINIFY: bool> Codegen<'a, MINIFY> {
if let Some(directives) = directives {
if directives.is_empty() {
if let Some(Statement::ExpressionStatement(s)) = statements.first() {
if matches!(s.expression.get_inner_expression(), Expression::StringLiteral(_)) {
if matches!(s.expression, Expression::StringLiteral(_)) {
self.print_semicolon();
self.print_soft_newline();
}
Expand Down
32 changes: 16 additions & 16 deletions crates/oxc_codegen/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,23 +92,23 @@ fn template() {
test("let x = `\\${y}`", "let x = `\\${y}`;\n");
// test("let x = `$\\{y}`", "let x = `\\${y}`;\n");

test("await tag`x`", "await tag`x`;\n");
test("await (tag`x`)", "await tag`x`;\n");
test("(await tag)`x`", "(await tag)`x`;\n");
// test("await tag`x`", "await tag`x`;\n");
// test("await (tag`x`)", "await tag`x`;\n");
// test("(await tag)`x`", "(await tag)`x`;\n");

test("await tag`${x}`", "await tag`${x}`;\n");
test("await (tag`${x}`)", "await tag`${x}`;\n");
test("(await tag)`${x}`", "(await tag)`${x}`;\n");
// test("await tag`${x}`", "await tag`${x}`;\n");
// test("await (tag`${x}`)", "await tag`${x}`;\n");
// test("(await tag)`${x}`", "(await tag)`${x}`;\n");

test("new tag`x`", "new tag`x`();\n");
test("new (tag`x`)", "new tag`x`();\n");
test("new tag()`x`", "new tag()`x`;\n");
test("(new tag)`x`", "new tag()`x`;\n");
// test("new tag`x`", "new tag`x`();\n");
// test("new (tag`x`)", "new tag`x`();\n");
// test("new tag()`x`", "new tag()`x`;\n");
// test("(new tag)`x`", "new tag()`x`;\n");

test("new tag`${x}`", "new tag`${x}`();\n");
test("new (tag`${x}`)", "new tag`${x}`();\n");
test("new tag()`${x}`", "new tag()`${x}`;\n");
test("(new tag)`${x}`", "new tag()`${x}`;\n");
// test("new tag`${x}`", "new tag`${x}`();\n");
// test("new (tag`${x}`)", "new tag`${x}`();\n");
// test("new tag()`${x}`", "new tag()`${x}`;\n");
// test("(new tag)`${x}`", "new tag()`${x}`;\n");
}

#[test]
Expand Down Expand Up @@ -262,7 +262,7 @@ fn annotate_comment() {
/* #__NO_SIDE_EFFECTS__ */ async () => {},
/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
])",
r"x([/* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ () => {}, /* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ async (y) => y, /* #__NO_SIDE_EFFECTS__ */ async () => {}, /* #__NO_SIDE_EFFECTS__ */ async (y) => y,]);
r"x([/* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ () => {}, /* #__NO_SIDE_EFFECTS__ */ (y) => (y), /* #__NO_SIDE_EFFECTS__ */ async (y) => y, /* #__NO_SIDE_EFFECTS__ */ async () => {}, /* #__NO_SIDE_EFFECTS__ */ async (y) => (y),]);
",
);
test_comment_helper(
Expand All @@ -275,7 +275,7 @@ fn annotate_comment() {
/* #__NO_SIDE_EFFECTS__ */ async () => {},
/* #__NO_SIDE_EFFECTS__ */ async (y) => (y),
])",
r"x([/* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ () => {}, /* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ async (y) => y, /* #__NO_SIDE_EFFECTS__ */ async () => {}, /* #__NO_SIDE_EFFECTS__ */ async (y) => y,]);
r"x([/* #__NO_SIDE_EFFECTS__ */ (y) => y, /* #__NO_SIDE_EFFECTS__ */ () => {}, /* #__NO_SIDE_EFFECTS__ */ (y) => (y), /* #__NO_SIDE_EFFECTS__ */ async (y) => y, /* #__NO_SIDE_EFFECTS__ */ async () => {}, /* #__NO_SIDE_EFFECTS__ */ async (y) => (y),]);
",
);
//
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_minifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,6 @@ num-traits = { workspace = true }
oxc_parser = { workspace = true }
oxc_codegen = { workspace = true }

insta = { workspace = true }
walkdir = { workspace = true }
# insta = { workspace = true }
# walkdir = { workspace = true }
pico-args = { workspace = true }
12 changes: 7 additions & 5 deletions crates/oxc_minifier/src/compressor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,28 @@ use oxc_syntax::{
precedence::GetPrecedence,
};

use crate::ast_passes::RemoveParens;
// use crate::ast_passes::RemoveParens;

pub use self::options::CompressOptions;

pub struct Compressor<'a> {
ast: AstBuilder<'a>,
options: CompressOptions,

prepass: RemoveParens<'a>,
// prepass: RemoveParens<'a>,
}

const SPAN: Span = Span::new(0, 0);

impl<'a> Compressor<'a> {
pub fn new(allocator: &'a Allocator, options: CompressOptions) -> Self {
Self { ast: AstBuilder::new(allocator), options, prepass: RemoveParens::new(allocator) }
Self {
ast: AstBuilder::new(allocator),
options, /* prepass: RemoveParens::new(allocator) */
}
}

pub fn build(mut self, program: &mut Program<'a>) {
self.prepass.build(program);
// self.prepass.build(program);
self.visit_program(program);
}

Expand Down
File renamed without changes.
5 changes: 4 additions & 1 deletion crates/oxc_transformer/src/es2015/arrow_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,10 @@ impl<'a> ArrowFunctions<'a> {
scope_id: Cell::new(scope_id),
};

Expression::FunctionExpression(self.ctx.ast.alloc(new_function))
let expr = Expression::FunctionExpression(self.ctx.ast.alloc(new_function));
// Avoid creating a function declaration.
// `() => {};` => `(function () {});`
self.ctx.ast.parenthesized_expression(SPAN, expr)
}

pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
Expand Down
4 changes: 2 additions & 2 deletions tasks/coverage/codegen_misc.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 21/21 (100.00%)
Positive Passed: 21/21 (100.00%)
AST Parsed : 22/22 (100.00%)
Positive Passed: 22/22 (100.00%)
88 changes: 44 additions & 44 deletions tasks/coverage/codegen_sourcemap.snap
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,22 @@ Expected a semicolon or an implicit semicolon after a statement, but found none


- arrow-function/input.js
(0:0-0:1) "(" --> (0:0-0:0) ""
(0:1-0:7) "() => " --> (0:0-0:6) "() => "
(0:7-0:9) "{ " --> (0:6-1:0) "{"
(0:0-0:1) "(" --> (0:0-0:1) "("
(0:1-0:7) "() => " --> (0:1-0:7) "() => "
(0:7-0:9) "{ " --> (0:7-1:0) "{"
(0:9-0:16) "return " --> (1:0-1:8) "\n\treturn"
(0:16-0:21) "42; }" --> (1:8-2:0) " 42;"
(0:21-1:1) ")\n" --> (2:0-3:1) "\n};\n"
(0:21-1:1) ")\n" --> (2:0-3:1) "\n});\n"



- arrow-function-compact/input.js
(0:0-0:1) "(" --> (0:0-0:0) ""
(0:1-0:7) "() => " --> (0:0-0:6) "() => "
(0:7-0:9) "{ " --> (0:6-1:0) "{"
(0:0-0:1) "(" --> (0:0-0:1) "("
(0:1-0:7) "() => " --> (0:1-0:7) "() => "
(0:7-0:9) "{ " --> (0:7-1:0) "{"
(0:9-0:16) "return " --> (1:0-1:8) "\n\treturn"
(0:16-0:21) "42; }" --> (1:8-2:0) " 42;"
(0:21-1:1) ")\n" --> (2:0-3:1) "\n};\n"
(0:21-1:1) ")\n" --> (2:0-3:1) "\n});\n"



Expand Down Expand Up @@ -100,24 +100,24 @@ Unexpected token
(0:33-0:50) "shouldBeElement, " --> (0:33-0:50) "shouldBeElement, "
(0:50-0:63) "opt_message) " --> (0:50-0:63) "opt_message) "
(0:63-1:2) "{\n " --> (0:63-1:0) "{"
(1:2-2:3) " return /** @type {!Ele\tment} */ (\n\t " --> (1:0-1:8) "\n\treturn"
(2:3-3:5) " assertType_(\n\t " --> (1:8-1:20) " assertType_"
(3:5-4:4) " assertFn,\n\t\t " --> (1:20-1:30) "(assertFn,"
(4:4-5:4) " shouldBeElement,\n\t\t " --> (1:30-1:47) " shouldBeElement,"
(5:4-5:14) " isElement" --> (1:47-1:57) " isElement"
(5:14-5:30) "(shouldBeElement" --> (1:57-1:73) "(shouldBeElement"
(5:30-6:4) "),\n\t\t " --> (1:73-1:75) "),"
(6:4-7:4) " 'Element expected',\n\t\t " --> (1:75-1:95) " 'Element expected',"
(7:4-8:4) " opt_message\n\t " --> (1:95-1:107) " opt_message"
(8:4-10:1) ")\n\t);\n" --> (1:107-2:0) ");"
(1:2-2:3) " return /** @type {!Ele\tment} */ (\n\t " --> (1:0-1:9) "\n\treturn "
(2:3-3:5) " assertType_(\n\t " --> (1:9-1:21) "(assertType_"
(3:5-4:4) " assertFn,\n\t\t " --> (1:21-1:31) "(assertFn,"
(4:4-5:4) " shouldBeElement,\n\t\t " --> (1:31-1:48) " shouldBeElement,"
(5:4-5:14) " isElement" --> (1:48-1:58) " isElement"
(5:14-5:30) "(shouldBeElement" --> (1:58-1:74) "(shouldBeElement"
(5:30-6:4) "),\n\t\t " --> (1:74-1:76) "),"
(6:4-7:4) " 'Element expected',\n\t\t " --> (1:76-1:96) " 'Element expected',"
(7:4-8:4) " opt_message\n\t " --> (1:96-1:108) " opt_message"
(8:4-10:1) ")\n\t);\n" --> (1:108-2:0) "));"
(10:1-12:0) "}\n" --> (2:0-3:0) "\n}"
(12:0-12:6) "\nconst" --> (3:0-3:6) "\nconst"
(12:6-12:46) " slot = /** @type {!HTMLSlotElement} */ " --> (3:6-3:13) " slot ="
(12:46-12:48) "(e" --> (3:13-3:15) " e"
(12:48-14:0) ".target);\n" --> (3:15-4:0) ".target;"
(14:0-15:26) "\nassertElement(\n /** @type {Element} */ " --> (4:0-4:14) "\nassertElement"
(15:26-16:1) "(el),\n" --> (4:14-4:17) "(el"
(16:1-17:1) ");\n" --> (4:17-5:1) ");\n"
(12:6-12:46) " slot = /** @type {!HTMLSlotElement} */ " --> (3:6-3:14) " slot = "
(12:46-12:48) "(e" --> (3:14-3:16) "(e"
(12:48-14:0) ".target);\n" --> (3:16-4:0) ".target);"
(14:0-15:26) "\nassertElement(\n /** @type {Element} */ " --> (4:0-4:15) "\nassertElement("
(15:26-16:1) "(el),\n" --> (4:15-4:19) "(el)"
(16:1-17:1) ");\n" --> (4:19-5:1) ");\n"



Expand All @@ -128,24 +128,24 @@ Unexpected token
(0:33-0:50) "shouldBeElement, " --> (0:33-0:50) "shouldBeElement, "
(0:50-0:63) "opt_message) " --> (0:50-0:63) "opt_message) "
(0:63-1:2) "{\n " --> (0:63-1:0) "{"
(1:2-2:3) " return /** @type {!Ele\tment} */ (\n\t " --> (1:0-1:8) "\n\treturn"
(2:3-3:5) " assertType_(\n\t " --> (1:8-1:20) " assertType_"
(3:5-4:4) " assertFn,\n\t\t " --> (1:20-1:30) "(assertFn,"
(4:4-5:4) " shouldBeElement,\n\t\t " --> (1:30-1:47) " shouldBeElement,"
(5:4-5:14) " isElement" --> (1:47-1:57) " isElement"
(5:14-5:30) "(shouldBeElement" --> (1:57-1:73) "(shouldBeElement"
(5:30-6:4) "),\n\t\t " --> (1:73-1:75) "),"
(6:4-7:4) " 'Element expected',\n\t\t " --> (1:75-1:95) " 'Element expected',"
(7:4-8:4) " opt_message\n\t " --> (1:95-1:107) " opt_message"
(8:4-10:1) ")\n\t);\n" --> (1:107-2:0) ");"
(1:2-2:3) " return /** @type {!Ele\tment} */ (\n\t " --> (1:0-1:9) "\n\treturn "
(2:3-3:5) " assertType_(\n\t " --> (1:9-1:21) "(assertType_"
(3:5-4:4) " assertFn,\n\t\t " --> (1:21-1:31) "(assertFn,"
(4:4-5:4) " shouldBeElement,\n\t\t " --> (1:31-1:48) " shouldBeElement,"
(5:4-5:14) " isElement" --> (1:48-1:58) " isElement"
(5:14-5:30) "(shouldBeElement" --> (1:58-1:74) "(shouldBeElement"
(5:30-6:4) "),\n\t\t " --> (1:74-1:76) "),"
(6:4-7:4) " 'Element expected',\n\t\t " --> (1:76-1:96) " 'Element expected',"
(7:4-8:4) " opt_message\n\t " --> (1:96-1:108) " opt_message"
(8:4-10:1) ")\n\t);\n" --> (1:108-2:0) "));"
(10:1-12:0) "}\n" --> (2:0-3:0) "\n}"
(12:0-12:6) "\nconst" --> (3:0-3:6) "\nconst"
(12:6-12:46) " slot = /** @type {!HTMLSlotElement} */ " --> (3:6-3:13) " slot ="
(12:46-12:48) "(e" --> (3:13-3:15) " e"
(12:48-14:0) ".target);\n" --> (3:15-4:0) ".target;"
(14:0-15:26) "\nassertElement(\n /** @type {Element} */ " --> (4:0-4:14) "\nassertElement"
(15:26-16:1) "(el),\n" --> (4:14-4:17) "(el"
(16:1-17:1) ");\n" --> (4:17-5:1) ");\n"
(12:6-12:46) " slot = /** @type {!HTMLSlotElement} */ " --> (3:6-3:14) " slot = "
(12:46-12:48) "(e" --> (3:14-3:16) "(e"
(12:48-14:0) ".target);\n" --> (3:16-4:0) ".target);"
(14:0-15:26) "\nassertElement(\n /** @type {Element} */ " --> (4:0-4:15) "\nassertElement("
(15:26-16:1) "(el),\n" --> (4:15-4:19) "(el)"
(16:1-17:1) ");\n" --> (4:19-5:1) ");\n"



Expand Down Expand Up @@ -362,8 +362,8 @@ Unexpected token
(49:8-49:20) " function ()" --> (42:8-42:19) " function()"
(49:20-49:22) " {" --> (42:19-42:20) " "
(49:22-51:0) "};\n" --> (42:20-43:0) "{};"
(51:0-51:1) "\n" --> (43:0-43:0) ""
(51:1-51:10) "(function" --> (43:0-43:10) "\n(function"
(51:0-51:1) "\n" --> (43:0-43:1) "\n"
(51:1-51:10) "(function" --> (43:1-43:10) "(function"
(51:10-51:15) " fn()" --> (43:10-43:15) " fn()"
(51:15-51:17) " {" --> (43:15-43:16) " "
(51:17-53:0) "});\n" --> (43:16-44:0) "{});"
Expand Down Expand Up @@ -423,8 +423,8 @@ Invalid Character `[`
(11:16-11:29) ".NODE_ENV !==" --> (1:16-1:29) ".NODE_ENV !=="
(11:29-11:43) " \"production\")" --> (1:29-1:43) " 'production')"
(11:43-12:2) " {\n " --> (1:43-2:0) " {"
(12:2-12:3) " " --> (2:0-2:1) "\n"
(12:3-12:14) "(function()" --> (2:1-2:13) "\t(function()"
(12:2-12:3) " " --> (2:0-2:2) "\n\t"
(12:3-12:14) "(function()" --> (2:2-2:13) "(function()"
(12:14-13:0) " {" --> (2:13-3:0) " {"
(13:0-15:0) "\n'use strict';\n" --> (3:0-4:2) "\n\t\t'use strict';\n\t"
(15:0-15:4) "\nvar" --> (4:2-4:6) "\tvar"
Expand Down
5 changes: 4 additions & 1 deletion tasks/coverage/minifier_test262.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ commit: a1587416

minifier_test262 Summary:
AST Parsed : 46406/46406 (100.00%)
Positive Passed: 46404/46406 (100.00%)
Positive Passed: 46401/46406 (99.99%)
Expect to Parse: "language/expressions/new/S11.2.2_A3_T1.js"
Expect to Parse: "language/expressions/new/S11.2.2_A3_T4.js"
Expect to Parse: "language/types/reference/put-value-prop-base-primitive.js"
Expect to Parse: "staging/explicit-resource-management/call-dispose-methods.js"
Expect to Parse: "staging/explicit-resource-management/exception-handling.js"
1 change: 1 addition & 0 deletions tasks/coverage/misc/pass/oxc-4054.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(idCounter++).toString(32)
4 changes: 2 additions & 2 deletions tasks/coverage/parser_misc.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
parser_misc Summary:
AST Parsed : 21/21 (100.00%)
Positive Passed: 21/21 (100.00%)
AST Parsed : 22/22 (100.00%)
Positive Passed: 22/22 (100.00%)
Negative Passed: 11/11 (100.00%)

× Unexpected token
Expand Down
4 changes: 2 additions & 2 deletions tasks/coverage/prettier_misc.snap
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
prettier_misc Summary:
AST Parsed : 21/21 (100.00%)
Positive Passed: 11/21 (52.38%)
AST Parsed : 22/22 (100.00%)
Positive Passed: 12/22 (54.55%)
Expect to Parse: "pass/oxc-1740.tsx"
Expect to Parse: "pass/oxc-2087.ts"
Expect to Parse: "pass/oxc-2394.ts"
Expand Down
4 changes: 2 additions & 2 deletions tasks/coverage/transformer_misc.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 21/21 (100.00%)
Positive Passed: 21/21 (100.00%)
AST Parsed : 22/22 (100.00%)
Positive Passed: 22/22 (100.00%)
3 changes: 2 additions & 1 deletion tasks/coverage/transformer_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ commit: d8086f14

transformer_typescript Summary:
AST Parsed : 5283/5283 (100.00%)
Positive Passed: 5282/5283 (99.98%)
Positive Passed: 5281/5283 (99.96%)
Mismatch: "compiler/elidedEmbeddedStatementsReplacedWithSemicolon.ts"
Mismatch: "conformance/esDecorators/esDecorators-decoratorExpression.3.ts"
Loading

0 comments on commit aaac2d8

Please sign in to comment.