From 21bbd33f410895a8ddf52bee55c57bebcf285e4e Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 1 Aug 2026 11:53:04 +0000 Subject: [PATCH 1/6] js_parser: don't inline an optional chain out of [x][0] The minify_syntax fold that turns `[x][0]` into `x` was willing to inline an optional chain expression. When the surrounding index is itself the start of an optional chain (so the next access is a continuation), the printer splices the two chains together: [[a?.b]][0]?.[0].c became `a?.b.c`, which short-circuits to `undefined` for `a == null` instead of throwing on the trailing `.c` like the original (and Node/esbuild) do. The direct `[a?.b]?.[0].c` form was hidden because the parse-time `?.`-on-literal simplification drops the outer chain first, but one level of indirection (`[[a?.b]][0]?.[0].c` or `({f:[a?.b]}).f?.[0].c`) exposes it on current main. Teach can_be_inlined_from_property_access to reject EDot/EIndex/ECall nodes that carry an optional chain, and turn the downstream debug_assert into a real guard so the multi-item path can bail in release builds too. --- src/ast/expr.rs | 9 ++++ src/js_parser/visit/visit_expr.rs | 7 +-- test/bundler/transpiler/transpiler.test.js | 50 ++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 1bcfb0ae7747..38f43a5ad3ff 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -56,6 +56,15 @@ impl Expr { // https://github.com/oven-sh/bun/issues/2594 Data::ESpread(_) => false, Data::EMissing(_) => false, + // Inlining `a?.b` as the target of a surrounding member/call + // expression can splice two unrelated optional chains together: + // `[[a?.b]][0]?.[0].c` would become `a?.b.c`, which short-circuits + // past `.c` instead of throwing. The printer can only insert the + // `(a?.b)` wrapper when the parent's own optional_chain is None, + // which we cannot observe here, so bail out conservatively. + Data::EDot(e) => e.optional_chain.is_none(), + Data::EIndex(e) => e.optional_chain.is_none(), + Data::ECall(e) => e.optional_chain.is_none(), _ => true, } } diff --git a/src/js_parser/visit/visit_expr.rs b/src/js_parser/visit/visit_expr.rs index 0d5c834fde53..14ca13a5c4ea 100644 --- a/src/js_parser/visit/visit_expr.rs +++ b/src/js_parser/visit/visit_expr.rs @@ -1105,9 +1105,10 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O *e = p.new_expr(E::Undefined {}, inlined.loc); return; } - debug_assert!(inlined.can_be_inlined_from_property_access()); - *e = inlined; - return; + if inlined.can_be_inlined_from_property_access() { + *e = inlined; + return; + } } } } diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index d72bcc6fddcd..0cab61d38bbb 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -139,6 +139,56 @@ describe("Bun.Transpiler", () => { it("works nested", () => { ts.expectPrintedMin_('const a = ["hey"][0][0];', 'const a = "h"'); }); + it("bails out when the array item is an optional chain", () => { + // Folding `[a?.b][0]` to `a?.b` is unsafe when the result lands as the + // target of a surrounding optional-chain continuation: the two chains + // would be spliced into one. `[[a?.b]][0]?.[0].c` must not become + // `a?.b.c`, which short-circuits to `undefined` for `a == null` instead + // of throwing on the trailing `.c`. + ts.expectPrintedMin_("x = [[a?.b]][0]?.[0].c", "x = [a?.b]?.[0].c"); + ts.expectPrintedMin_("x = [[a?.b]][0]?.[0]()", "x = [a?.b]?.[0]()"); + ts.expectPrintedMin_("x = [[a?.b]][0]?.[0][c]", "x = [a?.b]?.[0][c]"); + ts.expectPrintedMin_("x = ({ f: [a?.b] }).f?.[0].c", "x = [a?.b]?.[0].c"); + ts.expectPrintedMin_("x = [[a?.[b]]][0]?.[0].c", "x = [a?.[b]]?.[0].c"); + ts.expectPrintedMin_("x = [[a?.()]][0]?.[0].c", "x = [a?.()]?.[0].c"); + + // The outer `?.` on an array literal is dropped at parse time, so these + // reach the fold with `optional_chain == None` on the index and the + // printer adds the `(a?.b)` wrapper itself. Keep bailing on the fold so + // the wrapper isn't load-bearing. + ts.expectPrintedMin_("x = [a?.b][0]", "x = [a?.b][0]"); + ts.expectPrintedMin_("x = [a?.b]?.[0]", "x = [a?.b][0]"); + ts.expectPrintedMin_("x = [a?.b][0].c", "x = [a?.b][0].c"); + ts.expectPrintedMin_("x = [a?.b]?.[0].c", "x = [a?.b][0].c"); + ts.expectPrintedMin_("x = [a?.b][0]()", "x = [a?.b][0]()"); + + // Non-chain items are still inlined. + ts.expectPrintedMin_("x = [[y]][0]?.[0].c", "x = y.c"); + ts.expectPrintedMin_("x = [a.b][0].c", "x = a.b.c"); + ts.expectPrintedMin_("x = [(a?.b)][0]", "x = [a?.b][0]"); + }); + it("preserves the TypeError when an inlined optional chain is followed by a non-optional access", async () => { + const cases = [ + "[[a?.b]][0]?.[0].c", + "[[a?.b]][0]?.[0]()", + "[[a?.b]][0]?.[0][0]", + "({ f: [a?.b] }).f?.[0].c", + "[a?.b][0].c", + "[a?.b]?.[0].c", + ]; + const src = cases + .map(e => `try { void (${e}); console.log("no throw"); } catch (e) { console.log(e.constructor.name); }`) + .join("\n"); + await using proc = Bun.spawn({ + cmd: [bunExe(), "-e", `var a = null;\n${src}`], + env: bunEnv, + stderr: "pipe", + }); + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + expect(stderr).toBe(""); + expect(stdout.trim().split("\n")).toEqual(cases.map(() => "TypeError")); + expect(exitCode).toBe(0); + }); it("bails out on optional-chain index into enum", () => { const pre = "enum Foo { A }\nenum Bar { 'a-b' = 1 }\n"; const lastLine = out => out.trimEnd().split("\n").at(-1); From a1c5e685cab32b234145477dc833352353f092ec Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 1 Aug 2026 12:41:00 +0000 Subject: [PATCH 2/6] js_parser: guard the [x][0] fold against call/assignment parent context Expand the same fold site to also respect the parent expression: - As a call target: `[obj.m][0]()` now emits `(0, obj.m)()` instead of `obj.m()`, matching the sibling comma/??/||/&&/ternary folds, so `this` inside `m` is not rebound to `obj`. - As an assignment target: `[obj.p][0] = v`, `[,][0] = v`, `"s"[n] = v` stay as written instead of folding to `obj.p = v` / `void 0 = v` / `"s" = v` (the last two are SyntaxErrors in the emitted output). - Add coverage for the Continuation (a?.b.c) and multi-item @__PURE__ removable paths that reach the second fold arm. The `!is_delete_target` check is included for completeness but is currently always false because `p.delete_target` is never set by the UnDelete handler; that is tracked separately. --- src/js_parser/visit/visit_expr.rs | 37 ++++++---- test/bundler/transpiler/transpiler.test.js | 83 ++++++++++++++++++---- 2 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/js_parser/visit/visit_expr.rs b/src/js_parser/visit/visit_expr.rs index 14ca13a5c4ea..897963556bf5 100644 --- a/src/js_parser/visit/visit_expr.rs +++ b/src/js_parser/visit/visit_expr.rs @@ -1057,7 +1057,14 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O let target = e_.target.unwrap_inlined(); let index = e_.index.unwrap_inlined(); - if p.options.features.minify_syntax { + // These folds replace an index expression (a Reference into a temporary + // string/array) with a plain value. That is only sound when the parent + // uses the value, not the Reference: `delete [x][0]` and `[x][0] = v` + // act on the temporary, and inlining would redirect them at `x`. + if p.options.features.minify_syntax + && !is_delete_target + && in_.assign_target == js_ast::AssignTarget::None + { if let Some(number) = index.data.as_e_number() { if number.value() >= 0.0 && number.value() < (usize::MAX as f64) @@ -1085,28 +1092,32 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O } } } else if let Some(array) = target.data.as_e_array() { + let int: usize = number.value() as usize; // [x][0] -> x - if array.items.len_u32() == 1 && number.value() == 0.0 { - let inlined = *array.items.at(0); - if inlined.can_be_inlined_from_property_access() { - *e = inlined; - return; - } - } - // ['a', 'b', 'c'][1] -> 'b' - let int: usize = number.value() as usize; - if int < array.items.len_u32() as usize + let inlined = if array.items.len_u32() == 1 && int == 0 { + Some(*array.items.at(0)) + } else if int < array.items.len_u32() as usize && p.expr_can_be_removed_if_unused(&target) { - let inlined = *array.items.at(int); + Some(*array.items.at(int)) + } else { + None + }; + if let Some(inlined) = inlined { // ['a', , 'c'][1] -> undefined if matches!(inlined.data, Data::EMissing(..)) { *e = p.new_expr(E::Undefined {}, inlined.loc); return; } if inlined.can_be_inlined_from_property_access() { - *e = inlined; + // "[obj.m][0]()" => "(0, obj.m)()" + *e = if is_call_target && inlined.has_value_for_this_in_call() { + p.new_expr(E::Number::new(0.0), expr.loc) + .join_with_comma(inlined) + } else { + inlined + }; return; } } diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index 0cab61d38bbb..d6d56d893ead 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -151,6 +151,14 @@ describe("Bun.Transpiler", () => { ts.expectPrintedMin_("x = ({ f: [a?.b] }).f?.[0].c", "x = [a?.b]?.[0].c"); ts.expectPrintedMin_("x = [[a?.[b]]][0]?.[0].c", "x = [a?.[b]]?.[0].c"); ts.expectPrintedMin_("x = [[a?.()]][0]?.[0].c", "x = [a?.()]?.[0].c"); + // Continuation (not Start) on the inlined item's outermost node: + ts.expectPrintedMin_("x = [[a?.b.c]][0]?.[0].d", "x = [a?.b.c]?.[0].d"); + ts.expectPrintedMin_("x = [[a?.b[c]]][0]?.[0].d", "x = [a?.b[c]]?.[0].d"); + ts.expectPrintedMin_("x = [[a?.b()]][0]?.[0].d", "x = [a?.b()]?.[0].d"); + // Multi-item path (expr_can_be_removed_if_unused): a @__PURE__ optional + // call is removable, so the second fold arm sees it. + ts.expectPrintedMin_("x = [[0, /* @__PURE__ */ a?.()]][0]?.[1].c", "x = [0, a?.()]?.[1].c"); + ts.expectPrintedMin_("x = [0, /* @__PURE__ */ a?.()][1]", "x = [0, a?.()][1]"); // The outer `?.` on an array literal is dropped at parse time, so these // reach the fold with `optional_chain == None` on the index and the @@ -162,31 +170,78 @@ describe("Bun.Transpiler", () => { ts.expectPrintedMin_("x = [a?.b]?.[0].c", "x = [a?.b][0].c"); ts.expectPrintedMin_("x = [a?.b][0]()", "x = [a?.b][0]()"); + // Same bailout protects LHS / delete / new / tagged-template positions + // from becoming `a?.b = v` / `delete a?.b` / `new a?.b()`. + ts.expectPrintedMin_("[a?.b][0] = v", "[a?.b][0] = v"); + ts.expectPrintedMin_("delete [a?.b][0]", "delete [a?.b][0]"); + ts.expectPrintedMin_("x = new [a?.b][0]()", "x = new [a?.b][0]"); + ts.expectPrintedMin_("[a?.b][0]`x`", "[a?.b][0]`x`"); + // Non-chain items are still inlined. ts.expectPrintedMin_("x = [[y]][0]?.[0].c", "x = y.c"); ts.expectPrintedMin_("x = [a.b][0].c", "x = a.b.c"); ts.expectPrintedMin_("x = [(a?.b)][0]", "x = [a?.b][0]"); }); - it("preserves the TypeError when an inlined optional chain is followed by a non-optional access", async () => { - const cases = [ - "[[a?.b]][0]?.[0].c", - "[[a?.b]][0]?.[0]()", - "[[a?.b]][0]?.[0][0]", - "({ f: [a?.b] }).f?.[0].c", - "[a?.b][0].c", - "[a?.b]?.[0].c", - ]; - const src = cases - .map(e => `try { void (${e}); console.log("no throw"); } catch (e) { console.log(e.constructor.name); }`) - .join("\n"); + it("bails out or strips `this` when the index is a call/assignment target", () => { + // `[obj.m][0]()` calls through a Reference into the temporary array, + // so `this` is the array; inlining to `obj.m()` would bind `this` to + // `obj`. Match the sibling folds and emit `(0, obj.m)()`. + ts.expectPrintedMin_("x = [obj.m][0]()", "x = (0, obj.m)()"); + ts.expectPrintedMin_("x = [obj[m]][0]()", "x = (0, obj[m])()"); + ts.expectPrintedMin_("x = [obj.m][0]", "x = obj.m"); + ts.expectPrintedMin_("x = [y][0]()", "x = y()"); + ts.expectPrintedMin_("x = [() => y][0]()", "x = (() => y)()"); + + // `[x][0] = v` writes into the temporary, not `x`. Same for `"s"[n]`. + ts.expectPrintedMin_("[obj.p][0] = 5", "[obj.p][0] = 5"); + ts.expectPrintedMin_("[obj.p][0] += 5", "[obj.p][0] += 5"); + ts.expectPrintedMin_("[obj.p][0]++", "[obj.p][0]++"); + ts.expectPrintedMin_("[x][0] = 1", "[x][0] = 1"); + ts.expectPrintedMin_("[,][0] = 1", "[,][0] = 1"); + ts.expectPrintedMin_('"foo"[2] = 1', '"foo"[2] = 1'); + ts.expectPrintedMin_('["a", "b"][1] = 1', '["a", "b"][1] = 1'); + ts.expectPrintedMin_("({ a: [obj.p][0] } = {})", "({ a: [obj.p][0] } = {})"); + }); + it("preserves runtime semantics when inlining from a literal index", async () => { + const src = ` + var a = null; + function check(label, fn, expected) { + var got; + try { got = "=> " + fn(); } catch (e) { got = e.constructor.name; } + console.log(label + ": " + (got === expected ? "ok" : got + " (want " + expected + ")")); + } + check("chain .c", () => [[a?.b]][0]?.[0].c, "TypeError"); + check("chain ()", () => [[a?.b]][0]?.[0](), "TypeError"); + check("chain [0]", () => [[a?.b]][0]?.[0][0], "TypeError"); + check("chain obj", () => ({ f: [a?.b] }).f?.[0].c, "TypeError"); + check("chain flat", () => [a?.b][0].c, "TypeError"); + check("chain ?.[", () => [a?.b]?.[0].c, "TypeError"); + check("chain cont", () => [[a?.b.c]][0]?.[0].d, "TypeError"); + check("chain pure", () => [[0, /* @__PURE__ */ a?.()]][0]?.[1].c, "TypeError"); + var obj = { n: "obj", m() { return this === obj; } }; + check("this", () => [obj.m][0](), "=> false"); + var o2 = { p: 1 }; + check("assign", () => ([o2.p][0] = 5, o2.p), "=> 1"); + `; await using proc = Bun.spawn({ - cmd: [bunExe(), "-e", `var a = null;\n${src}`], + cmd: [bunExe(), "-e", src], env: bunEnv, stderr: "pipe", }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect(stderr).toBe(""); - expect(stdout.trim().split("\n")).toEqual(cases.map(() => "TypeError")); + expect(stdout.trim().split("\n")).toEqual([ + "chain .c: ok", + "chain (): ok", + "chain [0]: ok", + "chain obj: ok", + "chain flat: ok", + "chain ?.[: ok", + "chain cont: ok", + "chain pure: ok", + "this: ok", + "assign: ok", + ]); expect(exitCode).toBe(0); }); it("bails out on optional-chain index into enum", () => { From 8786d337f7a9cfb0dfa43a0c20c13db026f89a15 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 1 Aug 2026 12:43:21 +0000 Subject: [PATCH 3/6] Shorten code comments --- src/ast/expr.rs | 8 ++------ src/js_parser/visit/visit_expr.rs | 5 +---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 38f43a5ad3ff..6c679c67d7ae 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -56,12 +56,8 @@ impl Expr { // https://github.com/oven-sh/bun/issues/2594 Data::ESpread(_) => false, Data::EMissing(_) => false, - // Inlining `a?.b` as the target of a surrounding member/call - // expression can splice two unrelated optional chains together: - // `[[a?.b]][0]?.[0].c` would become `a?.b.c`, which short-circuits - // past `.c` instead of throwing. The printer can only insert the - // `(a?.b)` wrapper when the parent's own optional_chain is None, - // which we cannot observe here, so bail out conservatively. + // `[[a?.b]][0]?.[0].c` must not become `a?.b.c`: inlining would + // splice this chain onto the parent's `?.` continuation. Data::EDot(e) => e.optional_chain.is_none(), Data::EIndex(e) => e.optional_chain.is_none(), Data::ECall(e) => e.optional_chain.is_none(), diff --git a/src/js_parser/visit/visit_expr.rs b/src/js_parser/visit/visit_expr.rs index 897963556bf5..3de727262e08 100644 --- a/src/js_parser/visit/visit_expr.rs +++ b/src/js_parser/visit/visit_expr.rs @@ -1057,10 +1057,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O let target = e_.target.unwrap_inlined(); let index = e_.index.unwrap_inlined(); - // These folds replace an index expression (a Reference into a temporary - // string/array) with a plain value. That is only sound when the parent - // uses the value, not the Reference: `delete [x][0]` and `[x][0] = v` - // act on the temporary, and inlining would redirect them at `x`. + // `delete [x][0]` and `[x][0] = v` act on the temporary, not on `x`. if p.options.features.minify_syntax && !is_delete_target && in_.assign_target == js_ast::AssignTarget::None From 62cf6b398fd330075032bb1502a6e5d15c62eb8f Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 1 Aug 2026 13:22:05 +0000 Subject: [PATCH 4/6] Apply can_be_inlined_from_property_access to the {f:x}.f fold; drop dead is_delete_target guard The object-literal fold in maybe_rewrite_property_access returned the value without consulting can_be_inlined_from_property_access, so `new ({f: a?.b}).f()` minified to `new a?.b` (a hard SyntaxError) and `({f: a?.b}).f`x`` minified to `a?.b`x``. Reuse the same predicate there. Drop the `!is_delete_target` clause from the array-fold guard: `p.delete_target` is never set by the UnDelete arm so the clause was dead, and the comment claimed `delete [x][0]` was protected when it is not. That wiring is tracked separately. --- src/js_parser/fold.rs | 1 + src/js_parser/visit/visit_expr.rs | 3 +-- test/bundler/transpiler/transpiler.test.js | 13 +++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/js_parser/fold.rs b/src/js_parser/fold.rs index 085318aaa987..911e1e76de30 100644 --- a/src/js_parser/fold.rs +++ b/src/js_parser/fold.rs @@ -483,6 +483,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O name, ) && name != b"__proto__" + && value.can_be_inlined_from_property_access() { return Some(value); } diff --git a/src/js_parser/visit/visit_expr.rs b/src/js_parser/visit/visit_expr.rs index 3de727262e08..0c8abb8f4d8f 100644 --- a/src/js_parser/visit/visit_expr.rs +++ b/src/js_parser/visit/visit_expr.rs @@ -1057,9 +1057,8 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O let target = e_.target.unwrap_inlined(); let index = e_.index.unwrap_inlined(); - // `delete [x][0]` and `[x][0] = v` act on the temporary, not on `x`. + // `[x][0] = v` writes into the temporary, not `x`. if p.options.features.minify_syntax - && !is_delete_target && in_.assign_target == js_ast::AssignTarget::None { if let Some(number) = index.data.as_e_number() { diff --git a/test/bundler/transpiler/transpiler.test.js b/test/bundler/transpiler/transpiler.test.js index d6d56d893ead..f19b5896a37d 100644 --- a/test/bundler/transpiler/transpiler.test.js +++ b/test/bundler/transpiler/transpiler.test.js @@ -177,10 +177,20 @@ describe("Bun.Transpiler", () => { ts.expectPrintedMin_("x = new [a?.b][0]()", "x = new [a?.b][0]"); ts.expectPrintedMin_("[a?.b][0]`x`", "[a?.b][0]`x`"); + // Same predicate on the sibling `{f: x}.f -> x` fold: `new a?.b` / + // `a?.b`x`` are syntax errors, so bail there too. + ts.expectPrintedMin_("x = new ({f: a?.b}).f()", "x = new { f: a?.b }.f"); + ts.expectPrintedMin_("x = new ({f: a?.[b]}).f()", "x = new { f: a?.[b] }.f"); + ts.expectPrintedMin_("x = new ({f: a?.b.c}).f()", "x = new { f: a?.b.c }.f"); + ts.expectPrintedMin_("({f: a?.b}).f`x`", "({ f: a?.b }).f`x`"); + ts.expectPrintedMin_("x = ({f: a?.b}).f", "x = { f: a?.b }.f"); + // Non-chain items are still inlined. ts.expectPrintedMin_("x = [[y]][0]?.[0].c", "x = y.c"); ts.expectPrintedMin_("x = [a.b][0].c", "x = a.b.c"); ts.expectPrintedMin_("x = [(a?.b)][0]", "x = [a?.b][0]"); + ts.expectPrintedMin_("x = ({f: y}).f", "x = y"); + ts.expectPrintedMin_("x = new ({f: C}).f()", "x = new C"); }); it("bails out or strips `this` when the index is a call/assignment target", () => { // `[obj.m][0]()` calls through a Reference into the temporary array, @@ -222,6 +232,8 @@ describe("Bun.Transpiler", () => { check("this", () => [obj.m][0](), "=> false"); var o2 = { p: 1 }; check("assign", () => ([o2.p][0] = 5, o2.p), "=> 1"); + var ab = { b: class {} }; + check("new obj", () => new ({ f: ab?.b }).f() instanceof ab.b, "=> true"); `; await using proc = Bun.spawn({ cmd: [bunExe(), "-e", src], @@ -241,6 +253,7 @@ describe("Bun.Transpiler", () => { "chain pure: ok", "this: ok", "assign: ok", + "new obj: ok", ]); expect(exitCode).toBe(0); }); From 9e1ad644019e37feb2f6c58292f09855c1198680 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 1 Aug 2026 13:24:19 +0000 Subject: [PATCH 5/6] [autofix.ci] apply automated fixes --- src/js_parser/visit/visit_expr.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/js_parser/visit/visit_expr.rs b/src/js_parser/visit/visit_expr.rs index 0c8abb8f4d8f..e6db88d251a3 100644 --- a/src/js_parser/visit/visit_expr.rs +++ b/src/js_parser/visit/visit_expr.rs @@ -1058,9 +1058,7 @@ impl<'a, const TYPESCRIPT: bool, const SCAN_ONLY: bool> P<'a, TYPESCRIPT, SCAN_O let index = e_.index.unwrap_inlined(); // `[x][0] = v` writes into the temporary, not `x`. - if p.options.features.minify_syntax - && in_.assign_target == js_ast::AssignTarget::None - { + if p.options.features.minify_syntax && in_.assign_target == js_ast::AssignTarget::None { if let Some(number) = index.data.as_e_number() { if number.value() >= 0.0 && number.value() < (usize::MAX as f64) From aa68cffb95ebeb9aba9d034b396dcaa45b875bc2 Mon Sep 17 00:00:00 2001 From: robobun <117481402+robobun@users.noreply.github.com> Date: Sat, 1 Aug 2026 15:30:44 +0000 Subject: [PATCH 6/6] ci: retrigger