fix(transformer_plugins): unwrap ChainExpression after define replacement removes optional markers#20058
Merged
Dunqing merged 8 commits intooxc-project:mainfrom Mar 9, 2026
Conversation
replacement removes optional markers
Contributor
Author
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a panic in the transformer pipeline when ReplaceGlobalDefines rewrites an optional chain in a way that leaves a ChainExpression wrapper without any remaining optional markers (notably in playground-like “define first, then lower” flows).
Changes:
- Unwrap
Expression::ChainExpressionnodes after define-walking when the chain no longer contains anyoptional: truemarkers. - Add an integration test that reproduces the define-then-transform optional-chaining lowering scenario (target < ES2020).
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| crates/oxc_transformer_plugins/src/replace_global_defines.rs | Adds post-walk normalization to unwrap “empty” ChainExpressions and helper logic to detect remaining optional markers. |
| crates/oxc_transformer_plugins/tests/integrations/replace_global_defines.rs | Adds an integration test that runs defines before transformer lowering to ensure the prior panic path is covered. |
crates/oxc_transformer_plugins/tests/integrations/replace_global_defines.rs
Outdated
Show resolved
Hide resolved
Dunqing
approved these changes
Mar 9, 2026
camc314
pushed a commit
that referenced
this pull request
Mar 9, 2026
### 🚀 Features - e8547cc parser: Report error for using declarations in ambient contexts (#19934) (camc314) - 8345318 allocator: Add methods for boxed slices `ArenaBox<[T]>` (#19968) (overlookmotel) - f83be30 allocator: Add `Vec::push_fast` method (#19959) (overlookmotel) ### 🐛 Bug Fixes - 291d867 transformer_plugins: Unwrap ChainExpression after define replacement removes optional markers (#20058) (IWANABETHATGUY) - 36b2e56 codegen: Print type for TSImportEqualsDeclaration (#20128) (camc314) - 5a246ec codegen: Print type arguments for JSXOpeningElement (#20127) (camc314) - a40870e codegen: Preserve parens for TSNonNullExpression (#20125) (camc314) - ae830b2 codegen: Print `declare` for `TSInterfaceDeclaration` (#20124) (camc314) - 92cfb14 linter/plugins: Fix types for `walkProgram` and `walkProgramWithCfg` (#20081) (overlookmotel) - ee0491e apps,napi: Explicitly specify libs in tsconfigs (#20071) (camc314) - 588009e codegen: Print `static` keyword for TSIndexSignature (#19755) (Dunqing) - 5a8799c codegen: Print `with_clause` for `ExportNamedDeclaration` (#20002) (Dunqing) - 7502afe parser: Correct capacity for tokens `Vec` (#19967) (overlookmotel) ### ⚡ Performance - 4ea8f9a napi: Remove `napi_build::setup()` from `oxc_napi` to avoid redundant rebuilds (#20094) (Boshen) - 2baa5fb napi: Unify build-test profile to coverage for cache sharing (#20090) (Boshen) - 8ba61dd parser: Make pushing tokens faster (#19960) (overlookmotel) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ReplaceGlobalDefinesremoves alloptionalmarkers from aChainExpression, causing the optional chaining lowering transformer to hitunreachable!()process?.env[0]withprocess.env -> {}Root Cause
When
ReplaceGlobalDefinesreplaces a member expression inside aChainExpression(e.g.,process?.env[0]with defineprocess.env -> {}), the innerStaticMemberExpressionthat carriedoptional: trueis replaced by{}. This leaves aChainExpressionwrapper with no optional elements. When the optional chaining transformer later tries to lower this (target < ES2020), it panics attransform_chain_expression_implincrates/oxc_transformer/src/es2020/optional_chaining.rsbecause it expects at least one optional element in the chain.This affects the playground pipeline where define runs before the transformer.
Fix
After
walk_expressioninvisit_expression, check if the current expression is aChainExpressionwith no remainingoptionalmarkers. If so, unwrap it to a plain expression (e.g.,ChainExpression(ComputedMemberExpression(...))becomesComputedMemberExpression(...)), producing a valid AST.Test plan
Added integration test
define_then_transform_optional_chainthat reproduces the exact panic (define first, then transformer with ES2019 target)cargo test -p oxc_transformer_plugins— all 42 integration tests passjust lintpasses clean