From 7ba7b6ec1fd7170ef7a321a6bd4931984e1a08d4 Mon Sep 17 00:00:00 2001 From: Felipe Armoni Date: Fri, 28 Jul 2023 01:40:19 -0300 Subject: [PATCH] fix(es/compat): Fix handling of private members in optional chaining pass (#7610) **Related issue:** - Closes #7561. --- .../src/es2020/optional_chaining.rs | 49 ++++++++++++++++++- .../optional-chaining/issue-7561/input.js | 7 +++ .../optional-chaining/issue-7561/output.js | 6 +++ 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/input.js create mode 100644 crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/output.js diff --git a/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs b/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs index 1ce54d218496..b7f60dd6cb14 100644 --- a/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs +++ b/crates/swc_ecma_transforms_compat/src/es2020/optional_chaining.rs @@ -1,6 +1,7 @@ use std::mem; use serde::Deserialize; +use swc_atoms::js_word; use swc_common::{util::take::Take, DUMMY_SP}; use swc_ecma_ast::*; use swc_ecma_utils::{ @@ -181,7 +182,13 @@ impl OptChaining { next = m.obj.take(); m.prop.visit_mut_with(self); chain.push(if optional { - Gathering::OptMember(m.take(), self.memoize(&next)) + match *next { + Expr::This(_) => Gathering::OptMember( + m.take(), + Ident::new(js_word!("this"), DUMMY_SP), + ), + _ => Gathering::OptMember(m.take(), self.memoize(&next)), + } } else { Gathering::Member(m.take()) }); @@ -299,7 +306,11 @@ impl OptChaining { Gathering::OptMember(mut m, memo) => { committed_cond.push(CondExpr { span: DUMMY_SP, - test: init_and_eq_null_or_undefined(&memo, current, no_document_all), + test: if memo.sym == js_word!("this") { + eq_null_or_undefined(&memo, no_document_all) + } else { + init_and_eq_null_or_undefined(&memo, current, no_document_all) + }, cons: if is_delete { true.into() } else { @@ -409,3 +420,37 @@ fn init_and_eq_null_or_undefined(i: &Ident, init: Expr, no_document_all: bool) - right: void_cmp, })) } + +fn eq_null_or_undefined(i: &Ident, no_document_all: bool) -> Box { + let lhs = Box::new(Expr::Ident(i.clone())); + + if no_document_all { + return Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: lhs, + op: op!("=="), + right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), + })); + } + + let null_cmp = Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: lhs, + op: op!("==="), + right: Box::new(Expr::Lit(Lit::Null(Null { span: DUMMY_SP }))), + })); + + let void_cmp = Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: Box::new(Expr::Ident(i.clone())), + op: op!("==="), + right: undefined(DUMMY_SP), + })); + + Box::new(Expr::Bin(BinExpr { + span: DUMMY_SP, + left: null_cmp, + op: op!("||"), + right: void_cmp, + })) +} diff --git a/crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/input.js b/crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/input.js new file mode 100644 index 000000000000..1bedd2a42410 --- /dev/null +++ b/crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/input.js @@ -0,0 +1,7 @@ +class Foo { + #x; + + test() { + this?.y.#x; + } +} diff --git a/crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/output.js b/crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/output.js new file mode 100644 index 000000000000..1fbf4ad231a1 --- /dev/null +++ b/crates/swc_ecma_transforms_compat/tests/optional-chaining/issue-7561/output.js @@ -0,0 +1,6 @@ +class Foo { + #x; + test() { + this === null || this === void 0 ? void 0 : this.y.#x; + } +}