Repository: swc-project/swc (rust)

Issue:
Wrongly transformed private field in nested class
### Describe the bug

JS has a lexcial scope of private field, so any function and class inside a class could use the private field outside of it

### Input code

_No response_

### Config

_No response_

### Playground link

https://play.swc.rs/?version=1.2.133&code=H4sIAAAAAAAAAz2NQQrDMAwE73rFQg6xL%2FpBD%2BlPVGO3BWEF26WU4r83MWl0EGh20AaVWrHgS8AkuGBOKc3bkcycHxgosb1KRhju9YDATcqp7BMsV9PIanfXHs%2FKk%2Fgj7PTfnTpRjm8szvMo4bVYs%2FZZI28fOYiqG4L%2FAQfD0aWdAAAA&config=H4sIAAAAAAAAA0WMWwoCMQxFtyL57p%2BoMHtwEaFmtNIXSUYsQ%2FduWgb8u49z7w5v8bDsUJGFeChpWfELC5BPKJ5DVXCGWbRiFOoOFPlJOhC5WBdLEfq3qTy2SONKW6VJXWHkIYe1HZyDIPcDVN7MU%2F7MzfwWm%2FkXl0Sn2xl6%2FwFhK12uqAAAAA%3D%3D

### Expected behavior

Code could run

### Actual behavior

throw `a is not found`

### Version

1.2.133

### Additional context

_No response_

Pull request:
fix(ecma/transform/compat): handle private field in nested class
**Related issue (if exists):**
closes #3368 


Patch:
diff --git a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs
index 5ea8990223..931d6d869d 100644
--- a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/mod.rs
@@ -2,12 +2,11 @@
 
 use self::{
     class_name_tdz::ClassNameTdzFolder,
-    private_field::{BrandCheckHandler, FieldAccessFolder},
+    private_field::{BrandCheckHandler, FieldAccessFolder, Private, PrivateKind, PrivateRecord},
     this_in_static::ThisInStaticFolder,
     used_name::UsedNameCollector,
 };
-use std::collections::HashSet;
-use swc_common::{util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
+use swc_common::{collections::AHashSet, util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
 use swc_ecma_ast::*;
 use swc_ecma_transforms_base::{helper, perf::Check};
 use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
@@ -35,8 +34,7 @@ mod used_name;
 pub fn class_properties(config: Config) -> impl Fold + VisitMut {
     as_folder(ClassProperties {
         config,
-        mark: Mark::root(),
-        method_mark: Mark::root(),
+        private: PrivateRecord::new(),
     })
 }
 
@@ -45,11 +43,9 @@ pub struct Config {
     pub loose: bool,
 }
 
-#[derive(Clone)]
 struct ClassProperties {
     config: Config,
-    mark: Mark,
-    method_mark: Mark,
+    private: PrivateRecord,
 }
 
 #[fast_path(ShouldWork)]
@@ -111,8 +107,6 @@ impl VisitMut for ClassProperties {
     }
 
     fn visit_mut_expr(&mut self, expr: &mut Expr) {
-        expr.visit_mut_children_with(self);
-
         if let Expr::Class(ClassExpr { ident, class }) = expr {
             let ident = ident.take().unwrap_or_else(|| private_ident!("_class"));
             let mut stmts = vec![];
@@ -168,6 +162,8 @@ impl VisitMut for ClassProperties {
                 args: vec![],
                 type_args: Default::default(),
             });
+        } else {
+            expr.visit_mut_children_with(self);
         };
     }
 }
@@ -183,8 +179,6 @@ impl ClassProperties {
             match T::try_into_stmt(stmt) {
                 Err(node) => match node.try_into_module_decl() {
                     Ok(mut decl) => {
-                        decl.visit_mut_children_with(self);
-
                         match decl {
                             ModuleDecl::ExportDefaultDecl(ExportDefaultDecl {
                                 span,
@@ -261,16 +255,18 @@ impl ClassProperties {
                                 );
                                 buf.extend(stmts.into_iter().map(T::from_stmt));
                             }
-                            _ => buf.push(match T::try_from_module_decl(decl) {
-                                Ok(t) => t,
-                                Err(..) => unreachable!(),
-                            }),
+                            _ => {
+                                decl.visit_mut_children_with(self);
+                                buf.push(match T::try_from_module_decl(decl) {
+                                    Ok(t) => t,
+                                    Err(..) => unreachable!(),
+                                })
+                            }
                         };
                     }
                     Err(..) => unreachable!(),
                 },
                 Ok(mut stmt) => {
-                    stmt.visit_mut_children_with(self);
                     // Fold class
                     match stmt {
                         Stmt::Decl(Decl::Class(ClassDecl {
@@ -290,7 +286,10 @@ impl ClassProperties {
                             buf.push(T::from_stmt(Stmt::Decl(Decl::Class(decl))));
                             buf.extend(stmts.into_iter().map(T::from_stmt));
                         }
-                        _ => buf.push(T::from_stmt(stmt)),
+                        _ => {
+                            stmt.visit_mut_children_with(self);
+                            buf.push(T::from_stmt(stmt))
+                        }
                     }
                 }
             }
@@ -307,8 +306,38 @@ impl ClassProperties {
         mut class: Class,
     ) -> (Vec<VarDeclarator>, ClassDecl, Vec<Stmt>) {
         // Create one mark per class
-        self.mark = Mark::fresh(Mark::root());
-        self.method_mark = Mark::fresh(Mark::root());
+        let private = Private {
+            mark: Mark::fresh(Mark::root()),
+            class_name: class_ident.clone(),
+            ident: class
+                .body
+                .iter()
+                .filter_map(|member| match member {
+                    ClassMember::PrivateMethod(method) => Some((
+                        method.key.id.sym.clone(),
+                        PrivateKind {
+                            is_method: true,
+                            is_static: method.is_static,
+                        },
+                    )),
+
+                    ClassMember::PrivateProp(prop) => Some((
+                        prop.key.id.sym.clone(),
+                        PrivateKind {
+                            is_method: false,
+                            is_static: prop.is_static,
+                        },
+                    )),
+
+                    _ => None,
+                })
+                .collect(),
+        };
+
+        self.private.push(private);
+
+        // we must collect outer class's private first
+        class.visit_mut_children_with(self);
 
         let has_super = class.super_class.is_some();
 
@@ -322,48 +351,11 @@ impl ClassProperties {
         let mut constructor = None;
         let mut used_names = vec![];
         let mut used_key_names = vec![];
-        let mut names_used_for_brand_checks = HashSet::default();
-
-        let statics = {
-            let mut s = HashSet::default();
-
-            for member in &class.body {
-                match member {
-                    ClassMember::PrivateMethod(method) => {
-                        if method.is_static {
-                            s.insert(method.key.id.sym.clone());
-                        }
-                    }
-
-                    ClassMember::PrivateProp(prop) => {
-                        if prop.is_static {
-                            s.insert(prop.key.id.sym.clone());
-                        }
-                    }
-
-                    _ => {}
-                }
-            }
-
-            s
-        };
-        let private_methods = {
-            let mut s = HashSet::default();
-
-            for member in &class.body {
-                if let ClassMember::PrivateMethod(method) = member {
-                    s.insert(method.key.id.sym.clone());
-                }
-            }
-
-            s
-        };
+        let mut names_used_for_brand_checks = AHashSet::default();
 
         class.body.visit_mut_with(&mut BrandCheckHandler {
-            mark: self.mark,
-            class_name: &class_ident,
             names: &mut names_used_for_brand_checks,
-            statics: &statics,
+            private: &self.private,
         });
 
         for member in class.body {
@@ -505,7 +497,7 @@ impl ClassProperties {
                     let ident = Ident::new(
                         format!("_{}", prop.key.id.sym).into(),
                         // We use `self.mark` for private variables.
-                        prop.key.span.apply_mark(self.mark),
+                        prop.key.span.apply_mark(self.private.curr_mark()),
                     );
                     prop.value.visit_with(&mut UsedNameCollector {
                         used_names: &mut used_names,
@@ -594,30 +586,31 @@ impl ClassProperties {
                 }
 
                 ClassMember::PrivateMethod(method) => {
+                    let is_static = method.is_static;
                     let prop_span = method.span;
                     let fn_name = Ident::new(
                         method.key.id.sym.clone(),
                         method
                             .span
                             .with_ctxt(SyntaxContext::empty())
-                            .apply_mark(self.method_mark),
+                            .apply_mark(self.private.curr_mark()),
                     );
 
                     let should_use_map =
                         matches!(method.kind, MethodKind::Getter | MethodKind::Setter)
                             && names_used_for_brand_checks.contains(&method.key.id.sym)
-                            && !statics.contains(&method.key.id.sym);
+                            && !is_static;
 
                     let weak_coll_var = Ident::new(
                         format!("_{}", method.key.id.sym).into(),
                         // We use `self.mark` for private variables.
-                        method.key.span.apply_mark(self.mark),
+                        method.key.span.apply_mark(self.private.curr_mark()),
                     );
                     method.function.visit_with(&mut UsedNameCollector {
                         used_names: &mut used_names,
                     });
 
-                    if should_use_map || !statics.contains(&method.key.id.sym) {
+                    if should_use_map || !is_static {
                         vars.push(VarDeclarator {
                             span: DUMMY_SP,
                             definite: false,
@@ -650,7 +643,7 @@ impl ClassProperties {
                             props: vec![get, set],
                         };
 
-                        let obj = if statics.contains(&method.key.id.sym) {
+                        let obj = if is_static {
                             let var_name = private_ident!("static_method");
 
                             vars.push(VarDeclarator {
@@ -674,7 +667,7 @@ impl ClassProperties {
                             args: vec![ThisExpr { span: DUMMY_SP }.as_arg(), obj],
                             type_args: Default::default(),
                         })));
-                    } else if !statics.contains(&method.key.id.sym) {
+                    } else if !is_static {
                         // Add `_get.add(this);` to the constructor where `_get` is the name of the
                         // weak set.
                         constructor_exprs.push(Box::new(Expr::Call(CallExpr {
@@ -712,27 +705,21 @@ impl ClassProperties {
         }
 
         private_method_fn_decls.visit_mut_with(&mut FieldAccessFolder {
-            mark: self.mark,
-            method_mark: self.method_mark,
-            private_methods: &private_methods,
-            statics: &statics,
+            private: &self.private,
             vars: vec![],
-            class_name: &class_ident,
             in_assign_pat: false,
         });
 
         extra_stmts.extend(private_method_fn_decls);
 
         members.visit_mut_with(&mut FieldAccessFolder {
-            mark: self.mark,
-            method_mark: self.method_mark,
-            private_methods: &private_methods,
-            statics: &statics,
+            private: &self.private,
             vars: vec![],
-            class_name: &class_ident,
             in_assign_pat: false,
         });
 
+        self.private.pop();
+
         (
             vars,
             ClassDecl {
diff --git a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs
index 0fc310f7e2..6864df7d52 100644
--- a/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs
+++ b/crates/swc_ecma_transforms_compat/src/es2022/class_properties/private_field.rs
@@ -1,21 +1,66 @@
 use std::iter;
 use swc_atoms::JsWord;
-use swc_common::{collections::AHashSet, util::take::Take, Mark, Spanned, SyntaxContext, DUMMY_SP};
+use swc_common::{
+    collections::{AHashMap, AHashSet},
+    util::take::Take,
+    Mark, Spanned, SyntaxContext, DUMMY_SP,
+};
 use swc_ecma_ast::*;
 use swc_ecma_transforms_base::helper;
 use swc_ecma_utils::{alias_ident_for, alias_if_required, prepend, quote_ident, ExprFactory};
 use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
 
-pub(super) struct BrandCheckHandler<'a> {
-    /// Mark for the private `WeakSet` variable.
+pub(super) struct Private {
     pub mark: Mark,
+    pub class_name: Ident,
+    pub ident: AHashMap<JsWord, PrivateKind>,
+}
+
+pub(super) struct PrivateRecord(Vec<Private>);
+
+impl PrivateRecord {
+    pub fn new() -> Self {
+        PrivateRecord(Vec::new())
+    }
+
+    pub fn curr_class(&self) -> &Ident {
+        &self.0.last().unwrap().class_name
+    }
+
+    pub fn curr_mark(&self) -> Mark {
+        self.0.last().unwrap().mark
+    }
 
-    pub class_name: &'a Ident,
+    pub fn push(&mut self, p: Private) {
+        self.0.push(p)
+    }
+
+    pub fn pop(&mut self) {
+        self.0.pop();
+    }
+
+    pub fn get(&self, name: &JsWord) -> (Mark, PrivateKind, &Ident) {
+        for p in self.0.iter().rev() {
+            if let Some(kind) = p.ident.get(name) {
+                return (p.mark, kind.clone(), &p.class_name);
+            }
+        }
+        // TODO: better error information with span
+        panic!("Private name #{name} is not defined.");
+    }
+}
 
+#[derive(Clone, PartialEq)]
+pub(super) struct PrivateKind {
+    pub is_static: bool,
+    pub is_method: bool,
+}
+
+pub(super) struct BrandCheckHandler<'a> {
     /// Private names used for brand checks.
     pub names: &'a mut AHashSet<JsWord>,
 
-    pub statics: &'a AHashSet<JsWord>,
+    pub private: &'a PrivateRecord,
 }
 
 impl VisitMut for BrandCheckHandler<'_> {
@@ -31,20 +76,14 @@ impl VisitMut for BrandCheckHandler<'_> {
                 left,
                 right,
             }) if left.is_private_name() => {
-                let n = match &**left {
-                    Expr::PrivateName(ref n) => n,
-                    _ => {
-                        unreachable!()
-                    }
-                };
+                let n = left.as_private_name().unwrap();
                 if let Expr::Ident(right) = &**right {
-                    if self.class_name.sym == right.sym
-                        && self.class_name.span.ctxt == right.span.ctxt
-                    {
+                    let curr_class = self.private.curr_class();
+                    if curr_class.sym == right.sym && curr_class.span.ctxt == right.span.ctxt {
                         *e = Expr::Bin(BinExpr {
                             span: *span,
                             op: op!("==="),
-                            left: Box::new(Expr::Ident(self.class_name.clone())),
+                            left: Box::new(Expr::Ident(curr_class.clone())),
                             right: Box::new(Expr::Ident(right.clone())),
                         });
                         return;
@@ -53,22 +92,20 @@ impl VisitMut for BrandCheckHandler<'_> {
 
                 self.names.insert(n.id.sym.clone());
 
-                let is_static = self.statics.contains(&n.id.sym);
+                let (mark, kind, class_name) = self.private.get(&n.id.sym);
 
-                if is_static {
+                if kind.is_static {
                     *e = Expr::Bin(BinExpr {
                         span: *span,
                         op: op!("==="),
                         left: right.take(),
-                        right: Box::new(Expr::Ident(self.class_name.clone())),
+                        right: Box::new(Expr::Ident(class_name.clone())),
                     });
                     return;
                 }
 
-                let weak_coll_ident = Ident::new(
-                    format!("_{}", n.id.sym).into(),
-                    n.id.span.apply_mark(self.mark),
-                );
+                let weak_coll_ident =
+                    Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
 
                 *e = Expr::Call(CallExpr {
                     span: *span,
@@ -84,14 +121,8 @@ impl VisitMut for BrandCheckHandler<'_> {
 }
 
 pub(super) struct FieldAccessFolder<'a> {
-    /// Mark for the private `WeakSet` variable.
-    pub mark: Mark,
-    pub method_mark: Mark,
-
-    pub class_name: &'a Ident,
-    pub private_methods: &'a AHashSet<JsWord>,
     pub vars: Vec<VarDeclarator>,
-    pub statics: &'a AHashSet<JsWord>,
+    pub private: &'a PrivateRecord,
     pub in_assign_pat: bool,
 }
 
@@ -158,17 +189,14 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
 
                 let obj = arg.obj.clone();
 
-                let is_static = self.statics.contains(&n.id.sym);
-                let ident = Ident::new(
-                    format!("_{}", n.id.sym).into(),
-                    n.id.span.apply_mark(self.mark),
-                );
+                let (mark, kind, class_name) = self.private.get(&n.id.sym);
+                let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
 
                 let var = alias_ident_for(&obj, "_ref");
 
                 let this = if matches!(*obj, Expr::This(..)) {
                     ThisExpr { span: DUMMY_SP }.as_arg()
-                } else if is_static {
+                } else if kind.is_static {
                     obj.as_arg()
                 } else {
                     self.vars.push(VarDeclarator {
@@ -234,19 +262,14 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
                     .as_arg()
                 };
 
-                let expr = if is_static {
+                let expr = if kind.is_static {
                     Expr::Call(CallExpr {
                         span: DUMMY_SP,
                         callee: helper!(
                             class_static_private_field_spec_set,
                             "classStaticPrivateFieldSpecSet"
                         ),
-                        args: vec![
-                            this,
-                            self.class_name.clone().as_arg(),
-                            ident.as_arg(),
-                            value,
-                        ],
+                        args: vec![this, class_name.clone().as_arg(), ident.as_arg(), value],
 
                         type_args: Default::default(),
                     })
@@ -297,11 +320,8 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
 
                 let obj = left.obj.clone();
 
-                let is_static = self.statics.contains(&n.id.sym);
-                let ident = Ident::new(
-                    format!("_{}", n.id.sym).into(),
-                    n.id.span.apply_mark(self.mark),
-                );
+                let (mark, kind, class_name) = self.private.get(&n.id.sym);
+                let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
 
                 let var = alias_ident_for(&obj, "_ref");
 
@@ -339,19 +359,14 @@ impl<'a> VisitMut for FieldAccessFolder<'a> {
                     .as_arg()
                 };
 
-                if is_static {
+                if kind.is_static {
                     *e = Expr::Call(CallExpr {
                         span: DUMMY_SP,
                         callee: helper!(
                             class_static_private_field_spec_set,
                             "classStaticPrivateFieldSpecSet"
                         ),
-                        args: vec![
-                            this,
-                            self.class_name.clone().as_arg(),
-                            ident.as_arg(),
-                            value,
-                        ],
+                        args: vec![this, class_name.clone().as_arg(), ident.as_arg(), value],
 
                         type_args: Default::default(),
                     });
@@ -488,21 +503,15 @@ impl<'a> FieldAccessFolder<'a> {
 
         let mut obj = e.obj.take();
 
-        let is_method = self.private_methods.contains(&n.id.sym);
-        let is_static = self.statics.contains(&n.id.sym);
+        let (mark, kind, class_name) = self.private.get(&n.id.sym);
         let method_name = Ident::new(
             n.id.sym.clone(),
-            n.id.span
-                .with_ctxt(SyntaxContext::empty())
-                .apply_mark(self.method_mark),
-        );
-        let ident = Ident::new(
-            format!("_{}", n.id.sym).into(),
-            n.id.span.apply_mark(self.mark),
+            n.id.span.with_ctxt(SyntaxContext::empty()).apply_mark(mark),
         );
+        let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
 
-        if is_static {
-            if is_method {
+        if kind.is_static {
+            if kind.is_method {
                 let h = helper!(
                     class_static_private_method_get,
                     "classStaticPrivateMethodGet"
@@ -514,12 +523,12 @@ impl<'a> FieldAccessFolder<'a> {
                         callee: h,
                         args: vec![
                             obj.as_arg(),
-                            self.class_name.clone().as_arg(),
+                            class_name.clone().as_arg(),
                             method_name.as_arg(),
                         ],
                         type_args: Default::default(),
                     }),
-                    Some(Expr::Ident(self.class_name.clone())),
+                    Some(Expr::Ident(class_name.clone())),
                 );
             }
 
@@ -532,14 +541,10 @@ impl<'a> FieldAccessFolder<'a> {
                 Expr::Call(CallExpr {
                     span: DUMMY_SP,
                     callee: get,
-                    args: vec![
-                        obj.as_arg(),
-                        self.class_name.clone().as_arg(),
-                        ident.as_arg(),
-                    ],
+                    args: vec![obj.as_arg(), class_name.clone().as_arg(), ident.as_arg()],
                     type_args: Default::default(),
                 }),
-                Some(Expr::Ident(self.class_name.clone())),
+                Some(Expr::Ident(class_name.clone())),
             )
         } else {
             if self.in_assign_pat {
@@ -564,7 +569,7 @@ impl<'a> FieldAccessFolder<'a> {
                 };
             }
 
-            let get = if is_method {
+            let get = if kind.is_method {
                 helper!(class_private_method_get, "classPrivateMethodGet")
             } else {
                 helper!(class_private_field_get, "classPrivateFieldGet")
@@ -572,7 +577,7 @@ impl<'a> FieldAccessFolder<'a> {
 
             match &*obj {
                 Expr::This(this) => (
-                    if is_method {
+                    if kind.is_method {
                         CallExpr {
                             span: DUMMY_SP,
                             callee: get,
@@ -622,7 +627,7 @@ impl<'a> FieldAccessFolder<'a> {
                         var.clone().as_arg()
                     };
 
-                    let args = if is_method {
+                    let args = if kind.is_method {
                         vec![first_arg, ident.as_arg(), method_name.as_arg()]
                     } else {
                         vec![first_arg, ident.as_arg()]


Test patch:
diff --git a/crates/swc/tests/fixture/issue-1333/case1/input/index.js b/crates/swc/tests/fixture/issue-1333/case1/input/index.js
index bd1eb7435f..f682057f06 100644
--- a/crates/swc/tests/fixture/issue-1333/case1/input/index.js
+++ b/crates/swc/tests/fixture/issue-1333/case1/input/index.js
@@ -1,4 +1,6 @@
 class Foo {
+    #ws;
+    #ws2;
     get connected() {
         return this.#ws2 && this.#ws.readyState === _ws1.default.OPEN;
     }
diff --git a/crates/swc/tests/fixture/issue-1333/case1/output/index.js b/crates/swc/tests/fixture/issue-1333/case1/output/index.js
index dcbd640187..f205ac3159 100644
--- a/crates/swc/tests/fixture/issue-1333/case1/output/index.js
+++ b/crates/swc/tests/fixture/issue-1333/case1/output/index.js
@@ -9,4 +9,16 @@ class Foo {
     get connected() {
         return _classPrivateFieldGet(this, _ws2) && _classPrivateFieldGet(this, _ws).readyState === _ws1.default.OPEN;
     }
+    constructor(){
+        _ws.set(this, {
+            writable: true,
+            value: void 0
+        });
+        _ws2.set(this, {
+            writable: true,
+            value: void 0
+        });
+    }
 }
+var _ws = new WeakMap();
+var _ws2 = new WeakMap();
diff --git a/crates/swc/tests/tsc-references/privateNameAndAny_es2015.1.normal.js b/crates/swc/tests/tsc-references/privateNameAndAny_es2015.1.normal.js
deleted file mode 100644
index 530aec3d83..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndAny_es2015.1.normal.js
+++ /dev/null
@@ -1,21 +0,0 @@
-function _classPrivateFieldGet(receiver, privateMap) {
-    if (!privateMap.has(receiver)) {
-        throw new TypeError("attempted to get private field on non-instance");
-    }
-    return privateMap.get(receiver).value;
-}
-// @strict: true
-// @target: es6
-class A {
-    method(thing) {
-        _classPrivateFieldGet(thing, _foo); // OK
-        _classPrivateFieldGet(thing, _bar); // Error
-    }
-    constructor(){
-        _foo.set(this, {
-            writable: true,
-            value: true
-        });
-    }
-}
-var _foo = new WeakMap();
diff --git a/crates/swc/tests/tsc-references/privateNameAndAny_es2015.2.minified.js b/crates/swc/tests/tsc-references/privateNameAndAny_es2015.2.minified.js
deleted file mode 100644
index 73263530f4..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndAny_es2015.2.minified.js
+++ /dev/null
@@ -1,1 +0,0 @@
-new WeakMap();
diff --git a/crates/swc/tests/tsc-references/privateNameAndAny_es5.1.normal.js b/crates/swc/tests/tsc-references/privateNameAndAny_es5.1.normal.js
deleted file mode 100644
index 3e815cd97f..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndAny_es5.1.normal.js
+++ /dev/null
@@ -1,48 +0,0 @@
-function _classCallCheck(instance, Constructor) {
-    if (!(instance instanceof Constructor)) {
-        throw new TypeError("Cannot call a class as a function");
-    }
-}
-function _classPrivateFieldGet(receiver, privateMap) {
-    if (!privateMap.has(receiver)) {
-        throw new TypeError("attempted to get private field on non-instance");
-    }
-    return privateMap.get(receiver).value;
-}
-function _defineProperties(target, props) {
-    for(var i = 0; i < props.length; i++){
-        var descriptor = props[i];
-        descriptor.enumerable = descriptor.enumerable || false;
-        descriptor.configurable = true;
-        if ("value" in descriptor) descriptor.writable = true;
-        Object.defineProperty(target, descriptor.key, descriptor);
-    }
-}
-function _createClass(Constructor, protoProps, staticProps) {
-    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
-    if (staticProps) _defineProperties(Constructor, staticProps);
-    return Constructor;
-}
-var A = // @strict: true
-// @target: es6
-/*#__PURE__*/ function() {
-    "use strict";
-    function A() {
-        _classCallCheck(this, A);
-        _foo.set(this, {
-            writable: true,
-            value: true
-        });
-    }
-    _createClass(A, [
-        {
-            key: "method",
-            value: function method(thing) {
-                _classPrivateFieldGet(thing, _foo); // OK
-                _classPrivateFieldGet(thing, _bar); // Error
-            }
-        }
-    ]);
-    return A;
-}();
-var _foo = new WeakMap();
diff --git a/crates/swc/tests/tsc-references/privateNameAndAny_es5.2.minified.js b/crates/swc/tests/tsc-references/privateNameAndAny_es5.2.minified.js
deleted file mode 100644
index c5509d1d5d..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndAny_es5.2.minified.js
+++ /dev/null
@@ -1,30 +0,0 @@
-function _classPrivateFieldGet(receiver, privateMap) {
-    if (!privateMap.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
-    return privateMap.get(receiver).value;
-}
-function _defineProperties(target, props) {
-    for(var i = 0; i < props.length; i++){
-        var descriptor = props[i];
-        descriptor.enumerable = descriptor.enumerable || !1, descriptor.configurable = !0, "value" in descriptor && (descriptor.writable = !0), Object.defineProperty(target, descriptor.key, descriptor);
-    }
-}
-var A = function() {
-    "use strict";
-    var Constructor, protoProps, staticProps;
-    function A() {
-        !function(instance, Constructor) {
-            if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
-        }(this, A), _foo.set(this, {
-            writable: !0,
-            value: !0
-        });
-    }
-    return Constructor = A, protoProps = [
-        {
-            key: "method",
-            value: function(thing) {
-                _classPrivateFieldGet(thing, _foo), _classPrivateFieldGet(thing, _bar);
-            }
-        }
-    ], _defineProperties(Constructor.prototype, protoProps), staticProps && _defineProperties(Constructor, staticProps), A;
-}(), _foo = new WeakMap();
diff --git a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es2015.1.normal.js b/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es2015.1.normal.js
deleted file mode 100644
index dda3825080..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es2015.1.normal.js
+++ /dev/null
@@ -1,29 +0,0 @@
-function _classPrivateFieldSet(receiver, privateMap, value) {
-    if (!privateMap.has(receiver)) {
-        throw new TypeError("attempted to set private field on non-instance");
-    }
-    var descriptor = privateMap.get(receiver);
-    if (!descriptor.writable) {
-        throw new TypeError("attempted to set read only private field");
-    }
-    descriptor.value = value;
-    return value;
-}
-var _key;
-// @strict: true
-// @target: es6
-class A {
-    constructor(message){
-        _foo.set(this, {
-            writable: true,
-            value: 3
-        });
-        this[_key] // Error (private identifiers should not prevent circularity checking for computeds)
-         = this["#bar"];
-        _classPrivateFieldSet(this, _f, 3 // Error (index signatures do not implicitly declare private names)
-        );
-        this["#foo"] = 3; // Okay (type has index signature and "#foo" does not collide with private identifier #foo)
-    }
-}
-var _foo = new WeakMap();
-_key = "#bar";
diff --git a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es2015.2.minified.js b/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es2015.2.minified.js
deleted file mode 100644
index c8b32e0275..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es2015.2.minified.js
+++ /dev/null
@@ -1,2 +0,0 @@
-var _key;
-new WeakMap(), _key = "#bar";
diff --git a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es5.1.normal.js b/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es5.1.normal.js
deleted file mode 100644
index b33db9f303..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es5.1.normal.js
+++ /dev/null
@@ -1,34 +0,0 @@
-function _classCallCheck(instance, Constructor) {
-    if (!(instance instanceof Constructor)) {
-        throw new TypeError("Cannot call a class as a function");
-    }
-}
-function _classPrivateFieldSet(receiver, privateMap, value) {
-    if (!privateMap.has(receiver)) {
-        throw new TypeError("attempted to set private field on non-instance");
-    }
-    var descriptor = privateMap.get(receiver);
-    if (!descriptor.writable) {
-        throw new TypeError("attempted to set read only private field");
-    }
-    descriptor.value = value;
-    return value;
-}
-var _key;
-var A = function A(message) {
-    "use strict";
-    _classCallCheck(this, A);
-    _foo.set(this, {
-        writable: true,
-        value: 3
-    });
-    // @strict: true
-    // @target: es6
-    this[_key] // Error (private identifiers should not prevent circularity checking for computeds)
-     = this["#bar"];
-    _classPrivateFieldSet(this, _f, 3 // Error (index signatures do not implicitly declare private names)
-    );
-    this["#foo"] = 3; // Okay (type has index signature and "#foo" does not collide with private identifier #foo)
-};
-var _foo = new WeakMap();
-_key = "#bar";
diff --git a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es5.2.minified.js b/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es5.2.minified.js
deleted file mode 100644
index a68ff088d0..0000000000
--- a/crates/swc/tests/tsc-references/privateNameAndIndexSignature_es5.2.minified.js
+++ /dev/null
@@ -1,15 +0,0 @@
-var _key, A = function(message) {
-    "use strict";
-    !function(instance, Constructor) {
-        if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
-    }(this, A), _foo.set(this, {
-        writable: !0,
-        value: 3
-    }), this[_key] = this["#bar"], (function(receiver, privateMap, value) {
-        if (!privateMap.has(receiver)) throw new TypeError("attempted to set private field on non-instance");
-        var descriptor = privateMap.get(receiver);
-        if (!descriptor.writable) throw new TypeError("attempted to set read only private field");
-        descriptor.value = 3;
-    })(this, _f, 3), this["#foo"] = 3;
-}, _foo = new WeakMap();
-_key = "#bar";
diff --git a/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es2015.1.normal.js b/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es2015.1.normal.js
deleted file mode 100644
index 269a6daa4c..0000000000
--- a/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es2015.1.normal.js
+++ /dev/null
@@ -1,15 +0,0 @@
-function _classPrivateFieldGet(receiver, privateMap) {
-    if (!privateMap.has(receiver)) {
-        throw new TypeError("attempted to get private field on non-instance");
-    }
-    return privateMap.get(receiver).value;
-}
-// @allowJs: true
-// @checkJs: true
-// @noEmit: true
-// @Filename: privateNameImplicitDeclaration.js
-class C {
-    constructor(){
-        _classPrivateFieldGet(/** @type {string} */ this, _x);
-    }
-}
diff --git a/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es5.1.normal.js b/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es5.1.normal.js
deleted file mode 100644
index c7266e9690..0000000000
--- a/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es5.1.normal.js
+++ /dev/null
@@ -1,16 +0,0 @@
-function _classCallCheck(instance, Constructor) {
-    if (!(instance instanceof Constructor)) {
-        throw new TypeError("Cannot call a class as a function");
-    }
-}
-function _classPrivateFieldGet(receiver, privateMap) {
-    if (!privateMap.has(receiver)) {
-        throw new TypeError("attempted to get private field on non-instance");
-    }
-    return privateMap.get(receiver).value;
-}
-var C = function C() {
-    "use strict";
-    _classCallCheck(this, C);
-    _classPrivateFieldGet(/** @type {string} */ this, _x);
-};
diff --git a/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es5.2.minified.js b/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es5.2.minified.js
deleted file mode 100644
index 5641969f92..0000000000
--- a/crates/swc/tests/tsc-references/privateNameImplicitDeclaration_es5.2.minified.js
+++ /dev/null
@@ -1,9 +0,0 @@
-var C = function() {
-    "use strict";
-    !function(instance, Constructor) {
-        if (!(instance instanceof Constructor)) throw new TypeError("Cannot call a class as a function");
-    }(this, C), (function(receiver, privateMap) {
-        if (!privateMap.has(receiver)) throw new TypeError("attempted to get private field on non-instance");
-        privateMap.get(receiver).value;
-    })(this, _x);
-};
diff --git a/crates/swc/tests/tsc.rs b/crates/swc/tests/tsc.rs
index d9bd2289c8..167dac81a2 100644
--- a/crates/swc/tests/tsc.rs
+++ b/crates/swc/tests/tsc.rs
@@ -20,6 +20,10 @@ use testing::{NormalizedOutput, Tester};
         "propertyAccessChain\\.3.ts",
         "objectRestNegative.ts",
         "objectRestPropertyMustBeLast.ts",
+        // shall panic because not finding private field
+        "privateNameAndAny.ts",
+        "privateNameAndIndexSignature.ts",
+        "privateNameImplicitDeclaration.ts"    
     )
 )]
 #[testing::fixture("../swc_ecma_parser/tests/tsc/**/*.tsx")]
diff --git a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
index ae43ef539a..cc5cb69521 100644
--- a/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
+++ b/crates/swc_ecma_transforms_compat/tests/es2022_class_properties.rs
@@ -4930,6 +4930,8 @@ test!(
     issue_1333_1,
     "
   class Foo {
+    #ws;
+    #ws2;
     get connected() {
         return this.#ws2 && this.#ws.readyState === _ws1.default.OPEN;
     }
@@ -4941,7 +4943,19 @@ test!(
           return _classPrivateFieldGet(this, _ws2) && _classPrivateFieldGet(this, _ws).readyState \
      === _ws1.default.OPEN;
       }
+      constructor(){
+        _ws.set(this, {
+            writable: true,
+            value: void 0
+        });
+        _ws2.set(this, {
+            writable: true,
+            value: void 0
+        });
+      }
     }
+    var _ws = new WeakMap();
+    var _ws2 = new WeakMap();
     "
 );
 
@@ -4952,6 +4966,8 @@ test!(
     "
   class Test {
     #ws;
+    #serialization;
+    #seq;
 
     _packet(raw) {
         /** @type {DiscordPacket} */
@@ -5112,9 +5128,19 @@ test!(
               writable: true,
               value: void 0
           });
+          _serialization.set(this, {
+            writable: true,
+            value: void 0
+          });
+          _seq.set(this, {
+            writable: true,
+            value: void 0
+          });
       }
   }
   var _ws = new WeakMap();
+  var _serialization = new WeakMap();
+  var _seq = new WeakMap();
     "
 );
 
@@ -5125,6 +5151,7 @@ test!(
     "
     class Test {
       #ws;
+      #serialization;
 
       _packet(raw) {
         /** @type {DiscordPacket} */
@@ -5165,10 +5192,14 @@ test!(
               writable: true,
               value: void 0
           });
+          _serialization.set(this, {
+            writable: true,
+            value: void 0
+          });
       }
   }
   var _ws = new WeakMap();
-
+  var _serialization = new WeakMap();
     "
 );
 
@@ -5179,6 +5210,7 @@ test!(
     "
   class Test {
     #ws;
+    #serialization;
 
     _packet(raw) {
       /** @type {DiscordPacket} */
@@ -5206,9 +5238,14 @@ test!(
               writable: true,
               value: void 0
           });
+          _serialization.set(this, {
+            writable: true,
+            value: void 0
+          });
       }
     }
     var _ws = new WeakMap();
+    var _serialization = new WeakMap();
     "
 );
 
@@ -5218,6 +5255,7 @@ test!(
     issue_1333_5,
     "
     class Test {
+      #serialization;
       _packet(raw) {
         pak = this.#serialization.decode(raw);
       }
@@ -5228,7 +5266,14 @@ test!(
       _packet(raw) {
           pak = _classPrivateFieldGet(this, _serialization).decode(raw);
       }
+      constructor(){
+        _serialization.set(this, {
+          writable: true,
+          value: void 0
+        });
+      }
     }
+    var _serialization = new WeakMap();
     "
 );
 
@@ -5238,6 +5283,7 @@ test!(
     issue_1333_6,
     "
     class Test {
+      #serialization;
       _packet(raw) {
         this.#serialization.decode(raw);
       }
@@ -5248,7 +5294,14 @@ test!(
       _packet(raw) {
           _classPrivateFieldGet(this, _serialization).decode(raw);
       }
+      constructor(){
+        _serialization.set(this, {
+          writable: true,
+          value: void 0
+        });
+      }
     }
+    var _serialization = new WeakMap();
     "
 );
 
@@ -5638,6 +5691,7 @@ test!(
     issue_3229_1,
     "
 class A {
+  #D;
   B() {
     1;
     C.#D++;
@@ -5653,7 +5707,14 @@ class A {
         _classPrivateFieldSet(_C = C, _D, (_this_D = +_classPrivateFieldGet(_C, _D)) + 1), _this_D;
         E(function() {});
     }
+    constructor(){
+      _D.set(this, {
+          writable: true,
+          value: void 0
+      });
+    }
 }
+var _D = new WeakMap();
   "
 );
 
@@ -5663,6 +5724,7 @@ test!(
     issue_3229_2,
     "
 class A {
+    #b;
     foo() {
         A.#b += 123
         class B {
@@ -5680,9 +5742,96 @@ class A {
             foo() {}
         }
     }
+    constructor(){
+      _b.set(this, {
+          writable: true,
+          value: void 0
+      });
+  }
+}
+var _b = new WeakMap();
+"
+);
+
+test!(
+    syntax(),
+    |_| class_properties(class_properties::Config { loose: false }),
+    issue_3368,
+    "
+class A {
+  #a = 'fff'
+  static #b = 123
+  foo() {
+    return class B {
+      bar() {
+        console.log(this.#a, this.#b, this.#bar)
+      }
+    }
+  }
+  #bar() {}
+}
+",
+    "
+var _bar = new WeakSet();
+class A {
+    foo() {
+        return class B {
+            bar() {
+              console.log(_classPrivateFieldGet(this, _a), _classStaticPrivateFieldSpecGet(this, \
+     A, _b), _classPrivateMethodGet(this, _bar, bar));
+            }
+        };
+    }
+    constructor(){
+        _a.set(this, {
+            writable: true,
+            value: 'fff'
+        });
+        _bar.add(this);
+    }
+}
+var _a = new WeakMap();
+var _b = {
+    writable: true,
+    value: 123
+};
+function bar() {}
+"
+);
+
+test!(
+    syntax(),
+    |_| class_properties(class_properties::Config { loose: false }),
+    nested_class_in_arrow,
+    "
+const a = () => class {
+  a = 123
+  foo() {
+    return class B {
+      b = 456
+    }
+  }
 }
+",
+    "
+const a = ()=>{
+    class _class {
+        foo() {
+            return class B {
+                constructor() {
+                    _defineProperty(this, 'b', 456);
+                }
+            };
+        }
+        constructor(){
+            _defineProperty(this, 'a', 123);
+        }
+    }
+    return _class;
+};
 "
 );
+
 #[testing::fixture("tests/fixture/classes/**/exec.js")]
 fn exec(input: PathBuf) {
     let src = read_to_string(&input).unwrap();
diff --git a/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class-other-redeclared/output.js b/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class-other-redeclared/output.js
index ec96a83e2e..8f498ec127 100644
--- a/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class-other-redeclared/output.js
+++ b/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class-other-redeclared/output.js
@@ -2,7 +2,7 @@ let Foo = function() {
     "use strict";
     function Foo() {
         _classCallCheck(this, Foo);
-        _foo1.set(this, {
+        _foo.set(this, {
             writable: true,
             value: 1
         });
@@ -35,12 +35,12 @@ let Foo = function() {
                     return Nested;
                 }();
                 var _bar1 = new WeakMap();
-                _foo1.has(this);
+                _foo.has(this);
                 _bar.has(this);
             }
         }
     ]);
     return Foo;
 }();
-var _foo1 = new WeakMap();
+var _foo = new WeakMap();
 var _bar = new WeakMap();
diff --git a/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class/output.js b/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class/output.js
index 778a02a029..48884ce30f 100644
--- a/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class/output.js
+++ b/crates/swc_ecma_transforms_compat/tests/private-in-object/private/nested-class/output.js
@@ -2,7 +2,7 @@ let Foo = function() {
     "use strict";
     function Foo() {
         _classCallCheck(this, Foo);
-        _foo1.set(this, {
+        _foo.set(this, {
             writable: true,
             value: 1
         });
@@ -25,10 +25,10 @@ let Foo = function() {
                     ]);
                     return Nested;
                 }();
-                _foo1.has(this);
+                _foo.has(this);
             }
         }
     ]);
     return Foo;
 }();
-var _foo1 = new WeakMap();
+var _foo = new WeakMap();