Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 234 additions & 2 deletions turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,12 @@
match self {
ConditionalKind::If { then: block }
| ConditionalKind::Else { r#else: block }
| ConditionalKind::Labeled { body: block } => block.normalize(),
| ConditionalKind::And { expr: block, .. }

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rust check / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / stable - x86_64-unknown-linux-gnu - node@16

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rust check / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo benches / Test

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo benches / Test

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo unit / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo unit / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native-windows / build

variant `ConditionalKind::And` does not have a field named `expr`

Check failure on line 93 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native-windows / build

variant `ConditionalKind::And` does not have a field named `expr`
| ConditionalKind::Or { expr: block, .. }

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rust check / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / stable - x86_64-unknown-linux-gnu - node@16

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rust check / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo benches / Test

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo benches / Test

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo unit / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo unit / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native-windows / build

variant `ConditionalKind::Or` does not have a field named `expr`

Check failure on line 94 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native-windows / build

variant `ConditionalKind::Or` does not have a field named `expr`
| ConditionalKind::NullishCoalescing { expr: block, .. }

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rust check / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / stable - x86_64-unknown-linux-gnu - node@16

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rust check / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / rustdoc check / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo benches / Test

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo benches / Test

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo unit / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / test cargo unit / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native-windows / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`

Check failure on line 95 in turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs

View workflow job for this annotation

GitHub Actions / build-native-windows / build

variant `ConditionalKind::NullishCoalescing` does not have a field named `expr`
Comment on lines +93 to +95
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| ConditionalKind::And { expr: block, .. }
| ConditionalKind::Or { expr: block, .. }
| ConditionalKind::NullishCoalescing { expr: block, .. }

The code attempts to destructure non-existent fields in enum variants, which will cause a compilation error. Lines 93-95 try to access an expr field that doesn't exist on ConditionalKind::And, ConditionalKind::Or, and ConditionalKind::NullishCoalescing.

View Details

Analysis

Incorrect enum field destructuring in ConditionalKind::normalize()

What fails: The normalize() method in ConditionalKind impl (lines 90-117) attempts to destructure an expr field that doesn't exist on And, Or, and NullishCoalescing enum variants, causing a compilation error.

How to reproduce:

# From turbopack root
cargo check -p turbopack-ecmascript

Expected to fail with error:

error[E0026]: variant `And` does not have a field named `expr`

What was wrong vs expected:

The first match arm (lines 93-95 in original code) tried to match:

ConditionalKind::And { expr: block, .. }
| ConditionalKind::Or { expr: block, .. }
| ConditionalKind::NullishCoalescing { expr: block, .. }

But the enum variants are defined with rhs_effects field only (lines 78-82):

And { rhs_effects: Vec<Effect> },
Or { rhs_effects: Vec<Effect> },
NullishCoalescing { rhs_effects: Vec<Effect> },

Additionally, these three variants were already correctly handled in the second match arm (lines 104-106) with the correct field name rhs_effects.

Fix: Removed the three incorrect pattern arms from the first match arm, allowing them to be handled exclusively by the second match arm with the correct field destructuring.

Verification: After the fix, all 9 enum variants are correctly matched:

  • First arm: If, Else, Labeled (using then/r#else/body fields)
  • Second arm: IfElse, Ternary (using then/r#else fields)
  • Third arm: And, Or, NullishCoalescing (using rhs_effects field)
  • Fourth arm: IfElseMultiple (using then/r#else fields)

Reference: Rust pattern matching on struct fields

| ConditionalKind::Labeled { body: block } => {
block.normalize();
}
ConditionalKind::IfElse { then, r#else, .. }
| ConditionalKind::Ternary { then, r#else, .. } => {
then.normalize();
Expand All @@ -105,7 +110,7 @@
}
ConditionalKind::IfElseMultiple { then, r#else, .. } => {
for block in then.iter_mut().chain(r#else.iter_mut()) {
block.normalize();
block.normalize()
}
}
}
Expand Down Expand Up @@ -171,6 +176,12 @@
ast_path: Vec<AstParentKind>,
span: Span,
},
/// The receiver of a null safe expression
NullSafeAccessReceiver {
obj: Box<JsValue>,
ast_path: Vec<AstParentKind>,
effects: Box<EffectsBlock>,
},
/// A reference to an imported binding.
ImportedBinding {
esm_reference_index: usize,
Expand Down Expand Up @@ -229,6 +240,10 @@
obj.normalize();
prop.normalize();
}
Effect::NullSafeAccessReceiver { obj, effects, .. } => {
obj.normalize();
effects.normalize();
}
Effect::ImportedBinding { .. } => {}
Effect::TypeOf { arg, .. } => {
arg.normalize();
Expand Down Expand Up @@ -800,6 +815,87 @@
_ => JsValue::unknown_empty(true, "compound assignment expression"),
},

Expr::OptChain(OptChainExpr { base, optional, .. }) => {
// Optional chaining: obj?.prop or obj?.method()
// If optional is true and receiver is null/undefined, return undefined
// Otherwise, evaluate normally
match &**base {
OptChainBase::Member(MemberExpr { obj, prop, .. }) => {
let obj_value = self.eval(obj);

// If this is an optional access and obj is null/undefined, return undefined
if *optional {
if let JsValue::Constant(ConstantValue::Null) = obj_value {
return JsValue::Constant(ConstantValue::Undefined);
}
// Check if it's undefined
if matches!(obj_value, JsValue::Constant(ConstantValue::Undefined)) {
return JsValue::Constant(ConstantValue::Undefined);
}
}

// Otherwise, evaluate the member access
let prop_value = match prop {
MemberProp::Ident(i) => i.sym.clone().into(),
MemberProp::PrivateName(_) => {
return JsValue::unknown_empty(
false,
"private names in optional chaining are not supported",
);
}
MemberProp::Computed(ComputedPropName { expr, .. }) => self.eval(expr),
};

JsValue::member(Box::new(obj_value), Box::new(prop_value))
}
OptChainBase::Call(OptCall { callee, args, .. }) => {
let callee_value = self.eval(callee);

// If this is an optional call and callee is null/undefined, return
// undefined
if *optional {
if let JsValue::Constant(ConstantValue::Null) = callee_value {
return JsValue::Constant(ConstantValue::Undefined);
}
if matches!(callee_value, JsValue::Constant(ConstantValue::Undefined)) {
return JsValue::Constant(ConstantValue::Undefined);
}
}

// We currently do not handle spreads.
if args.iter().any(|arg| arg.spread.is_some()) {
return JsValue::unknown_empty(
true,
"spread in optional chaining calls is not supported",
);
}

// Evaluate the call
let args_values = args.iter().map(|arg| self.eval(&arg.expr)).collect();

if let Expr::Member(MemberExpr { obj, prop, .. }) = unparen(callee) {
let obj = Box::new(self.eval(obj));
let prop = Box::new(match prop {
MemberProp::Ident(i) => i.sym.clone().into(),
MemberProp::PrivateName(_) => {
return JsValue::unknown_empty(
false,
"private names in optional chaining are not supported",
);
}
MemberProp::Computed(ComputedPropName { expr, .. }) => {
self.eval(expr)
}
});
JsValue::member_call(obj, prop, args_values)
} else {
let func = Box::new(callee_value);
JsValue::call(func, args_values)
}
}
}
}

_ => JsValue::unknown_empty(true, "unsupported expression"),
}
}
Expand Down Expand Up @@ -1700,6 +1796,142 @@
member_expr.visit_children_with_ast_path(self, ast_path);
}

fn visit_opt_chain_expr<'ast: 'r, 'r>(
&mut self,
node: &'ast OptChainExpr,
ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>,
) {
// Optional chaining has control flow semantics:
// a?.b means: if a is null/undefined, return undefined, else access a.b
//
// The key insight from the AST structure:
// - OptChainExpr.optional indicates if THIS level has ?.
// - OptChainExpr.base contains either a MemberExpr or OptCall
// - The object/callee of that base may itself be an OptChainExpr (for nested ?.)
//
// We handle this by:
// 1. Visit the receiver (object/callee) normally - this may recursively hit another
// OptChainExpr
// 2. If this level is optional, collect effects from the access/call and wrap in
// NullSafeAccessReceiver
// 3. Otherwise, just visit normally

if !node.optional {
// No optional operator at this level, just visit children normally
// The visitor will handle member accesses and calls appropriately
node.visit_children_with_ast_path(self, ast_path);
return;
}

// This level has an optional operator (?.)
match &*node.base {
OptChainBase::Member(member) => {
// Visit the object first (may be another opt chain)
member.obj.visit_with_ast_path(self, ast_path);

// Get the receiver value for the null check
let receiver_value = Box::new(self.eval_context.eval(&member.obj));

// Collect effects from the member access
let prev_effects = take(&mut self.effects);

// Use the existing helper to add member effect
self.check_member_expr_for_effects(member, ast_path);

let optional_effects = Box::new(EffectsBlock {
effects: take(&mut self.effects),
range: AstPathRange::Exact(as_parent_path(ast_path)),
});

self.effects = prev_effects;

// Add the NullSafeAccessReceiver effect
self.add_effect(Effect::NullSafeAccessReceiver {
obj: receiver_value,
ast_path: as_parent_path(ast_path),
effects: optional_effects,
});
Comment on lines +1828 to +1853
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Computed property expressions in optional member chains are not being visited, which means side effects from computed properties (like function calls) would be lost during analysis.

View Details
📝 Patch Details
diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs
index 57cc0de40d..cbbb3fb515 100644
--- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs
+++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs
@@ -1822,6 +1822,11 @@ impl VisitAstPath for Analyzer<'_> {
                 // Visit the object first (may be another opt chain)
                 member.obj.visit_with_ast_path(self, ast_path);
 
+                // Visit computed property expressions to capture any side effects
+                if let MemberProp::Computed(ComputedPropName { expr, .. }) = &member.prop {
+                    expr.visit_with_ast_path(self, ast_path);
+                }
+
                 // Get the receiver value for the null check
                 let receiver_value = Box::new(self.eval_context.eval(&member.obj));
 

Analysis

Computed property expressions in optional member chains are not being visited for side effects

What fails: Side effects from computed property expressions in optional member chains (e.g., obj?.[getKey()]) are not collected during analysis, meaning function calls and other effects within the computed property are not tracked.

How to reproduce: Create an optional chain with a computed property containing a function call:

let result = obj?.[getKey()];

The analysis should collect an effect for the getKey() call, but it does not because the computed property expression is never visited through the visitor pattern.

Expected behavior: The computed property expression should be visited to capture any side effects (like function calls), similar to how the Call case properly visits the entire callee expression. This ensures all side effects are tracked in the optional chain's NullSafeAccessReceiver effect block.

Root cause: In visit_opt_chain_expr, when handling OptChainBase::Member, the code visits only the object expression via member.obj.visit_with_ast_path() but never visits member.prop when it's a Computed variant. In contrast, the Call case correctly visits the entire callee expression, which properly handles computed property expressions through the normal visit_member_expr flow.

Fix: Added code to explicitly visit computed property expressions in the Member case, ensuring side effects are captured before effects collection:

if let MemberProp::Computed(ComputedPropName { expr, .. }) = &member.prop {
    expr.visit_with_ast_path(self, ast_path);
}

This aligns the Member case behavior with the Call case and ensures consistency with the previous default visitor behavior before the recent visit_opt_chain_expr implementation.

}
OptChainBase::Call(call) => {
// Visit the callee first (may be another opt chain)
call.callee.visit_with_ast_path(self, ast_path);

// Visit arguments
for arg in call.args.iter() {
arg.visit_with_ast_path(self, ast_path);
}

// Get the receiver value for the null check
let receiver_value = Box::new(self.eval_context.eval(&call.callee));

// Collect effects from the call
let prev_effects = take(&mut self.effects);

// Add call effect (reuse logic from handle_call)
let args: Vec<EffectArg> = call
.args
.iter()
.map(|arg| {
if arg.spread.is_some() {
EffectArg::Spread
} else {
EffectArg::Value(self.eval_context.eval(&arg.expr))
}
})
.collect();

if let Expr::Member(member) = unparen(&call.callee) {
let obj = Box::new(self.eval_context.eval(&member.obj));
let prop = Box::new(match &member.prop {
MemberProp::Ident(i) => i.sym.clone().into(),
MemberProp::PrivateName(_) => JsValue::unknown_empty(
false,
"private names in member expressions are not supported",
),
MemberProp::Computed(ComputedPropName { expr, .. }) => {
self.eval_context.eval(expr)
}
});

self.add_effect(Effect::MemberCall {
obj,
prop,
args,
ast_path: as_parent_path(ast_path),
span: call.span,
in_try: is_in_try(ast_path),
new: false,
});
} else {
let func = Box::new(self.eval_context.eval(&call.callee));

self.add_effect(Effect::Call {
func,
args,
ast_path: as_parent_path(ast_path),
span: call.span,
in_try: is_in_try(ast_path),
new: false,
});
}

let optional_effects = Box::new(EffectsBlock {
effects: take(&mut self.effects),
range: AstPathRange::Exact(as_parent_path(ast_path)),
});

self.effects = prev_effects;

// Add the NullSafeAccessReceiver effect
self.add_effect(Effect::NullSafeAccessReceiver {
obj: receiver_value,
ast_path: as_parent_path(ast_path),
effects: optional_effects,
});
}
}
}

fn visit_expr<'ast: 'r, 'r>(
&mut self,
n: &'ast Expr,
Expand Down
13 changes: 13 additions & 0 deletions turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4215,6 +4215,19 @@ mod tests {
));
obj_steps + prop_steps
}
Effect::NullSafeAccessReceiver { obj, effects, .. } => {
let (obj, obj_steps) = resolve(
&var_graph,
*obj,
ImportAttributes::empty_ref(),
&var_cache,
)
.await;

resolved.push((format!("{parent} -> {i} null safe receiver"), obj));
queue.extend(effects.effects.into_iter().rev().map(|e| (i, e)));
obj_steps
}
Effect::Unreachable { .. } => {
resolved.push((
format!("{parent} -> {i} unreachable"),
Expand Down
26 changes: 25 additions & 1 deletion turbopack/crates/turbopack-ecmascript/src/references/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ impl AnalyzeEcmascriptModuleResultBuilder {
self.async_module = ResolvedVc::cell(Some(async_module));
}

/// Set whether this module is side-efffect free according to a user-provided directive.
/// Set whether this module is side-effect free according to a user-provided directive.
pub fn set_has_side_effect_free_directive(&mut self, value: bool) {
self.has_side_effect_free_directive = value;
}
Expand Down Expand Up @@ -1373,6 +1373,30 @@ async fn analyze_ecmascript_module_internal(
handle_member(&ast_path, obj, prop, span, &analysis_state, &mut analysis)
.await?;
}
Effect::NullSafeAccessReceiver {
obj,
ast_path,
effects,
} => {
// Intentionally not awaited because `handle_member` reads this only when needed
let obj = analysis_state
.link_value(*obj, ImportAttributes::empty_ref())
.await?;
if matches!(obj.is_nullish(), Some(true)) {
// Replace the receiver expression but don't bother simplifying the chain,
// SWC can do that as part of minification.
if analyze_mode.is_code_gen() && !obj.has_side_effects() {
analysis.add_code_gen(ConstantConditionCodeGen::new(
ConstantConditionValue::Nullish,
ast_path.to_vec().into(),
));
}
} else {
queue_stack
.get_mut()
.extend(effects.effects.into_iter().map(Action::Effect).rev())
}
}
Effect::ImportedBinding {
esm_reference_index,
export,
Expand Down
Loading
Loading