@@ -3,7 +3,7 @@ use rustc_hash::FxHashSet;
33use oxc_allocator:: { ArenaVec , ReplaceWith , TakeIn } ;
44use oxc_ast:: { ast:: * , builder:: NONE } ;
55use oxc_semantic:: { ScopeFlags , ScopeId } ;
6- use oxc_span:: SPAN ;
6+ use oxc_span:: { GetSpan , SPAN } ;
77use oxc_str:: Ident ;
88use oxc_syntax:: operator:: AssignmentOperator ;
99use oxc_traverse:: BoundIdentifier ;
@@ -411,13 +411,28 @@ impl<'a> TypeScript<'a> {
411411 return ;
412412 }
413413
414- let params = & constructor. value . params . items ;
415- let assignments = Self :: convert_constructor_params ( params, ctx) . collect :: < Vec < _ > > ( ) ;
414+ let constructor_scope_id = constructor. value . scope_id ( ) ;
415+ let Function { params, body, .. } = & mut * constructor. value ;
416+ let params = & params. items ;
417+ if !params
418+ . iter ( )
419+ . any ( |param| param. has_modifier ( ) && param. pattern . get_binding_identifier ( ) . is_some ( ) )
420+ {
421+ return ;
422+ }
416423
417- let constructor_body_statements = & mut constructor . value . body . as_mut ( ) . unwrap ( ) . statements ;
424+ let constructor_body_statements = & mut body. as_mut ( ) . unwrap ( ) . statements ;
418425 let super_call_position = Self :: get_super_call_position ( constructor_body_statements) ;
426+ if super_call_position > 0
427+ && let Statement :: IfStatement ( stmt) =
428+ & mut constructor_body_statements[ super_call_position - 1 ]
429+ && Self :: can_insert_constructor_params_in_if_branches ( stmt)
430+ {
431+ Self :: insert_constructor_params_in_if_branches ( stmt, params, constructor_scope_id, ctx) ;
432+ return ;
433+ }
419434
420- // Insert the assignments after the `super()` call
435+ let assignments = Self :: convert_constructor_params ( params , ctx ) ;
421436 constructor_body_statements. splice ( super_call_position..super_call_position, assignments) ;
422437 }
423438
@@ -479,19 +494,111 @@ impl<'a> TypeScript<'a> {
479494 Self :: create_assignment ( target, value, ctx)
480495 }
481496
482- /// Find the position of the `super()` call in the constructor body, otherwise return 0.
497+ /// Find the position after the `super()` call in the constructor body, otherwise return 0.
483498 ///
484- /// Don't need to handle nested `super()` call because `TypeScript` doesn't allow it.
499+ /// If `super()` is nested inside a top-level control flow statement, return the position after
500+ /// the containing statement.
485501 pub fn get_super_call_position ( statements : & [ Statement < ' a > ] ) -> usize {
486- // Find the position of the `super()` call in the constructor body.
487- // Don't need to handle nested `super()` call because `TypeScript` doesn't allow it.
488- statements
489- . iter ( )
490- . position ( |stmt| {
491- matches ! ( stmt, Statement :: ExpressionStatement ( stmt)
492- if stmt. expression. is_super_call_expression( ) )
493- } )
494- . map_or ( 0 , |pos| pos + 1 )
502+ statements. iter ( ) . position ( Self :: statement_contains_super_call) . map_or ( 0 , |pos| pos + 1 )
503+ }
504+
505+ fn statement_contains_super_call ( stmt : & Statement < ' a > ) -> bool {
506+ if Self :: statement_contains_direct_super_call ( stmt) {
507+ return true ;
508+ }
509+
510+ match stmt {
511+ Statement :: BlockStatement ( stmt) => {
512+ stmt. body . iter ( ) . any ( Self :: statement_contains_super_call)
513+ }
514+ Statement :: IfStatement ( stmt) => {
515+ Self :: statement_contains_super_call ( & stmt. consequent )
516+ || stmt. alternate . as_ref ( ) . is_some_and ( Self :: statement_contains_super_call)
517+ }
518+ Statement :: SwitchStatement ( stmt) => stmt
519+ . cases
520+ . iter ( )
521+ . any ( |case| case. consequent . iter ( ) . any ( Self :: statement_contains_super_call) ) ,
522+ Statement :: TryStatement ( stmt) => {
523+ stmt. block . body . iter ( ) . any ( Self :: statement_contains_super_call)
524+ || stmt. handler . as_ref ( ) . is_some_and ( |handler| {
525+ handler. body . body . iter ( ) . any ( Self :: statement_contains_super_call)
526+ } )
527+ || stmt. finalizer . as_ref ( ) . is_some_and ( |block| {
528+ block. body . iter ( ) . any ( Self :: statement_contains_super_call)
529+ } )
530+ }
531+ Statement :: LabeledStatement ( stmt) => Self :: statement_contains_super_call ( & stmt. body ) ,
532+ _ => false ,
533+ }
534+ }
535+
536+ fn can_insert_constructor_params_in_if_branches ( stmt : & IfStatement < ' a > ) -> bool {
537+ Self :: can_insert_constructor_params_in_if_branch ( & stmt. consequent )
538+ && stmt. alternate . as_ref ( ) . is_some_and ( Self :: can_insert_constructor_params_in_if_branch)
539+ }
540+
541+ fn can_insert_constructor_params_in_if_branch ( stmt : & Statement < ' a > ) -> bool {
542+ Self :: statement_contains_direct_super_call ( stmt)
543+ || matches ! ( stmt, Statement :: BlockStatement ( block)
544+ if block. body. iter( ) . any( Self :: statement_contains_direct_super_call) )
545+ }
546+
547+ fn insert_constructor_params_in_if_branches (
548+ stmt : & mut IfStatement < ' a > ,
549+ params : & ArenaVec < ' a , FormalParameter < ' a > > ,
550+ constructor_scope_id : ScopeId ,
551+ ctx : & mut TraverseCtx < ' a > ,
552+ ) {
553+ Self :: insert_constructor_params_in_if_branch (
554+ & mut stmt. consequent ,
555+ params,
556+ constructor_scope_id,
557+ ctx,
558+ ) ;
559+ Self :: insert_constructor_params_in_if_branch (
560+ stmt. alternate . as_mut ( ) . unwrap ( ) ,
561+ params,
562+ constructor_scope_id,
563+ ctx,
564+ ) ;
565+ }
566+
567+ fn insert_constructor_params_in_if_branch (
568+ stmt : & mut Statement < ' a > ,
569+ params : & ArenaVec < ' a , FormalParameter < ' a > > ,
570+ constructor_scope_id : ScopeId ,
571+ ctx : & mut TraverseCtx < ' a > ,
572+ ) {
573+ match stmt {
574+ Statement :: BlockStatement ( stmt) => {
575+ let position =
576+ stmt. body . iter ( ) . position ( Self :: statement_contains_direct_super_call) . unwrap ( )
577+ + 1 ;
578+ stmt. body . splice ( position..position, Self :: convert_constructor_params ( params, ctx) ) ;
579+ }
580+ _ if Self :: statement_contains_direct_super_call ( stmt) => {
581+ let scope_id = ctx. insert_scope_below_statement_from_scope_id (
582+ stmt,
583+ constructor_scope_id,
584+ ScopeFlags :: empty ( ) ,
585+ ) ;
586+ let span = stmt. span ( ) ;
587+ let mut body = ArenaVec :: from_array_in ( [ stmt. take_in ( ctx) ] , ctx) ;
588+ body. extend ( Self :: convert_constructor_params ( params, ctx) ) ;
589+ * stmt = Statement :: new_block_statement_with_scope_id ( span, body, scope_id, ctx) ;
590+ }
591+ _ => { }
592+ }
593+ }
594+
595+ fn statement_contains_direct_super_call ( stmt : & Statement < ' a > ) -> bool {
596+ matches ! ( stmt, Statement :: ExpressionStatement ( stmt) if match & stmt. expression {
597+ Expression :: SequenceExpression ( seq) => {
598+ seq. expressions. iter( ) . any( Expression :: is_super_call_expression)
599+ }
600+ expr => expr. is_super_call_expression( ) ,
601+ } )
495602 }
496603
497604 /// Convert computed key to sequence expression if there are assignments.
0 commit comments