diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs index 19d819b99ad378..24d88b811353c2 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs @@ -90,7 +90,12 @@ impl ConditionalKind { match self { ConditionalKind::If { then: block } | ConditionalKind::Else { r#else: block } - | ConditionalKind::Labeled { body: block } => block.normalize(), + | ConditionalKind::And { expr: block, .. } + | ConditionalKind::Or { expr: block, .. } + | ConditionalKind::NullishCoalescing { expr: block, .. } + | ConditionalKind::Labeled { body: block } => { + block.normalize(); + } ConditionalKind::IfElse { then, r#else, .. } | ConditionalKind::Ternary { then, r#else, .. } => { then.normalize(); @@ -105,7 +110,7 @@ impl ConditionalKind { } ConditionalKind::IfElseMultiple { then, r#else, .. } => { for block in then.iter_mut().chain(r#else.iter_mut()) { - block.normalize(); + block.normalize() } } } @@ -171,6 +176,12 @@ pub enum Effect { ast_path: Vec, span: Span, }, + /// The receiver of a null safe expression + NullSafeAccessReceiver { + obj: Box, + ast_path: Vec, + effects: Box, + }, /// A reference to an imported binding. ImportedBinding { esm_reference_index: usize, @@ -229,6 +240,10 @@ impl Effect { obj.normalize(); prop.normalize(); } + Effect::NullSafeAccessReceiver { obj, effects, .. } => { + obj.normalize(); + effects.normalize(); + } Effect::ImportedBinding { .. } => {} Effect::TypeOf { arg, .. } => { arg.normalize(); @@ -800,6 +815,87 @@ impl EvalContext { _ => 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"), } } @@ -1700,6 +1796,142 @@ impl VisitAstPath for Analyzer<'_> { 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, + }); + } + 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 = 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, diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs index 83d477bde6e0fb..91587d82ba7331 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs @@ -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"), diff --git a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs index 461a0fe4ddf334..a193b55eec718e 100644 --- a/turbopack/crates/turbopack-ecmascript/src/references/mod.rs +++ b/turbopack/crates/turbopack-ecmascript/src/references/mod.rs @@ -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; } @@ -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, diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-effects.snapshot index f8835069973cbb..285fa190c84889 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-effects.snapshot @@ -1601,4 +1601,2787 @@ ], span: 378..393, }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 15, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 434..437, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 15, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 15, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 434..443, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 15, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 16, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 456..459, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 16, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: Constant( + Str( + Atom( + "method", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 16, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + span: 456..467, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 16, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + Member { + obj: Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + prop: Constant( + Str( + Atom( + "nested", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 17, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + ], + span: 482..498, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 17, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 482..485, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 17, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 17, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + ], + span: 482..491, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 17, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 511..514, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + span: 511..520, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + prop: Constant( + Str( + Atom( + "nested", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 511..528, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 18, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 541..544, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: Constant( + Str( + Atom( + "method", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + span: 541..552, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Call( + 4, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Call( + 4, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + prop: Constant( + Str( + Atom( + "result", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 541..562, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 19, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + Member { + obj: Call( + 6, + Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + prop: Constant( + Str( + Atom( + "result", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + ], + span: 575..603, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 575..578, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + span: 575..584, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + prop: Constant( + Str( + Atom( + "method", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + span: 575..592, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Call { + func: Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + args: [], + ast_path: [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + ], + span: 575..596, + in_try: false, + new: false, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 20, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Member, + ), + MemberExpr( + Obj, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 21, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 616..619, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 21, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: FreeVar( + "computed", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 21, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 616..631, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 21, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + FreeVar { + var: "obj", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + Expr( + Ident, + ), + ], + span: 644..647, + }, + NullSafeAccessReceiver { + obj: FreeVar( + "obj", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: FreeVar( + "obj", + ), + prop: FreeVar( + "computed", + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + span: 644..659, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Member( + 3, + FreeVar( + "obj", + ), + FreeVar( + "computed", + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Member( + 3, + FreeVar( + "obj", + ), + FreeVar( + "computed", + ), + ), + prop: Constant( + Str( + Atom( + "nested", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 644..667, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 22, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Variable( + ( + "nullValue", + #2, + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 25, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Variable( + ( + "nullValue", + #2, + ), + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 25, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 789..804, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 25, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Variable( + ( + "undefinedValue", + #2, + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 26, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Variable( + ( + "undefinedValue", + #2, + ), + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 26, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 818..838, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 26, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Variable( + ( + "nullValue", + #2, + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 27, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Variable( + ( + "nullValue", + #2, + ), + ), + prop: Constant( + Str( + Atom( + "method", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 27, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + span: 852..869, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 27, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + OptChainExpr( + Base, + ), + OptChainBase( + Call, + ), + OptCall( + Callee, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Constant( + Null, + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 28, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Constant( + Null, + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 28, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 885..895, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 28, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, + NullSafeAccessReceiver { + obj: Constant( + Undefined, + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 29, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + effects: EffectsBlock { + effects: [ + Member { + obj: Constant( + Undefined, + ), + prop: Constant( + Str( + Atom( + "prop", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 29, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + span: 909..924, + }, + ], + range: Exact( + [ + Program( + Script, + ), + Script( + Body( + 29, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + OptChain, + ), + ], + ), + }, + }, ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-explained.snapshot index fd26f10b109e56..7c66c1dc7e8b20 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph-explained.snapshot @@ -12,6 +12,34 @@ d (const after eval) = !(x) e (const after eval) = !(!(x)) +nullValue (const after eval) = null + +opt1 (const after eval) = FreeVar(obj)["prop"] + +opt10 (const after eval) = undefinedValue["prop"] + +opt11 (const after eval) = nullValue["method"]() + +opt12 (const after eval) = undefined + +opt13 (const after eval) = undefined + +opt2 (const after eval) = FreeVar(obj)["method"]() + +opt3 (const after eval) = FreeVar(obj)["prop"]["nested"] + +opt4 (const after eval) = FreeVar(obj)["prop"]["nested"] + +opt5 (const after eval) = FreeVar(obj)["method"]()["result"] + +opt6 (const after eval) = FreeVar(obj)["prop"]["method"]()["result"] + +opt7 (const after eval) = FreeVar(obj)[FreeVar(computed)] + +opt8 (const after eval) = FreeVar(obj)[FreeVar(computed)]["nested"] + +opt9 (const after eval) = nullValue["prop"] + resolve1 (const after eval) = (1 && 2 && FreeVar(global) && 3 && 4) resolve2 (const after eval) = (1 && 2 && 0 && FreeVar(global) && 4) @@ -24,6 +52,8 @@ resolve5 (const after eval) = (FreeVar(global) && false) resolve6 (const after eval) = (false && FreeVar(global)) +undefinedValue (const after eval) = undefined + x (const after eval) = true y (const after eval) = false diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph.snapshot index 6efb464593823e..d79ccbdb99ba04 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/graph.snapshot @@ -168,6 +168,277 @@ ), ), ), + ( + "nullValue", + Constant( + Null, + ), + ), + ( + "opt1", + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + ), + ( + "opt10", + Member( + 3, + Variable( + ( + "undefinedValue", + #2, + ), + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + ), + ( + "opt11", + Call( + 4, + Member( + 3, + Variable( + ( + "nullValue", + #2, + ), + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + ), + ( + "opt12", + Constant( + Undefined, + ), + ), + ( + "opt13", + Constant( + Undefined, + ), + ), + ( + "opt2", + Call( + 4, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + ), + ( + "opt3", + Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + Constant( + Str( + Atom( + "nested", + ), + ), + ), + ), + ), + ( + "opt4", + Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + Constant( + Str( + Atom( + "nested", + ), + ), + ), + ), + ), + ( + "opt5", + Member( + 6, + Call( + 4, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + Constant( + Str( + Atom( + "result", + ), + ), + ), + ), + ), + ( + "opt6", + Member( + 8, + Call( + 6, + Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + Constant( + Str( + Atom( + "method", + ), + ), + ), + ), + [], + ), + Constant( + Str( + Atom( + "result", + ), + ), + ), + ), + ), + ( + "opt7", + Member( + 3, + FreeVar( + "obj", + ), + FreeVar( + "computed", + ), + ), + ), + ( + "opt8", + Member( + 5, + Member( + 3, + FreeVar( + "obj", + ), + FreeVar( + "computed", + ), + ), + Constant( + Str( + Atom( + "nested", + ), + ), + ), + ), + ), + ( + "opt9", + Member( + 3, + Variable( + ( + "nullValue", + #2, + ), + ), + Constant( + Str( + Atom( + "prop", + ), + ), + ), + ), + ), ( "resolve1", Logical( @@ -308,6 +579,12 @@ ], ), ), + ( + "undefinedValue", + Constant( + Undefined, + ), + ), ( "x", Constant( diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/input.js b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/input.js index 49e1e4f561456f..782b899442d74c 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/input.js +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/input.js @@ -14,3 +14,22 @@ let resolve3 = global || true; let resolve4 = true || global; let resolve5 = global && false; let resolve6 = false && global; + +// Optional chaining tests +let opt1 = obj?.prop; +let opt2 = obj?.method(); +let opt3 = obj?.prop.nested; +let opt4 = obj?.prop?.nested; +let opt5 = obj?.method()?.result; +let opt6 = obj?.prop?.method?.().result; +let opt7 = obj?.[computed]; +let opt8 = obj?.[computed]?.nested; + +// Optional chaining with known null/undefined values +let nullValue = null; +let undefinedValue = undefined; +let opt9 = nullValue?.prop; +let opt10 = undefinedValue?.prop; +let opt11 = nullValue?.method(); +let opt12 = null?.prop; +let opt13 = undefined?.prop; diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-effects.snapshot index d67f5f494e1045..75cb8ced684e26 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-effects.snapshot @@ -75,3 +75,123 @@ 0 -> 29 conditional = false 29 -> 30 free var = FreeVar(global) + +0 -> 31 free var = FreeVar(obj) + +0 -> 32 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 34 free var = FreeVar(obj) + +0 -> 35 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 38 free var = FreeVar(obj) + +0 -> 39 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 41 free var = FreeVar(obj) + +0 -> 42 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 44 null safe receiver = ???*0* +- *0* ???*1*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 46 free var = FreeVar(obj) + +0 -> 47 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 49 null safe receiver = ???*0*() +- *0* ???*1*["method"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 52 free var = FreeVar(obj) + +0 -> 53 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 55 null safe receiver = ???*0* +- *0* ???*1*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 57 null safe receiver = ???*0* +- *0* ???*1*["method"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* ???*2*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *2* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +57 -> 58 call = ???*0*() +- *0* ???*1*["method"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* ???*2*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *2* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 59 free var = FreeVar(obj) + +0 -> 60 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 62 free var = FreeVar(obj) + +0 -> 63 null safe receiver = ???*0* +- *0* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 65 null safe receiver = ???*0* +- *0* ???*1*[FreeVar(computed)] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 67 null safe receiver = null + +0 -> 69 null safe receiver = undefined + +0 -> 71 null safe receiver = null + +0 -> 73 null safe receiver = null + +0 -> 75 null safe receiver = undefined diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-explained.snapshot index 7d775543bbb1df..22dd0bc20bfe5a 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/logical/resolved-explained.snapshot @@ -18,6 +18,94 @@ d = false e = true +nullValue = null + +opt1 = ???*0* +- *0* ???*1*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt10 = undefined["prop"] + +opt11 = null["method"]() + +opt12 = undefined + +opt13 = undefined + +opt2 = ???*0*() +- *0* ???*1*["method"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt3 = ???*0* +- *0* ???*1*["nested"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* ???*2*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *2* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt4 = ???*0* +- *0* ???*1*["nested"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* ???*2*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *2* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt5 = ???*0*()["result"] +- *0* ???*1*["method"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt6 = ???*0*()["result"] +- *0* ???*1*["method"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* ???*2*["prop"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *2* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt7 = ???*0* +- *0* ???*1*[FreeVar(computed)] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt8 = ???*0* +- *0* ???*1*["nested"] + ⚠️ unknown object + ⚠️ This value might have side effects +- *1* ???*2*[FreeVar(computed)] + ⚠️ unknown object + ⚠️ This value might have side effects +- *2* FreeVar(obj) + ⚠️ unknown global + ⚠️ This value might have side effects + +opt9 = null["prop"] + resolve1 = (???*0* | 4) - *0* FreeVar(global) ⚠️ unknown global @@ -39,6 +127,8 @@ resolve5 = (???*0* | false){falsy} resolve6 = false +undefinedValue = undefined + x = true y = false diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input/index.js b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input/index.js index 464d187bb55fa4..abd6a76907849f 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input/index.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input/index.js @@ -58,5 +58,6 @@ p.env.NODE_ENV == 'production' p.env.NODE_ENV != 'production' && console.log('development') p.env.NODE_ENV == 'production' && console.log('production') +console.log(DEFINED_NULL?.foo ?? console.log('defined')) console.log(__dirname) diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js index e467e929f2419a..e152acafcf5478 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js @@ -54,10 +54,11 @@ console.log(("TURBOPACK compile-time value", "development")); if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable ; ("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : console.log('development'); -"TURBOPACK simplified expression", console.log('development'); -"TURBOPACK simplified expression", ("TURBOPACK compile-time value", "development") == 'production'; +("TURBOPACK compile-time truthy", 1) && console.log('development'); +("TURBOPACK compile-time value", "development") == 'production' && "TURBOPACK unreachable"; +console.log(DEFINED_NULL?.foo ?? console.log('defined')); console.log(("TURBOPACK compile-time value", "/ROOT/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input")); }), ]); -//# sourceMappingURL=aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js.map \ No newline at end of file +//# sourceMappingURL=aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js.map diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js.map b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js.map index 8c2ea0322511fb..a0921829e6c10a 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js.map +++ b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_define_input_index_aec19b5a.js.map @@ -2,5 +2,5 @@ "version": 3, "sources": [], "sections": [ - {"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input/index.js"],"sourcesContent":["if (DEFINED_VALUE) {\n console.log('DEFINED_VALUE')\n}\n\nif (DEFINED_TRUE) {\n console.log('DEFINED_VALUE')\n}\n\nif (!DEFINED_NULL) {\n console.log('DEFINED_NULL', DEFINED_NULL)\n}\n\nif (DEFINED_INT) {\n console.log('DEFINED_INT', DEFINED_INT)\n}\n\nif (DEFINED_FLOAT) {\n console.log('DEFINED_FLOAT', DEFINED_FLOAT)\n}\n\nif (DEFINED_ARRAY) {\n console.log('DEFINED_ARRAY', DEFINED_ARRAY)\n}\n\nif (DEFINED_EVALUATE) {\n console.log('DEFINED_EVALUATE', DEFINED_EVALUATE)\n}\n\nif (DEFINED_EVALUATE_NESTED) {\n console.log('DEFINED_EVALUATE_NESTED', DEFINED_EVALUATE_NESTED)\n}\n\nif (A.VERY.LONG.DEFINED.VALUE) {\n console.log('A.VERY.LONG.DEFINED.VALUE')\n}\n\nif (process.env.NODE_ENV) {\n console.log('something')\n}\n\nif (process.env.NODE_ENV === 'production') {\n console.log('production')\n}\n\nvar p = process\n\nconsole.log(A.VERY.LONG.DEFINED.VALUE)\nconsole.log(DEFINED_VALUE)\nconsole.log(p.env.NODE_ENV)\n\nif (p.env.NODE_ENV === 'production') {\n console.log('production')\n}\n\np.env.NODE_ENV == 'production'\n ? console.log('production')\n : console.log('development')\n\np.env.NODE_ENV != 'production' && console.log('development')\np.env.NODE_ENV == 'production' && console.log('production')\n\nconsole.log(__dirname)\n"],"names":[],"mappings":"AAAA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAkB;IAChB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAiB;IACf,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;;;;;;;;;AACd;AAEA,wCAAsB;IACpB,QAAQ,GAAG,CAAC,qDAzBd,IAAI;AA0BJ;AAEA,wCAA6B;IAC3B,QAAQ,GAAG,CAAC;;;yCA7Bd,IAAM;;AA8BN;AAEA,wCAA+B;IAC7B,QAAQ,GAAG,CAAC;AACd;AAEA,wCAA0B;IACxB,QAAQ,GAAG,CAAC;AACd;AAEA;;AAIA,IAAI,IAAI;AAER,QAAQ,GAAG;;;AACX,QAAQ,GAAG;AACX,QAAQ,GAAG;AAEX;;AAIA,sCACI,0BACA,QAAQ,GAAG,CAAC;mCAEkB,QAAQ,GAAG,CAAC;mCAC9C,mDAAkB;AAElB,QAAQ,GAAG"}}] -} \ No newline at end of file + {"offset": {"line": 3, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/comptime/define/input/index.js"],"sourcesContent":["if (DEFINED_VALUE) {\n console.log('DEFINED_VALUE')\n}\n\nif (DEFINED_TRUE) {\n console.log('DEFINED_VALUE')\n}\n\nif (!DEFINED_NULL) {\n console.log('DEFINED_NULL', DEFINED_NULL)\n}\n\nif (DEFINED_INT) {\n console.log('DEFINED_INT', DEFINED_INT)\n}\n\nif (DEFINED_FLOAT) {\n console.log('DEFINED_FLOAT', DEFINED_FLOAT)\n}\n\nif (DEFINED_ARRAY) {\n console.log('DEFINED_ARRAY', DEFINED_ARRAY)\n}\n\nif (DEFINED_EVALUATE) {\n console.log('DEFINED_EVALUATE', DEFINED_EVALUATE)\n}\n\nif (DEFINED_EVALUATE_NESTED) {\n console.log('DEFINED_EVALUATE_NESTED', DEFINED_EVALUATE_NESTED)\n}\n\nif (A.VERY.LONG.DEFINED.VALUE) {\n console.log('A.VERY.LONG.DEFINED.VALUE')\n}\n\nif (process.env.NODE_ENV) {\n console.log('something')\n}\n\nif (process.env.NODE_ENV === 'production') {\n console.log('production')\n}\n\nvar p = process\n\nconsole.log(A.VERY.LONG.DEFINED.VALUE)\nconsole.log(DEFINED_VALUE)\nconsole.log(p.env.NODE_ENV)\n\nif (p.env.NODE_ENV === 'production') {\n console.log('production')\n}\n\np.env.NODE_ENV == 'production'\n ? console.log('production')\n : console.log('development')\n\np.env.NODE_ENV != 'production' && console.log('development')\np.env.NODE_ENV == 'production' && console.log('production')\nconsole.log(DEFINED_NULL?.foo ?? console.log('defined'))\n\nconsole.log(__dirname)\n"],"names":[],"mappings":"AAAA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAkB;IAChB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAiB;IACf,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;AACd;AAEA,wCAAmB;IACjB,QAAQ,GAAG,CAAC;;;;;;;;;AACd;AAEA,wCAAsB;IACpB,QAAQ,GAAG,CAAC,qDAzBd,IAAI;AA0BJ;AAEA,wCAA6B;IAC3B,QAAQ,GAAG,CAAC;;;yCA7Bd,IAAM;;AA8BN;AAEA,wCAA+B;IAC7B,QAAQ,GAAG,CAAC;AACd;AAEA,wCAA0B;IACxB,QAAQ,GAAG,CAAC;AACd;AAEA;;AAIA,IAAI,IAAI;AAER,QAAQ,GAAG;;;AACX,QAAQ,GAAG;AACX,QAAQ,GAAG;AAEX;;AAIA,sCACI,0BACA,QAAQ,GAAG,CAAC;AAEhB,wCAAkC,QAAQ,GAAG,CAAC;AAC9C,mDAAkB,gBAAgB;AAClC,QAAQ,GAAG,CAAC,cAAc,OAAO,QAAQ,GAAG,CAAC;AAE7C,QAAQ,GAAG"}}] +}