diff --git a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs index 11b2bf80c9e59..9c0ea18622159 100644 --- a/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs +++ b/turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs @@ -357,8 +357,6 @@ pub fn create_graph( eval_context, effects: Default::default(), hoisted_effects: Default::default(), - early_return_stack: Default::default(), - var_decl_kind: Default::default(), }, &mut Default::default(), ); @@ -885,11 +883,8 @@ struct Analyzer<'a> { // Tracked separately so we can preserve effects from hoisted declarations even when we don't // collect effects from the declaring context. hoisted_effects: Vec, - early_return_stack: Vec, eval_context: &'a EvalContext, - - var_decl_kind: Option, } trait FunctionLike { @@ -900,6 +895,9 @@ trait FunctionLike { false } fn span(&self) -> Span; + fn binds_this(&self) -> bool { + true + } } impl FunctionLike for Function { @@ -923,6 +921,9 @@ impl FunctionLike for ArrowExpr { fn span(&self) -> Span { self.span } + fn binds_this(&self) -> bool { + false + } } impl FunctionLike for Constructor { @@ -941,6 +942,21 @@ impl FunctionLike for SetterProp { } } +#[derive(PartialEq, Eq, Debug, Copy, Clone)] +enum LexicalContext { + // In the root of a function scope + Function { id: u32, binds_this: bool }, + // A placeholder for identify anonymous blocks + // If we have Block->Block then we are in an anonymous block + // If we have Function->Block or ControlFlow->Block then we are just in a function root + Block, + // In some kind of control flow + ControlFlow { is_try: bool }, + + // Class bodies do rebind `this` and are in many ways like a function + ClassBody, +} + mod analyzer_state { use super::*; @@ -948,23 +964,25 @@ mod analyzer_state { /// intentionally private to the rest of the `Analyzer` implementation. pub struct AnalyzerState { pat_value: Option, - /// A unique identifier for the current function, based on the span of the function - /// declaration. [`None`] if we are in the root scope. These are only used as opaque - /// identifiers to distinguish function arguments. - cur_fn_id: Option, /// Return values of the current function. /// /// This is configured to [Some] by function handlers and filled by the /// return statement handler. cur_fn_return_values: Option>, + /// Stack of early returns for control flow analysis. + early_return_stack: Vec, + lexical_stack: Vec, + var_decl_kind: Option, } impl AnalyzerState { pub fn new() -> AnalyzerState { AnalyzerState { pat_value: None, - cur_fn_id: None, cur_fn_return_values: None, + early_return_stack: Default::default(), + lexical_stack: Default::default(), + var_decl_kind: None, } } } @@ -972,13 +990,69 @@ mod analyzer_state { impl Analyzer<'_> { /// Returns true if we are in a function. False if we are in the root scope. pub(super) fn is_in_fn(&self) -> bool { - self.state.cur_fn_id.is_some() + self.state + .lexical_stack + .iter() + .any(|b| matches!(b, LexicalContext::Function { .. })) + } + + pub(super) fn is_in_try(&self) -> bool { + self.state + .lexical_stack + .iter() + .rev() + .take_while(|b| !matches!(b, LexicalContext::Function { .. })) + .any(|b| *b == LexicalContext::ControlFlow { is_try: true }) + } + + /// Returns true if we are currently in a block scope that isn't at the root of a function + /// or a module. + pub(super) fn is_in_nested_block_scope(&self) -> bool { + match &self.state.lexical_stack[self.state.lexical_stack.len().saturating_sub(2)..] { + [LexicalContext::Block] + | [LexicalContext::Function { .. }, LexicalContext::Block] => false, + [] => { + unreachable!() + } + + _ => true, + } + } + + pub(super) fn cur_lexical_context(&self) -> LexicalContext { + *self.state.lexical_stack.last().unwrap() } /// Returns the identifier of the current function. /// must be called only if `is_in_fn` is true pub(super) fn cur_fn_ident(&self) -> u32 { - self.state.cur_fn_id.expect("not in a function") + *self + .state + .lexical_stack + .iter() + .rev() + .filter_map(|b| { + if let LexicalContext::Function { id, .. } = b { + Some(id) + } else { + None + } + }) + .next() + .expect("not in a function") + } + + /// Returns true if `this` is bound in any active scope + pub(super) fn is_this_bound(&self) -> bool { + self.state.lexical_stack.iter().rev().any(|b| { + matches!( + b, + LexicalContext::Function { + id: _, + binds_this: true + } | LexicalContext::ClassBody + ) + }) } /// Adds a return value to the current function. @@ -1015,6 +1089,24 @@ mod analyzer_state { out } + /// Runs `func` with the given variable declaration kind, restoring the previous kind + /// afterwards. + pub(super) fn with_decl_kind( + &mut self, + kind: Option, + func: impl FnOnce(&mut Self) -> T, + ) -> T { + let prev_kind = replace(&mut self.state.var_decl_kind, kind); + let out = func(self); + self.state.var_decl_kind = prev_kind; + out + } + + /// Returns the current variable declaration kind. + pub(super) fn var_decl_kind(&self) -> Option { + self.state.var_decl_kind + } + /// Runs `func` with the current function identifier and return values initialized for the /// block. pub(super) fn enter_fn( @@ -1023,14 +1115,18 @@ mod analyzer_state { visitor: impl FnOnce(&mut Self), ) -> JsValue { let fn_id = function.span().lo.0; - let prev_fn_id = self.state.cur_fn_id.replace(fn_id); let prev_return_values = self.state.cur_fn_return_values.replace(vec![]); - visitor(self); + self.with_block( + LexicalContext::Function { + id: fn_id, + binds_this: function.binds_this(), + }, + |this| visitor(this), + ); let return_values = self.state.cur_fn_return_values.take().unwrap(); - - self.state.cur_fn_id = prev_fn_id; self.state.cur_fn_return_values = prev_return_values; + JsValue::function( fn_id, function.is_async(), @@ -1042,6 +1138,147 @@ mod analyzer_state { }, ) } + + /// Helper to access the early_return_stack mutably (for push operations) + pub(super) fn early_return_stack_mut(&mut self) -> &mut Vec { + &mut self.state.early_return_stack + } + + /// Records an unconditional early return (return, throw, or finally block that always + /// returns). Takes ownership of current effects and pushes them onto the early return + /// stack. + pub(super) fn add_early_return_always( + &mut self, + ast_path: &AstNodePath>, + ) { + let early_return = EarlyReturn::Always { + prev_effects: take(&mut self.effects), + start_ast_path: as_parent_path(ast_path), + }; + self.early_return_stack_mut().push(early_return); + } + + /// Runs `func` with a fresh early return stack, restoring the previous stack afterwards. + /// Returns the result of `func` and whether the block always returns (from + /// `end_early_return_block`). + pub(super) fn enter_control_flow( + &mut self, + func: impl FnOnce(&mut Self) -> T, + ) -> (T, bool) { + self.enter_block(LexicalContext::ControlFlow { is_try: false }, |this| { + func(this) + }) + } + /// Runs `func` with a fresh early return stack, restoring the previous stack afterwards. + /// Returns the result of `func` and whether the block always returns (from + /// `end_early_return_block`). + pub(super) fn enter_try(&mut self, func: impl FnOnce(&mut Self) -> T) -> (T, bool) { + self.enter_block(LexicalContext::ControlFlow { is_try: true }, |this| { + func(this) + }) + } + + /// Runs `func` with a fresh early return stack, restoring the previous stack afterwards. + /// Returns the result of `func` and whether the block always returns (from + /// `end_early_return_block`). + pub(super) fn enter_block( + &mut self, + block_kind: LexicalContext, + func: impl FnOnce(&mut Self) -> T, + ) -> (T, bool) { + let prev_early_return_stack = take(&mut self.state.early_return_stack); + let result = self.with_block(block_kind, func); + let always_returns = self.end_early_return_block(); + self.state.early_return_stack = prev_early_return_stack; + (result, always_returns) + } + + /// Pushes a block onto the stack without performing early return logic. + pub(super) fn with_block( + &mut self, + block_kind: LexicalContext, + func: impl FnOnce(&mut Self) -> T, + ) -> T { + self.state.lexical_stack.push(block_kind); + let result = func(self); + let old = self.state.lexical_stack.pop(); + debug_assert_eq!(old, Some(block_kind)); + result + } + + /// Ends a conditional block. All early returns are integrated into the + /// effects. Returns true if the whole block always early returns. + fn end_early_return_block(&mut self) -> bool { + let mut always_returns = false; + while let Some(early_return) = self.state.early_return_stack.pop() { + match early_return { + EarlyReturn::Always { + prev_effects, + start_ast_path, + } => { + self.effects = prev_effects; + if self.analyze_mode.is_code_gen() { + self.effects.push(Effect::Unreachable { start_ast_path }); + } + always_returns = true; + } + EarlyReturn::Conditional { + prev_effects, + start_ast_path, + condition, + then, + r#else, + condition_ast_path, + span, + early_return_condition_value, + } => { + let block = Box::new(EffectsBlock { + effects: take(&mut self.effects), + range: AstPathRange::StartAfter(start_ast_path), + }); + self.effects = prev_effects; + let kind = match (then, r#else, early_return_condition_value) { + (None, None, false) => ConditionalKind::If { then: block }, + (None, None, true) => ConditionalKind::IfElseMultiple { + then: vec![block], + r#else: vec![], + }, + (Some(then), None, false) => ConditionalKind::IfElseMultiple { + then: vec![then, block], + r#else: vec![], + }, + (Some(then), None, true) => ConditionalKind::IfElse { + then, + r#else: block, + }, + (Some(then), Some(r#else), false) => ConditionalKind::IfElseMultiple { + then: vec![then, block], + r#else: vec![r#else], + }, + (Some(then), Some(r#else), true) => ConditionalKind::IfElseMultiple { + then: vec![then], + r#else: vec![r#else, block], + }, + (None, Some(r#else), false) => ConditionalKind::IfElse { + then: block, + r#else, + }, + (None, Some(r#else), true) => ConditionalKind::IfElseMultiple { + then: vec![], + r#else: vec![r#else, block], + }, + }; + self.effects.push(Effect::Conditional { + condition, + kind: Box::new(kind), + ast_path: condition_ast_path, + span, + }) + } + } + } + always_returns + } } } @@ -1060,24 +1297,6 @@ pub fn as_parent_path_with( .collect() } -fn is_in_try(ast_path: &AstNodePath>) -> bool { - ast_path - .iter() - .rev() - .find_map(|ast_ref| match ast_ref.kind() { - AstParentKind::ArrowExpr(ArrowExprField::Body) - | AstParentKind::Function(FunctionField::Body) - | AstParentKind::Constructor(ConstructorField::Body) - | AstParentKind::ClassMethod(ClassMethodField::Function) - | AstParentKind::GetterProp(GetterPropField::Body) - | AstParentKind::SetterProp(SetterPropField::Body) - | AstParentKind::MethodProp(MethodPropField::Function) => Some(false), - AstParentKind::TryStmt(TryStmtField::Block) => Some(true), - _ => None, - }) - .unwrap_or(false) -} - enum CallOrNewExpr<'ast> { Call(&'ast CallExpr), New(&'ast NewExpr), @@ -1423,7 +1642,7 @@ impl Analyzer<'_> { args, ast_path: as_parent_path(ast_path), span, - in_try: is_in_try(ast_path), + in_try: self.is_in_try(), new, }); } @@ -1447,7 +1666,7 @@ impl Analyzer<'_> { args, ast_path: as_parent_path(ast_path), span, - in_try: is_in_try(ast_path), + in_try: self.is_in_try(), new, }); } else { @@ -1457,7 +1676,7 @@ impl Analyzer<'_> { args, ast_path: as_parent_path(ast_path), span, - in_try: is_in_try(ast_path), + in_try: self.is_in_try(), new, }); } @@ -1471,7 +1690,7 @@ impl Analyzer<'_> { args, ast_path: as_parent_path(ast_path), span, - in_try: is_in_try(ast_path), + in_try: self.is_in_try(), new, }), } @@ -1504,80 +1723,6 @@ impl Analyzer<'_> { span: member_expr.span(), }); } - - /// Ends a conditional block. All early returns are integrated into the - /// effects. Returns true if the whole block always early returns. - fn end_early_return_block(&mut self) -> bool { - let mut always_returns = false; - while let Some(early_return) = self.early_return_stack.pop() { - match early_return { - EarlyReturn::Always { - prev_effects, - start_ast_path, - } => { - self.effects = prev_effects; - if self.analyze_mode.is_code_gen() { - self.effects.push(Effect::Unreachable { start_ast_path }); - } - always_returns = true; - } - EarlyReturn::Conditional { - prev_effects, - start_ast_path, - condition, - then, - r#else, - condition_ast_path, - span, - early_return_condition_value, - } => { - let block = Box::new(EffectsBlock { - effects: take(&mut self.effects), - range: AstPathRange::StartAfter(start_ast_path), - }); - self.effects = prev_effects; - let kind = match (then, r#else, early_return_condition_value) { - (None, None, false) => ConditionalKind::If { then: block }, - (None, None, true) => ConditionalKind::IfElseMultiple { - then: vec![block], - r#else: vec![], - }, - (Some(then), None, false) => ConditionalKind::IfElseMultiple { - then: vec![then, block], - r#else: vec![], - }, - (Some(then), None, true) => ConditionalKind::IfElse { - then, - r#else: block, - }, - (Some(then), Some(r#else), false) => ConditionalKind::IfElseMultiple { - then: vec![then, block], - r#else: vec![r#else], - }, - (Some(then), Some(r#else), true) => ConditionalKind::IfElseMultiple { - then: vec![then], - r#else: vec![r#else, block], - }, - (None, Some(r#else), false) => ConditionalKind::IfElse { - then: block, - r#else, - }, - (None, Some(r#else), true) => ConditionalKind::IfElseMultiple { - then: vec![], - r#else: vec![r#else, block], - }, - }; - self.effects.push(Effect::Conditional { - condition, - kind: Box::new(kind), - ast_path: condition_ast_path, - span, - }) - } - } - } - always_returns - } } impl VisitAstPath for Analyzer<'_> { @@ -1720,10 +1865,9 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast Expr, ast_path: &mut AstNodePath>, ) { - let old = self.var_decl_kind; - self.var_decl_kind = None; - n.visit_children_with_ast_path(self, ast_path); - self.var_decl_kind = old; + self.with_decl_kind(None, |this| { + n.visit_children_with_ast_path(this, ast_path); + }); } fn visit_params<'ast: 'r, 'r>( @@ -1745,25 +1889,25 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast Param, ast_path: &mut AstNodePath>, ) { - let old = self.var_decl_kind; let Param { decorators, pat, span: _, } = n; - self.var_decl_kind = None; - self.with_pat_value(None, |this| { - let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param( - n, - ParamField::Decorators(usize::MAX), - )); - this.visit_decorators(decorators, &mut ast_path); + self.with_decl_kind(None, |this| { + // Decorators don't have access to the parameter values, so omit them + this.with_pat_value(None, |this| { + let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param( + n, + ParamField::Decorators(usize::MAX), + )); + this.visit_decorators(decorators, &mut ast_path); + }); + { + let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(n, ParamField::Pat)); + this.visit_pat(pat, &mut ast_path); + } }); - { - let mut ast_path = ast_path.with_guard(AstParentNodeRef::Param(n, ParamField::Pat)); - self.visit_pat(pat, &mut ast_path); - } - self.var_decl_kind = old; } fn visit_fn_decl<'ast: 'r, 'r>( @@ -1776,8 +1920,8 @@ impl VisitAstPath for Analyzer<'_> { }); // Take all effects produced by the function and move them to hoisted effects since // function declarations are hoisted. - // This accounts for the fact that even with `if (false) { function f() {} }` `f` is - // hoisted out of the condition. so we still need to process effects for it. + // This accounts for the fact that even with `if (true) { return f} function f() {} ` `f` is + // hoisted earlier of the condition. so we still need to process effects for it. // TODO(lukesandberg): shouldn't this just be the effects associated with the function. self.hoisted_effects.append(&mut self.effects); @@ -1849,7 +1993,6 @@ impl VisitAstPath for Analyzer<'_> { ) { self.add_value_from_expr( decl.ident.to_id(), - // TODO avoid clone &Expr::Class(ClassExpr { ident: Some(decl.ident.clone()), class: decl.class.clone(), @@ -1858,6 +2001,16 @@ impl VisitAstPath for Analyzer<'_> { decl.visit_children_with_ast_path(self, ast_path); } + fn visit_class<'ast: 'r, 'r>( + &mut self, + node: &'ast Class, + ast_path: &mut AstNodePath>, + ) { + self.enter_block(LexicalContext::ClassBody, |this| { + node.visit_children_with_ast_path(this, ast_path); + }); + } + fn visit_getter_prop<'ast: 'r, 'r>( &mut self, node: &'ast GetterProp, @@ -1923,10 +2076,9 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast VarDecl, ast_path: &mut AstNodePath>, ) { - let old = self.var_decl_kind; - self.var_decl_kind = Some(n.kind); - n.visit_children_with_ast_path(self, ast_path); - self.var_decl_kind = old; + self.with_decl_kind(Some(n.kind), |this| { + n.visit_children_with_ast_path(this, ast_path); + }); } fn visit_var_declarator<'ast: 'r, 'r>( @@ -1939,7 +2091,7 @@ impl VisitAstPath for Analyzer<'_> { let mut ast_path = ast_path.with_guard(AstParentNodeRef::VarDeclarator(n, VarDeclaratorField::Name)); - if self.var_decl_kind.is_some() + if let Some(var_decl_kind) = self.var_decl_kind() && let Some(init) = &n.init { // For case like @@ -1952,8 +2104,8 @@ impl VisitAstPath for Analyzer<'_> { // // The variable `x` is undefined - let should_include_undefined = matches!(self.var_decl_kind, Some(VarDeclKind::Var)) - && is_lexically_block_scope(&mut ast_path); + let should_include_undefined = + var_decl_kind == VarDeclKind::Var && self.is_in_nested_block_scope(); let init_value = self.eval_context.eval(init); let pat_value = Some(if should_include_undefined { JsValue::alternatives(vec![ @@ -2013,10 +2165,9 @@ impl VisitAstPath for Analyzer<'_> { let mut ast_path = ast_path.with_guard(AstParentNodeRef::ForInStmt(n, ForInStmtField::Body)); - let prev_early_return_stack = take(&mut self.early_return_stack); - n.body.visit_with_ast_path(self, &mut ast_path); - self.end_early_return_block(); - self.early_return_stack = prev_early_return_stack; + self.enter_control_flow(|this| { + n.body.visit_with_ast_path(this, &mut ast_path); + }); } fn visit_for_of_stmt<'ast: 'r, 'r>( @@ -2042,10 +2193,9 @@ impl VisitAstPath for Analyzer<'_> { let mut ast_path = ast_path.with_guard(AstParentNodeRef::ForOfStmt(n, ForOfStmtField::Body)); - let prev_early_return_stack = take(&mut self.early_return_stack); - n.body.visit_with_ast_path(self, &mut ast_path); - self.end_early_return_block(); - self.early_return_stack = prev_early_return_stack; + self.enter_control_flow(|this| { + n.body.visit_with_ast_path(this, &mut ast_path); + }); } fn visit_for_stmt<'ast: 'r, 'r>( @@ -2053,10 +2203,28 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast ForStmt, ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { - let prev_early_return_stack = take(&mut self.early_return_stack); - n.visit_children_with_ast_path(self, ast_path); - self.end_early_return_block(); - self.early_return_stack = prev_early_return_stack; + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Init)); + n.init.visit_with_ast_path(self, &mut ast_path); + } + self.enter_control_flow(|this| { + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Test)); + n.test.visit_with_ast_path(this, &mut ast_path); + } + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Body)); + n.body.visit_with_ast_path(this, &mut ast_path); + } + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::ForStmt(n, ForStmtField::Update)); + n.update.visit_with_ast_path(this, &mut ast_path); + } + }); } fn visit_while_stmt<'ast: 'r, 'r>( @@ -2064,10 +2232,19 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast WhileStmt, ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { - let prev_early_return_stack = take(&mut self.early_return_stack); - n.visit_children_with_ast_path(self, ast_path); - self.end_early_return_block(); - self.early_return_stack = prev_early_return_stack; + // Enter control flow for everything (test and body both repeat in loop iterations) + self.enter_control_flow(|this| { + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::WhileStmt(n, WhileStmtField::Test)); + n.test.visit_with_ast_path(this, &mut ast_path); + } + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::WhileStmt(n, WhileStmtField::Body)); + n.body.visit_with_ast_path(this, &mut ast_path); + } + }); } fn visit_do_while_stmt<'ast: 'r, 'r>( @@ -2075,10 +2252,19 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast DoWhileStmt, ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { - let prev_early_return_stack = take(&mut self.early_return_stack); - n.visit_children_with_ast_path(self, ast_path); - self.end_early_return_block(); - self.early_return_stack = prev_early_return_stack; + // Enter control flow for everything (body and test both are part of loop iterations) + self.enter_control_flow(|this| { + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::DoWhileStmt(n, DoWhileStmtField::Body)); + n.body.visit_with_ast_path(this, &mut ast_path); + } + { + let mut ast_path = + ast_path.with_guard(AstParentNodeRef::DoWhileStmt(n, DoWhileStmtField::Test)); + n.test.visit_with_ast_path(this, &mut ast_path); + } + }); } fn visit_simple_assign_target<'ast: 'r, 'r>( @@ -2187,10 +2373,7 @@ impl VisitAstPath for Analyzer<'_> { self.add_return_value(return_value); } - self.early_return_stack.push(EarlyReturn::Always { - prev_effects: take(&mut self.effects), - start_ast_path: as_parent_path(ast_path), - }); + self.add_early_return_always(ast_path); } fn visit_ident<'ast: 'r, 'r>( @@ -2262,27 +2445,7 @@ impl VisitAstPath for Analyzer<'_> { node: &'ast ThisExpr, ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { - // TODO: would it be better to compute this while traversing? - // `this` is rebound within functions and class method members - if ast_path.iter().rev().any(|node| { - matches!( - node.kind(), - AstParentKind::MethodProp(MethodPropField::Function) - | AstParentKind::GetterProp(GetterPropField::Body) - | AstParentKind::SetterProp(SetterPropField::Body) - | AstParentKind::Constructor(ConstructorField::Body) - | AstParentKind::ClassMethod(ClassMethodField::Function) - | AstParentKind::ClassDecl(ClassDeclField::Class) - | AstParentKind::ClassExpr(ClassExprField::Class) - | AstParentKind::Function(FunctionField::Body) - | AstParentKind::Function(FunctionField::Params(_)) - ) - }) { - // We are in some scope that will rebind this - return; - } - - if self.analyze_mode.is_code_gen() { + if self.analyze_mode.is_code_gen() && !self.is_this_bound() { // Otherwise 'this' is free self.add_effect(Effect::FreeVar { var: atom!("this"), @@ -2313,8 +2476,9 @@ impl VisitAstPath for Analyzer<'_> { ast_path: &mut AstNodePath>, ) { self.effects = take(&mut self.data.effects); - program.visit_children_with_ast_path(self, ast_path); - self.end_early_return_block(); + self.enter_block(LexicalContext::Block, |this| { + program.visit_children_with_ast_path(this, ast_path); + }); self.effects.append(&mut self.hoisted_effects); self.data.effects = take(&mut self.effects); } @@ -2414,13 +2578,16 @@ impl VisitAstPath for Analyzer<'_> { stmt.test.visit_with_ast_path(self, &mut ast_path); } let prev_effects = take(&mut self.effects); - let prev_early_return_stack = take(&mut self.early_return_stack); let then_returning; let then = { let mut ast_path = ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Cons)); - stmt.cons.visit_with_ast_path(self, &mut ast_path); - then_returning = self.end_early_return_block(); + then_returning = self + .enter_control_flow(|this| { + stmt.cons.visit_with_ast_path(this, &mut ast_path); + }) + .1; + Box::new(EffectsBlock { effects: take(&mut self.effects), range: AstPathRange::Exact(as_parent_path(&ast_path)), @@ -2430,14 +2597,17 @@ impl VisitAstPath for Analyzer<'_> { let r#else = stmt.alt.as_ref().map(|alt| { let mut ast_path = ast_path.with_guard(AstParentNodeRef::IfStmt(stmt, IfStmtField::Alt)); - alt.visit_with_ast_path(self, &mut ast_path); - else_returning = self.end_early_return_block(); + else_returning = self + .enter_control_flow(|this| { + alt.visit_with_ast_path(this, &mut ast_path); + }) + .1; + Box::new(EffectsBlock { effects: take(&mut self.effects), range: AstPathRange::Exact(as_parent_path(&ast_path)), }) }); - self.early_return_stack = prev_early_return_stack; self.effects = prev_effects; self.add_conditional_if_effect_with_early_return( &stmt.test, @@ -2456,40 +2626,43 @@ impl VisitAstPath for Analyzer<'_> { stmt: &'ast TryStmt, ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { + // TODO: if both try and catch return unconditionally, then so does the whole try statement let prev_effects = take(&mut self.effects); - let prev_early_return_stack = take(&mut self.early_return_stack); + let mut block = { let mut ast_path = ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Block)); - stmt.block.visit_with_ast_path(self, &mut ast_path); - self.end_early_return_block(); + self.enter_try(|this| { + stmt.block.visit_with_ast_path(this, &mut ast_path); + }); + take(&mut self.effects) }; let mut handler = if let Some(handler) = stmt.handler.as_ref() { let mut ast_path = ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Handler)); - handler.visit_with_ast_path(self, &mut ast_path); - self.end_early_return_block(); + self.enter_control_flow(|this| { + handler.visit_with_ast_path(this, &mut ast_path); + }); take(&mut self.effects) } else { vec![] }; - self.early_return_stack = prev_early_return_stack; self.effects = prev_effects; self.effects.append(&mut block); self.effects.append(&mut handler); if let Some(finalizer) = stmt.finalizer.as_ref() { - { + let finally_returns_unconditionally = { let mut ast_path = ast_path.with_guard(AstParentNodeRef::TryStmt(stmt, TryStmtField::Finalizer)); - finalizer.visit_with_ast_path(self, &mut ast_path); - } + self.enter_control_flow(|this| { + finalizer.visit_with_ast_path(this, &mut ast_path); + }) + .1 + }; // If a finally block early returns the parent block does too. - if self.end_early_return_block() { - self.early_return_stack.push(EarlyReturn::Always { - prev_effects: take(&mut self.effects), - start_ast_path: as_parent_path(ast_path), - }); + if finally_returns_unconditionally { + self.add_early_return_always(ast_path); } }; } @@ -2500,11 +2673,10 @@ impl VisitAstPath for Analyzer<'_> { ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { let prev_effects = take(&mut self.effects); - let prev_early_return_stack = take(&mut self.early_return_stack); - case.visit_children_with_ast_path(self, ast_path); - self.end_early_return_block(); + self.enter_control_flow(|this| { + case.visit_children_with_ast_path(this, ast_path); + }); let mut effects = take(&mut self.effects); - self.early_return_stack = prev_early_return_stack; self.effects = prev_effects; self.effects.append(&mut effects); } @@ -2514,75 +2686,45 @@ impl VisitAstPath for Analyzer<'_> { n: &'ast BlockStmt, ast_path: &mut swc_core::ecma::visit::AstNodePath<'r>, ) { - enum ScopeType { - Function, - Block, - ControlFlow, - } - let block_type = if ast_path.len() < 2 { - ScopeType::Block - } else if matches!( - &ast_path[ast_path.len() - 2..], - [ - AstParentNodeRef::IfStmt(_, IfStmtField::Cons), - AstParentNodeRef::Stmt(_, StmtField::Block) - ] | [ - AstParentNodeRef::IfStmt(_, IfStmtField::Alt), - AstParentNodeRef::Stmt(_, StmtField::Block) - ] | [_, AstParentNodeRef::TryStmt(_, TryStmtField::Block,)] - | [ - AstParentNodeRef::TryStmt(_, TryStmtField::Handler), - AstParentNodeRef::CatchClause(_, CatchClauseField::Body) - ] - | [ - AstParentNodeRef::LabeledStmt(_, LabeledStmtField::Body), - AstParentNodeRef::Stmt(_, StmtField::Block) - ] - ) { - ScopeType::ControlFlow - } else if matches!( - &ast_path[ast_path.len() - 2..], - [_, AstParentNodeRef::Function(_, FunctionField::Body)] - | [ - AstParentNodeRef::ArrowExpr(_, ArrowExprField::Body), - AstParentNodeRef::BlockStmtOrExpr(_, BlockStmtOrExprField::BlockStmt) - ] - | [_, AstParentNodeRef::GetterProp(_, GetterPropField::Body)] - | [_, AstParentNodeRef::SetterProp(_, SetterPropField::Body)] - | [_, AstParentNodeRef::Constructor(_, ConstructorField::Body)] - ) { - ScopeType::Function - } else { - ScopeType::Block - }; - match block_type { - ScopeType::Function => { - let early_return_stack = take(&mut self.early_return_stack); + match self.cur_lexical_context() { + LexicalContext::Function { .. } => { let mut effects = take(&mut self.effects); let hoisted_effects = take(&mut self.hoisted_effects); - n.visit_children_with_ast_path(self, ast_path); - if !self.end_early_return_block() { - // NOTE: this only occurs if the function has a return statement on one branch - // but not another. if there are no return statements, - // `end_early_return_block` will return false. + + let (_, returns_unconditionally) = + self.enter_block(LexicalContext::Block, |this| { + n.visit_children_with_ast_path(this, ast_path); + }); + // By handling this logic here instead of in enter_fn, we naturally skip it + // for arrow functions with single expression bodies, since they just don't hit this + // path. + if !returns_unconditionally { self.add_return_value(JsValue::Constant(ConstantValue::Undefined)); } self.effects.append(&mut self.hoisted_effects); effects.append(&mut self.effects); self.hoisted_effects = hoisted_effects; self.effects = effects; - self.early_return_stack = early_return_stack; } - ScopeType::Block => { - n.visit_children_with_ast_path(self, ast_path); - if self.end_early_return_block() { - self.early_return_stack.push(EarlyReturn::Always { - prev_effects: take(&mut self.effects), - start_ast_path: as_parent_path(ast_path), - }); + LexicalContext::ControlFlow { .. } => { + self.with_block(LexicalContext::Block, |this| { + n.visit_children_with_ast_path(this, ast_path) + }); + } + LexicalContext::Block => { + // Handle anonymous block statement + // e.g., enter a new control flow context and because it is 'unconditiona' we + // need to propagate early returns + let (_, returns_early) = self.enter_control_flow(|this| { + n.visit_children_with_ast_path(this, ast_path); + }); + if returns_early { + self.add_early_return_always(ast_path); } } - ScopeType::ControlFlow => { + LexicalContext::ClassBody => { + // this would be something like a `static` initialization block + // there is no early return logic required here so just visit children n.visit_children_with_ast_path(self, ast_path); } } @@ -2612,11 +2754,9 @@ impl VisitAstPath for Analyzer<'_> { ast_path: &mut AstNodePath>, ) { let mut prev_effects = take(&mut self.effects); - let prev_early_return_stack = take(&mut self.early_return_stack); - - stmt.visit_children_with_ast_path(self, ast_path); - - self.end_early_return_block(); + self.enter_control_flow(|this| { + stmt.visit_children_with_ast_path(this, ast_path); + }); let effects = take(&mut self.effects); @@ -2636,27 +2776,9 @@ impl VisitAstPath for Analyzer<'_> { }); self.effects = prev_effects; - self.early_return_stack = prev_early_return_stack; } } -fn is_lexically_block_scope(ast_path: &mut AstNodePath) -> bool { - let mut iter = ast_path.iter().rev().peekable(); - - while let Some(cur) = iter.next() { - // If it's a block statement, we need to check if it's Function#body - if matches!(cur.kind(), AstParentKind::BlockStmt(..)) { - if let Some(next) = iter.peek() { - return !matches!(next.kind(), AstParentKind::Function(FunctionField::Body)); - } - return false; - } - } - - // This `var` is not in a block scope - false -} - impl Analyzer<'_> { fn add_conditional_if_effect_with_early_return( &mut self, @@ -2686,7 +2808,7 @@ impl Analyzer<'_> { let condition = Box::new(condition); match (early_return_when_true, early_return_when_false) { (true, false) => { - self.early_return_stack.push(EarlyReturn::Conditional { + let early_return = EarlyReturn::Conditional { prev_effects: take(&mut self.effects), start_ast_path: as_parent_path(ast_path), condition, @@ -2695,10 +2817,11 @@ impl Analyzer<'_> { condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind), span, early_return_condition_value: true, - }); + }; + self.early_return_stack_mut().push(early_return); } (false, true) => { - self.early_return_stack.push(EarlyReturn::Conditional { + let early_return = EarlyReturn::Conditional { prev_effects: take(&mut self.effects), start_ast_path: as_parent_path(ast_path), condition, @@ -2707,7 +2830,8 @@ impl Analyzer<'_> { condition_ast_path: as_parent_path_with(ast_path, condition_ast_kind), span, early_return_condition_value: false, - }); + }; + self.early_return_stack_mut().push(early_return); } (false, false) | (true, true) => { let kind = match (then, r#else) { @@ -2727,10 +2851,11 @@ impl Analyzer<'_> { }); } if early_return_when_false && early_return_when_true { - self.early_return_stack.push(EarlyReturn::Always { + let early_return = EarlyReturn::Always { prev_effects: take(&mut self.effects), start_ast_path: as_parent_path(ast_path), - }); + }; + self.early_return_stack_mut().push(early_return); } } } diff --git a/turbopack/crates/turbopack-ecmascript/src/path_visitor.rs b/turbopack/crates/turbopack-ecmascript/src/path_visitor.rs index c32b6ba0b6661..ce45b23a67c07 100644 --- a/turbopack/crates/turbopack-ecmascript/src/path_visitor.rs +++ b/turbopack/crates/turbopack-ecmascript/src/path_visitor.rs @@ -113,7 +113,7 @@ impl<'a> ApplyVisitors<'a, '_> { } return; } else { - // `current_visitors` has the invariant that is must not be empty. + // `current_visitors` has the invariant that it must not be empty. // When it becomes empty, we must early exit current_visitors = &visitors[nested_visitors_start..]; if current_visitors.is_empty() { diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/graph-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/graph-explained.snapshot index ded879e18f49b..e4fc3cf6fdaa0 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/graph-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/graph-explained.snapshot @@ -2843,7 +2843,7 @@ b#494 = di() b#5 = arguments[1] -b#50 = (a["render"] | (a["displayName"] || null) | a["_payload"]) +b#50 = (a["render"] | undefined | (a["displayName"] || null) | a["_payload"]) b#501 = di() @@ -3230,7 +3230,7 @@ b#979 = arguments[1] b#986 = arguments[1] -b#990 = a["stateNode"] +b#990 = (a["stateNode"] | undefined) b#992 = Zg(a, 1) @@ -3454,6 +3454,7 @@ c#361 = Bg(5, null, null, 0) c#362 = ( | a["type"] + | undefined | ((null !== qg) ? {"id": rg, "overflow": sg} : null) | Bg(18, null, null, 0) ) @@ -3687,7 +3688,7 @@ c#64 = b["getValue"]() c#644 = arguments[2] -c#645 = (null | b | a["tail"] | c["sibling"]) +c#645 = (null | undefined | b | a["tail"] | c["sibling"]) c#646 = (0 | ???*0*) - *0* unsupported assign operation @@ -4228,7 +4229,7 @@ d#621 = (b["pendingProps"] | M["current"] | ???*0* | ???*1*) - *1* unsupported assign operation ⚠️ This value might have side effects -d#631 = (b["type"]["_context"] | b["memoizedState"] | (0 !== ???*0*)) +d#631 = (b["type"]["_context"] | undefined | b["memoizedState"] | (0 !== ???*0*)) - *0* unsupported expression ⚠️ This value might have side effects @@ -4241,7 +4242,7 @@ d#64 = ( d#644 = arguments[3] -d#645 = (null | c) +d#645 = (null | undefined | c) d#646 = (0 | ???*0*) - *0* unsupported assign operation @@ -4287,13 +4288,13 @@ d#703 = a["tag"] d#704 = a["tag"] -d#706 = (X | c["updateQueue"] | d["lastEffect"] | d["next"] | c["stateNode"] | U) +d#706 = (X | undefined | c["updateQueue"] | d["lastEffect"] | d["next"] | c["stateNode"] | U) d#71 = b["type"] d#715 = ck["bind"](null, a, b) -d#716 = (0 | ???*0*) +d#716 = (0 | undefined | ???*0*) - *0* updated with update expression ⚠️ This value might have side effects @@ -4357,10 +4358,11 @@ d#910 = (b["stateNode"] | undefined) d#915 = a["pingCache"] -d#918 = a["stateNode"] +d#918 = (a["stateNode"] | undefined) d#919 = ( | b["type"] + | undefined | b["elementType"] | e(d["_payload"]) | b["pendingProps"] @@ -4491,7 +4493,7 @@ e#266 = (c["textContent"]["length"] | undefined | d | Ke(c, f)) e#275 = (d["event"] | undefined) -e#285 = (ed | gd | fd | undefined | !(0)) +e#285 = (ed | undefined | gd | fd | !(0)) e#286 = arguments[4] @@ -4619,14 +4621,15 @@ e#620 = arguments[4] e#621 = (d["revealOrder"] | null | c | b["child"] | c["sibling"] | a) -e#631 = (b["memoizedProps"]["value"] | b["memoizedState"]) +e#631 = (b["memoizedProps"]["value"] | undefined | b["memoizedState"]) e#639 = (a["memoizedProps"] | Ya(a, e) | A({}, e, {"value": undefined}) | gb(a, e)) -e#646 = (a["child"] | e["sibling"]) +e#646 = (a["child"] | undefined | e["sibling"]) e#647 = ( | Hh(Gh["current"]) + | undefined | 0 | ???*0* | null @@ -4644,7 +4647,7 @@ e#673 = (d["anchorOffset"] | undefined) e#687 = (d["next"] | undefined | e["next"]) -e#706 = (Yj | d["next"] | e["next"]) +e#706 = (Yj | undefined | d["next"] | e["next"]) e#716 = (c[d] | undefined) @@ -4687,10 +4690,11 @@ e#883 = (K | undefined) e#9 = arguments[4] -e#918 = a["memoizedState"] +e#918 = (a["memoizedState"] | undefined) e#919 = ( | Yf(b, H["current"]) + | undefined | Xh(null, b, d, a, e, c) | d["_init"] | $k(d) @@ -4776,7 +4780,7 @@ f#169 = (???*0* | undefined) f#175 = arguments[5] -f#176 = e["pointerId"] +f#176 = (e["pointerId"] | undefined) f#193 = cd["transition"] @@ -4959,6 +4963,7 @@ f#9 = arguments[5] f#919 = ( | bi() + | undefined | !(0) | !(1) | b["memoizedState"] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-effects.snapshot index e3fbd4fd41d36..860b3b8677cdc 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-effects.snapshot @@ -1,4 +1,7 @@ -0 -> 6 conditional = ???*0* +0 -> 6 conditional = ( + | ???*0* + | undefined["current"]["memoizedState"]["isDehydrated"] +) - *0* ???*1*["isDehydrated"] ⚠️ unknown object - *1* ???*2*["memoizedState"] @@ -10,7 +13,7 @@ - *4* arguments[0] ⚠️ function calls are not analyzed yet -6 -> 8 call = (...) => (1 | 2 | 4 | 8 | 16 | 32 | ???*0* | 134217728 | 268435456 | 536870912 | 1073741824 | a | undefined)(???*1*) +6 -> 8 call = (...) => (1 | 2 | 4 | 8 | 16 | 32 | ???*0* | 134217728 | 268435456 | 536870912 | 1073741824 | a | undefined)((???*1* | undefined["pendingLanes"])) - *0* unsupported expression ⚠️ This value might have side effects - *1* ???*2*["pendingLanes"] @@ -33,6 +36,7 @@ | 536870912 | 1073741824 | ???*1* + | undefined["pendingLanes"] | undefined )) - *0* unsupported expression @@ -44,7 +48,7 @@ - *3* arguments[0] ⚠️ function calls are not analyzed yet -9 -> 10 call = (...) => undefined(???*0*, ???*2*) +9 -> 10 call = (...) => undefined((???*0* | undefined), ???*2*) - *0* ???*1*["stateNode"] ⚠️ unknown object - *1* arguments[0] @@ -54,7 +58,7 @@ 9 -> 11 call = module["unstable_now"]() -9 -> 12 call = (...) => undefined(???*0*, module["unstable_now"]()) +9 -> 12 call = (...) => undefined((???*0* | undefined), module["unstable_now"]()) - *0* ???*1*["stateNode"] ⚠️ unknown object - *1* arguments[0] @@ -4188,7 +4192,7 @@ ${(???*0* | ???*7*)}` 552 -> 620 free var = FreeVar(Error) -0 -> 621 conditional = (???*0* | (???*1* ? ???*2* : "")) +552 -> 621 conditional = (???*0* | (???*1* ? ???*2* : "")) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* a @@ -4210,7 +4214,7 @@ ${(???*0* | ???*7*)}` - *4* a ⚠️ circular variable reference -0 -> 625 conditional = ((???*0* | ???*1*) ? ???*5* : "") +552 -> 625 conditional = ((???*0* | ???*1*) ? ???*5* : "") - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* (???*2* ? ???*3* : "") @@ -4237,7 +4241,7 @@ ${La}${a}`((???*0* | (???*1* ? ???*2* : ""))) - *3* a ⚠️ circular variable reference -0 -> 627 unreachable = ???*0* +552 -> 627 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects @@ -4326,7 +4330,16 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -0 -> 649 conditional = (null == (???*0* | ???*1* | null["displayName"] | null["name"] | "" | ???*3*)) +0 -> 649 conditional = (null == ( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | ???*3* +)) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["displayName"] @@ -4348,7 +4361,16 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -649 -> 651 typeof = typeof((???*0* | ???*1* | null["displayName"] | null["name"] | "" | (???*3* ? ???*5* : "ForwardRef"))) +649 -> 651 typeof = typeof(( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*3* ? ???*5* : "ForwardRef") +)) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["displayName"] @@ -4365,7 +4387,16 @@ ${La}${a}`("SuspenseList") ⚠️ circular variable reference 649 -> 652 conditional = ("function" === ???*0*) -- *0* typeof((???*1* | ???*2* | null["displayName"] | null["name"] | "" | ???*4*)) +- *0* typeof(( + | ???*1* + | ???*2* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | ???*4* + )) ⚠️ nested operation - *1* arguments[0] ⚠️ function calls are not analyzed yet @@ -4386,7 +4417,9 @@ ${La}${a}`("SuspenseList") 652 -> 654 conditional = ( | ???*0* + | undefined["displayName"]["displayName"] | null["displayName"]["displayName"] + | undefined["name"]["displayName"] | null["name"]["displayName"] | ""["displayName"] | (???*2* ? ???*4* : "ForwardRef")["displayName"] @@ -4408,7 +4441,16 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -652 -> 657 typeof = typeof((???*0* | ???*1* | null["displayName"] | null["name"] | "" | (???*3* ? ???*5* : "ForwardRef"))) +652 -> 657 typeof = typeof(( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*3* ? ???*5* : "ForwardRef") +)) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["displayName"] @@ -4425,7 +4467,16 @@ ${La}${a}`("SuspenseList") ⚠️ circular variable reference 652 -> 658 conditional = ("string" === ???*0*) -- *0* typeof((???*1* | ???*2* | null["displayName"] | null["name"] | "" | ???*4*)) +- *0* typeof(( + | ???*1* + | ???*2* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | ???*4* + )) ⚠️ nested operation - *1* arguments[0] ⚠️ function calls are not analyzed yet @@ -4472,7 +4523,16 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -658 -> 666 typeof = typeof((???*0* | ???*1* | null["displayName"] | null["name"] | "" | (???*3* ? ???*5* : "ForwardRef"))) +658 -> 666 typeof = typeof(( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*3* ? ???*5* : "ForwardRef") +)) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["displayName"] @@ -4489,7 +4549,16 @@ ${La}${a}`("SuspenseList") ⚠️ circular variable reference 658 -> 667 conditional = ("object" === ???*0*) -- *0* typeof((???*1* | ???*2* | null["displayName"] | null["name"] | "" | ???*4*)) +- *0* typeof(( + | ???*1* + | ???*2* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | ???*4* + )) ⚠️ nested operation - *1* arguments[0] ⚠️ function calls are not analyzed yet @@ -4516,7 +4585,16 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -667 -> 676 conditional = (???*0* | ???*1* | null["displayName"] | null["name"] | "" | (???*3* ? ???*5* : "ForwardRef")) +667 -> 676 conditional = ( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*3* ? ???*5* : "ForwardRef") +) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["displayName"] @@ -4536,6 +4614,7 @@ ${La}${a}`("SuspenseList") | ???*0* | ""["render"]["displayName"] | (???*3* ? ???*5* : "ForwardRef")["render"]["displayName"] + | undefined["displayName"] | ""["displayName"]["displayName"] | (???*7* ? ???*9* : "ForwardRef")["displayName"]["displayName"] | null["displayName"] @@ -4577,7 +4656,7 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -667 -> 682 conditional = (null !== (???*0* | ""["render"] | ""["displayName"] | null | ""["_payload"])) +667 -> 682 conditional = (null !== (???*0* | ""["render"] | undefined | ""["displayName"] | null | ""["_payload"])) - *0* ???*1*["render"] ⚠️ unknown object - *1* arguments[0] @@ -4600,7 +4679,9 @@ ${La}${a}`("SuspenseList") )( ( | ???*0* + | undefined["displayName"]["type"] | null["displayName"]["type"] + | undefined["name"]["type"] | null["name"]["type"] | ""["type"] | (???*2* ? ???*4* : "ForwardRef")["type"] @@ -4623,11 +4704,21 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -667 -> 688 call = (???*0* | ???*1* | null["displayName"] | null["name"] | "" | (???*3* ? ???*5* : "ForwardRef"))( +667 -> 688 call = ( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*3* ? ???*5* : "ForwardRef") +)( ( | ???*7* | ""["render"] | (???*9* ? ???*11* : "ForwardRef")["render"] + | undefined | ""["displayName"] | (???*13* ? ???*15* : "ForwardRef")["displayName"] | null @@ -4693,7 +4784,16 @@ ${La}${a}`("SuspenseList") | ((null !== b) ? b : (Qa(a["type"]) || "Memo")) | Qa(a(b)) )(???*0*) -- *0* (???*1* | ???*2* | null["displayName"] | null["name"] | "" | (???*4* ? ???*6* : "ForwardRef"))(b) +- *0* ( + | ???*1* + | ???*2* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*4* ? ???*6* : "ForwardRef") + )(b) ⚠️ non-function callee - *1* arguments[0] ⚠️ function calls are not analyzed yet @@ -10000,7 +10100,7 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -0 -> 1475 member call = new ???*0*()["get"](???*1*) +0 -> 1475 member call = new ???*0*()["get"]((???*1* | undefined)) - *0* FreeVar(Map) ⚠️ unknown global ⚠️ This value might have side effects @@ -10013,7 +10113,7 @@ ${La}${a}`("SuspenseList") - *0* a ⚠️ sequence with side effects ⚠️ This value might have side effects -- *1* ???*2*(???*5*) +- *1* ???*2*((???*5* | undefined)) ⚠️ unknown callee - *2* ???*3*["get"] ⚠️ unknown object @@ -10038,7 +10138,7 @@ ${La}${a}`("SuspenseList") ⚠️ function calls are not analyzed yet 0 -> 1477 member call = new ???*0*()["set"]( - ???*1*, + (???*1* | undefined), ( | ???*3* | ???*4* @@ -10062,7 +10162,7 @@ ${La}${a}`("SuspenseList") - *3* a ⚠️ sequence with side effects ⚠️ This value might have side effects -- *4* ???*5*(???*8*) +- *4* ???*5*((???*8* | undefined)) ⚠️ unknown callee - *5* ???*6*["get"] ⚠️ unknown object @@ -10108,7 +10208,7 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -0 -> 1482 member call = new ???*0*()["get"](???*1*) +0 -> 1482 member call = new ???*0*()["get"]((???*1* | undefined)) - *0* FreeVar(Map) ⚠️ unknown global ⚠️ This value might have side effects @@ -10121,7 +10221,7 @@ ${La}${a}`("SuspenseList") - *0* a ⚠️ sequence with side effects ⚠️ This value might have side effects -- *1* ???*2*(???*5*) +- *1* ???*2*((???*5* | undefined)) ⚠️ unknown callee - *2* ???*3*["get"] ⚠️ unknown object @@ -10146,7 +10246,7 @@ ${La}${a}`("SuspenseList") ⚠️ function calls are not analyzed yet 0 -> 1484 member call = new ???*0*()["set"]( - ???*1*, + (???*1* | undefined), ( | ???*3* | ???*4* @@ -10170,7 +10270,7 @@ ${La}${a}`("SuspenseList") - *3* a ⚠️ sequence with side effects ⚠️ This value might have side effects -- *4* ???*5*(???*8*) +- *4* ???*5*((???*8* | undefined)) ⚠️ unknown callee - *5* ???*6*["get"] ⚠️ unknown object @@ -19696,7 +19796,7 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -2604 -> 2606 conditional = (???*0* | ???*1*) +2604 -> 2607 conditional = (???*0* | ???*1*) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["return"] @@ -20461,19 +20561,19 @@ ${La}${a}`("SuspenseList") - *0* arguments[1] ⚠️ function calls are not analyzed yet -0 -> 2702 conditional = (8 === (???*0* | undefined)) +0 -> 2701 conditional = (8 === (???*0* | undefined)) - *0* ???*1*["nodeType"] ⚠️ unknown object - *1* arguments[0] ⚠️ function calls are not analyzed yet -2702 -> 2704 conditional = ("/$" === (???*0* | undefined)) +2701 -> 2703 conditional = ("/$" === (???*0* | undefined)) - *0* ???*1*["nodeType"] ⚠️ unknown object - *1* arguments[0] ⚠️ function calls are not analyzed yet -2704 -> 2705 unreachable = ???*0* +2703 -> 2704 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects @@ -29695,11 +29795,7 @@ ${La}${a}`("SuspenseList") - *0* max number of linking steps reached ⚠️ This value might have side effects -0 -> 3780 member call = ???*0*["next"]() -- *0* max number of linking steps reached - ⚠️ This value might have side effects - -0 -> 3784 call = (...) => ( +0 -> 3782 call = (...) => ( | ((null !== e) ? null : h(a, b, `${c}`, d)) | ((c["key"] === e) ? k(a, b, c, d) : null) | ((c["key"] === e) ? l(a, b, c, d) : null) @@ -29719,13 +29815,13 @@ ${La}${a}`("SuspenseList") - *4* arguments[3] ⚠️ function calls are not analyzed yet -0 -> 3785 conditional = (???*0* | ???*1*) +0 -> 3783 conditional = (???*0* | ???*1*) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* max number of linking steps reached ⚠️ This value might have side effects -0 -> 3787 conditional = (???*0* | ???*1* | (null === ???*2*)) +0 -> 3785 conditional = (???*0* | ???*1* | (null === ???*2*)) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* max number of linking steps reached @@ -29736,13 +29832,13 @@ ${La}${a}`("SuspenseList") - *3* max number of linking steps reached ⚠️ This value might have side effects -3787 -> 3788 call = (...) => undefined(???*0*, ???*1*) +3785 -> 3786 call = (...) => undefined(???*0*, ???*1*) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* max number of linking steps reached ⚠️ This value might have side effects -0 -> 3789 call = (...) => (???*0* | (???*1* ? ???*2* : d) | c)(???*3*, ???*4*, (0 | ???*5*)) +0 -> 3787 call = (...) => (???*0* | (???*1* ? ???*2* : d) | c)(???*3*, ???*4*, (0 | ???*5*)) - *0* c ⚠️ sequence with side effects ⚠️ This value might have side effects @@ -29758,7 +29854,11 @@ ${La}${a}`("SuspenseList") - *5* updated with update expression ⚠️ This value might have side effects -0 -> 3790 conditional = (null === ???*0*) +0 -> 3788 conditional = (null === ???*0*) +- *0* max number of linking steps reached + ⚠️ This value might have side effects + +0 -> 3791 member call = ???*0*["next"]() - *0* max number of linking steps reached ⚠️ This value might have side effects @@ -29791,11 +29891,7 @@ ${La}${a}`("SuspenseList") - *0* max number of linking steps reached ⚠️ This value might have side effects -3798 -> 3801 member call = ???*0*["next"]() -- *0* max number of linking steps reached - ⚠️ This value might have side effects - -3798 -> 3803 call = (...) => (???*0* | q(a, d(b["_payload"]), c) | null)(???*1*, ???*2*, ???*4*) +3798 -> 3801 call = (...) => (???*0* | q(a, d(b["_payload"]), c) | null)(???*1*, ???*2*, ???*4*) - *0* b ⚠️ sequence with side effects ⚠️ This value might have side effects @@ -29809,11 +29905,11 @@ ${La}${a}`("SuspenseList") - *4* arguments[3] ⚠️ function calls are not analyzed yet -3798 -> 3804 conditional = (null !== ???*0*) +3798 -> 3802 conditional = (null !== ???*0*) - *0* max number of linking steps reached ⚠️ This value might have side effects -3804 -> 3805 call = (...) => (???*0* | (???*1* ? ???*2* : d) | c)(???*3*, ???*4*, (0 | ???*5*)) +3802 -> 3803 call = (...) => (???*0* | (???*1* ? ???*2* : d) | c)(???*3*, ???*4*, (0 | ???*5*)) - *0* c ⚠️ sequence with side effects ⚠️ This value might have side effects @@ -29829,7 +29925,11 @@ ${La}${a}`("SuspenseList") - *5* updated with update expression ⚠️ This value might have side effects -3804 -> 3806 conditional = (null === ???*0*) +3802 -> 3804 conditional = (null === ???*0*) +- *0* max number of linking steps reached + ⚠️ This value might have side effects + +3798 -> 3807 member call = ???*0*["next"]() - *0* max number of linking steps reached ⚠️ This value might have side effects @@ -29851,11 +29951,7 @@ ${La}${a}`("SuspenseList") - *1* max number of linking steps reached ⚠️ This value might have side effects -3798 -> 3814 member call = ???*0*["next"]() -- *0* max number of linking steps reached - ⚠️ This value might have side effects - -3798 -> 3816 call = (...) => (???*0* | y(a, b, c, f(d["_payload"]), e) | null)(???*1*, ???*2*, (0 | ???*3*), ???*4*, ???*6*) +3798 -> 3814 call = (...) => (???*0* | y(a, b, c, f(d["_payload"]), e) | null)(???*1*, ???*2*, (0 | ???*3*), ???*4*, ???*6*) - *0* h(b, a, ("" + d), e) ⚠️ sequence with side effects ⚠️ This value might have side effects @@ -29873,15 +29969,15 @@ ${La}${a}`("SuspenseList") - *6* arguments[3] ⚠️ function calls are not analyzed yet -3798 -> 3817 conditional = (null !== ???*0*) +3798 -> 3815 conditional = (null !== ???*0*) - *0* max number of linking steps reached ⚠️ This value might have side effects -3817 -> 3818 conditional = ???*0* +3815 -> 3816 conditional = ???*0* - *0* arguments[0] ⚠️ function calls are not analyzed yet -3817 -> 3820 conditional = (???*0* | (null !== ???*1*)) +3815 -> 3818 conditional = (???*0* | (null !== ???*1*)) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["alternate"] @@ -29890,14 +29986,14 @@ ${La}${a}`("SuspenseList") - *2* max number of linking steps reached ⚠️ This value might have side effects -3820 -> 3823 conditional = (null === ???*0*) +3818 -> 3821 conditional = (null === ???*0*) - *0* ???*1*["key"] ⚠️ unknown object ⚠️ This value might have side effects - *1* max number of linking steps reached ⚠️ This value might have side effects -3820 -> 3825 member call = ???*0*["delete"]((???*1* ? (0 | ???*4*) : ???*5*)) +3818 -> 3823 member call = ???*0*["delete"]((???*1* ? (0 | ???*4*) : ???*5*)) - *0* max number of linking steps reached ⚠️ This value might have side effects - *1* (null === ???*2*) @@ -29915,7 +30011,7 @@ ${La}${a}`("SuspenseList") - *6* max number of linking steps reached ⚠️ This value might have side effects -3817 -> 3826 call = (...) => (???*0* | (???*1* ? ???*2* : d) | c)(???*3*, ???*4*, (0 | ???*5*)) +3815 -> 3824 call = (...) => (???*0* | (???*1* ? ???*2* : d) | c)(???*3*, ???*4*, (0 | ???*5*)) - *0* c ⚠️ sequence with side effects ⚠️ This value might have side effects @@ -29931,7 +30027,11 @@ ${La}${a}`("SuspenseList") - *5* updated with update expression ⚠️ This value might have side effects -3817 -> 3827 conditional = (null === ???*0*) +3815 -> 3825 conditional = (null === ???*0*) +- *0* max number of linking steps reached + ⚠️ This value might have side effects + +3798 -> 3828 member call = ???*0*["next"]() - *0* max number of linking steps reached ⚠️ This value might have side effects @@ -43666,7 +43766,10 @@ ${La}${a}`("SuspenseList") - *3* arguments[1] ⚠️ function calls are not analyzed yet -0 -> 5351 call = (...) => undefined({"current": null}, (???*0* | (0 !== ???*4*)["_currentValue"])) +0 -> 5351 call = (...) => undefined( + {"current": null}, + (???*0* | undefined["_currentValue"] | (0 !== ???*4*)["_currentValue"]) +) - *0* ???*1*["_currentValue"] ⚠️ unknown object - *1* ???*2*["_context"] @@ -43678,7 +43781,7 @@ ${La}${a}`("SuspenseList") - *4* unsupported expression ⚠️ This value might have side effects -0 -> 5354 conditional = (null !== (???*0* | ???*3*)) +0 -> 5354 conditional = (null !== (???*0* | undefined | ???*3*)) - *0* ???*1*["_context"] ⚠️ unknown object - *1* ???*2*["type"] @@ -43690,7 +43793,7 @@ ${La}${a}`("SuspenseList") - *4* unsupported expression ⚠️ This value might have side effects -5354 -> 5356 conditional = (null !== ???*0*) +5354 -> 5356 conditional = (null !== (???*0* | undefined["dehydrated"])) - *0* ???*1*["dehydrated"] ⚠️ unknown object - *1* ???*2*["_context"] @@ -43770,7 +43873,7 @@ ${La}${a}`("SuspenseList") - *0* unsupported expression ⚠️ This value might have side effects -5376 -> 5377 conditional = (???*0* | (0 !== ???*3*)) +5376 -> 5377 conditional = (???*0* | undefined | (0 !== ???*3*)) - *0* ???*1*["_context"] ⚠️ unknown object - *1* ???*2*["type"] @@ -43796,7 +43899,7 @@ ${La}${a}`("SuspenseList") - *0* unreachable ⚠️ This value might have side effects -0 -> 5382 conditional = (null !== ???*0*) +0 -> 5382 conditional = (null !== (???*0* | undefined)) - *0* ???*1*["value"] ⚠️ unknown object - *1* ???*2*["memoizedProps"] @@ -43808,7 +43911,7 @@ ${La}${a}`("SuspenseList") - *0* unknown mutation ⚠️ This value might have side effects -0 -> 5388 conditional = (???*0* | (0 !== ???*3*)) +0 -> 5388 conditional = (???*0* | undefined | (0 !== ???*3*)) - *0* ???*1*["_context"] ⚠️ unknown object - *1* ???*2*["type"] @@ -47866,7 +47969,7 @@ ${La}${a}`("SuspenseList") 0 -> 5518 conditional = !((false | true)) -5518 -> 5523 conditional = (null === (null | ???*0* | ???*1*)) +5518 -> 5523 conditional = (null === (null | undefined | ???*0* | ???*1*)) - *0* arguments[1] ⚠️ function calls are not analyzed yet - *1* ???*2*["tail"] @@ -47874,7 +47977,7 @@ ${La}${a}`("SuspenseList") - *2* arguments[0] ⚠️ function calls are not analyzed yet -5518 -> 5529 conditional = (null === (null | ???*0* | ???*1*)) +5518 -> 5529 conditional = (null === (null | undefined | ???*0* | ???*1*)) - *0* arguments[1] ⚠️ function calls are not analyzed yet - *1* ???*2*["tail"] @@ -56116,7 +56219,7 @@ ${La}${a}`("SuspenseList") - *3* arguments[0] ⚠️ function calls are not analyzed yet -0 -> 7504 conditional = (null !== ???*0*) +0 -> 7504 conditional = (null !== (???*0* | undefined)) - *0* ???*1*["memoizedState"] ⚠️ unknown object - *1* arguments[0] @@ -56135,13 +56238,13 @@ ${La}${a}`("SuspenseList") - *1* `https://reactjs.org/docs/error-decoder.html?invariant=${314}` ⚠️ nested operation -0 -> 7510 conditional = (null !== ???*0*) +0 -> 7510 conditional = (null !== (???*0* | undefined)) - *0* ???*1*["stateNode"] ⚠️ unknown object - *1* arguments[0] ⚠️ function calls are not analyzed yet -7510 -> 7512 member call = ???*0*["delete"](???*2*) +7510 -> 7512 member call = (???*0* | undefined)["delete"](???*2*) - *0* ???*1*["stateNode"] ⚠️ unknown object - *1* arguments[0] @@ -56149,7 +56252,7 @@ ${La}${a}`("SuspenseList") - *2* arguments[1] ⚠️ function calls are not analyzed yet -0 -> 7513 call = (...) => undefined(???*0*, (0 | ???*1*)) +0 -> 7513 call = (...) => undefined(???*0*, (0 | ???*1* | undefined["retryLane"])) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["retryLane"] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-explained.snapshot index 45cf0a5fdd7ab..90216f8d3c9a6 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/react-dom-production/resolved-explained.snapshot @@ -4060,7 +4060,16 @@ a#5 = (???*0* | 0 | ???*1*) - *1* updated with update expression ⚠️ This value might have side effects -a#50 = (???*0* | ???*1* | null["displayName"] | null["name"] | "" | (???*3* ? ???*5* : "ForwardRef")) +a#50 = ( + | ???*0* + | ???*1* + | undefined["displayName"] + | null["displayName"] + | undefined["name"] + | null["name"] + | "" + | (???*3* ? ???*5* : "ForwardRef") +) - *0* arguments[0] ⚠️ function calls are not analyzed yet - *1* ???*2*["displayName"] @@ -7563,6 +7572,7 @@ b#50 = ( | ???*0* | ""["render"] | (???*2* ? ???*4* : "ForwardRef")["render"] + | undefined | ""["displayName"] | (???*6* ? ???*8* : "ForwardRef")["displayName"] | null @@ -9282,7 +9292,7 @@ b#986 = ???*0* - *0* arguments[1] ⚠️ function calls are not analyzed yet -b#990 = ???*0* +b#990 = (???*0* | undefined) - *0* ???*1*["stateNode"] ⚠️ unknown object - *1* arguments[0] @@ -11486,7 +11496,7 @@ c#644 = ???*0* - *0* arguments[2] ⚠️ function calls are not analyzed yet -c#645 = (null | ???*0* | ???*1*) +c#645 = (null | undefined | ???*0* | ???*1*) - *0* arguments[1] ⚠️ function calls are not analyzed yet - *1* ???*2*["tail"] @@ -11884,7 +11894,7 @@ c#917 = (0 | ???*0*) - *2* arguments[0] ⚠️ function calls are not analyzed yet -c#918 = (0 | ???*0*) +c#918 = (0 | ???*0* | undefined["retryLane"]) - *0* ???*1*["retryLane"] ⚠️ unknown object - *1* ???*2*["memoizedState"] @@ -12051,6 +12061,7 @@ c#990 = ( | 536870912 | 1073741824 | ???*1* + | undefined["pendingLanes"] | undefined ) - *0* unsupported expression @@ -13878,7 +13889,7 @@ d#621 = (???*0* | 0 | ???*2* | ???*3* | ???*4*) - *4* unsupported assign operation ⚠️ This value might have side effects -d#631 = (???*0* | (0 !== ???*3*)) +d#631 = (???*0* | undefined | (0 !== ???*3*)) - *0* ???*1*["_context"] ⚠️ unknown object - *1* ???*2*["type"] @@ -13951,7 +13962,7 @@ d#644 = ???*0* - *0* arguments[3] ⚠️ function calls are not analyzed yet -d#645 = (null | ???*0* | ???*1*) +d#645 = (null | undefined | ???*0* | ???*1*) - *0* arguments[1] ⚠️ function calls are not analyzed yet - *1* ???*2*["tail"] @@ -14057,7 +14068,7 @@ d#715 = (...) => undefined["bind"](null, ???*0*, ???*1*) - *1* arguments[0] ⚠️ function calls are not analyzed yet -d#716 = (0 | ???*0*) +d#716 = (0 | undefined | ???*0*) - *0* updated with update expression ⚠️ This value might have side effects @@ -14407,7 +14418,7 @@ d#915 = ???*0* - *1* arguments[0] ⚠️ function calls are not analyzed yet -d#918 = ???*0* +d#918 = (???*0* | undefined) - *0* ???*1*["stateNode"] ⚠️ unknown object - *1* arguments[0] @@ -16298,7 +16309,7 @@ e#621 = (???*0* | 0["revealOrder"] | ???*3* | null | ???*5* | ???*6* | null["sib - *6* e ⚠️ circular variable reference -e#631 = ???*0* +e#631 = (???*0* | undefined) - *0* ???*1*["value"] ⚠️ unknown object - *1* ???*2*["memoizedProps"] @@ -16343,7 +16354,7 @@ e#639 = (???*0* | ???*2*) - *12* arguments[0] ⚠️ function calls are not analyzed yet -e#646 = ???*0* +e#646 = (???*0* | undefined) - *0* ???*1*["child"] ⚠️ unknown object - *1* arguments[0] @@ -16577,7 +16588,7 @@ e#9 = ???*0* - *0* arguments[4] ⚠️ function calls are not analyzed yet -e#918 = ???*0* +e#918 = (???*0* | undefined) - *0* ???*1*["memoizedState"] ⚠️ unknown object - *1* arguments[0] @@ -16891,7 +16902,7 @@ f#175 = ???*0* - *0* arguments[5] ⚠️ function calls are not analyzed yet -f#176 = ???*0* +f#176 = (???*0* | undefined) - *0* ???*1*["pointerId"] ⚠️ unknown object - *1* arguments[4] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph-effects.snapshot new file mode 100644 index 0000000000000..b0f4b7667743c --- /dev/null +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph-effects.snapshot @@ -0,0 +1,81 @@ +[ + Member { + obj: Unknown { + original_value: None, + reason: "unsupported expression", + has_side_effects: true, + }, + prop: Constant( + Str( + Atom( + "name", + ), + ), + ), + ast_path: [ + Program( + Script, + ), + Script( + Body( + 0, + ), + ), + Stmt( + Decl, + ), + Decl( + Class, + ), + ClassDecl( + Class, + ), + Class( + Body( + 0, + ), + ), + ClassMember( + ClassProp, + ), + ClassProp( + Value, + ), + Expr( + Member, + ), + ], + span: 39..48, + }, + FreeVar { + var: "this", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 1, + ), + ), + Stmt( + Decl, + ), + Decl( + Var, + ), + VarDecl( + Decls( + 0, + ), + ), + VarDeclarator( + Init, + ), + Expr( + This, + ), + ], + span: 243..247, + }, +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph-explained.snapshot new file mode 100644 index 0000000000000..22194a535e9d9 --- /dev/null +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph-explained.snapshot @@ -0,0 +1,15 @@ +X (const after eval) = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects + +bound_this_ctor = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects + +bound_this_static (const after eval) = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects + +free_this_root (const after eval) = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph.snapshot new file mode 100644 index 0000000000000..8ce2072da48a6 --- /dev/null +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/graph.snapshot @@ -0,0 +1,34 @@ +[ + ( + "X", + Unknown { + original_value: None, + reason: "unsupported expression", + has_side_effects: true, + }, + ), + ( + "bound_this_ctor", + Unknown { + original_value: None, + reason: "unsupported expression", + has_side_effects: true, + }, + ), + ( + "bound_this_static", + Unknown { + original_value: None, + reason: "unsupported expression", + has_side_effects: true, + }, + ), + ( + "free_this_root", + Unknown { + original_value: None, + reason: "unsupported expression", + has_side_effects: true, + }, + ), +] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/input.js b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/input.js new file mode 100644 index 0000000000000..5268ad7beb8c9 --- /dev/null +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/input.js @@ -0,0 +1,11 @@ +class X { + static bound_this_field = this.name + static { + const bound_this_static = this; + } + constructor() { + const bound_this_ctor = this; + } +} +// We should generate a freevar effect for this and only this +const free_this_root = this; diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/resolved-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/resolved-effects.snapshot new file mode 100644 index 0000000000000..cce0004307164 --- /dev/null +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/resolved-effects.snapshot @@ -0,0 +1 @@ +0 -> 2 free var = FreeVar(this) diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/resolved-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/resolved-explained.snapshot new file mode 100644 index 0000000000000..5df4c930d0c0d --- /dev/null +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/this-binding/resolved-explained.snapshot @@ -0,0 +1,15 @@ +X = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects + +bound_this_ctor = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects + +bound_this_static = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects + +free_this_root = ???*0* +- *0* unsupported expression + ⚠️ This value might have side effects diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/graph-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/graph-effects.snapshot index d9fc7ae9da589..c35800cb9f5f9 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/graph-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/graph-effects.snapshot @@ -166,47 +166,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Module, - ), - Module( - Body( - 1, - ), - ), - ModuleItem( - Stmt, - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 1, - ), - ), - Stmt( - While, - ), - WhileStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, ImportedBinding { esm_reference_index: 7, export: Some( @@ -520,47 +479,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Module, - ), - Module( - Body( - 2, - ), - ), - ModuleItem( - Stmt, - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - While, - ), - WhileStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, ImportedBinding { esm_reference_index: 2, export: Some( @@ -874,47 +792,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Module, - ), - Module( - Body( - 3, - ), - ), - ModuleItem( - Stmt, - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - While, - ), - WhileStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, ImportedBinding { esm_reference_index: 4, export: Some( diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/resolved-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/resolved-effects.snapshot index 6cc3fbce3229e..ddb14bba93f37 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/resolved-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable-break/resolved-effects.snapshot @@ -4,32 +4,20 @@ - *0* unreachable ⚠️ This value might have side effects -0 -> 4 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects - -0 -> 6 call = module["unreachableA3"]() +0 -> 5 call = module["unreachableA3"]() -0 -> 8 call = module["unreachableB1"]() +0 -> 7 call = module["unreachableB1"]() -0 -> 9 unreachable = ???*0* +0 -> 8 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects -0 -> 10 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects +0 -> 10 call = module["calledB1"]() -0 -> 12 call = module["calledB1"]() - -0 -> 14 call = module["calledC1"]() - -0 -> 15 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects +0 -> 12 call = module["calledC1"]() -0 -> 16 unreachable = ???*0* +0 -> 13 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects -0 -> 18 call = module["calledC2"]() +0 -> 15 call = module["calledC2"]() diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-effects.snapshot index ad7400175ee9c..f8295c3a8a7fd 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-effects.snapshot @@ -293,44 +293,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Script, - ), - Script( - Body( - 1, - ), - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - While, - ), - WhileStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, FreeVar { var: "b3", ast_path: [ @@ -593,44 +555,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Script, - ), - Script( - Body( - 2, - ), - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - DoWhile, - ), - DoWhileStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, FreeVar { var: "c3", ast_path: [ @@ -893,44 +817,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Script, - ), - Script( - Body( - 3, - ), - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - For, - ), - ForStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, FreeVar { var: "d3", ast_path: [ @@ -1193,44 +1079,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Script, - ), - Script( - Body( - 4, - ), - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - ForIn, - ), - ForInStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, FreeVar { var: "e3", ast_path: [ @@ -1493,44 +1341,6 @@ ), ], }, - Unreachable { - start_ast_path: [ - Program( - Script, - ), - Script( - Body( - 5, - ), - ), - Stmt( - Decl, - ), - Decl( - Fn, - ), - FnDecl( - Function, - ), - Function( - Body, - ), - BlockStmt( - Stmts( - 0, - ), - ), - Stmt( - ForOf, - ), - ForOfStmt( - Body, - ), - Stmt( - Block, - ), - ], - }, FreeVar { var: "f3", ast_path: [ @@ -2210,4 +2020,186 @@ in_try: false, new: false, }, + FreeVar { + var: "i1", + ast_path: [ + Program( + Script, + ), + Script( + Body( + 8, + ), + ), + Stmt( + Decl, + ), + Decl( + Fn, + ), + FnDecl( + Function, + ), + Function( + Body, + ), + BlockStmt( + Stmts( + 0, + ), + ), + Stmt( + Block, + ), + BlockStmt( + Stmts( + 0, + ), + ), + Stmt( + Expr, + ), + ExprStmt( + Expr, + ), + Expr( + Call, + ), + CallExpr( + Callee, + ), + Callee( + Expr, + ), + Expr( + Ident, + ), + ], + span: 645..647, + }, + Call { + func: FreeVar( + "i1", + ), + args: [], + ast_path: [ + Program( + Script, + ), + Script( + Body( + 8, + ), + ), + Stmt( + Decl, + ), + Decl( + Fn, + ), + FnDecl( + Function, + ), + Function( + Body, + ), + BlockStmt( + Stmts( + 0, + ), + ), + Stmt( + Block, + ), + BlockStmt( + Stmts( + 0, + ), + ), + Stmt( + Expr, + ), + ExprStmt( + Expr, + ), + Expr( + Call, + ), + ], + span: 645..649, + in_try: false, + new: false, + }, + Unreachable { + start_ast_path: [ + Program( + Script, + ), + Script( + Body( + 8, + ), + ), + Stmt( + Decl, + ), + Decl( + Fn, + ), + FnDecl( + Function, + ), + Function( + Body, + ), + BlockStmt( + Stmts( + 0, + ), + ), + Stmt( + Block, + ), + BlockStmt( + Stmts( + 1, + ), + ), + Stmt( + Return, + ), + ], + }, + Unreachable { + start_ast_path: [ + Program( + Script, + ), + Script( + Body( + 8, + ), + ), + Stmt( + Decl, + ), + Decl( + Fn, + ), + FnDecl( + Function, + ), + Function( + Body, + ), + BlockStmt( + Stmts( + 0, + ), + ), + Stmt( + Block, + ), + ], + }, ] diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-explained.snapshot index 8b13955e4d91a..5bc89d2b2e22b 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph-explained.snapshot @@ -20,6 +20,8 @@ g (const after eval) = (...) => undefined h (const after eval) = (...) => undefined +i (const after eval) = (...) => undefined + x#10 = ???*0* - *0* for-in variable currently not analyzed diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph.snapshot index c3f462aa2d57e..10d56e31fc62d 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/graph.snapshot @@ -104,6 +104,16 @@ ), ), ), + ( + "i", + Function( + 2, + 622, + Constant( + Undefined, + ), + ), + ), ( "x#10", Unknown { diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/input.js b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/input.js index 63badf25f3c15..8a694245f4c7a 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/input.js +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/input.js @@ -67,3 +67,11 @@ function h() { }; h3(); } +function i() { + { + i1(); + return; + i2(); + } + i3(); +} diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-effects.snapshot index 408e148350602..ddcf2187ef313 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-effects.snapshot @@ -20,109 +20,107 @@ - *0* unreachable ⚠️ This value might have side effects -0 -> 7 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects - -0 -> 8 free var = FreeVar(b3) +0 -> 7 free var = FreeVar(b3) -0 -> 9 call = ???*0*() +0 -> 8 call = ???*0*() - *0* FreeVar(b3) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 10 free var = FreeVar(c1) +0 -> 9 free var = FreeVar(c1) -0 -> 11 call = ???*0*() +0 -> 10 call = ???*0*() - *0* FreeVar(c1) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 12 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects - -0 -> 13 unreachable = ???*0* +0 -> 11 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects -0 -> 14 free var = FreeVar(c3) +0 -> 12 free var = FreeVar(c3) -0 -> 15 call = ???*0*() +0 -> 13 call = ???*0*() - *0* FreeVar(c3) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 16 free var = FreeVar(d1) +0 -> 14 free var = FreeVar(d1) -0 -> 17 call = ???*0*() +0 -> 15 call = ???*0*() - *0* FreeVar(d1) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 18 unreachable = ???*0* +0 -> 16 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects -0 -> 19 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects +0 -> 17 free var = FreeVar(d3) -0 -> 20 free var = FreeVar(d3) - -0 -> 21 call = ???*0*() +0 -> 18 call = ???*0*() - *0* FreeVar(d3) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 22 free var = FreeVar(e1) +0 -> 19 free var = FreeVar(e1) -0 -> 23 call = ???*0*() +0 -> 20 call = ???*0*() - *0* FreeVar(e1) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 24 unreachable = ???*0* -- *0* unreachable - ⚠️ This value might have side effects - -0 -> 25 unreachable = ???*0* +0 -> 21 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects -0 -> 26 free var = FreeVar(e3) +0 -> 22 free var = FreeVar(e3) -0 -> 27 call = ???*0*() +0 -> 23 call = ???*0*() - *0* FreeVar(e3) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 28 free var = FreeVar(f1) +0 -> 24 free var = FreeVar(f1) -0 -> 29 call = ???*0*() +0 -> 25 call = ???*0*() - *0* FreeVar(f1) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 30 unreachable = ???*0* +0 -> 26 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects +0 -> 27 free var = FreeVar(f3) + +0 -> 28 call = ???*0*() +- *0* FreeVar(f3) + ⚠️ unknown global + ⚠️ This value might have side effects + +0 -> 29 free var = FreeVar(g1) + +0 -> 30 call = ???*0*() +- *0* FreeVar(g1) + ⚠️ unknown global + ⚠️ This value might have side effects + 0 -> 31 unreachable = ???*0* - *0* unreachable ⚠️ This value might have side effects -0 -> 32 free var = FreeVar(f3) +0 -> 32 free var = FreeVar(g3) 0 -> 33 call = ???*0*() -- *0* FreeVar(f3) +- *0* FreeVar(g3) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 34 free var = FreeVar(g1) +0 -> 34 free var = FreeVar(h1) 0 -> 35 call = ???*0*() -- *0* FreeVar(g1) +- *0* FreeVar(h1) ⚠️ unknown global ⚠️ This value might have side effects @@ -130,17 +128,17 @@ - *0* unreachable ⚠️ This value might have side effects -0 -> 37 free var = FreeVar(g3) +0 -> 37 free var = FreeVar(h3) 0 -> 38 call = ???*0*() -- *0* FreeVar(g3) +- *0* FreeVar(h3) ⚠️ unknown global ⚠️ This value might have side effects -0 -> 39 free var = FreeVar(h1) +0 -> 39 free var = FreeVar(i1) 0 -> 40 call = ???*0*() -- *0* FreeVar(h1) +- *0* FreeVar(i1) ⚠️ unknown global ⚠️ This value might have side effects @@ -148,9 +146,6 @@ - *0* unreachable ⚠️ This value might have side effects -0 -> 42 free var = FreeVar(h3) - -0 -> 43 call = ???*0*() -- *0* FreeVar(h3) - ⚠️ unknown global +0 -> 42 unreachable = ???*0* +- *0* unreachable ⚠️ This value might have side effects diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-explained.snapshot index 5cf472b05ae83..bf53673346112 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/unreachable/resolved-explained.snapshot @@ -20,6 +20,8 @@ g = (...) => undefined h = (...) => undefined +i = (...) => undefined + x#10 = ???*0* - *0* for-in variable currently not analyzed diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph-explained.snapshot index 4215290a9b58f..5440276abc611 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph-explained.snapshot @@ -6,19 +6,19 @@ __unused_webpack_module = arguments[0] -__webpack_exec__ = (*arrow function 1213* | undefined) +__webpack_exec__ = *arrow function 1213* -__webpack_exports__#3 = (__webpack_exec__(5166) | undefined) +__webpack_exports__#3 = __webpack_exec__(5166) __webpack_exports__#4 = arguments[1] -__webpack_require__#3 = (FreeVar(require)("../../webpack-api-runtime.js") | undefined) +__webpack_require__#3 = FreeVar(require)("../../webpack-api-runtime.js") __webpack_require__#4 = arguments[2] _req = arguments[0] -exports = ({} | undefined) +exports = {} handler = (...) => Promise diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph.snapshot index d96e7425a1dc4..c5db7eb80083d 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/graph.snapshot @@ -55,51 +55,33 @@ ), ( "__webpack_exec__", - Alternatives { - total_nodes: 3, - values: [ - Variable( - ( - "*arrow function 1213*", - #0, - ), - ), - Constant( - Undefined, - ), - ], - logical_property: None, - }, + Variable( + ( + "*arrow function 1213*", + #0, + ), + ), ), ( "__webpack_exports__#3", - Alternatives { - total_nodes: 5, - values: [ - Call( - 3, - Variable( - ( - "__webpack_exec__", - #3, - ), - ), - [ - Constant( - Num( - ConstantNumber( - 5166.0, - ), - ), - ), - ], + Call( + 3, + Variable( + ( + "__webpack_exec__", + #3, ), + ), + [ Constant( - Undefined, + Num( + ConstantNumber( + 5166.0, + ), + ), ), ], - logical_property: None, - }, + ), ), ( "__webpack_exports__#4", @@ -110,30 +92,21 @@ ), ( "__webpack_require__#3", - Alternatives { - total_nodes: 5, - values: [ - Call( - 3, - FreeVar( - "require", - ), - [ - Constant( - Str( - Atom( - "../../webpack-api-runtime.js", - ), - ), - ), - ], - ), + Call( + 3, + FreeVar( + "require", + ), + [ Constant( - Undefined, + Str( + Atom( + "../../webpack-api-runtime.js", + ), + ), ), ], - logical_property: None, - }, + ), ), ( "__webpack_require__#4", @@ -151,19 +124,10 @@ ), ( "exports", - Alternatives { - total_nodes: 3, - values: [ - Object { - total_nodes: 1, - parts: [], - mutable: true, - }, - Constant( - Undefined, - ), - ], - logical_property: None, + Object { + total_nodes: 1, + parts: [], + mutable: true, }, ), ( diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-effects.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-effects.snapshot index 9efdc7839a1ee..a8b83592bbd8d 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-effects.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-effects.snapshot @@ -39,12 +39,12 @@ 0 -> 17 call = require*0*("../../webpack-api-runtime.js") - *0* require: The require method from CommonJS -0 -> 19 member call = (module<../../webpack-api-runtime.js, {}> | undefined)["C"](({} | undefined)) +0 -> 19 member call = module<../../webpack-api-runtime.js, {}>["C"]({}) -0 -> 21 call = (module<../../webpack-api-runtime.js, {}> | undefined)(???*0*) +0 -> 21 call = module<../../webpack-api-runtime.js, {}>(???*0*) - *0* arguments[0] ⚠️ function calls are not analyzed yet -0 -> 22 call = ((...) => __webpack_require__(moduleId) | undefined)(5166) +0 -> 22 call = (...) => __webpack_require__(moduleId)(5166) 0 -> 24 free var = FreeVar(module) diff --git a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-explained.snapshot b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-explained.snapshot index e20bcaf3522fb..dc2ff73c54bc2 100644 --- a/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-explained.snapshot +++ b/turbopack/crates/turbopack-ecmascript/tests/analyzer/graph/webpack-target-node/resolved-explained.snapshot @@ -8,17 +8,15 @@ __unused_webpack_module = ???*0* - *0* arguments[0] ⚠️ function calls are not analyzed yet -__webpack_exec__ = ((...) => __webpack_require__(moduleId) | undefined) +__webpack_exec__ = (...) => __webpack_require__(moduleId) -__webpack_exports__#3 = (???*0* | undefined) -- *0* ((...) => __webpack_require__(moduleId) | undefined)(5166) - ⚠️ non-function callee +__webpack_exports__#3 = module<../../webpack-api-runtime.js, {}>(5166) __webpack_exports__#4 = ???*0* - *0* arguments[1] ⚠️ function calls are not analyzed yet -__webpack_require__#3 = (module<../../webpack-api-runtime.js, {}> | undefined) +__webpack_require__#3 = module<../../webpack-api-runtime.js, {}> __webpack_require__#4 = ???*0* - *0* arguments[2] @@ -28,7 +26,7 @@ _req = ???*0* - *0* arguments[0] ⚠️ function calls are not analyzed yet -exports = ({} | undefined) +exports = {} handler = (...) => Promise diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/module.js b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/module.js index 11178a6a6d1c0..9de12b02e747a 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/module.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/module.js @@ -200,6 +200,24 @@ function p() { p4() } +function q() { + while (false) { + q1() + return + q2() + } + q3() +} + +function r() { + { + r1() + return + r2() + } + r3() +} + z1() return diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js index d2d69df5fe8df..b97218e0fc60b 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js +++ b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js @@ -195,6 +195,25 @@ function p() { //TURBOPACK unreachable ; } +function q() { + while(false){ + q1(); + return; + //TURBOPACK unreachable + ; + } + q3(); +} +function r() { + { + r1(); + return; + //TURBOPACK unreachable + ; + } + //TURBOPACK unreachable + ; +} z1(); return; z2(); diff --git a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js.map b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js.map index a47a43d026965..f223d4bc24fc6 100644 --- a/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js.map +++ b/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/output/aaf3a_crates_turbopack-tests_tests_snapshot_comptime_early-return_input_022ddac5._.js.map @@ -2,6 +2,6 @@ "version": 3, "sources": [], "sections": [ - {"offset": {"line": 4, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/module.js"],"sourcesContent":["export function a() {\n if (true) {\n a1()\n return\n }\n a2()\n var a3 = 3\n function a4() {\n var a5\n }\n ;(function a6() {\n var a7\n })\n const a8 = () => {\n var a9\n }\n class a10 {}\n let a11 = 11\n let {\n a12 = 12,\n a14: {\n a15,\n a16: [a17, ...a18],\n },\n ...a19\n } = {}\n function a20() {\n return\n a21()\n }\n ;({\n get a22() {\n var a23\n },\n set a22(value) {\n var a24\n },\n a25() {\n var a26\n },\n })\n {\n let a27\n var a28\n }\n}\n\nexport function b() {\n if (true) {\n b1()\n return\n } else {\n b2()\n }\n b3()\n}\n\nexport function c() {\n if (true) {\n return\n }\n c1()\n}\n\nexport function d() {\n if (true) {\n return\n } else {\n d1()\n }\n d2()\n}\n\nexport function e() {\n if (false) {\n e1()\n } else {\n return\n }\n e2()\n}\n\nexport function f() {\n if (false) {\n } else {\n return\n }\n f1()\n}\n\nexport function g() {\n if (false) {\n g1()\n } else {\n g2()\n return\n }\n g3()\n}\n\nexport function h() {\n if (false) {\n } else {\n h1()\n return\n }\n h2()\n}\n\nexport function i(j) {\n if (j < 1) return i1()\n return i2()\n}\n\nexport function j(j) {\n if (j < 1) {\n return i1()\n }\n return i2()\n}\n\nclass K {\n constructor() {\n try {\n k1()\n } catch (e) {\n k2()\n return\n k3()\n } finally {\n k4()\n }\n k5()\n }\n\n l() {\n try {\n l1()\n } catch (e) {\n l2()\n } finally {\n l3()\n return\n l4()\n }\n l5()\n }\n\n get m() {\n if (true) {\n m1()\n return\n }\n m2()\n }\n\n set m(value) {\n m1()\n return m2()\n m3()\n }\n\n n = () => {\n switch (42) {\n case 1:\n n1()\n return\n n2()\n case 2:\n n3()\n break\n default:\n n4()\n }\n n5()\n }\n\n o() {\n if (something) {\n require('./module')\n return\n } else {\n require('./module')\n return\n }\n }\n}\n\nfunction p() {\n class C {\n constructor() {\n p1()\n return\n p2()\n }\n }\n\n p3()\n return\n p4()\n}\n\nz1()\n\nreturn\n\nz2()\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAO,SAAS;IACd,wCAAU;QACR;QACA;IACF;;;IAEA,IAAI;IACJ,SAAS;QACP,IAAI;IACN;IAIA,MAAM;IAGA,IAAA;IACN,IAAI;IACJ,IACE,KAEE,KACM,KAAQ,KAEb;IAEL,SAAS;QACP;;;IAEF;IAcE,IAAI;AAER;AAEO,SAAS;IACd,wCAAU;QACR;QACA;IACF;;;;AAIF;AAEO,SAAS;IACd,wCAAU;QACR;IACF;;;AAEF;AAEO,SAAS;IACd,wCAAU;QACR;IACF;;;;AAIF;AAEO,SAAS;IACd;;SAEO;QACL;IACF;;;AAEF;AAEO,SAAS;IACd,uCAAW,CACX,OAAO;QACL;IACF;;;AAEF;AAEO,SAAS;IACd;;SAEO;QACL;QACA;IACF;;;AAEF;AAEO,SAAS;IACd,uCAAW,CACX,OAAO;QACL;QACA;IACF;;;AAEF;AAEO,SAAS,EAAE,CAAC;IACjB,IAAI,IAAI,GAAG,OAAO;IAClB,OAAO;AACT;AAEO,SAAS,EAAE,CAAC;IACjB,IAAI,IAAI,GAAG;QACT,OAAO;IACT;IACA,OAAO;AACT;AAEA,MAAM;IACJ,aAAc;QACZ,IAAI;YACF;QACF,EAAE,OAAO,GAAG;YACV;YACA;;;QAEF,SAAU;YACR;QACF;QACA;IACF;IAEA,IAAI;QACF,IAAI;YACF;QACF,EAAE,OAAO,GAAG;YACV;QACF,SAAU;YACR;YACA;;;QAEF;;;IAEF;IAEA,IAAI,IAAI;QACN,wCAAU;YACR;YACA;QACF;;;IAEF;IAEA,IAAI,EAAE,KAAK,EAAE;QACX;QACA,OAAO;;;IAET;IAEA,IAAI;QACF,OAAQ;YACN,KAAK;gBACH;gBACA;;;YAEF,KAAK;gBACH;gBACA;YACF;gBACE;QACJ;QACA;IACF,EAAC;IAED,IAAI;QACF,IAAI,WAAW;;YAEb;QACF,OAAO;;YAEL;QACF;IACF;AACF;AAEA,SAAS;IACP,MAAM;QACJ,aAAc;YACZ;YACA;;;QAEF;IACF;IAEA;IACA;;;AAEF;AAEA;AAEA;AAEA"}}, - {"offset": {"line": 204, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/index.js"],"sourcesContent":["import * as module from './module'\nconsole.log(module)\n"],"names":[],"mappings":";AAAA;;AACA,QAAQ,GAAG,CAAC"}}] + {"offset": {"line": 4, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/module.js"],"sourcesContent":["export function a() {\n if (true) {\n a1()\n return\n }\n a2()\n var a3 = 3\n function a4() {\n var a5\n }\n ;(function a6() {\n var a7\n })\n const a8 = () => {\n var a9\n }\n class a10 {}\n let a11 = 11\n let {\n a12 = 12,\n a14: {\n a15,\n a16: [a17, ...a18],\n },\n ...a19\n } = {}\n function a20() {\n return\n a21()\n }\n ;({\n get a22() {\n var a23\n },\n set a22(value) {\n var a24\n },\n a25() {\n var a26\n },\n })\n {\n let a27\n var a28\n }\n}\n\nexport function b() {\n if (true) {\n b1()\n return\n } else {\n b2()\n }\n b3()\n}\n\nexport function c() {\n if (true) {\n return\n }\n c1()\n}\n\nexport function d() {\n if (true) {\n return\n } else {\n d1()\n }\n d2()\n}\n\nexport function e() {\n if (false) {\n e1()\n } else {\n return\n }\n e2()\n}\n\nexport function f() {\n if (false) {\n } else {\n return\n }\n f1()\n}\n\nexport function g() {\n if (false) {\n g1()\n } else {\n g2()\n return\n }\n g3()\n}\n\nexport function h() {\n if (false) {\n } else {\n h1()\n return\n }\n h2()\n}\n\nexport function i(j) {\n if (j < 1) return i1()\n return i2()\n}\n\nexport function j(j) {\n if (j < 1) {\n return i1()\n }\n return i2()\n}\n\nclass K {\n constructor() {\n try {\n k1()\n } catch (e) {\n k2()\n return\n k3()\n } finally {\n k4()\n }\n k5()\n }\n\n l() {\n try {\n l1()\n } catch (e) {\n l2()\n } finally {\n l3()\n return\n l4()\n }\n l5()\n }\n\n get m() {\n if (true) {\n m1()\n return\n }\n m2()\n }\n\n set m(value) {\n m1()\n return m2()\n m3()\n }\n\n n = () => {\n switch (42) {\n case 1:\n n1()\n return\n n2()\n case 2:\n n3()\n break\n default:\n n4()\n }\n n5()\n }\n\n o() {\n if (something) {\n require('./module')\n return\n } else {\n require('./module')\n return\n }\n }\n}\n\nfunction p() {\n class C {\n constructor() {\n p1()\n return\n p2()\n }\n }\n\n p3()\n return\n p4()\n}\n\nfunction q() {\n while (false) {\n q1()\n return\n q2()\n }\n q3()\n}\n\nfunction r() {\n {\n r1()\n return\n r2()\n }\n r3()\n}\n\nz1()\n\nreturn\n\nz2()\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAO,SAAS;IACd,wCAAU;QACR;QACA;IACF;;;IAEA,IAAI;IACJ,SAAS;QACP,IAAI;IACN;IAIA,MAAM;IAGA,IAAA;IACN,IAAI;IACJ,IACE,KAEE,KACM,KAAQ,KAEb;IAEL,SAAS;QACP;;;IAEF;IAcE,IAAI;AAER;AAEO,SAAS;IACd,wCAAU;QACR;QACA;IACF;;;;AAIF;AAEO,SAAS;IACd,wCAAU;QACR;IACF;;;AAEF;AAEO,SAAS;IACd,wCAAU;QACR;IACF;;;;AAIF;AAEO,SAAS;IACd;;SAEO;QACL;IACF;;;AAEF;AAEO,SAAS;IACd,uCAAW,CACX,OAAO;QACL;IACF;;;AAEF;AAEO,SAAS;IACd;;SAEO;QACL;QACA;IACF;;;AAEF;AAEO,SAAS;IACd,uCAAW,CACX,OAAO;QACL;QACA;IACF;;;AAEF;AAEO,SAAS,EAAE,CAAC;IACjB,IAAI,IAAI,GAAG,OAAO;IAClB,OAAO;AACT;AAEO,SAAS,EAAE,CAAC;IACjB,IAAI,IAAI,GAAG;QACT,OAAO;IACT;IACA,OAAO;AACT;AAEA,MAAM;IACJ,aAAc;QACZ,IAAI;YACF;QACF,EAAE,OAAO,GAAG;YACV;YACA;;;QAEF,SAAU;YACR;QACF;QACA;IACF;IAEA,IAAI;QACF,IAAI;YACF;QACF,EAAE,OAAO,GAAG;YACV;QACF,SAAU;YACR;YACA;;;QAEF;;;IAEF;IAEA,IAAI,IAAI;QACN,wCAAU;YACR;YACA;QACF;;;IAEF;IAEA,IAAI,EAAE,KAAK,EAAE;QACX;QACA,OAAO;;;IAET;IAEA,IAAI;QACF,OAAQ;YACN,KAAK;gBACH;gBACA;;;YAEF,KAAK;gBACH;gBACA;YACF;gBACE;QACJ;QACA;IACF,EAAC;IAED,IAAI;QACF,IAAI,WAAW;;YAEb;QACF,OAAO;;YAEL;QACF;IACF;AACF;AAEA,SAAS;IACP,MAAM;QACJ,aAAc;YACZ;YACA;;;QAEF;IACF;IAEA;IACA;;;AAEF;AAEA,SAAS;IACP,MAAO,MAAO;QACZ;QACA;;;IAEF;IACA;AACF;AAEA,SAAS;IACP;QACE;QACA;;;IAEF;;;AAEF;AAEA;AAEA;AAEA"}}, + {"offset": {"line": 223, "column": 0}, "map": {"version":3,"sources":["turbopack:///[project]/turbopack/crates/turbopack-tests/tests/snapshot/comptime/early-return/input/index.js"],"sourcesContent":["import * as module from './module'\nconsole.log(module)\n"],"names":[],"mappings":";AAAA;;AACA,QAAQ,GAAG,CAAC"}}] } \ No newline at end of file