Repository: swc-project/swc
File: crates/swc_ecma_transformer/src/es2017/async_to_generator.rs (Rust)

Code before:
            replace_this_in_expr(&mut paren.expr, this_var);
        }
        Expr::Seq(seq) => {
            for expr in &mut seq.exprs {
                replace_this_in_expr(expr, this_var);
            }
        }
        Expr::OptChain(opt) => {
            replace_this_in_opt_chain_base(&mut opt.base, this_var);
        }
        // Don't traverse into nested functions/classes/arrows as they have their own `this`
        // context
        Expr::Fn(_) | Expr::Arrow(_) | Expr::Class(_) => {}
        // These don't contain expressions that could have `this`
        Expr::Ident(_)
        | Expr::Lit(_)
        | Expr::PrivateName(_)
        | Expr::JSXMember(_)
        | Expr::JSXNamespacedName(_)
        | Expr::JSXEmpty(_)
        | Expr::JSXElement(_)
        | Expr::JSXFragment(_)
        | Expr::TsTypeAssertion(_)
        | Expr::TsConstAssertion(_)
        | Expr::TsNonNull(_)
        | Expr::TsAs(_)
        | Expr::TsInstantiation(_)
        | Expr::TsSatisfies(_)
        | Expr::MetaProp(_)
        | Expr::Invalid(_) => {}
        #[cfg(swc_ast_unknown)]
        _ => {}
    }
}

fn replace_this_in_member_expr(member: &mut MemberExpr, this_var: &Ident) {
    replace_this_in_expr(&mut member.obj, this_var);
    if let MemberProp::Computed(computed) = &mut member.prop {
        replace_this_in_expr(&mut computed.expr, this_var);
    }
}

fn replace_this_in_opt_chain_base(base: &mut OptChainBase, this_var: &Ident) {
    match base {
        OptChainBase::Member(member) => {
            replace_this_in_expr(&mut member.obj, this_var);
            if let MemberProp::Computed(computed) = &mut member.prop {
                replace_this_in_expr(&mut computed.expr, this_var);
            }
        }
        OptChainBase::Call(call) => {

Review comment (bug):
The JSX expressions `Expr::JSXElement(_)` and `Expr::JSXFragment(_)` are listed as not containing expressions with `this`, but this is incorrect. JSX elements can contain expressions in attributes and children that may include `this` references. For example, `<div onClick={this.handleClick}>{this.state.value}</div>` contains `this` references that should be replaced. These cases should traverse into JSX element attributes and children.
```suggestion
        | Expr::JSXElement(el) => {
            // Traverse attributes
            for attr in &mut el.opening.attrs {
                match attr {
                    JSXAttrOrSpread::JSXAttr(attr) => {
                        if let Some(JSXAttrValue::JSXExprContainer(expr_container)) = &mut attr.value {
                            match expr_container {
                                JSXExprContainer::Expr(expr) => {
                                    if let Some(expr) = expr {
                                        replace_this_in_expr(expr, this_var);
                                    }
                                }
                                JSXExprContainer::Empty => {}
                            }
                        }
                    }
                    JSXAttrOrSpread::SpreadElement(spread) => {
                        replace_this_in_expr(&mut spread.expr, this_var);
                    }
                }
            }
            // Traverse children
            for child in &mut el.children {
                match child {
                    JSXElementChild::JSXExprContainer(expr_container) => {
                        match expr_container {
                            JSXExprContainer::Expr(expr) => {
                                if let Some(expr) = expr {
                                    replace_this_in_expr(expr, this_var);
                                }
                            }
                            JSXExprContainer::Empty => {}
                        }
                    }
                    JSXElementChild::JSXElement(elem) => {
                        replace_this_in_expr(&mut Expr::JSXElement(elem.clone()), this_var);
                    }
                    JSXElementChild::JSXFragment(frag) => {
                        replace_this_in_expr(&mut Expr::JSXFragment(frag.clone()), this_var);
                    }
                    _ => {}
                }
            }
        }
        | Expr::JSXFragment(frag) => {
            for child in &mut frag.children {
                match child {
                    JSXElementChild::JSXExprContainer(expr_container) => {
                        match expr_container {
                            JSXExprContainer::Expr(expr) => {
                                if let Some(expr) = expr {
                                    replace_this_in_expr(expr, this_var);
                                }
                            }
                            JSXExprContainer::Empty => {}
                        }
                    }
                    JSXElementChild::JSXElement(elem) => {
                        replace_this_in_expr(&mut Expr::JSXElement(elem.clone()), this_var);
                    }
                    JSXElementChild::JSXFragment(frag) => {
                        replace_this_in_expr(&mut Expr::JSXFragment(frag.clone()), this_var);
                    }
                    _ => {}
                }
            }
        }
```

Code after:
        }
        Expr::Paren(paren) => {
            replace_this_in_expr(&mut paren.expr, this_var);
        }
        Expr::Seq(seq) => {
            for expr in &mut seq.exprs {
                replace_this_in_expr(expr, this_var);
            }
        }
        Expr::OptChain(opt) => {
            replace_this_in_opt_chain_base(&mut opt.base, this_var);
        }
        // Don't traverse into nested functions/classes/arrows as they have their own `this`
        // context
        Expr::Fn(_) | Expr::Arrow(_) | Expr::Class(_) => {}
        // These don't contain expressions that could have `this`
        Expr::Ident(_)
        | Expr::Lit(_)
        | Expr::PrivateName(_)
        | Expr::JSXMember(_)
        | Expr::JSXNamespacedName(_)
        | Expr::JSXEmpty(_)
        | Expr::JSXElement(_)
        | Expr::JSXFragment(_)
        | Expr::TsTypeAssertion(_)
        | Expr::TsConstAssertion(_)
        | Expr::TsNonNull(_)
        | Expr::TsAs(_)
        | Expr::TsInstantiation(_)
        | Expr::TsSatisfies(_)
        | Expr::MetaProp(_)
        | Expr::Invalid(_) => {}
        #[cfg(swc_ast_unknown)]
        _ => {}
    }
}

fn replace_this_in_member_expr(member: &mut MemberExpr, this_var: &Ident) {
    replace_this_in_expr(&mut member.obj, this_var);
    if let MemberProp::Computed(computed) = &mut member.prop {
        replace_this_in_expr(&mut computed.expr, this_var);
    }
}

fn replace_this_in_opt_chain_base(base: &mut OptChainBase, this_var: &Ident) {
    match base {
        OptChainBase::Member(member) => {
            replace_this_in_expr(&mut member.obj, this_var);
            if let MemberProp::Computed(computed) = &mut member.prop {
                replace_this_in_expr(&mut computed.expr, this_var);
            }