Repository: swc-project/swc (rust)

Issue:
Arrow functions as class properties compile incorrectly
### Describe the bug

When using specific browser target to reduce how much compilation is being applied to the code, an edge case surfaces that incorrectly transpiles arrow functions as class properties, losing the correct reference to `this`. See the attached playground and you'll notice `_this.getBar(b)` is referencing `_this` which is not a valid reference.

### Input code

```typescript
class Foo {
  getBar(b) {
    return b * 2
  }

  getFoo = ({ b } = {}) => {
    return this.getBar(b)
  }
}

const f = new Foo()
console.log(f.getFoo({ b: 2 }))
```


### Config

```js
{
    env: {
      targets: '> 0.25% and last 2 versions, not dead, not ie <= 11',
      coreJs: 3,
      mode: 'entry'
    },
    jsc: {
      parser: {
        jsx: true,
        exportDefaultFrom: true
      },
      transform: {}
    }
  }
}
```


### Playground link

https://play.swc.rs/?version=1.2.133&code=H4sIAAAAAAAAA0vOSSwuVnDLz1eo5lJQSE8tcUos0kjSBPMUFIpSS0qL8hSSFLQUjIACtVwQNSDltgoa1UCJWiCjulZTwdYOVUtJRmaxHtw4sF6g7uT8vOIShTSgnrzUcpCtGppgsfycVL2c%2FHSNND2I6SCjrRSMFGo1NQHVZOk3oQAAAA%3D%3D&config=H4sIAAAAAAAAA0WO3QqDMAyFXyUEdidjE9yFbHuDPUSwUTq0lSbKRPrua53Du5Pz85EV39JgveJIQThkJYtT%2BmCN3AwkTbCjYpFqyWqpF44FKoWONVekSlnvvfCeFjhYZ9slkxo%2FjIFFjohc1%2F%2BbMYEGb6ZsrKjLyBvwhvFg7Dsrr72oYUo3u3nbbG8kPD7hci6rE5Az0JMolDBzEOudFOC8gmEyP2UZ7g%2B4XjHGL6hpzCH9AAAA

### Expected behavior

this.getBar(b) call should work!

### Actual behavior

Error is thrown `Uncaught TypeError: _this.getBar is not a function`, because `_this` is not pointing to the class instance.

### Version

1.2.133

### Additional context

_No response_

Pull request description:
fix(ecma/transform/compat): arrow parameters in class field
**Related issue (if exists):**
closes #3471 

Patch:
diff --git a/crates/swc_ecma_transforms_compat/src/es2015/parameters.rs b/crates/swc_ecma_transforms_compat/src/es2015/parameters.rs
index d4c686187d..69a218ff1f 100644
--- a/crates/swc_ecma_transforms_compat/src/es2015/parameters.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2015/parameters.rs
@@ -27,6 +27,7 @@ struct Params {
     /// transform an arrow expression to a function expression.
     hoister: FnEnvHoister,
     in_subclass: bool,
+    in_prop: bool,
     c: Config,
 }
 
@@ -394,6 +395,25 @@ impl Params {
 impl VisitMut for Params {
     noop_visit_mut_type!();
 
+    // generally speaking, there won't be class field in here, but Safari 14.1
+    // still has bugs in parameters
+    fn visit_mut_class_prop(&mut self, prop: &mut ClassProp) {
+        prop.key.visit_mut_children_with(self);
+
+        let old_in_prop = self.in_prop;
+        self.in_prop = !prop.is_static;
+        prop.value.visit_mut_with(self);
+        self.in_prop = old_in_prop;
+    }
+
+    // same for private prop
+    fn visit_mut_private_prop(&mut self, prop: &mut PrivateProp) {
+        let old_in_prop = self.in_prop;
+        self.in_prop = !prop.is_static;
+        prop.value.visit_mut_with(self);
+        self.in_prop = old_in_prop;
+    }
+
     fn visit_mut_block_stmt_or_expr(&mut self, body: &mut BlockStmtOrExpr) {
         let old_rep = self.hoister.take();
 
@@ -492,9 +512,17 @@ impl VisitMut for Params {
                     _ => false,
                 });
 
+                let mut local_vars = None;
+
                 // this needs to happen before rest parameter transform
                 if need_arrow_to_function {
-                    f.visit_mut_children_with(&mut self.hoister);
+                    if !self.in_prop {
+                        f.visit_mut_children_with(&mut self.hoister)
+                    } else {
+                        let mut hoister = FnEnvHoister::default();
+                        f.visit_mut_children_with(&mut hoister);
+                        local_vars = hoister.to_stmt();
+                    }
                 }
 
                 let body_span = f.body.span();
@@ -523,7 +551,7 @@ impl VisitMut for Params {
                 self.visit_mut_fn_like(&mut params, &mut body, false);
 
                 if need_arrow_to_function {
-                    *e = Expr::Fn(FnExpr {
+                    let func = Expr::Fn(FnExpr {
                         ident: None,
                         function: Function {
                             params,
@@ -536,6 +564,29 @@ impl VisitMut for Params {
                             return_type: Default::default(),
                         },
                     });
+                    *e = match (self.in_prop, local_vars) {
+                        (true, Some(var_decl)) => Expr::Arrow(ArrowExpr {
+                            span: f.span,
+                            params: Vec::new(),
+                            is_async: false,
+                            is_generator: false,
+                            body: BlockStmtOrExpr::BlockStmt(BlockStmt {
+                                span: f.span,
+                                stmts: vec![
+                                    var_decl,
+                                    Stmt::Return(ReturnStmt {
+                                        span: f.span,
+                                        arg: Some(Box::new(func)),
+                                    }),
+                                ],
+                            }),
+                            type_params: Default::default(),
+                            return_type: Default::default(),
+                        })
+                        .as_iife()
+                        .into(),
+                        _ => func,
+                    };
                     return;
                 }
 
@@ -574,12 +625,20 @@ impl VisitMut for Params {
             return;
         }
 
+        let old_in_subclass = self.in_subclass;
+        let old_in_prop = self.in_prop;
+        self.in_subclass = false;
+        self.in_prop = false;
+
         f.visit_mut_children_with(self);
 
         let mut body = f.body.take().unwrap();
         self.visit_mut_fn_like(&mut f.params, &mut body, false);
 
         f.body = Some(body);
+
+        self.in_subclass = old_in_subclass;
+        self.in_prop = old_in_prop;
     }
 
     fn visit_mut_getter_prop(&mut self, f: &mut GetterProp) {
@@ -620,11 +679,15 @@ impl VisitMut for Params {
     }
 
     fn visit_mut_class(&mut self, c: &mut Class) {
-        if c.super_class.is_some() {
-            self.in_subclass = true;
-        }
+        let old_in_subclass = self.in_subclass;
+        let old_in_prop = self.in_prop;
+
+        self.in_subclass = c.super_class.is_some();
+        self.in_prop = false;
         c.visit_mut_children_with(self);
-        self.in_subclass = false;
+
+        self.in_subclass = old_in_subclass;
+        self.in_prop = old_in_prop;
     }
 
     fn visit_mut_module_items(&mut self, stmts: &mut Vec<ModuleItem>) {


Test patch:
diff --git a/crates/swc_ecma_transforms_compat/tests/es2015_parameters.rs b/crates/swc_ecma_transforms_compat/tests/es2015_parameters.rs
index 74f7335a75..d040d0e970 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2015_parameters.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2015_parameters.rs
@@ -1860,6 +1860,47 @@ constructor() {
 "
 );
 
+test!(
+    syntax(),
+    |_| parameters(Default::default()),
+    issue_3471,
+    "
+class A {
+  a = 1 + ((...a) => a)
+  b = (...b) => b + this 
+  static c = (c = 123) => c + this 
+}
+  ",
+    "
+var _this = this;
+
+class A {
+  a = 1 + function () {
+    for (var _len = arguments.length, a = new Array(_len), _key = 0; _key < _len; _key++) {
+      a[_key] = arguments[_key];
+    }
+
+    return a;
+  };
+  b = (() => {
+    var _this1 = this;
+
+    return function () {
+      for (var _len = arguments.length, b = new Array(_len), _key = 0; _key < _len; _key++) {
+        b[_key] = arguments[_key];
+      }
+
+      return b + _this1;
+    };
+  })();
+  static c = function () {
+    let c = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : 123;
+    return c + _this;
+  };
+}
+  "
+);
+
 test!(
     syntax(),
     |_| tr(Config {