feat(es/parser): Implement explicit resource management (#7322)

**Description:**

 - Add `UsingDecl`.
 - Add `UsingDecl` to `Decl`.
 - Rename `VarDeclOrPat` to `ForHead`.
 - Add `UsingDecl` to `ForHead`.
 - Implement parser for using declarations.

**Related issue:**

 - #7316.
This commit is contained in:
Donny/강동윤 2023-05-10 13:16:44 +09:00 committed by GitHub
parent 6432e1f5c5
commit 041b491466
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
99 changed files with 2057 additions and 125 deletions

View File

@ -870,8 +870,8 @@ XPathExpression
XPathResult XPathResult
XSLTProcessor XSLTProcessor
__proto__ __proto__
_define_property
_defineProperty _defineProperty
_define_property
_extends _extends
_toConsumableArray _toConsumableArray
a a
@ -2374,6 +2374,7 @@ url
use use
usemap usemap
user-select user-select
using
values values
var var
vb vb

View File

@ -892,7 +892,8 @@ where
Decl::TsInterface(_) Decl::TsInterface(_)
| Decl::TsTypeAlias(_) | Decl::TsTypeAlias(_)
| Decl::TsEnum(_) | Decl::TsEnum(_)
| Decl::TsModule(_) => continue, | Decl::TsModule(_)
| Decl::Using(..) => continue,
}; };
tracing::trace!( tracing::trace!(

View File

@ -540,6 +540,7 @@ fn mark(item: &mut ModuleItem, ctxt: SyntaxContext) {
Decl::Class(ClassDecl { class: v, .. }) => v.span.ctxt = ctxt, Decl::Class(ClassDecl { class: v, .. }) => v.span.ctxt = ctxt,
Decl::Fn(FnDecl { function: v, .. }) => v.span.ctxt = ctxt, Decl::Fn(FnDecl { function: v, .. }) => v.span.ctxt = ctxt,
Decl::Var(v) => v.span.ctxt = ctxt, Decl::Var(v) => v.span.ctxt = ctxt,
Decl::Using(u) => u.span.ctxt = ctxt,
Decl::TsInterface(v) => v.span.ctxt = ctxt, Decl::TsInterface(v) => v.span.ctxt = ctxt,
Decl::TsTypeAlias(v) => v.span.ctxt = ctxt, Decl::TsTypeAlias(v) => v.span.ctxt = ctxt,
Decl::TsEnum(v) => v.span.ctxt = ctxt, Decl::TsEnum(v) => v.span.ctxt = ctxt,

View File

@ -22,6 +22,9 @@ pub enum Decl {
Fn(FnDecl), Fn(FnDecl),
#[tag("VariableDeclaration")] #[tag("VariableDeclaration")]
Var(Box<VarDecl>), Var(Box<VarDecl>),
#[tag("UsingDeclaration")]
Using(Box<UsingDecl>),
#[tag("TsInterfaceDeclaration")] #[tag("TsInterfaceDeclaration")]
TsInterface(Box<TsInterfaceDecl>), TsInterface(Box<TsInterfaceDecl>),
#[tag("TsTypeAliasDeclaration")] #[tag("TsTypeAliasDeclaration")]
@ -33,6 +36,7 @@ pub enum Decl {
} }
bridge_decl_from!(Box<VarDecl>, VarDecl); bridge_decl_from!(Box<VarDecl>, VarDecl);
bridge_decl_from!(Box<UsingDecl>, UsingDecl);
bridge_decl_from!(Box<TsInterfaceDecl>, TsInterfaceDecl); bridge_decl_from!(Box<TsInterfaceDecl>, TsInterfaceDecl);
bridge_decl_from!(Box<TsTypeAliasDecl>, TsTypeAliasDecl); bridge_decl_from!(Box<TsTypeAliasDecl>, TsTypeAliasDecl);
bridge_decl_from!(Box<TsEnumDecl>, TsEnumDecl); bridge_decl_from!(Box<TsEnumDecl>, TsEnumDecl);
@ -166,3 +170,21 @@ impl Take for VarDeclarator {
} }
} }
} }
#[ast_node("UsingDeclaration")]
#[derive(Eq, Hash, EqIgnoreSpan)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct UsingDecl {
pub span: Span,
pub decls: Vec<VarDeclarator>,
}
impl Take for UsingDecl {
fn dummy() -> Self {
Self {
span: DUMMY_SP,
decls: Take::dummy(),
}
}
}

View File

@ -18,7 +18,7 @@ pub use self::{
AutoAccessor, Class, ClassMember, ClassMethod, ClassProp, Constructor, Decorator, Key, AutoAccessor, Class, ClassMember, ClassMethod, ClassProp, Constructor, Decorator, Key,
MethodKind, PrivateMethod, PrivateProp, StaticBlock, MethodKind, PrivateMethod, PrivateProp, StaticBlock,
}, },
decl::{ClassDecl, Decl, FnDecl, VarDecl, VarDeclKind, VarDeclarator}, decl::{ClassDecl, Decl, FnDecl, UsingDecl, VarDecl, VarDeclKind, VarDeclarator},
expr::{ expr::{
ArrayLit, ArrowExpr, AssignExpr, AwaitExpr, BinExpr, BlockStmtOrExpr, CallExpr, Callee, ArrayLit, ArrowExpr, AssignExpr, AwaitExpr, BinExpr, BlockStmtOrExpr, CallExpr, Callee,
ClassExpr, CondExpr, Expr, ExprOrSpread, FnExpr, Import, MemberExpr, MemberProp, ClassExpr, CondExpr, Expr, ExprOrSpread, FnExpr, Import, MemberExpr, MemberProp,
@ -54,8 +54,8 @@ pub use self::{
source_map::{SourceMapperExt, SpanExt}, source_map::{SourceMapperExt, SpanExt},
stmt::{ stmt::{
BlockStmt, BreakStmt, CatchClause, ContinueStmt, DebuggerStmt, DoWhileStmt, EmptyStmt, BlockStmt, BreakStmt, CatchClause, ContinueStmt, DebuggerStmt, DoWhileStmt, EmptyStmt,
ExprStmt, ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, ReturnStmt, Stmt, SwitchCase, ExprStmt, ForHead, ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, ReturnStmt, Stmt,
SwitchStmt, ThrowStmt, TryStmt, VarDeclOrExpr, VarDeclOrPat, WhileStmt, WithStmt, SwitchCase, SwitchStmt, ThrowStmt, TryStmt, VarDeclOrExpr, WhileStmt, WithStmt,
}, },
typescript::{ typescript::{
Accessibility, TruePlusMinus, TsArrayType, TsAsExpr, TsCallSignatureDecl, Accessibility, TruePlusMinus, TsArrayType, TsAsExpr, TsCallSignatureDecl,

View File

@ -6,6 +6,7 @@ use crate::{
expr::Expr, expr::Expr,
ident::Ident, ident::Ident,
pat::Pat, pat::Pat,
UsingDecl,
}; };
/// Use when only block statements are allowed. /// Use when only block statements are allowed.
@ -303,7 +304,7 @@ pub struct ForStmt {
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct ForInStmt { pub struct ForInStmt {
pub span: Span, pub span: Span,
pub left: VarDeclOrPat, pub left: ForHead,
pub right: Box<Expr>, pub right: Box<Expr>,
pub body: Box<Stmt>, pub body: Box<Stmt>,
} }
@ -320,7 +321,7 @@ pub struct ForOfStmt {
/// for-await-of statements, e.g., `for await (const x of xs) {` /// for-await-of statements, e.g., `for await (const x of xs) {`
#[cfg_attr(feature = "serde-impl", serde(default, rename = "await"))] #[cfg_attr(feature = "serde-impl", serde(default, rename = "await"))]
pub is_await: bool, pub is_await: bool,
pub left: VarDeclOrPat, pub left: ForHead,
pub right: Box<Expr>, pub right: Box<Expr>,
pub body: Box<Stmt>, pub body: Box<Stmt>,
} }
@ -376,23 +377,27 @@ pub struct CatchClause {
pub body: BlockStmt, pub body: BlockStmt,
} }
/// A head for for-in and for-of loop.
#[ast_node] #[ast_node]
#[derive(Eq, Hash, Is, EqIgnoreSpan)] #[derive(Eq, Hash, Is, EqIgnoreSpan)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))] #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum VarDeclOrPat { pub enum ForHead {
#[tag("VariableDeclaration")] #[tag("VariableDeclaration")]
VarDecl(Box<VarDecl>), VarDecl(Box<VarDecl>),
#[tag("UsingDeclaration")]
UsingDecl(Box<UsingDecl>),
#[tag("*")] #[tag("*")]
Pat(Box<Pat>), Pat(Box<Pat>),
} }
bridge_from!(VarDeclOrPat, Box<VarDecl>, VarDecl); bridge_from!(ForHead, Box<VarDecl>, VarDecl);
bridge_from!(VarDeclOrPat, Box<Pat>, Pat); bridge_from!(ForHead, Box<Pat>, Pat);
impl Take for VarDeclOrPat { impl Take for ForHead {
fn dummy() -> Self { fn dummy() -> Self {
VarDeclOrPat::Pat(Take::dummy()) ForHead::Pat(Take::dummy())
} }
} }

View File

@ -12,7 +12,7 @@ where
{ {
#[emitter] #[emitter]
fn emit_decl(&mut self, node: &Decl) -> Result { fn emit_decl(&mut self, node: &Decl) -> Result {
match *node { match node {
Decl::Class(ref n) => emit!(n), Decl::Class(ref n) => emit!(n),
Decl::Fn(ref n) => emit!(n), Decl::Fn(ref n) => emit!(n),
@ -21,6 +21,7 @@ where
formatting_semi!(); formatting_semi!();
srcmap!(n, false); srcmap!(n, false);
} }
Decl::Using(n) => emit!(n),
Decl::TsEnum(ref n) => emit!(n), Decl::TsEnum(ref n) => emit!(n),
Decl::TsInterface(ref n) => emit!(n), Decl::TsInterface(ref n) => emit!(n),
Decl::TsModule(ref n) => emit!(n), Decl::TsModule(ref n) => emit!(n),
@ -33,6 +34,20 @@ where
self.emit_class_decl_inner(node, false)?; self.emit_class_decl_inner(node, false)?;
} }
#[emitter]
fn emit_using_decl(&mut self, node: &UsingDecl) -> Result {
self.emit_leading_comments_of_span(node.span(), false)?;
keyword!("using");
space!();
self.emit_list(
node.span,
Some(&node.decls),
ListFormat::VariableDeclarationList,
)?;
}
pub(super) fn emit_class_decl_inner( pub(super) fn emit_class_decl_inner(
&mut self, &mut self,
node: &ClassDecl, node: &ClassDecl,

View File

@ -2676,10 +2676,11 @@ where
} }
#[emitter] #[emitter]
fn emit_var_decl_or_pat(&mut self, node: &VarDeclOrPat) -> Result { fn emit_for_head(&mut self, node: &ForHead) -> Result {
match *node { match node {
VarDeclOrPat::Pat(ref n) => emit!(n), ForHead::Pat(n) => emit!(n),
VarDeclOrPat::VarDecl(ref n) => emit!(n), ForHead::VarDecl(n) => emit!(n),
ForHead::UsingDecl(n) => emit!(n),
} }
} }
} }

View File

@ -4,11 +4,12 @@ pub trait EndsWithAlphaNum {
fn ends_with_alpha_num(&self) -> bool; fn ends_with_alpha_num(&self) -> bool;
} }
impl EndsWithAlphaNum for VarDeclOrPat { impl EndsWithAlphaNum for ForHead {
fn ends_with_alpha_num(&self) -> bool { fn ends_with_alpha_num(&self) -> bool {
match self { match self {
VarDeclOrPat::VarDecl(n) => n.ends_with_alpha_num(), ForHead::VarDecl(n) => n.ends_with_alpha_num(),
VarDeclOrPat::Pat(n) => n.ends_with_alpha_num(), ForHead::Pat(n) => n.ends_with_alpha_num(),
ForHead::UsingDecl(n) => n.ends_with_alpha_num(),
} }
} }
} }
@ -37,6 +38,18 @@ impl EndsWithAlphaNum for VarDecl {
} }
} }
impl EndsWithAlphaNum for UsingDecl {
fn ends_with_alpha_num(&self) -> bool {
match self.decls.last() {
None => true,
Some(d) => match d.init.as_deref() {
Some(e) => e.ends_with_alpha_num(),
None => d.name.ends_with_alpha_num(),
},
}
}
}
impl EndsWithAlphaNum for Expr { impl EndsWithAlphaNum for Expr {
fn ends_with_alpha_num(&self) -> bool { fn ends_with_alpha_num(&self) -> bool {
!matches!( !matches!(
@ -230,7 +243,8 @@ impl StartsWithAlphaNum for Decl {
| Decl::TsEnum(..) | Decl::TsEnum(..)
| Decl::TsInterface(..) | Decl::TsInterface(..)
| Decl::TsModule(..) | Decl::TsModule(..)
| Decl::TsTypeAlias(..) => true, | Decl::TsTypeAlias(..)
| Decl::Using(..) => true,
} }
} }
} }

View File

@ -255,7 +255,7 @@ impl Visit for NoParamReassign {
} }
fn visit_for_of_stmt(&mut self, for_of_stmt: &ForOfStmt) { fn visit_for_of_stmt(&mut self, for_of_stmt: &ForOfStmt) {
if let VarDeclOrPat::Pat(pat) = &for_of_stmt.left { if let ForHead::Pat(pat) = &for_of_stmt.left {
self.check_pat(pat); self.check_pat(pat);
} }
@ -263,7 +263,7 @@ impl Visit for NoParamReassign {
} }
fn visit_for_in_stmt(&mut self, for_in_stmt: &ForInStmt) { fn visit_for_in_stmt(&mut self, for_in_stmt: &ForInStmt) {
if let VarDeclOrPat::Pat(pat) = &for_in_stmt.left { if let ForHead::Pat(pat) = &for_in_stmt.left {
self.check_pat(pat); self.check_pat(pat);
} }

View File

@ -563,6 +563,10 @@ where
// Variable declarations are handled by other functions. // Variable declarations are handled by other functions.
} }
Decl::Using(..) => {
// TODO: Optimize
}
Decl::TsInterface(_) | Decl::TsTypeAlias(_) | Decl::TsEnum(_) | Decl::TsModule(_) => { Decl::TsInterface(_) | Decl::TsTypeAlias(_) | Decl::TsEnum(_) | Decl::TsModule(_) => {
// Nothing to do. We might change this to unreachable!() // Nothing to do. We might change this to unreachable!()
} }

View File

@ -355,7 +355,7 @@ impl Visit for VarWithOutInitCounter {
n.visit_children_with(self); n.visit_children_with(self);
} }
fn visit_var_decl_or_pat(&mut self, _: &VarDeclOrPat) {} fn visit_for_head(&mut self, _: &ForHead) {}
} }
/// Moves all variable without initializer. /// Moves all variable without initializer.
@ -425,7 +425,7 @@ impl VisitMut for VarMover {
self.var_decl_kind = old; self.var_decl_kind = old;
} }
fn visit_mut_var_decl_or_pat(&mut self, _: &mut VarDeclOrPat) {} fn visit_mut_for_head(&mut self, _: &mut ForHead) {}
fn visit_mut_var_declarators(&mut self, d: &mut Vec<VarDeclarator>) { fn visit_mut_var_declarators(&mut self, d: &mut Vec<VarDeclarator>) {
d.visit_mut_children_with(self); d.visit_mut_children_with(self);

View File

@ -1651,8 +1651,8 @@ impl Visit for Shower<'_> {
n.visit_children_with(self) n.visit_children_with(self)
} }
fn visit_var_decl_or_pat(&mut self, n: &VarDeclOrPat) { fn visit_for_head(&mut self, n: &ForHead) {
self.show("VarDeclOrPat", n); self.show("ForHead", n);
n.visit_children_with(self) n.visit_children_with(self)
} }

View File

@ -45,6 +45,12 @@ pub enum SyntaxError {
Eof, Eof,
DeclNotAllowed, DeclNotAllowed,
UsingDeclNotAllowed,
UsingDeclNotAllowedForForInLoop,
UsingDeclNotEnabled,
InvalidNameInUsingDecl,
InitRequiredForUsingDecl,
PrivateNameInInterface, PrivateNameInInterface,
InvalidSuperCall, InvalidSuperCall,
@ -523,6 +529,19 @@ impl SyntaxError {
"The operand of a delete operator must be a property reference.".into() "The operand of a delete operator must be a property reference.".into()
} }
SyntaxError::DeclNotAllowed => "Declaration is not allowed".into(), SyntaxError::DeclNotAllowed => "Declaration is not allowed".into(),
SyntaxError::UsingDeclNotAllowed => "Using declaration is not allowed".into(),
SyntaxError::UsingDeclNotAllowedForForInLoop => {
"Using declaration is not allowed in for-in loop".into()
}
SyntaxError::UsingDeclNotEnabled => {
"Using declaration is not enabled. Set jsc.parser.usingDecl to true".into()
}
SyntaxError::InvalidNameInUsingDecl => {
"Using declaration only allows identifiers".into()
}
SyntaxError::InitRequiredForUsingDecl => {
"Using declaration requires initializer".into()
}
SyntaxError::InvalidSuperCall => "Invalid `super()`".into(), SyntaxError::InvalidSuperCall => "Invalid `super()`".into(),
SyntaxError::InvalidSuper => "Invalid access to super".into(), SyntaxError::InvalidSuper => "Invalid access to super".into(),
SyntaxError::InvalidSuperPrivateName => { SyntaxError::InvalidSuperPrivateName => {

View File

@ -281,6 +281,13 @@ impl Syntax {
_ => false, _ => false,
} }
} }
fn using_decl(&self) -> bool {
match self {
Syntax::Es(EsConfig { using_decl, .. }) => *using_decl,
Syntax::Typescript(_) => true,
}
}
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
@ -345,6 +352,9 @@ pub struct EsConfig {
#[serde(default)] #[serde(default)]
pub auto_accessors: bool, pub auto_accessors: bool,
#[serde(default)]
pub using_decl: bool,
} }
/// Syntactic context. /// Syntactic context.
@ -403,6 +413,8 @@ pub struct Context {
ignore_else_clause: bool, ignore_else_clause: bool,
disallow_conditional_types: bool, disallow_conditional_types: bool,
allow_using_decl: bool,
} }
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]

View File

@ -417,6 +417,9 @@ macro_rules! tok {
("protected") => { ("protected") => {
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("protected"))) crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("protected")))
}; };
("using") => {
crate::token::Token::Word(crate::token::Word::Ident(swc_atoms::js_word!("using")))
};
} }
macro_rules! token_including_semi { macro_rules! token_including_semi {

View File

@ -1618,7 +1618,7 @@ impl<I: Tokens> Parser<I> {
Ok(callee) Ok(callee)
} }
pub(super) fn parse_expr_or_pat(&mut self) -> PResult<Box<Expr>> { pub(super) fn parse_for_head_prefix(&mut self) -> PResult<Box<Expr>> {
self.parse_expr() self.parse_expr()
} }

View File

@ -301,6 +301,13 @@ impl<'a, I: Tokens> Parser<I> {
} }
} }
tok!("using") if include_decl => {
let v = self.parse_using_decl()?;
if let Some(v) = v {
return Ok(Stmt::Decl(Decl::Using(v)));
}
}
tok!("interface") => { tok!("interface") => {
if is_typescript if is_typescript
&& peeked_is!(self, IdentName) && peeked_is!(self, IdentName)
@ -328,7 +335,11 @@ impl<'a, I: Tokens> Parser<I> {
} }
tok!('{') => { tok!('{') => {
return self.parse_block(false).map(Stmt::Block); let ctx = Context {
allow_using_decl: true,
..self.ctx()
};
return self.with_ctx(ctx).parse_block(false).map(Stmt::Block);
} }
_ => {} _ => {}
@ -751,6 +762,70 @@ impl<'a, I: Tokens> Parser<I> {
} }
} }
pub(super) fn parse_using_decl(&mut self) -> PResult<Option<Box<UsingDecl>>> {
// using
// reader = init()
// is two statements
let _ = cur!(self, false);
if self.input.has_linebreak_between_cur_and_peeked() {
return Ok(None);
}
if !peeked_is!(self, BindingIdent) {
return Ok(None);
}
let start = cur_pos!(self);
assert_and_bump!(self, "using");
let mut decls = vec![];
let mut first = true;
while first || eat!(self, ',') {
if first {
first = false;
}
// Handle
// var a,;
//
// NewLine is ok
if is_exact!(self, ';') || eof!(self) {
let span = self.input.prev_span();
self.emit_err(span, SyntaxError::TS1009);
break;
}
decls.push(self.parse_var_declarator(false, VarDeclKind::Var)?);
}
if !self.syntax().using_decl() {
self.emit_err(span!(self, start), SyntaxError::UsingDeclNotEnabled);
}
if !self.ctx().allow_using_decl {
self.emit_err(span!(self, start), SyntaxError::UsingDeclNotAllowed);
}
for decl in &decls {
match decl.name {
Pat::Ident(..) => {}
_ => {
self.emit_err(span!(self, start), SyntaxError::InvalidNameInUsingDecl);
}
}
if decl.init.is_none() {
self.emit_err(span!(self, start), SyntaxError::InitRequiredForUsingDecl);
}
}
Ok(Some(Box::new(UsingDecl {
span: span!(self, start),
decls,
})))
}
pub(super) fn parse_var_stmt(&mut self, for_loop: bool) -> PResult<Box<VarDecl>> { pub(super) fn parse_var_stmt(&mut self, for_loop: bool) -> PResult<Box<VarDecl>> {
let start = cur_pos!(self); let start = cur_pos!(self);
let kind = match bump!(self) { let kind = match bump!(self) {
@ -1024,6 +1099,7 @@ impl<'a, I: Tokens> Parser<I> {
fn parse_labelled_stmt(&mut self, l: Ident) -> PResult<Stmt> { fn parse_labelled_stmt(&mut self, l: Ident) -> PResult<Stmt> {
let ctx = Context { let ctx = Context {
is_break_allowed: true, is_break_allowed: true,
allow_using_decl: false,
..self.ctx() ..self.ctx()
}; };
self.with_ctx(ctx).parse_with(|p| { self.with_ctx(ctx).parse_with(|p| {
@ -1094,7 +1170,7 @@ impl<'a, I: Tokens> Parser<I> {
let span = span!(self, start); let span = span!(self, start);
Ok(match head { Ok(match head {
ForHead::For { init, test, update } => { TempForHead::For { init, test, update } => {
if let Some(await_token) = await_token { if let Some(await_token) = await_token {
syntax_error!(self, await_token, SyntaxError::AwaitForStmt); syntax_error!(self, await_token, SyntaxError::AwaitForStmt);
} }
@ -1107,7 +1183,7 @@ impl<'a, I: Tokens> Parser<I> {
body, body,
}) })
} }
ForHead::ForIn { left, right } => { TempForHead::ForIn { left, right } => {
if let Some(await_token) = await_token { if let Some(await_token) = await_token {
syntax_error!(self, await_token, SyntaxError::AwaitForStmt); syntax_error!(self, await_token, SyntaxError::AwaitForStmt);
} }
@ -1119,7 +1195,7 @@ impl<'a, I: Tokens> Parser<I> {
body, body,
}) })
} }
ForHead::ForOf { left, right } => Stmt::ForOf(ForOfStmt { TempForHead::ForOf { left, right } => Stmt::ForOf(ForOfStmt {
span, span,
is_await: await_token.is_some(), is_await: await_token.is_some(),
left, left,
@ -1129,7 +1205,7 @@ impl<'a, I: Tokens> Parser<I> {
}) })
} }
fn parse_for_head(&mut self) -> PResult<ForHead> { fn parse_for_head(&mut self) -> PResult<TempForHead> {
let strict = self.ctx().strict; let strict = self.ctx().strict;
if is_one_of!(self, "const", "var") if is_one_of!(self, "const", "var")
@ -1168,18 +1244,51 @@ impl<'a, I: Tokens> Parser<I> {
} }
} }
return self.parse_for_each_head(VarDeclOrPat::VarDecl(decl)); return self.parse_for_each_head(ForHead::VarDecl(decl));
} }
expect_exact!(self, ';'); expect_exact!(self, ';');
return self.parse_normal_for_head(Some(VarDeclOrExpr::VarDecl(decl))); return self.parse_normal_for_head(Some(VarDeclOrExpr::VarDecl(decl)));
} }
let init = if eat_exact!(self, ';') { if eat_exact!(self, ';') {
return self.parse_normal_for_head(None); return self.parse_normal_for_head(None);
} else { }
self.include_in_expr(false).parse_expr_or_pat()?
}; let start = cur_pos!(self);
let init = self.include_in_expr(false).parse_for_head_prefix()?;
let is_using_decl = self.input.syntax().using_decl()
&& match *init {
Expr::Ident(Ident {
sym: js_word!("using"),
..
}) => {
is!(self, BindingIdent)
&& !is!(self, "of")
&& (peeked_is!(self, "of") || peeked_is!(self, "in"))
}
_ => false,
};
if is_using_decl {
let name = self.parse_binding_ident()?;
let decl = VarDeclarator {
name: Pat::Ident(name),
span: span!(self, start),
init: None,
definite: false,
};
let pat = Box::new(UsingDecl {
span: span!(self, start),
decls: vec![decl],
});
cur!(self, true)?;
return self.parse_for_each_head(ForHead::UsingDecl(pat));
}
// for (a of b) // for (a of b)
if is_one_of!(self, "of", "in") { if is_one_of!(self, "of", "in") {
@ -1196,7 +1305,7 @@ impl<'a, I: Tokens> Parser<I> {
} }
} }
return self.parse_for_each_head(VarDeclOrPat::Pat(Box::new(pat))); return self.parse_for_each_head(ForHead::Pat(Box::new(pat)));
} }
expect_exact!(self, ';'); expect_exact!(self, ';');
@ -1205,18 +1314,22 @@ impl<'a, I: Tokens> Parser<I> {
self.parse_normal_for_head(Some(VarDeclOrExpr::Expr(init))) self.parse_normal_for_head(Some(VarDeclOrExpr::Expr(init)))
} }
fn parse_for_each_head(&mut self, left: VarDeclOrPat) -> PResult<ForHead> { fn parse_for_each_head(&mut self, left: ForHead) -> PResult<TempForHead> {
let of = bump!(self) == tok!("of"); let is_of = bump!(self) == tok!("of");
if of { if is_of {
let right = self.include_in_expr(true).parse_assignment_expr()?; let right = self.include_in_expr(true).parse_assignment_expr()?;
Ok(ForHead::ForOf { left, right }) Ok(TempForHead::ForOf { left, right })
} else { } else {
if let ForHead::UsingDecl(d) = &left {
self.emit_err(d.span, SyntaxError::UsingDeclNotAllowedForForInLoop)
}
let right = self.include_in_expr(true).parse_expr()?; let right = self.include_in_expr(true).parse_expr()?;
Ok(ForHead::ForIn { left, right }) Ok(TempForHead::ForIn { left, right })
} }
} }
fn parse_normal_for_head(&mut self, init: Option<VarDeclOrExpr>) -> PResult<ForHead> { fn parse_normal_for_head(&mut self, init: Option<VarDeclOrExpr>) -> PResult<TempForHead> {
let test = if eat_exact!(self, ';') { let test = if eat_exact!(self, ';') {
None None
} else { } else {
@ -1231,23 +1344,23 @@ impl<'a, I: Tokens> Parser<I> {
self.include_in_expr(true).parse_expr().map(Some)? self.include_in_expr(true).parse_expr().map(Some)?
}; };
Ok(ForHead::For { init, test, update }) Ok(TempForHead::For { init, test, update })
} }
} }
#[allow(clippy::enum_variant_names)] #[allow(clippy::enum_variant_names)]
enum ForHead { enum TempForHead {
For { For {
init: Option<VarDeclOrExpr>, init: Option<VarDeclOrExpr>,
test: Option<Box<Expr>>, test: Option<Box<Expr>>,
update: Option<Box<Expr>>, update: Option<Box<Expr>>,
}, },
ForIn { ForIn {
left: VarDeclOrPat, left: ForHead,
right: Box<Expr>, right: Box<Expr>,
}, },
ForOf { ForOf {
left: VarDeclOrPat, left: ForHead,
right: Box<Expr>, right: Box<Expr>,
}, },
} }
@ -1417,7 +1530,7 @@ mod tests {
Stmt::ForOf(ForOfStmt { Stmt::ForOf(ForOfStmt {
span, span,
is_await: true, is_await: true,
left: VarDeclOrPat::VarDecl(Box::new(VarDecl { left: ForHead::VarDecl(Box::new(VarDecl {
span, span,
kind: VarDeclKind::Const, kind: VarDeclKind::Const,
decls: vec![VarDeclarator { decls: vec![VarDeclarator {
@ -1894,14 +2007,14 @@ export default function waitUntil(callback, options = {}) {
assert!(trailing.borrow().is_empty()); assert!(trailing.borrow().is_empty());
assert_eq!(leading.borrow().len(), 1); assert_eq!(leading.borrow().len(), 1);
} }
fn parse_for_head(str: &'static str) -> ForHead { fn parse_for_head(str: &'static str) -> TempForHead {
test_parser(str, Syntax::default(), |p| p.parse_for_head()) test_parser(str, Syntax::default(), |p| p.parse_for_head())
} }
#[test] #[test]
fn for_array_binding_pattern() { fn for_array_binding_pattern() {
match parse_for_head("let [, , t] = simple_array; t < 10; t++") { match parse_for_head("let [, , t] = simple_array; t < 10; t++") {
ForHead::For { init: Some(v), .. } => assert_eq_ignore_span!( TempForHead::For { init: Some(v), .. } => assert_eq_ignore_span!(
v, v,
VarDeclOrExpr::VarDecl(Box::new(VarDecl { VarDeclOrExpr::VarDecl(Box::new(VarDecl {
span, span,
@ -1933,7 +2046,7 @@ export default function waitUntil(callback, options = {}) {
#[test] #[test]
fn for_object_binding_pattern() { fn for_object_binding_pattern() {
match parse_for_head("let {num} = obj; num < 11; num++") { match parse_for_head("let {num} = obj; num < 11; num++") {
ForHead::For { init: Some(v), .. } => assert_eq_ignore_span!( TempForHead::For { init: Some(v), .. } => assert_eq_ignore_span!(
v, v,
VarDeclOrExpr::VarDecl(Box::new(VarDecl { VarDeclOrExpr::VarDecl(Box::new(VarDecl {
span, span,

View File

@ -2328,17 +2328,8 @@ impl<I: Tokens> Parser<I> {
match &*expr.sym { match &*expr.sym {
"declare" => { "declare" => {
let decl = self.try_parse_ts_declare(start, decorators)?; let decl = self.try_parse_ts_declare(start, decorators)?;
if let Some(mut decl) = decl { if let Some(decl) = decl {
match &mut decl { Ok(Some(make_decl_declare(decl)))
Decl::Class(ClassDecl { declare, .. })
| Decl::Fn(FnDecl { declare, .. }) => *declare = true,
Decl::Var(v) => v.declare = true,
Decl::TsInterface(v) => v.declare = true,
Decl::TsTypeAlias(v) => v.declare = true,
Decl::TsEnum(v) => v.declare = true,
Decl::TsModule(v) => v.declare = true,
}
Ok(Some(decl))
} else { } else {
Ok(None) Ok(None)
} }
@ -2824,6 +2815,7 @@ fn make_decl_declare(mut decl: Decl) -> Decl {
Decl::TsTypeAlias(ref mut a) => a.declare = true, Decl::TsTypeAlias(ref mut a) => a.declare = true,
Decl::TsEnum(ref mut e) => e.declare = true, Decl::TsEnum(ref mut e) => e.declare = true,
Decl::TsModule(ref mut m) => m.declare = true, Decl::TsModule(ref mut m) => m.declare = true,
Decl::Using(..) => unreachable!("Using is not a valid declaration for `declare` keyword"),
} }
decl decl

View File

@ -47,6 +47,7 @@ where
} else { } else {
::swc_ecma_parser::Syntax::Es(::swc_ecma_parser::EsConfig { ::swc_ecma_parser::Syntax::Es(::swc_ecma_parser::EsConfig {
jsx: is_jsx, jsx: is_jsx,
using_decl: true,
..Default::default() ..Default::default()
}) })
}; };
@ -64,6 +65,7 @@ where
#[cfg(feature = "verify")] #[cfg(feature = "verify")]
#[testing::fixture("tests/errors/**/*.js")] #[testing::fixture("tests/errors/**/*.js")]
#[testing::fixture("tests/errors/**/*.mjs")]
#[testing::fixture("tests/errors/**/*.ts")] #[testing::fixture("tests/errors/**/*.ts")]
#[testing::fixture("tests/errors/**/*.tsx")] #[testing::fixture("tests/errors/**/*.tsx")]
fn error(entry: PathBuf) { fn error(entry: PathBuf) {

View File

@ -0,0 +1,7 @@
{
using f, f = foo();
}
{
using g = foo();
using g = foo();
}

View File

@ -0,0 +1,8 @@
x Using declaration requires initializer
,-[$DIR/tests/errors/explicit-resource-management/invalid-duplicate-using-bindings/input.js:1:1]
1 | {
2 | using f, f = foo();
: ^^^^^^^^^^^^^^^^^^
3 | }
`----

View File

@ -0,0 +1,6 @@
x Using declaration is not allowed in for-in loop
,-[$DIR/tests/errors/explicit-resource-management/invalid-for-using-binding-in/input.js:1:1]
1 | for (using foo in {});
: ^^^^^^^^^
`----

View File

@ -0,0 +1,6 @@
x Expression expected
,-[$DIR/tests/errors/explicit-resource-management/invalid-for-using-binding-of-in/input.js:1:1]
1 | for (using of in []);
: ^^
`----

View File

@ -0,0 +1,8 @@
{
while (1) using a;
for (;;) using b;
do using c; while (1);
if (1) using d;
with (1) using e;
label: using f;
}

View File

@ -0,0 +1,9 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-in-single-statement-context/input.js:1:1]
1 | {
2 | while (1) using a;
: ^^|^^ ^
: `-- This is the expression part of an expression statement
3 | for (;;) using b;
`----

View File

@ -0,0 +1,3 @@
{
label: using x = bar();
}

View File

@ -0,0 +1,9 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-labeled-using-binding/input.js:1:1]
1 | {
2 | label: using x = bar();
: ^^|^^ ^
: `-- This is the expression part of an expression statement
3 | }
`----

View File

@ -0,0 +1,7 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-script-top-level-labeled-using-binding/input.js:1:1]
1 | label: using x = bar();
: ^^|^^ ^
: `-- This is the expression part of an expression statement
`----

View File

@ -0,0 +1,6 @@
x Using declaration is not allowed
,-[$DIR/tests/errors/explicit-resource-management/invalid-script-top-level-using-binding/input.js:1:1]
1 | using x = bar();
: ^^^^^^^^^^^^^^^
`----

View File

@ -0,0 +1,12 @@
{
using await = h();
}
{
using \u0061wait = h();
}
{
using x, await = h();
}
{
for (using await of []);
}

View File

@ -0,0 +1,9 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-using-binding-await-module/input.js:1:1]
1 | {
2 | using await = h();
: ^^|^^ ^^^^^
: `-- This is the expression part of an expression statement
3 | }
`----

View File

@ -0,0 +1,12 @@
{
using await = h();
}
{
using \u0061wait = h();
}
{
using x, await = h();
}
{
for (using await of []);
}

View File

@ -0,0 +1,9 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-using-binding-await-script/input.js:1:1]
1 | {
2 | using await = h();
: ^^|^^ ^^^^^
: `-- This is the expression part of an expression statement
3 | }
`----

View File

@ -0,0 +1,9 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-using-binding-let/input.js:1:1]
1 | {
2 | using let = h();
: ^^|^^ ^^^
: `-- This is the expression part of an expression statement
3 | }
`----

View File

@ -0,0 +1,9 @@
x Expected ';', '}' or <eof>
,-[$DIR/tests/errors/explicit-resource-management/invalid-using-binding-pattern-declaration/input.js:1:1]
1 | {
2 | using { foo } = f();
: ^^|^^ ^
: `-- This is the expression part of an expression statement
3 | }
`----

View File

@ -0,0 +1,3 @@
{
for (using { qux } of h());
}

View File

@ -0,0 +1,8 @@
x Expected ';', got '{'
,-[$DIR/tests/errors/explicit-resource-management/invalid-using-binding-pattern-for-lhs/input.js:1:1]
1 | {
2 | for (using { qux } of h());
: ^
3 | }
`----

View File

@ -8,7 +8,7 @@ use std::{
use swc_common::{comments::SingleThreadedComments, FileName}; use swc_common::{comments::SingleThreadedComments, FileName};
use swc_ecma_ast::*; use swc_ecma_ast::*;
use swc_ecma_parser::{lexer::Lexer, PResult, Parser, Syntax}; use swc_ecma_parser::{lexer::Lexer, EsConfig, PResult, Parser, Syntax};
use swc_ecma_visit::FoldWith; use swc_ecma_visit::FoldWith;
use testing::StdErr; use testing::StdErr;
@ -90,7 +90,10 @@ where
.unwrap_or_else(|e| panic!("failed to load {}: {}", file_name.display(), e)); .unwrap_or_else(|e| panic!("failed to load {}: {}", file_name.display(), e));
let lexer = Lexer::new( let lexer = Lexer::new(
Syntax::Es(Default::default()), Syntax::Es(EsConfig {
using_decl: true,
..Default::default()
}),
EsVersion::Es2015, EsVersion::Es2015,
(&*fm).into(), (&*fm).into(),
Some(&comments), Some(&comments),

View File

@ -0,0 +1,53 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 11,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 1,
"end": 11,
"ctxt": 0
},
"expression": {
"type": "CallExpression",
"span": {
"start": 1,
"end": 10,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 1,
"end": 6,
"ctxt": 0
},
"value": "using",
"optional": false
},
"arguments": [
{
"spread": null,
"expression": {
"type": "Identifier",
"span": {
"start": 8,
"end": 9,
"ctxt": 0
},
"value": "x",
"optional": false
}
}
],
"typeArguments": null
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,136 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 38,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 1,
"end": 15,
"ctxt": 0
},
"expression": {
"type": "AssignmentExpression",
"span": {
"start": 1,
"end": 14,
"ctxt": 0
},
"operator": "=",
"left": {
"type": "MemberExpression",
"span": {
"start": 1,
"end": 10,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 1,
"end": 6,
"ctxt": 0
},
"value": "using",
"optional": false
},
"property": {
"type": "Computed",
"span": {
"start": 7,
"end": 10,
"ctxt": 0
},
"expression": {
"type": "Identifier",
"span": {
"start": 8,
"end": 9,
"ctxt": 0
},
"value": "x",
"optional": false
}
}
},
"right": {
"type": "NumericLiteral",
"span": {
"start": 13,
"end": 14,
"ctxt": 0
},
"value": 0.0,
"raw": "0"
}
}
},
{
"type": "ForOfStatement",
"span": {
"start": 16,
"end": 38,
"ctxt": 0
},
"await": false,
"left": {
"type": "MemberExpression",
"span": {
"start": 21,
"end": 30,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 21,
"end": 26,
"ctxt": 0
},
"value": "using",
"optional": false
},
"property": {
"type": "Computed",
"span": {
"start": 27,
"end": 30,
"ctxt": 0
},
"expression": {
"type": "Identifier",
"span": {
"start": 28,
"end": 29,
"ctxt": 0
},
"value": "x",
"optional": false
}
}
},
"right": {
"type": "ArrayExpression",
"span": {
"start": 34,
"end": 36,
"ctxt": 0
},
"elements": []
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 37,
"end": 38,
"ctxt": 0
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,2 @@
using [x] = 0;
for (using [x] of []);

View File

@ -0,0 +1,136 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 38,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 1,
"end": 15,
"ctxt": 0
},
"expression": {
"type": "AssignmentExpression",
"span": {
"start": 1,
"end": 14,
"ctxt": 0
},
"operator": "=",
"left": {
"type": "MemberExpression",
"span": {
"start": 1,
"end": 10,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 1,
"end": 6,
"ctxt": 0
},
"value": "using",
"optional": false
},
"property": {
"type": "Computed",
"span": {
"start": 7,
"end": 10,
"ctxt": 0
},
"expression": {
"type": "Identifier",
"span": {
"start": 8,
"end": 9,
"ctxt": 0
},
"value": "x",
"optional": false
}
}
},
"right": {
"type": "NumericLiteral",
"span": {
"start": 13,
"end": 14,
"ctxt": 0
},
"value": 0.0,
"raw": "0"
}
}
},
{
"type": "ForOfStatement",
"span": {
"start": 16,
"end": 38,
"ctxt": 0
},
"await": false,
"left": {
"type": "MemberExpression",
"span": {
"start": 21,
"end": 30,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 21,
"end": 26,
"ctxt": 0
},
"value": "using",
"optional": false
},
"property": {
"type": "Computed",
"span": {
"start": 27,
"end": 30,
"ctxt": 0
},
"expression": {
"type": "Identifier",
"span": {
"start": 28,
"end": 29,
"ctxt": 0
},
"value": "x",
"optional": false
}
}
},
"right": {
"type": "ArrayExpression",
"span": {
"start": 34,
"end": 36,
"ctxt": 0
},
"elements": []
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 37,
"end": 38,
"ctxt": 0
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,77 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 27,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 1,
"end": 6,
"ctxt": 0
},
"expression": {
"type": "Identifier",
"span": {
"start": 1,
"end": 6,
"ctxt": 0
},
"value": "using",
"optional": false
}
},
{
"type": "ExpressionStatement",
"span": {
"start": 7,
"end": 27,
"ctxt": 0
},
"expression": {
"type": "AssignmentExpression",
"span": {
"start": 7,
"end": 27,
"ctxt": 0
},
"operator": "=",
"left": {
"type": "Identifier",
"span": {
"start": 7,
"end": 13,
"ctxt": 0
},
"value": "reader",
"optional": false,
"typeAnnotation": null
},
"right": {
"type": "CallExpression",
"span": {
"start": 16,
"end": 27,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 16,
"end": 25,
"ctxt": 0
},
"value": "getReader",
"optional": false
},
"arguments": [],
"typeArguments": null
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,49 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 25,
"ctxt": 0
},
"body": [
{
"type": "ForOfStatement",
"span": {
"start": 1,
"end": 25,
"ctxt": 0
},
"await": true,
"left": {
"type": "Identifier",
"span": {
"start": 12,
"end": 17,
"ctxt": 0
},
"value": "using",
"optional": false,
"typeAnnotation": null
},
"right": {
"type": "Identifier",
"span": {
"start": 21,
"end": 23,
"ctxt": 0
},
"value": "of",
"optional": false
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 24,
"end": 25,
"ctxt": 0
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,4 @@
for (using in []);
for (using.foo in []);
for (using().foo in []);
for (using``.foo in []);

View File

@ -0,0 +1,247 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 92,
"ctxt": 0
},
"body": [
{
"type": "ForInStatement",
"span": {
"start": 1,
"end": 19,
"ctxt": 0
},
"left": {
"type": "Identifier",
"span": {
"start": 6,
"end": 11,
"ctxt": 0
},
"value": "using",
"optional": false,
"typeAnnotation": null
},
"right": {
"type": "ArrayExpression",
"span": {
"start": 15,
"end": 17,
"ctxt": 0
},
"elements": []
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 18,
"end": 19,
"ctxt": 0
}
}
},
{
"type": "ForInStatement",
"span": {
"start": 20,
"end": 42,
"ctxt": 0
},
"left": {
"type": "MemberExpression",
"span": {
"start": 25,
"end": 34,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 25,
"end": 30,
"ctxt": 0
},
"value": "using",
"optional": false
},
"property": {
"type": "Identifier",
"span": {
"start": 31,
"end": 34,
"ctxt": 0
},
"value": "foo",
"optional": false
}
},
"right": {
"type": "ArrayExpression",
"span": {
"start": 38,
"end": 40,
"ctxt": 0
},
"elements": []
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 41,
"end": 42,
"ctxt": 0
}
}
},
{
"type": "ForInStatement",
"span": {
"start": 43,
"end": 67,
"ctxt": 0
},
"left": {
"type": "MemberExpression",
"span": {
"start": 48,
"end": 59,
"ctxt": 0
},
"object": {
"type": "CallExpression",
"span": {
"start": 48,
"end": 55,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 48,
"end": 53,
"ctxt": 0
},
"value": "using",
"optional": false
},
"arguments": [],
"typeArguments": null
},
"property": {
"type": "Identifier",
"span": {
"start": 56,
"end": 59,
"ctxt": 0
},
"value": "foo",
"optional": false
}
},
"right": {
"type": "ArrayExpression",
"span": {
"start": 63,
"end": 65,
"ctxt": 0
},
"elements": []
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 66,
"end": 67,
"ctxt": 0
}
}
},
{
"type": "ForInStatement",
"span": {
"start": 68,
"end": 92,
"ctxt": 0
},
"left": {
"type": "MemberExpression",
"span": {
"start": 73,
"end": 84,
"ctxt": 0
},
"object": {
"type": "TaggedTemplateExpression",
"span": {
"start": 73,
"end": 80,
"ctxt": 0
},
"tag": {
"type": "Identifier",
"span": {
"start": 73,
"end": 78,
"ctxt": 0
},
"value": "using",
"optional": false
},
"typeParameters": null,
"template": {
"type": "TemplateLiteral",
"span": {
"start": 78,
"end": 80,
"ctxt": 0
},
"expressions": [],
"quasis": [
{
"type": "TemplateElement",
"span": {
"start": 79,
"end": 79,
"ctxt": 0
},
"tail": true,
"cooked": "",
"raw": ""
}
]
}
},
"property": {
"type": "Identifier",
"span": {
"start": 81,
"end": 84,
"ctxt": 0
},
"value": "foo",
"optional": false
}
},
"right": {
"type": "ArrayExpression",
"span": {
"start": 88,
"end": 90,
"ctxt": 0
},
"elements": []
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 91,
"end": 92,
"ctxt": 0
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,3 @@
for (
using;
reader = getReader(););

View File

@ -0,0 +1,78 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 41,
"ctxt": 0
},
"body": [
{
"type": "ForStatement",
"span": {
"start": 1,
"end": 41,
"ctxt": 0
},
"init": {
"type": "Identifier",
"span": {
"start": 9,
"end": 14,
"ctxt": 0
},
"value": "using",
"optional": false
},
"test": {
"type": "AssignmentExpression",
"span": {
"start": 18,
"end": 38,
"ctxt": 0
},
"operator": "=",
"left": {
"type": "Identifier",
"span": {
"start": 18,
"end": 24,
"ctxt": 0
},
"value": "reader",
"optional": false,
"typeAnnotation": null
},
"right": {
"type": "CallExpression",
"span": {
"start": 27,
"end": 38,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 27,
"end": 36,
"ctxt": 0
},
"value": "getReader",
"optional": false
},
"arguments": [],
"typeArguments": null
}
},
"update": null,
"body": {
"type": "EmptyStatement",
"span": {
"start": 40,
"end": 41,
"ctxt": 0
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,4 @@
for (using of of);
for (using.foo of of);
for (using().foo of of);
for (using``.foo of of);

View File

@ -0,0 +1,255 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 92,
"ctxt": 0
},
"body": [
{
"type": "ForOfStatement",
"span": {
"start": 1,
"end": 19,
"ctxt": 0
},
"await": false,
"left": {
"type": "Identifier",
"span": {
"start": 6,
"end": 11,
"ctxt": 0
},
"value": "using",
"optional": false,
"typeAnnotation": null
},
"right": {
"type": "Identifier",
"span": {
"start": 15,
"end": 17,
"ctxt": 0
},
"value": "of",
"optional": false
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 18,
"end": 19,
"ctxt": 0
}
}
},
{
"type": "ForOfStatement",
"span": {
"start": 20,
"end": 42,
"ctxt": 0
},
"await": false,
"left": {
"type": "MemberExpression",
"span": {
"start": 25,
"end": 34,
"ctxt": 0
},
"object": {
"type": "Identifier",
"span": {
"start": 25,
"end": 30,
"ctxt": 0
},
"value": "using",
"optional": false
},
"property": {
"type": "Identifier",
"span": {
"start": 31,
"end": 34,
"ctxt": 0
},
"value": "foo",
"optional": false
}
},
"right": {
"type": "Identifier",
"span": {
"start": 38,
"end": 40,
"ctxt": 0
},
"value": "of",
"optional": false
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 41,
"end": 42,
"ctxt": 0
}
}
},
{
"type": "ForOfStatement",
"span": {
"start": 43,
"end": 67,
"ctxt": 0
},
"await": false,
"left": {
"type": "MemberExpression",
"span": {
"start": 48,
"end": 59,
"ctxt": 0
},
"object": {
"type": "CallExpression",
"span": {
"start": 48,
"end": 55,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 48,
"end": 53,
"ctxt": 0
},
"value": "using",
"optional": false
},
"arguments": [],
"typeArguments": null
},
"property": {
"type": "Identifier",
"span": {
"start": 56,
"end": 59,
"ctxt": 0
},
"value": "foo",
"optional": false
}
},
"right": {
"type": "Identifier",
"span": {
"start": 63,
"end": 65,
"ctxt": 0
},
"value": "of",
"optional": false
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 66,
"end": 67,
"ctxt": 0
}
}
},
{
"type": "ForOfStatement",
"span": {
"start": 68,
"end": 92,
"ctxt": 0
},
"await": false,
"left": {
"type": "MemberExpression",
"span": {
"start": 73,
"end": 84,
"ctxt": 0
},
"object": {
"type": "TaggedTemplateExpression",
"span": {
"start": 73,
"end": 80,
"ctxt": 0
},
"tag": {
"type": "Identifier",
"span": {
"start": 73,
"end": 78,
"ctxt": 0
},
"value": "using",
"optional": false
},
"typeParameters": null,
"template": {
"type": "TemplateLiteral",
"span": {
"start": 78,
"end": 80,
"ctxt": 0
},
"expressions": [],
"quasis": [
{
"type": "TemplateElement",
"span": {
"start": 79,
"end": 79,
"ctxt": 0
},
"tail": true,
"cooked": "",
"raw": ""
}
]
}
},
"property": {
"type": "Identifier",
"span": {
"start": 81,
"end": 84,
"ctxt": 0
},
"value": "foo",
"optional": false
}
},
"right": {
"type": "Identifier",
"span": {
"start": 88,
"end": 90,
"ctxt": 0
},
"value": "of",
"optional": false
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 91,
"end": 92,
"ctxt": 0
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1 @@
using in using instanceof using;

View File

@ -0,0 +1,67 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 33,
"ctxt": 0
},
"body": [
{
"type": "ExpressionStatement",
"span": {
"start": 1,
"end": 33,
"ctxt": 0
},
"expression": {
"type": "BinaryExpression",
"span": {
"start": 1,
"end": 32,
"ctxt": 0
},
"operator": "instanceof",
"left": {
"type": "BinaryExpression",
"span": {
"start": 1,
"end": 15,
"ctxt": 0
},
"operator": "in",
"left": {
"type": "Identifier",
"span": {
"start": 1,
"end": 6,
"ctxt": 0
},
"value": "using",
"optional": false
},
"right": {
"type": "Identifier",
"span": {
"start": 10,
"end": 15,
"ctxt": 0
},
"value": "using",
"optional": false
}
},
"right": {
"type": "Identifier",
"span": {
"start": 27,
"end": 32,
"ctxt": 0
},
"value": "using",
"optional": false
}
}
}
],
"interpreter": null
}

View File

@ -0,0 +1,3 @@
{
using basic = getReader();
}

View File

@ -0,0 +1,79 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 33,
"ctxt": 0
},
"body": [
{
"type": "BlockStatement",
"span": {
"start": 1,
"end": 33,
"ctxt": 0
},
"stmts": [
{
"type": "UsingDeclaration",
"span": {
"start": 5,
"end": 30,
"ctxt": 0
},
"decls": [
{
"type": "VariableDeclarator",
"span": {
"start": 11,
"end": 30,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 11,
"end": 16,
"ctxt": 0
},
"value": "basic",
"optional": false,
"typeAnnotation": null
},
"init": {
"type": "CallExpression",
"span": {
"start": 19,
"end": 30,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 19,
"end": 28,
"ctxt": 0
},
"value": "getReader",
"optional": false
},
"arguments": [],
"typeArguments": null
},
"definite": false
}
]
},
{
"type": "EmptyStatement",
"span": {
"start": 30,
"end": 31,
"ctxt": 0
}
}
]
}
],
"interpreter": null
}

View File

@ -0,0 +1,69 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 23,
"ctxt": 0
},
"body": [
{
"type": "BlockStatement",
"span": {
"start": 1,
"end": 23,
"ctxt": 0
},
"stmts": [
{
"type": "UsingDeclaration",
"span": {
"start": 3,
"end": 20,
"ctxt": 0
},
"decls": [
{
"type": "VariableDeclarator",
"span": {
"start": 9,
"end": 20,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 9,
"end": 16,
"ctxt": 0
},
"value": "ab",
"optional": false,
"typeAnnotation": null
},
"init": {
"type": "Identifier",
"span": {
"start": 19,
"end": 20,
"ctxt": 0
},
"value": "c",
"optional": false
},
"definite": false
}
]
},
{
"type": "EmptyStatement",
"span": {
"start": 20,
"end": 21,
"ctxt": 0
}
}
]
}
],
"interpreter": null
}

View File

@ -0,0 +1,79 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 24,
"ctxt": 0
},
"body": [
{
"type": "BlockStatement",
"span": {
"start": 1,
"end": 24,
"ctxt": 0
},
"stmts": [
{
"type": "UsingDeclaration",
"span": {
"start": 3,
"end": 21,
"ctxt": 0
},
"decls": [
{
"type": "VariableDeclarator",
"span": {
"start": 9,
"end": 21,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 9,
"end": 13,
"ctxt": 0
},
"value": "𠮷",
"optional": false,
"typeAnnotation": null
},
"init": {
"type": "CallExpression",
"span": {
"start": 16,
"end": 21,
"ctxt": 0
},
"callee": {
"type": "Identifier",
"span": {
"start": 16,
"end": 19,
"ctxt": 0
},
"value": "foo",
"optional": false
},
"arguments": [],
"typeArguments": null
},
"definite": false
}
]
},
{
"type": "EmptyStatement",
"span": {
"start": 21,
"end": 22,
"ctxt": 0
}
}
]
}
],
"interpreter": null
}

View File

@ -0,0 +1,4 @@
{
using using = of;
for (using using of of);
}

View File

@ -0,0 +1,127 @@
{
"type": "Script",
"span": {
"start": 1,
"end": 51,
"ctxt": 0
},
"body": [
{
"type": "BlockStatement",
"span": {
"start": 1,
"end": 51,
"ctxt": 0
},
"stmts": [
{
"type": "UsingDeclaration",
"span": {
"start": 5,
"end": 21,
"ctxt": 0
},
"decls": [
{
"type": "VariableDeclarator",
"span": {
"start": 11,
"end": 21,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 11,
"end": 16,
"ctxt": 0
},
"value": "using",
"optional": false,
"typeAnnotation": null
},
"init": {
"type": "Identifier",
"span": {
"start": 19,
"end": 21,
"ctxt": 0
},
"value": "of",
"optional": false
},
"definite": false
}
]
},
{
"type": "EmptyStatement",
"span": {
"start": 21,
"end": 22,
"ctxt": 0
}
},
{
"type": "ForOfStatement",
"span": {
"start": 25,
"end": 49,
"ctxt": 0
},
"await": false,
"left": {
"type": "UsingDeclaration",
"span": {
"start": 30,
"end": 41,
"ctxt": 0
},
"decls": [
{
"type": "VariableDeclarator",
"span": {
"start": 30,
"end": 41,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 36,
"end": 41,
"ctxt": 0
},
"value": "using",
"optional": false,
"typeAnnotation": null
},
"init": null,
"definite": false
}
]
},
"right": {
"type": "Identifier",
"span": {
"start": 45,
"end": 47,
"ctxt": 0
},
"value": "of",
"optional": false
},
"body": {
"type": "EmptyStatement",
"span": {
"start": 48,
"end": 49,
"ctxt": 0
}
}
}
]
}
],
"interpreter": null
}

View File

@ -1056,8 +1056,8 @@ impl Visit for Shower<'_> {
n.visit_children_with(self) n.visit_children_with(self)
} }
fn visit_var_decl_or_pat(&mut self, n: &VarDeclOrPat) { fn visit_for_head(&mut self, n: &ForHead) {
self.show("VarDeclOrPat", n); self.show("ForHead", n);
n.visit_children_with(self) n.visit_children_with(self)
} }

View File

@ -39,7 +39,7 @@
6 | `-> } 6 | `-> }
`---- `----
x VarDeclOrPat x ForHead
,-[$DIR/tests/span/js/stmt/for-in.js:1:1] ,-[$DIR/tests/span/js/stmt/for-in.js:1:1]
1 | for (const a in [1, 2, 3]) { 1 | for (const a in [1, 2, 3]) {
: ^^^^^^^ : ^^^^^^^

View File

@ -27,7 +27,7 @@
3 | `-> } 3 | `-> }
`---- `----
x VarDeclOrPat x ForHead
,-[$DIR/tests/span/js/stmt/for-of.js:1:1] ,-[$DIR/tests/span/js/stmt/for-of.js:1:1]
1 | for await (const a of foo) { 1 | for await (const a of foo) {
: ^^^^^^^ : ^^^^^^^

View File

@ -2,10 +2,20 @@ use swc_ecma_ast::*;
impl_enum!( impl_enum!(
Decl, Decl,
[Class, Fn, Var, TsInterface, TsTypeAlias, TsEnum, TsModule] [
Class,
Fn,
Var,
TsInterface,
TsTypeAlias,
TsEnum,
TsModule,
Using
]
); );
impl_struct!(ClassDecl, [ident, class]); impl_struct!(ClassDecl, [ident, class]);
impl_struct!(FnDecl, [ident, function]); impl_struct!(FnDecl, [ident, function]);
impl_struct!(VarDecl, [span, kind, declare, decls]); impl_struct!(VarDecl, [span, kind, declare, decls]);
impl_struct!(VarDeclarator, [span, name, init, definite]); impl_struct!(VarDeclarator, [span, name, init, definite]);
impl_struct!(UsingDecl, [span, decls]);

View File

@ -28,7 +28,7 @@ impl_struct!(ReturnStmt, [span, arg]);
impl_struct!(ExprStmt, [span, expr]); impl_struct!(ExprStmt, [span, expr]);
impl_enum!(VarDeclOrExpr, [VarDecl, Expr]); impl_enum!(VarDeclOrExpr, [VarDecl, Expr]);
impl_enum!(VarDeclOrPat, [VarDecl, Pat]); impl_enum!(ForHead, [VarDecl, UsingDecl, Pat]);
impl_struct!(SwitchCase, [span, test, cons]); impl_struct!(SwitchCase, [span, test, cons]);

View File

@ -496,7 +496,7 @@ impl VisitMut for Fixer<'_> {
if !s.is_await { if !s.is_await {
match &s.left { match &s.left {
VarDeclOrPat::Pat(p) ForHead::Pat(p)
if matches!( if matches!(
&**p, &**p,
Pat::Ident(BindingIdent { Pat::Ident(BindingIdent {
@ -509,12 +509,12 @@ impl VisitMut for Fixer<'_> {
) => ) =>
{ {
let expr = Expr::Ident(p.clone().expect_ident().id); let expr = Expr::Ident(p.clone().expect_ident().id);
s.left = VarDeclOrPat::Pat(Pat::Expr(Box::new(expr)).into()); s.left = ForHead::Pat(Pat::Expr(Box::new(expr)).into());
} }
_ => (), _ => (),
} }
if let VarDeclOrPat::Pat(e) = &mut s.left { if let ForHead::Pat(e) = &mut s.left {
if let Pat::Expr(expr) = &mut **e { if let Pat::Expr(expr) = &mut **e {
if let Expr::Ident(Ident { if let Expr::Ident(Ident {
sym: js_word!("async"), sym: js_word!("async"),

View File

@ -1855,9 +1855,9 @@ impl VisitMut for Hoister<'_, '_> {
} }
} }
fn visit_mut_var_decl_or_pat(&mut self, n: &mut VarDeclOrPat) { fn visit_mut_for_head(&mut self, n: &mut ForHead) {
match n { match n {
VarDeclOrPat::VarDecl(v) ForHead::VarDecl(v)
if matches!( if matches!(
&**v, &**v,
VarDecl { VarDecl {
@ -1874,7 +1874,7 @@ impl VisitMut for Hoister<'_, '_> {
// console.log(a); // console.log(a);
// } // }
// } // }
VarDeclOrPat::Pat(..) => {} ForHead::Pat(..) => {}
_ => { _ => {
n.visit_mut_children_with(self); n.visit_mut_children_with(self);
} }

View File

@ -438,7 +438,7 @@ impl VisitMut for BlockScoping {
fn visit_mut_for_in_stmt(&mut self, node: &mut ForInStmt) { fn visit_mut_for_in_stmt(&mut self, node: &mut ForInStmt) {
let blockifyed = self.blockify_for_stmt_body(&mut node.body); let blockifyed = self.blockify_for_stmt_body(&mut node.body);
let lexical_var = if let VarDeclOrPat::VarDecl(decl) = &node.left { let lexical_var = if let ForHead::VarDecl(decl) = &node.left {
find_lexical_vars(decl) find_lexical_vars(decl)
} else { } else {
Vec::new() Vec::new()
@ -463,7 +463,7 @@ impl VisitMut for BlockScoping {
fn visit_mut_for_of_stmt(&mut self, node: &mut ForOfStmt) { fn visit_mut_for_of_stmt(&mut self, node: &mut ForOfStmt) {
let blockifyed = self.blockify_for_stmt_body(&mut node.body); let blockifyed = self.blockify_for_stmt_body(&mut node.body);
let vars = if let VarDeclOrPat::VarDecl(decl) = &node.left { let vars = if let ForHead::VarDecl(decl) = &node.left {
find_lexical_vars(decl) find_lexical_vars(decl)
} else { } else {
Vec::new() Vec::new()

View File

@ -311,7 +311,7 @@ impl VisitMut for BlockScopedVars {
n.right.visit_mut_with(self); n.right.visit_mut_with(self);
match &n.left { match &n.left {
VarDeclOrPat::VarDecl(v) ForHead::VarDecl(v)
if matches!( if matches!(
&**v, &**v,
VarDecl { VarDecl {
@ -336,7 +336,7 @@ impl VisitMut for BlockScopedVars {
n.right.visit_mut_with(self); n.right.visit_mut_with(self);
match &n.left { match &n.left {
VarDeclOrPat::VarDecl(v) ForHead::VarDecl(v)
if matches!( if matches!(
&**v, &**v,
VarDecl { VarDecl {

View File

@ -54,7 +54,7 @@ macro_rules! impl_for_for_stmt {
($name:ident, $T:tt) => { ($name:ident, $T:tt) => {
fn $name(&mut self, for_stmt: &mut $T) { fn $name(&mut self, for_stmt: &mut $T) {
let (left, stmt) = match &mut for_stmt.left { let (left, stmt) = match &mut for_stmt.left {
VarDeclOrPat::VarDecl(var_decl) => { ForHead::VarDecl(var_decl) => {
let has_complex = var_decl.decls.iter().any(|d| match d.name { let has_complex = var_decl.decls.iter().any(|d| match d.name {
Pat::Ident(_) => false, Pat::Ident(_) => false,
_ => true, _ => true,
@ -99,13 +99,13 @@ macro_rules! impl_for_for_stmt {
.into(); .into();
(left, stmt) (left, stmt)
} }
VarDeclOrPat::Pat(pat) => match **pat { ForHead::Pat(pat) => match **pat {
Pat::Ident(..) => { Pat::Ident(..) => {
return; return;
} }
_ => { _ => {
let left_ident = make_ref_ident_for_for_stmt(); let left_ident = make_ref_ident_for_for_stmt();
let left = VarDeclOrPat::Pat(left_ident.clone().into()); let left = ForHead::Pat(left_ident.clone().into());
// Unpack variables // Unpack variables
let stmt = AssignExpr { let stmt = AssignExpr {
span: DUMMY_SP, span: DUMMY_SP,
@ -117,6 +117,10 @@ macro_rules! impl_for_for_stmt {
(left, stmt) (left, stmt)
} }
}, },
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
}; };
for_stmt.left = left; for_stmt.left = left;

View File

@ -140,7 +140,7 @@ impl ForOf {
}; };
match left { match left {
VarDeclOrPat::VarDecl(var) => { ForHead::VarDecl(var) => {
assert_eq!( assert_eq!(
var.decls.len(), var.decls.len(),
1, 1,
@ -163,7 +163,7 @@ impl ForOf {
) )
} }
VarDeclOrPat::Pat(pat) => prepend_stmt( ForHead::Pat(pat) => prepend_stmt(
&mut body.stmts, &mut body.stmts,
AssignExpr { AssignExpr {
span: DUMMY_SP, span: DUMMY_SP,
@ -173,6 +173,10 @@ impl ForOf {
} }
.into_stmt(), .into_stmt(),
), ),
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
} }
let stmt = Stmt::For(ForStmt { let stmt = Stmt::For(ForStmt {
@ -236,7 +240,7 @@ impl ForOf {
}; };
match left { match left {
VarDeclOrPat::VarDecl(var) => { ForHead::VarDecl(var) => {
assert_eq!( assert_eq!(
var.decls.len(), var.decls.len(),
1, 1,
@ -259,7 +263,7 @@ impl ForOf {
) )
} }
VarDeclOrPat::Pat(pat) => prepend_stmt( ForHead::Pat(pat) => prepend_stmt(
&mut body.stmts, &mut body.stmts,
AssignExpr { AssignExpr {
span: DUMMY_SP, span: DUMMY_SP,
@ -269,6 +273,10 @@ impl ForOf {
} }
.into_stmt(), .into_stmt(),
), ),
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
} }
// !(_step = _iterator()).done; // !(_step = _iterator()).done;
@ -332,7 +340,7 @@ impl ForOf {
body.stmts.insert( body.stmts.insert(
0, 0,
match left { match left {
VarDeclOrPat::VarDecl(mut var) => { ForHead::VarDecl(mut var) => {
assert_eq!(var.decls.len(), 1); assert_eq!(var.decls.len(), 1);
VarDecl { VarDecl {
span: var.span, span: var.span,
@ -345,13 +353,17 @@ impl ForOf {
} }
.into() .into()
} }
VarDeclOrPat::Pat(pat) => AssignExpr { ForHead::Pat(pat) => AssignExpr {
span: DUMMY_SP, span: DUMMY_SP,
left: PatOrExpr::Pat(pat), left: PatOrExpr::Pat(pat),
op: op!("="), op: op!("="),
right: step_value, right: step_value,
} }
.into_stmt(), .into_stmt(),
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
}, },
); );

View File

@ -949,7 +949,7 @@ impl VisitMut for Generator {
self.begin_script_loop_block(); self.begin_script_loop_block();
} }
if let VarDeclOrPat::VarDecl(initializer) = &mut node.left { if let ForHead::VarDecl(initializer) = &mut node.left {
for variable in &initializer.decls { for variable in &initializer.decls {
self.hoist_variable_declaration(variable.name.as_ident().unwrap()); self.hoist_variable_declaration(variable.name.as_ident().unwrap());
} }
@ -1755,7 +1755,7 @@ impl Generator {
node.right.visit_mut_with(self); node.right.visit_mut_with(self);
self.emit_stmt(Stmt::ForIn(ForInStmt { self.emit_stmt(Stmt::ForIn(ForInStmt {
span: DUMMY_SP, span: DUMMY_SP,
left: VarDeclOrPat::Pat(key.clone().into()), left: ForHead::Pat(key.clone().into()),
right: node.right.take(), right: node.right.take(),
body: Box::new(Stmt::Expr(ExprStmt { body: Box::new(Stmt::Expr(ExprStmt {
span: DUMMY_SP, span: DUMMY_SP,
@ -1788,17 +1788,21 @@ impl Generator {
); );
let variable = match node.left { let variable = match node.left {
VarDeclOrPat::VarDecl(initializer) => { ForHead::VarDecl(initializer) => {
for variable in initializer.decls.iter() { for variable in initializer.decls.iter() {
self.hoist_variable_declaration(variable.name.as_ident().unwrap()); self.hoist_variable_declaration(variable.name.as_ident().unwrap());
} }
initializer.decls[0].name.clone() initializer.decls[0].name.clone()
} }
VarDeclOrPat::Pat(mut initializer) => { ForHead::Pat(mut initializer) => {
initializer.visit_mut_with(self); initializer.visit_mut_with(self);
*initializer *initializer
} }
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
}; };
self.emit_assignment( self.emit_assignment(
PatOrExpr::Pat(Box::new(variable)), PatOrExpr::Pat(Box::new(variable)),

View File

@ -592,7 +592,7 @@ fn handle_await_for(stmt: &mut Stmt, is_async_generator: bool) {
} }
match s.left { match s.left {
VarDeclOrPat::VarDecl(v) => { ForHead::VarDecl(v) => {
let var = v.decls.into_iter().next().unwrap(); let var = v.decls.into_iter().next().unwrap();
let var_decl = VarDeclarator { let var_decl = VarDeclarator {
span: DUMMY_SP, span: DUMMY_SP,
@ -610,7 +610,7 @@ fn handle_await_for(stmt: &mut Stmt, is_async_generator: bool) {
.into(), .into(),
); );
} }
VarDeclOrPat::Pat(p) => { ForHead::Pat(p) => {
for_loop_body.push(Stmt::Expr(ExprStmt { for_loop_body.push(Stmt::Expr(ExprStmt {
span: DUMMY_SP, span: DUMMY_SP,
expr: Box::new(Expr::Assign(AssignExpr { expr: Box::new(Expr::Assign(AssignExpr {
@ -621,6 +621,10 @@ fn handle_await_for(stmt: &mut Stmt, is_async_generator: bool) {
})), })),
})); }));
} }
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
} }
for_loop_body.extend(orig_body); for_loop_body.extend(orig_body);

View File

@ -66,7 +66,7 @@ macro_rules! impl_for_for_stmt {
let mut stmt = None; let mut stmt = None;
let left = match &mut for_stmt.left { let left = match &mut for_stmt.left {
VarDeclOrPat::VarDecl(var_decl) => { ForHead::VarDecl(var_decl) => {
let ref_ident = private_ident!("_ref"); let ref_ident = private_ident!("_ref");
// Unpack variables // Unpack variables
@ -105,7 +105,7 @@ macro_rules! impl_for_for_stmt {
} }
.into() .into()
} }
VarDeclOrPat::Pat(pat) => { ForHead::Pat(pat) => {
let var_ident = private_ident!("_ref"); let var_ident = private_ident!("_ref");
let index = self.vars.len(); let index = self.vars.len();
let pat = pat.take(); let pat = pat.take();
@ -160,6 +160,10 @@ macro_rules! impl_for_for_stmt {
} }
.into() .into()
} }
ForHead::UsingDecl(..) => {
unreachable!("using declaration must be removed by previous pass")
}
}; };
for_stmt.left = left; for_stmt.left = left;

View File

@ -446,8 +446,8 @@ impl SystemJs {
} }
} }
fn hoist_for_var_decl(&mut self, var_decl_or_pat: VarDeclOrPat) -> VarDeclOrPat { fn hoist_for_var_decl(&mut self, var_decl_or_pat: ForHead) -> ForHead {
if let VarDeclOrPat::VarDecl(mut var_decl) = var_decl_or_pat { if let ForHead::VarDecl(mut var_decl) = var_decl_or_pat {
if var_decl.kind == VarDeclKind::Var { if var_decl.kind == VarDeclKind::Var {
let var_declarator = var_decl.decls.remove(0); let var_declarator = var_decl.decls.remove(0);
let mut tos: Vec<Id> = vec![]; let mut tos: Vec<Id> = vec![];
@ -469,9 +469,9 @@ impl SystemJs {
.push(Ident::new(to.0, DUMMY_SP.with_ctxt(to.1))); .push(Ident::new(to.0, DUMMY_SP.with_ctxt(to.1)));
} }
VarDeclOrPat::Pat(var_declarator.name.into()) ForHead::Pat(var_declarator.name.into())
} else { } else {
VarDeclOrPat::VarDecl(var_decl) ForHead::VarDecl(var_decl)
} }
} else { } else {
var_decl_or_pat var_decl_or_pat

View File

@ -1007,10 +1007,10 @@ impl VisitMut for TreeShaker {
} }
} }
fn visit_mut_var_decl_or_pat(&mut self, n: &mut VarDeclOrPat) { fn visit_mut_for_head(&mut self, n: &mut ForHead) {
match n { match n {
VarDeclOrPat::VarDecl(..) => {} ForHead::VarDecl(..) | ForHead::UsingDecl(..) => {}
VarDeclOrPat::Pat(v) => { ForHead::Pat(v) => {
v.visit_mut_with(self); v.visit_mut_with(self);
} }
} }

View File

@ -1616,7 +1616,7 @@ impl VisitMut for SimplifyExpr {
self.is_modifying = old; self.is_modifying = old;
} }
fn visit_mut_var_decl_or_pat(&mut self, n: &mut VarDeclOrPat) { fn visit_mut_for_head(&mut self, n: &mut ForHead) {
let old = self.is_modifying; let old = self.is_modifying;
self.is_modifying = true; self.is_modifying = true;
n.visit_mut_children_with(self); n.visit_mut_children_with(self);

View File

@ -400,6 +400,15 @@ where
} }
} }
Decl::Using(ref var) => {
let mut names: Vec<Id> = vec![];
var.decls.visit_with(&mut VarCollector { to: &mut names });
for name in names {
self.store(name.0.clone(), name.1, true);
}
}
Decl::TsEnum(e) => { Decl::TsEnum(e) => {
// Currently swc cannot remove constant enums // Currently swc cannot remove constant enums
self.store(e.id.sym.clone(), e.id.span.ctxt, true); self.store(e.id.sym.clone(), e.id.span.ctxt, true);
@ -1541,6 +1550,9 @@ where
self.decl_names.insert(class.ident.to_id()); self.decl_names.insert(class.ident.to_id());
class.class.visit_with(self); class.class.visit_with(self);
} }
Decl::Using(d) => {
d.decls.visit_with(self);
}
Decl::Fn(f) => { Decl::Fn(f) => {
self.decl_names.insert(f.ident.to_id()); self.decl_names.insert(f.ident.to_id());
f.function.visit_with(self) f.function.visit_with(self)
@ -1745,7 +1757,7 @@ fn is_decl_concrete(d: &Decl) -> bool {
match d { match d {
Decl::TsEnum(..) => true, Decl::TsEnum(..) => true,
Decl::TsTypeAlias(..) | Decl::TsInterface(..) => false, Decl::TsTypeAlias(..) | Decl::TsInterface(..) => false,
Decl::Class(_) | Decl::Fn(_) | Decl::Var(_) => true, Decl::Class(_) | Decl::Fn(_) | Decl::Var(_) | Decl::Using(..) => true,
Decl::TsModule(b) => ts_module_has_concrete(b), Decl::TsModule(b) => ts_module_has_concrete(b),
} }
} }

View File

@ -635,6 +635,7 @@ define!({
Class(ClassDecl), Class(ClassDecl),
Fn(FnDecl), Fn(FnDecl),
Var(Box<VarDecl>), Var(Box<VarDecl>),
Using(Box<UsingDecl>),
TsInterface(Box<TsInterfaceDecl>), TsInterface(Box<TsInterfaceDecl>),
TsTypeAlias(Box<TsTypeAliasDecl>), TsTypeAlias(Box<TsTypeAliasDecl>),
TsEnum(Box<TsEnumDecl>), TsEnum(Box<TsEnumDecl>),
@ -1405,14 +1406,14 @@ define!({
} }
pub struct ForInStmt { pub struct ForInStmt {
pub span: Span, pub span: Span,
pub left: VarDeclOrPat, pub left: ForHead,
pub right: Box<Expr>, pub right: Box<Expr>,
pub body: Box<Stmt>, pub body: Box<Stmt>,
} }
pub struct ForOfStmt { pub struct ForOfStmt {
pub span: Span, pub span: Span,
pub is_await: bool, pub is_await: bool,
pub left: VarDeclOrPat, pub left: ForHead,
pub right: Box<Expr>, pub right: Box<Expr>,
pub body: Box<Stmt>, pub body: Box<Stmt>,
} }
@ -1426,8 +1427,9 @@ define!({
pub param: Option<Pat>, pub param: Option<Pat>,
pub body: BlockStmt, pub body: BlockStmt,
} }
pub enum VarDeclOrPat { pub enum ForHead {
VarDecl(Box<VarDecl>), VarDecl(Box<VarDecl>),
UsingDecl(Box<UsingDecl>),
Pat(Box<Pat>), Pat(Box<Pat>),
} }
pub enum VarDeclOrExpr { pub enum VarDeclOrExpr {
@ -1884,6 +1886,12 @@ define!({
Private(PrivateName), Private(PrivateName),
Public(PropName), Public(PropName),
} }
pub struct UsingDecl {
pub span: Span,
pub decls: Vec<VarDeclarator>,
}
}); });
#[macro_export] #[macro_export]

View File

@ -28,6 +28,8 @@ pub enum Declaration {
FuncDecl(FunctionDeclaration), FuncDecl(FunctionDeclaration),
#[tag("VariableDeclaration")] #[tag("VariableDeclaration")]
VarDecl(VariableDeclaration), VarDecl(VariableDeclaration),
#[tag("UsingDeclaration")]
UsingDecl(UsingDeclaration),
#[tag("ClassDeclaration")] #[tag("ClassDeclaration")]
ClassDecl(ClassDeclaration), ClassDecl(ClassDeclaration),
#[tag("ExportAllDeclaration")] #[tag("ExportAllDeclaration")]
@ -282,3 +284,13 @@ pub struct EnumDeclaration {
pub id: Identifier, pub id: Identifier,
pub body: EnumBody, pub body: EnumBody,
} }
#[derive(Debug, Clone, PartialEq)]
#[ast_serde("EnumDeclaration")]
pub struct UsingDeclaration {
#[serde(flatten)]
pub base: BaseNode,
#[serde(default)]
pub declarations: Vec<VariableDeclarator>,
}

View File

@ -20,6 +20,7 @@ use crate::{
TSInterfaceDeclaration, TSModuleDeclaration, TSNamespaceExportDeclaration, TSInterfaceDeclaration, TSModuleDeclaration, TSNamespaceExportDeclaration,
TSTypeAliasDeclaration, TSTypeAliasDeclaration,
}, },
UsingDeclaration,
}; };
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -103,6 +104,8 @@ pub enum Statement {
TypeAlias(TypeAlias), TypeAlias(TypeAlias),
#[tag("EnumDeclaration")] #[tag("EnumDeclaration")]
EnumDecl(EnumDeclaration), EnumDecl(EnumDeclaration),
#[tag("UsingDeclaration")]
UsingDecl(UsingDeclaration),
#[tag("TSDeclareFunction")] #[tag("TSDeclareFunction")]
TSDeclFunc(TSDeclareFunction), TSDeclFunc(TSDeclareFunction),
#[tag("TSInterfaceDeclaration")] #[tag("TSInterfaceDeclaration")]

View File

@ -1,8 +1,8 @@
use copyless::BoxHelper; use copyless::BoxHelper;
use swc_ecma_ast::{ClassDecl, Decl, FnDecl, VarDecl, VarDeclKind, VarDeclarator}; use swc_ecma_ast::{ClassDecl, Decl, FnDecl, UsingDecl, VarDecl, VarDeclKind, VarDeclarator};
use swc_estree_ast::{ use swc_estree_ast::{
ClassBody, ClassDeclaration, Declaration, FunctionDeclaration, VariableDeclaration, ClassBody, ClassDeclaration, Declaration, FunctionDeclaration, UsingDeclaration,
VariableDeclarationKind, VariableDeclarator, VariableDeclaration, VariableDeclarationKind, VariableDeclarator,
}; };
use crate::babelify::{extract_class_body_span, Babelify, Context}; use crate::babelify::{extract_class_body_span, Babelify, Context};
@ -15,6 +15,7 @@ impl Babelify for Decl {
Decl::Class(d) => Declaration::ClassDecl(d.babelify(ctx)), Decl::Class(d) => Declaration::ClassDecl(d.babelify(ctx)),
Decl::Fn(d) => Declaration::FuncDecl(d.babelify(ctx)), Decl::Fn(d) => Declaration::FuncDecl(d.babelify(ctx)),
Decl::Var(d) => Declaration::VarDecl(d.babelify(ctx)), Decl::Var(d) => Declaration::VarDecl(d.babelify(ctx)),
Decl::Using(d) => Declaration::UsingDecl(d.babelify(ctx)),
Decl::TsInterface(d) => Declaration::TSInterfaceDecl(d.babelify(ctx)), Decl::TsInterface(d) => Declaration::TSInterfaceDecl(d.babelify(ctx)),
Decl::TsTypeAlias(d) => Declaration::TSTypeAliasDecl(d.babelify(ctx)), Decl::TsTypeAlias(d) => Declaration::TSTypeAliasDecl(d.babelify(ctx)),
Decl::TsEnum(d) => Declaration::TSEnumDecl(d.babelify(ctx)), Decl::TsEnum(d) => Declaration::TSEnumDecl(d.babelify(ctx)),
@ -123,3 +124,14 @@ impl Babelify for VarDeclarator {
} }
} }
} }
impl Babelify for UsingDecl {
type Output = UsingDeclaration;
fn babelify(self, ctx: &Context) -> Self::Output {
UsingDeclaration {
base: ctx.base(self.span),
declarations: self.decls.babelify(ctx),
}
}
}

View File

@ -1,8 +1,8 @@
use copyless::BoxHelper; use copyless::BoxHelper;
use swc_ecma_ast::{ use swc_ecma_ast::{
BlockStmt, BreakStmt, CatchClause, ContinueStmt, DebuggerStmt, Decl, DoWhileStmt, EmptyStmt, BlockStmt, BreakStmt, CatchClause, ContinueStmt, DebuggerStmt, Decl, DoWhileStmt, EmptyStmt,
ExprStmt, ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, ReturnStmt, Stmt, SwitchCase, ExprStmt, ForHead, ForInStmt, ForOfStmt, ForStmt, IfStmt, LabeledStmt, ReturnStmt, Stmt,
SwitchStmt, ThrowStmt, TryStmt, VarDeclOrExpr, VarDeclOrPat, WhileStmt, WithStmt, SwitchCase, SwitchStmt, ThrowStmt, TryStmt, VarDeclOrExpr, WhileStmt, WithStmt,
}; };
use swc_estree_ast::{ use swc_estree_ast::{
BlockStatement, BreakStatement, CatchClause as BabelCatchClause, ContinueStatement, BlockStatement, BreakStatement, CatchClause as BabelCatchClause, ContinueStatement,
@ -56,6 +56,7 @@ impl Babelify for Stmt {
Decl::Class(d) => Statement::ClassDecl(d.babelify(ctx)), Decl::Class(d) => Statement::ClassDecl(d.babelify(ctx)),
Decl::Fn(d) => Statement::FuncDecl(d.babelify(ctx)), Decl::Fn(d) => Statement::FuncDecl(d.babelify(ctx)),
Decl::Var(d) => Statement::VarDecl(d.babelify(ctx)), Decl::Var(d) => Statement::VarDecl(d.babelify(ctx)),
Decl::Using(d) => Statement::UsingDecl(d.babelify(ctx)),
Decl::TsInterface(d) => Statement::TSInterfaceDecl(d.babelify(ctx)), Decl::TsInterface(d) => Statement::TSInterfaceDecl(d.babelify(ctx)),
Decl::TsTypeAlias(d) => Statement::TSTypeAliasDecl(d.babelify(ctx)), Decl::TsTypeAlias(d) => Statement::TSTypeAliasDecl(d.babelify(ctx)),
Decl::TsEnum(d) => Statement::TSEnumDecl(d.babelify(ctx)), Decl::TsEnum(d) => Statement::TSEnumDecl(d.babelify(ctx)),
@ -300,13 +301,16 @@ impl Babelify for CatchClause {
} }
} }
impl Babelify for VarDeclOrPat { impl Babelify for ForHead {
type Output = ForStmtLeft; type Output = ForStmtLeft;
fn babelify(self, ctx: &Context) -> Self::Output { fn babelify(self, ctx: &Context) -> Self::Output {
match self { match self {
VarDeclOrPat::VarDecl(v) => ForStmtLeft::VarDecl(v.babelify(ctx)), ForHead::VarDecl(v) => ForStmtLeft::VarDecl(v.babelify(ctx)),
VarDeclOrPat::Pat(p) => ForStmtLeft::LVal(p.babelify(ctx).into()), ForHead::Pat(p) => ForStmtLeft::LVal(p.babelify(ctx).into()),
_ => {
todo!("ForHead::UsingDecl({self:?})")
}
} }
} }
} }

View File

@ -2,12 +2,12 @@ use swc_common::DUMMY_SP;
use swc_ecma_ast::{ use swc_ecma_ast::{
BlockStmt, BreakStmt, ClassDecl, ClassExpr, ContinueStmt, DebuggerStmt, Decl, DefaultDecl, BlockStmt, BreakStmt, ClassDecl, ClassExpr, ContinueStmt, DebuggerStmt, Decl, DefaultDecl,
DoWhileStmt, EmptyStmt, ExportAll, ExportDecl, ExportDefaultDecl, ExportDefaultExpr, DoWhileStmt, EmptyStmt, ExportAll, ExportDecl, ExportDefaultDecl, ExportDefaultExpr,
ExportNamedSpecifier, Expr, ExprStmt, FnDecl, FnExpr, ForInStmt, ForOfStmt, ForStmt, IfStmt, ExportNamedSpecifier, Expr, ExprStmt, FnDecl, FnExpr, ForHead, ForInStmt, ForOfStmt, ForStmt,
ImportDecl, ImportNamedSpecifier, ImportSpecifier, ImportStarAsSpecifier, KeyValueProp, IfStmt, ImportDecl, ImportNamedSpecifier, ImportSpecifier, ImportStarAsSpecifier, KeyValueProp,
LabeledStmt, Lit, ModuleDecl, ModuleItem, NamedExport, ObjectLit, Pat, Prop, PropName, LabeledStmt, Lit, ModuleDecl, ModuleItem, NamedExport, ObjectLit, Pat, Prop, PropName,
PropOrSpread, ReturnStmt, Stmt, SwitchStmt, ThrowStmt, TryStmt, TsExportAssignment, PropOrSpread, ReturnStmt, Stmt, SwitchStmt, ThrowStmt, TryStmt, TsExportAssignment,
TsInterfaceDecl, TsModuleDecl, TsTypeAliasDecl, VarDecl, VarDeclKind, VarDeclOrExpr, TsInterfaceDecl, TsModuleDecl, TsTypeAliasDecl, VarDecl, VarDeclKind, VarDeclOrExpr,
VarDeclOrPat, VarDeclarator, WhileStmt, WithStmt, VarDeclarator, WhileStmt, WithStmt,
}; };
use swc_estree_ast::{ use swc_estree_ast::{
BlockStatement, BreakStatement, ClassDeclaration, ContinueStatement, DebuggerStatement, BlockStatement, BreakStatement, ClassDeclaration, ContinueStatement, DebuggerStatement,
@ -178,12 +178,12 @@ impl Swcify for ForInStatement {
} }
impl Swcify for ForStmtLeft { impl Swcify for ForStmtLeft {
type Output = VarDeclOrPat; type Output = ForHead;
fn swcify(self, ctx: &Context) -> Self::Output { fn swcify(self, ctx: &Context) -> Self::Output {
match self { match self {
ForStmtLeft::VarDecl(v) => VarDeclOrPat::VarDecl(v.swcify(ctx).into()), ForStmtLeft::VarDecl(v) => ForHead::VarDecl(v.swcify(ctx).into()),
ForStmtLeft::LVal(v) => VarDeclOrPat::Pat(v.swcify(ctx).into()), ForStmtLeft::LVal(v) => ForHead::Pat(v.swcify(ctx).into()),
} }
} }
} }