diff --git a/crates/swc/tests/fixture/issues-5xxx/5865/input/.swcrc b/crates/swc/tests/fixture/issues-5xxx/5865/input/.swcrc new file mode 100644 index 00000000000..c8b07e24523 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5865/input/.swcrc @@ -0,0 +1,68 @@ +{ + "$schema": "https://json.schemastore.org/swcrc", + "jsc": { + "parser": { + "syntax": "ecmascript", + "jsx": false + }, + "target": "es2019", + "transform": { + "react": { + "runtime": "automatic" + } + }, + "minify": { + "compress": { + "arguments": false, + "arrows": true, + "booleans": true, + "booleans_as_integers": false, + "collapse_vars": true, + "comparisons": true, + "computed_props": true, + "conditionals": true, + "dead_code": true, + "directives": true, + "drop_console": false, + "drop_debugger": true, + "evaluate": true, + "expression": false, + "hoist_funs": false, + "hoist_props": true, + "hoist_vars": false, + "if_return": true, + "join_vars": true, + "keep_classnames": false, + "keep_fargs": true, + "keep_fnames": false, + "keep_infinity": false, + "loops": true, + "negate_iife": true, + "properties": true, + "reduce_funcs": false, + "reduce_vars": false, + "side_effects": true, + "switches": true, + "typeofs": true, + "unsafe": false, + "unsafe_arrows": false, + "unsafe_comps": false, + "unsafe_Function": false, + "unsafe_math": false, + "unsafe_symbols": false, + "unsafe_methods": false, + "unsafe_proto": false, + "unsafe_regexp": false, + "unsafe_undefined": false, + "unused": true, + "const_to_let": true, + "pristine_globals": true + } + } + }, + "isModule": true, + "module": { + "type": "es6" + }, + "minify": true +} \ No newline at end of file diff --git a/crates/swc/tests/fixture/issues-5xxx/5865/input/index.js b/crates/swc/tests/fixture/issues-5xxx/5865/input/index.js new file mode 100644 index 00000000000..15de19896e0 --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5865/input/index.js @@ -0,0 +1,6 @@ +v = ((a) => (b) => { + const n = a.map((t) => { + if (t) return ((e) => e.foo)(t); + }); + return n; +})(r); diff --git a/crates/swc/tests/fixture/issues-5xxx/5865/output/index.js b/crates/swc/tests/fixture/issues-5xxx/5865/output/index.js new file mode 100644 index 00000000000..9ef2045034a --- /dev/null +++ b/crates/swc/tests/fixture/issues-5xxx/5865/output/index.js @@ -0,0 +1 @@ +var a;v=(a=r,b=>{let n=a.map(t=>{if(t)return t.foo});return n}); diff --git a/crates/swc_common/src/syntax_pos.rs b/crates/swc_common/src/syntax_pos.rs index 29fc86ae455..9850efa5907 100644 --- a/crates/swc_common/src/syntax_pos.rs +++ b/crates/swc_common/src/syntax_pos.rs @@ -546,21 +546,7 @@ impl Span { "Cannot check if a span contains a `ROOT` mark" ); - let mut ctxt = self.ctxt; - - loop { - if ctxt == SyntaxContext::empty() { - return false; - } - - let m = ctxt.remove_mark(); - if m == mark { - return true; - } - if m == Mark::root() { - return false; - } - } + self.ctxt.has_mark(mark) } /// Dummy span, both position are extremely large numbers so they would be diff --git a/crates/swc_common/src/syntax_pos/hygiene.rs b/crates/swc_common/src/syntax_pos/hygiene.rs index 418c56e8a60..cab28a1f513 100644 --- a/crates/swc_common/src/syntax_pos/hygiene.rs +++ b/crates/swc_common/src/syntax_pos/hygiene.rs @@ -325,6 +325,33 @@ impl SyntaxContext { SyntaxContext(0) } + /// Returns `true` if `self` is marked with `mark`. + /// + /// Panics if `mark` is not a valid mark. + pub fn has_mark(self, mark: Mark) -> bool { + debug_assert_ne!( + mark, + Mark::root(), + "Cannot check if a span contains a `ROOT` mark" + ); + + let mut ctxt = self; + + loop { + if ctxt == SyntaxContext::empty() { + return false; + } + + let m = ctxt.remove_mark(); + if m == mark { + return true; + } + if m == Mark::root() { + return false; + } + } + } + #[inline] pub fn as_u32(self) -> u32 { self.0 diff --git a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs index 1c852dffdd1..128e849a47f 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/mod.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/mod.rs @@ -2924,7 +2924,9 @@ impl Take for SynthesizedStmts { impl Drop for SynthesizedStmts { fn drop(&mut self) { if !self.0.is_empty() { - panic!("We should not drop synthesized stmts"); + if !std::thread::panicking() { + panic!("We should not drop synthesized stmts"); + } } } } diff --git a/crates/swc_ecma_minifier/src/compress/optimize/util.rs b/crates/swc_ecma_minifier/src/compress/optimize/util.rs index 1e539659240..8c61180cd35 100644 --- a/crates/swc_ecma_minifier/src/compress/optimize/util.rs +++ b/crates/swc_ecma_minifier/src/compress/optimize/util.rs @@ -34,12 +34,20 @@ where } /// RAII guard to change context temporarically - pub(super) fn with_ctx(&mut self, ctx: Ctx) -> WithCtx<'_, 'b, M> { - #[cfg(debug_assertions)] - { - let scope_ctxt = ctx.scope; - if self.ctx.scope != scope_ctxt { - self.data.scopes.get(&scope_ctxt).expect("scope not found"); + pub(super) fn with_ctx(&mut self, mut ctx: Ctx) -> WithCtx<'_, 'b, M> { + let mut scope_ctxt = ctx.scope; + + if self.ctx.scope != scope_ctxt { + if scope_ctxt.clone().remove_mark() == self.marks.fake_block { + scope_ctxt.remove_mark(); + } + ctx.scope = scope_ctxt; + + #[cfg(debug_assertions)] + { + self.data.scopes.get(&scope_ctxt).unwrap_or_else(|| { + panic!("scope not found: {:?}; {:#?}", scope_ctxt, self.data.scopes) + }); } } diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/5865/analysis-snapshot.rust-debug b/crates/swc_ecma_minifier/tests/fixture/issues/5865/analysis-snapshot.rust-debug new file mode 100644 index 00000000000..e23c0242082 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/5865/analysis-snapshot.rust-debug @@ -0,0 +1,272 @@ +TestSnapshot { + vars: [ + ( + ( + Atom('a' type=static), + #2, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: false, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('b' type=static), + #3, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 0, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 0, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('e' type=inline), + #5, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: true, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: true, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('n' type=inline), + #3, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: false, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: false, + var_kind: Some( + "const", + ), + var_initialized: true, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('r' type=inline), + #1, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: false, + declared_count: 0, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 1, + usage_count: 1, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: true, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: false, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: true, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('t' type=inline), + #4, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 2, + cond_init: false, + declared: true, + declared_count: 1, + declared_as_fn_param: true, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 2, + usage_count: 2, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: true, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: false, + is_fn_local: true, + used_by_nested_fn: true, + executed_multiple_time: false, + used_in_cond: true, + var_kind: None, + var_initialized: false, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: true, + pure_fn: false, + infects: [], + }, + ), + ( + ( + Atom('v' type=inline), + #1, + ), + VarUsageInfo { + inline_prevented: false, + ref_count: 1, + cond_init: false, + declared: false, + declared_count: 0, + declared_as_fn_param: false, + declared_as_fn_expr: false, + assign_count: 0, + mutation_by_call_count: 0, + usage_count: 0, + reassigned_with_assignment: false, + reassigned_with_var_decl: false, + mutated: true, + has_property_access: false, + has_property_mutation: false, + accessed_props: {}, + exported: false, + used_above_decl: true, + is_fn_local: true, + used_by_nested_fn: false, + executed_multiple_time: false, + used_in_cond: false, + var_kind: None, + var_initialized: true, + declared_as_catch_param: false, + no_side_effect_for_member_access: false, + used_as_callee: false, + used_as_arg: false, + pure_fn: false, + infects: [], + }, + ), + ], +} diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/5865/config.json b/crates/swc_ecma_minifier/tests/fixture/issues/5865/config.json new file mode 100644 index 00000000000..e24c2f770eb --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/5865/config.json @@ -0,0 +1,46 @@ +{ + "arguments": false, + "arrows": true, + "booleans": true, + "booleans_as_integers": false, + "collapse_vars": true, + "comparisons": true, + "computed_props": true, + "conditionals": true, + "dead_code": true, + "directives": true, + "drop_console": false, + "drop_debugger": true, + "evaluate": true, + "expression": false, + "hoist_funs": false, + "hoist_props": true, + "hoist_vars": false, + "if_return": true, + "join_vars": true, + "keep_classnames": false, + "keep_fargs": true, + "keep_fnames": false, + "keep_infinity": false, + "loops": true, + "negate_iife": true, + "properties": true, + "reduce_funcs": false, + "reduce_vars": false, + "side_effects": true, + "switches": true, + "typeofs": true, + "unsafe": false, + "unsafe_arrows": false, + "unsafe_comps": false, + "unsafe_Function": false, + "unsafe_math": false, + "unsafe_symbols": false, + "unsafe_methods": false, + "unsafe_proto": false, + "unsafe_regexp": false, + "unsafe_undefined": false, + "unused": true, + "const_to_let": true, + "pristine_globals": true +} diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/5865/input.js b/crates/swc_ecma_minifier/tests/fixture/issues/5865/input.js new file mode 100644 index 00000000000..707356207cc --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/5865/input.js @@ -0,0 +1,6 @@ +v = ((a) => (b) => { + const n = a.map((t) => { + if (t) return ((e) => e.foo)(t); + }); + return n; +})(r); \ No newline at end of file diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/5865/output.js b/crates/swc_ecma_minifier/tests/fixture/issues/5865/output.js new file mode 100644 index 00000000000..8815d30ab87 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/5865/output.js @@ -0,0 +1,6 @@ +v = ((a)=>(b)=>{ + const n = a.map((t)=>{ + if (t) return ((e)=>e.foo)(t); + }); + return n; + })(r); diff --git a/crates/swc_ecma_minifier/tests/fixture/issues/5865/output.mangleOnly.js b/crates/swc_ecma_minifier/tests/fixture/issues/5865/output.mangleOnly.js new file mode 100644 index 00000000000..a4789a9c4f6 --- /dev/null +++ b/crates/swc_ecma_minifier/tests/fixture/issues/5865/output.mangleOnly.js @@ -0,0 +1,6 @@ +v = ((n)=>(o)=>{ + const t = n.map((n)=>{ + if (n) return ((n)=>n.foo)(n); + }); + return t; + })(r);