mirror of
https://github.com/swc-project/swc.git
synced 2024-11-24 10:12:42 +03:00
feat(es/compat): Check for duplicate private names and undefined private names (#3613)
This commit is contained in:
parent
6d5347100f
commit
5c03551341
@ -1,13 +1,17 @@
|
||||
#![allow(dead_code)]
|
||||
|
||||
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, perf::Check};
|
||||
use swc_ecma_transforms_classes::super_field::SuperFieldAccessFolder;
|
||||
use swc_ecma_transforms_macros::fast_path;
|
||||
use swc_ecma_utils::{
|
||||
alias_ident_for, alias_if_required, constructor::inject_after_super, default_constructor,
|
||||
private_ident, quote_ident, undefined, ExprFactory, ModuleItemLike, StmtLike,
|
||||
private_ident, quote_ident, undefined, ExprFactory, ModuleItemLike, StmtLike, HANDLER,
|
||||
};
|
||||
use swc_ecma_visit::{
|
||||
as_folder, noop_visit_mut_type, noop_visit_type, Fold, Visit, VisitMut, VisitMutWith, VisitWith,
|
||||
@ -16,8 +20,8 @@ use swc_ecma_visit::{
|
||||
use self::{
|
||||
class_name_tdz::ClassNameTdzFolder,
|
||||
private_field::{
|
||||
visit_private_in_expr, BrandCheckHandler, Private, PrivateAccessVisitor, PrivateKind,
|
||||
PrivateRecord,
|
||||
dup_private_method, visit_private_in_expr, BrandCheckHandler, Private,
|
||||
PrivateAccessVisitor, PrivateKind, PrivateRecord,
|
||||
},
|
||||
this_in_static::{NewTargetInProp, ThisInStaticFolder},
|
||||
used_name::UsedNameCollector,
|
||||
@ -313,29 +317,64 @@ impl ClassProperties {
|
||||
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,
|
||||
},
|
||||
)),
|
||||
ident: {
|
||||
let mut private_map = AHashMap::default();
|
||||
|
||||
ClassMember::PrivateProp(prop) => Some((
|
||||
prop.key.id.sym.clone(),
|
||||
PrivateKind {
|
||||
is_method: false,
|
||||
is_static: prop.is_static,
|
||||
},
|
||||
)),
|
||||
for member in class.body.iter() {
|
||||
match member {
|
||||
ClassMember::PrivateMethod(method) => {
|
||||
if let Some(kind) = private_map.get_mut(&method.key.id.sym) {
|
||||
if dup_private_method(kind, method) {
|
||||
let error =
|
||||
format!("duplicate private name #{}.", method.key.id.sym);
|
||||
HANDLER.with(|handler| {
|
||||
handler.struct_span_err(method.key.id.span, &error).emit()
|
||||
});
|
||||
} else {
|
||||
match method.kind {
|
||||
MethodKind::Getter => kind.has_getter = true,
|
||||
MethodKind::Setter => kind.has_setter = true,
|
||||
MethodKind::Method => unreachable!(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
private_map.insert(
|
||||
method.key.id.sym.clone(),
|
||||
PrivateKind {
|
||||
is_method: true,
|
||||
is_static: method.is_static,
|
||||
has_getter: method.kind == MethodKind::Getter,
|
||||
has_setter: method.kind == MethodKind::Setter,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
ClassMember::PrivateProp(prop) => {
|
||||
if private_map.get(&prop.key.id.sym).is_some() {
|
||||
let error = format!("duplicate private name #{}.", prop.key.id.sym);
|
||||
HANDLER.with(|handler| {
|
||||
handler.struct_span_err(prop.key.id.span, &error).emit()
|
||||
});
|
||||
} else {
|
||||
private_map.insert(
|
||||
prop.key.id.sym.clone(),
|
||||
PrivateKind {
|
||||
is_method: false,
|
||||
is_static: prop.is_static,
|
||||
has_getter: false,
|
||||
has_setter: false,
|
||||
},
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
|
||||
private_map
|
||||
},
|
||||
};
|
||||
|
||||
self.private.push(private);
|
||||
|
@ -8,7 +8,9 @@ use swc_common::{
|
||||
};
|
||||
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_utils::{
|
||||
alias_ident_for, alias_if_required, prepend, quote_ident, ExprFactory, HANDLER,
|
||||
};
|
||||
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith};
|
||||
|
||||
pub(super) struct Private {
|
||||
@ -40,21 +42,25 @@ impl PrivateRecord {
|
||||
self.0.pop();
|
||||
}
|
||||
|
||||
pub fn get(&self, name: &JsWord) -> (Mark, PrivateKind, &Ident) {
|
||||
pub fn get(&self, name: &Ident) -> (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);
|
||||
if let Some(kind) = p.ident.get(&name.sym) {
|
||||
return (p.mark, *kind, &p.class_name);
|
||||
}
|
||||
}
|
||||
// TODO: better error information with span
|
||||
panic!("Private name #{} is not defined.", name);
|
||||
|
||||
let error = format!("private name #{} is not defined.", name.sym);
|
||||
HANDLER.with(|handler| handler.struct_span_err(name.span, &error).emit());
|
||||
(Mark::root(), PrivateKind::default(), &self.0[0].class_name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Copy, Clone, PartialEq, Default)]
|
||||
pub(super) struct PrivateKind {
|
||||
pub is_static: bool,
|
||||
pub is_method: bool,
|
||||
pub has_getter: bool,
|
||||
pub has_setter: bool,
|
||||
}
|
||||
|
||||
pub(super) struct BrandCheckHandler<'a> {
|
||||
@ -93,7 +99,11 @@ impl VisitMut for BrandCheckHandler<'_> {
|
||||
|
||||
self.names.insert(n.id.sym.clone());
|
||||
|
||||
let (mark, kind, class_name) = self.private.get(&n.id.sym);
|
||||
let (mark, kind, class_name) = self.private.get(&n.id);
|
||||
|
||||
if mark == Mark::root() {
|
||||
return;
|
||||
}
|
||||
|
||||
if kind.is_static {
|
||||
*e = Expr::Bin(BinExpr {
|
||||
@ -191,7 +201,11 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> {
|
||||
|
||||
let obj = arg.obj.clone();
|
||||
|
||||
let (mark, kind, class_name) = self.private.get(&n.id.sym);
|
||||
let (mark, kind, class_name) = self.private.get(&n.id);
|
||||
if mark == Mark::root() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
|
||||
|
||||
let var = alias_ident_for(&obj, "_ref");
|
||||
@ -322,7 +336,11 @@ impl<'a> VisitMut for PrivateAccessVisitor<'a> {
|
||||
|
||||
let obj = left.obj.clone();
|
||||
|
||||
let (mark, kind, class_name) = self.private.get(&n.id.sym);
|
||||
let (mark, kind, class_name) = self.private.get(&n.id);
|
||||
if mark == Mark::root() {
|
||||
return;
|
||||
}
|
||||
|
||||
let ident = Ident::new(format!("_{}", n.id.sym).into(), n.id.span.apply_mark(mark));
|
||||
|
||||
let var = alias_ident_for(&obj, "_ref");
|
||||
@ -520,7 +538,11 @@ impl<'a> PrivateAccessVisitor<'a> {
|
||||
|
||||
let mut obj = e.obj.take();
|
||||
|
||||
let (mark, kind, class_name) = self.private.get(&n.id.sym);
|
||||
let (mark, kind, class_name) = self.private.get(&n.id);
|
||||
if mark == Mark::root() {
|
||||
return (Expr::dummy(), None);
|
||||
}
|
||||
|
||||
let method_name = Ident::new(
|
||||
n.id.sym.clone(),
|
||||
n.id.span.with_ctxt(SyntaxContext::empty()).apply_mark(mark),
|
||||
@ -665,3 +687,15 @@ impl<'a> PrivateAccessVisitor<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// only getter and setter in same scope could coexist
|
||||
pub(super) fn dup_private_method(kind: &PrivateKind, method: &PrivateMethod) -> bool {
|
||||
if !kind.is_method || kind.is_static != method.is_static || method.kind == MethodKind::Method {
|
||||
return true;
|
||||
}
|
||||
|
||||
!matches!(
|
||||
(method.kind, kind.has_getter, kind.has_setter),
|
||||
(MethodKind::Getter, false, true) | (MethodKind::Setter, true, false)
|
||||
)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user