mirror of
https://github.com/swc-project/swc.git
synced 2024-11-23 09:38:16 +03:00
Support es2019 (#63)
ast: - Update ast to match es2019 spec parser: - implement es2019 parser - ignore an old test codegen: - fix testing: - better output while testing (when failed) transforms: - fix
This commit is contained in:
parent
63ee25f0c5
commit
fb08c18f7e
@ -82,7 +82,22 @@ pub struct ArrayLit {
|
||||
#[ast_node]
|
||||
pub struct ObjectLit {
|
||||
pub span: Span,
|
||||
pub props: Vec<Prop>,
|
||||
pub props: Vec<PropOrSpread>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub enum PropOrSpread {
|
||||
Prop(Box<Prop>),
|
||||
/// Spread properties, e.g., `{a: 1, ...obj, b: 2}`.
|
||||
Spread(SpreadElement),
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub struct SpreadElement {
|
||||
#[span(lo)]
|
||||
pub dot3_token: Span,
|
||||
#[span(hi)]
|
||||
pub expr: Box<Expr>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
@ -212,7 +227,7 @@ pub struct TplLit {
|
||||
pub struct TplElement {
|
||||
pub span: Span,
|
||||
pub tail: bool,
|
||||
pub cooked: bool,
|
||||
pub cooked: Option<String>,
|
||||
pub raw: String,
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,8 @@ pub use self::{
|
||||
expr::{
|
||||
ArrayLit, ArrowExpr, AssignExpr, AwaitExpr, BinExpr, BlockStmtOrExpr, CallExpr, ClassExpr,
|
||||
CondExpr, Expr, ExprOrSpread, ExprOrSuper, FnExpr, MemberExpr, MetaPropExpr, NewExpr,
|
||||
ObjectLit, ParenExpr, PatOrExpr, SeqExpr, ThisExpr, TplElement, TplLit, UnaryExpr,
|
||||
UpdateExpr, YieldExpr,
|
||||
ObjectLit, ParenExpr, PatOrExpr, PropOrSpread, SeqExpr, SpreadElement, ThisExpr,
|
||||
TplElement, TplLit, UnaryExpr, UpdateExpr, YieldExpr,
|
||||
},
|
||||
function::Function,
|
||||
keywords::IdentExt,
|
||||
|
@ -37,18 +37,20 @@ pub struct AssignPat {
|
||||
pub right: Box<Expr>,
|
||||
}
|
||||
|
||||
/// EsTree `RestElement`
|
||||
#[ast_node]
|
||||
pub struct RestPat {
|
||||
#[span(lo)]
|
||||
pub dot3_token: Span,
|
||||
#[span(hi)]
|
||||
pub pat: Box<Pat>,
|
||||
pub arg: Box<Pat>,
|
||||
}
|
||||
|
||||
#[ast_node]
|
||||
pub enum ObjectPatProp {
|
||||
KeyValue(KeyValuePatProp),
|
||||
Assign(AssignPatProp),
|
||||
Rest(RestPat),
|
||||
}
|
||||
|
||||
/// `{key: value}`
|
||||
|
@ -150,6 +150,12 @@ pub struct ForInStmt {
|
||||
#[ast_node]
|
||||
pub struct ForOfStmt {
|
||||
pub span: Span,
|
||||
/// Span of the await token.
|
||||
///
|
||||
/// es2018
|
||||
///
|
||||
/// for-await-of statements, e.g., `for await (const x of xs) {`
|
||||
pub await_token: Option<Span>,
|
||||
pub left: VarDeclOrPat,
|
||||
pub right: Box<Expr>,
|
||||
pub body: Box<Stmt>,
|
||||
@ -167,7 +173,11 @@ pub struct SwitchCase {
|
||||
#[ast_node]
|
||||
pub struct CatchClause {
|
||||
pub span: Span,
|
||||
pub param: Pat,
|
||||
/// es2019
|
||||
///
|
||||
/// The param is null if the catch binding is omitted. E.g., try { foo() }
|
||||
/// catch { bar() }
|
||||
pub param: Option<Pat>,
|
||||
|
||||
pub body: BlockStmt,
|
||||
}
|
||||
|
@ -1070,8 +1070,23 @@ impl<'a> Emitter<'a> {
|
||||
#[emitter]
|
||||
pub fn emit_rest_pat(&mut self, node: &RestPat) -> Result {
|
||||
punct!("...");
|
||||
emit!(node.pat);
|
||||
emit!(node.arg);
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_prop_or_spread(&mut self, node: &PropOrSpread) -> Result {
|
||||
match *node {
|
||||
PropOrSpread::Prop(ref n) => emit!(n),
|
||||
PropOrSpread::Spread(ref n) => emit!(n),
|
||||
}
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_spread_element(&mut self, node: &SpreadElement) -> Result {
|
||||
punct!("...");
|
||||
emit!(node.expr)
|
||||
}
|
||||
|
||||
#[emitter]
|
||||
pub fn emit_pat_or_expr(&mut self, node: &PatOrExpr) -> Result {
|
||||
match *node {
|
||||
@ -1116,6 +1131,7 @@ impl<'a> Emitter<'a> {
|
||||
match *node {
|
||||
ObjectPatProp::KeyValue(ref node) => emit!(node),
|
||||
ObjectPatProp::Assign(ref node) => emit!(node),
|
||||
ObjectPatProp::Rest(ref node) => emit!(node),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1324,9 +1340,11 @@ impl<'a> Emitter<'a> {
|
||||
|
||||
let emit_as_single_stmt = node.cons.len() == 1 && {
|
||||
// treat synthesized nodes as located on the same line for emit purposes
|
||||
node.is_synthesized() || node.cons[0].is_synthesized() || self
|
||||
.cm
|
||||
.is_on_same_line(node.span().lo(), node.cons[0].span().lo())
|
||||
node.is_synthesized()
|
||||
|| node.cons[0].is_synthesized()
|
||||
|| self
|
||||
.cm
|
||||
.is_on_same_line(node.span().lo(), node.cons[0].span().lo())
|
||||
};
|
||||
|
||||
let mut format = ListFormat::CaseOrDefaultClauseStatements;
|
||||
@ -1529,11 +1547,7 @@ fn should_emit_whitespace_before_operand(node: &UnaryExpr) -> bool {
|
||||
| Expr::Unary(UnaryExpr {
|
||||
op: op!(unary, "+"),
|
||||
..
|
||||
})
|
||||
if node.op == op!(unary, "+") =>
|
||||
{
|
||||
true
|
||||
}
|
||||
}) if node.op == op!(unary, "+") => true,
|
||||
Expr::Update(UpdateExpr {
|
||||
op: op!("--"),
|
||||
prefix: true,
|
||||
@ -1542,11 +1556,7 @@ fn should_emit_whitespace_before_operand(node: &UnaryExpr) -> bool {
|
||||
| Expr::Unary(UnaryExpr {
|
||||
op: op!(unary, "-"),
|
||||
..
|
||||
})
|
||||
if node.op == op!(unary, "-") =>
|
||||
{
|
||||
true
|
||||
}
|
||||
}) if node.op == op!(unary, "-") => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -4,6 +4,7 @@
|
||||
extern crate slog;
|
||||
extern crate sourcemap;
|
||||
extern crate swc_common;
|
||||
extern crate swc_ecma_ast;
|
||||
extern crate swc_ecma_codegen;
|
||||
extern crate swc_ecma_parser;
|
||||
extern crate test;
|
||||
@ -18,8 +19,9 @@ use std::{
|
||||
sync::{Arc, RwLock},
|
||||
};
|
||||
use swc_common::{Fold, FoldWith, Span};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_codegen::Emitter;
|
||||
use swc_ecma_parser::{ast::*, Parser, Session, SourceFileInput};
|
||||
use swc_ecma_parser::{Parser, Session, SourceFileInput};
|
||||
use test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName};
|
||||
use testing::NormalizedOutput;
|
||||
|
||||
|
@ -120,6 +120,8 @@ pub(crate) enum SyntaxError {
|
||||
VarInitializerInForInHead,
|
||||
LabelledGenerator,
|
||||
YieldParamInGen,
|
||||
|
||||
AwaitForStmt,
|
||||
}
|
||||
|
||||
impl<'a> From<ErrorToDiag<'a>> for Error {
|
||||
@ -214,6 +216,7 @@ impl<'a> From<ErrorToDiag<'a>> for DiagnosticBuilder<'a> {
|
||||
VarInitializerInForInHead => "Unexpected initializer in for in/of loop".into(),
|
||||
LabelledGenerator => "Generator cannot be labelled".into(),
|
||||
YieldParamInGen => "'yield' cannot be used as a parameter within generator".into(),
|
||||
AwaitForStmt => "for await syntax is valid only for for-of statement".into(),
|
||||
};
|
||||
|
||||
let d = e.handler.error(&msg).span(e.span);
|
||||
|
@ -284,8 +284,9 @@ mod tests {
|
||||
{
|
||||
::with_test_sess(s, |sess, fm| {
|
||||
let mut l = Lexer::new(sess, fm.into());
|
||||
f(&mut l)
|
||||
Ok(f(&mut l))
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn num(s: &'static str) -> f64 {
|
||||
@ -372,8 +373,9 @@ mod tests {
|
||||
::with_test_sess(case, |mut sess, input| {
|
||||
let mut l = Lexer::new(sess, input);
|
||||
l.ctx.strict = strict;
|
||||
l.map(|ts| ts.token).collect::<Vec<_>>()
|
||||
Ok(l.map(|ts| ts.token).collect::<Vec<_>>())
|
||||
})
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
if success {
|
||||
|
@ -10,9 +10,9 @@ fn sp(r: Range<usize>) -> Span {
|
||||
)
|
||||
}
|
||||
|
||||
fn with_lexer<F, Ret>(s: &'static str, f: F) -> Ret
|
||||
fn with_lexer<F, Ret>(s: &'static str, f: F) -> Result<Ret, ::testing::StdErr>
|
||||
where
|
||||
F: FnOnce(&mut Lexer<SourceFileInput>) -> Ret,
|
||||
F: FnOnce(&mut Lexer<SourceFileInput>) -> Result<Ret, ()>,
|
||||
{
|
||||
::with_test_sess(s, |sess, fm| {
|
||||
let mut l = Lexer::new(sess, fm);
|
||||
@ -21,17 +21,18 @@ where
|
||||
}
|
||||
|
||||
fn lex(s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(s, |l| l.collect())
|
||||
with_lexer(s, |l| Ok(l.collect())).unwrap()
|
||||
}
|
||||
fn lex_module(s: &'static str) -> Vec<TokenAndSpan> {
|
||||
with_lexer(s, |l| {
|
||||
l.ctx.strict = true;
|
||||
l.ctx.module = true;
|
||||
l.collect()
|
||||
Ok(l.collect())
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
fn lex_tokens(s: &'static str) -> Vec<Token> {
|
||||
with_lexer(s, |l| l.map(|ts| ts.token).collect())
|
||||
with_lexer(s, |l| Ok(l.map(|ts| ts.token).collect())).unwrap()
|
||||
}
|
||||
|
||||
trait LineBreak: Into<TokenAndSpan> {
|
||||
@ -123,28 +124,24 @@ impl WithSpan for AssignOpToken {
|
||||
fn module_legacy_octal() {
|
||||
assert_eq!(
|
||||
lex_module("01"),
|
||||
vec![
|
||||
Token::Error(Error {
|
||||
span: sp(0..2),
|
||||
error: SyntaxError::LegacyOctal,
|
||||
})
|
||||
.span(0..2)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..2),
|
||||
error: SyntaxError::LegacyOctal,
|
||||
})
|
||||
.span(0..2)
|
||||
.lb(),]
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn module_legacy_decimal() {
|
||||
assert_eq!(
|
||||
lex_module("08"),
|
||||
vec![
|
||||
Token::Error(Error {
|
||||
span: sp(0..2),
|
||||
error: SyntaxError::LegacyDecimal,
|
||||
})
|
||||
.span(0..2)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..2),
|
||||
error: SyntaxError::LegacyDecimal,
|
||||
})
|
||||
.span(0..2)
|
||||
.lb(),]
|
||||
);
|
||||
}
|
||||
|
||||
@ -152,14 +149,12 @@ fn module_legacy_decimal() {
|
||||
fn module_legacy_comment_1() {
|
||||
assert_eq!(
|
||||
lex_module("<!-- foo oo"),
|
||||
vec![
|
||||
Token::Error(Error {
|
||||
span: sp(0..11),
|
||||
error: SyntaxError::LegacyCommentInModule,
|
||||
})
|
||||
.span(0..11)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..11),
|
||||
error: SyntaxError::LegacyCommentInModule,
|
||||
})
|
||||
.span(0..11)
|
||||
.lb(),]
|
||||
)
|
||||
}
|
||||
|
||||
@ -167,14 +162,12 @@ fn module_legacy_comment_1() {
|
||||
fn module_legacy_comment_2() {
|
||||
assert_eq!(
|
||||
lex_module("-->"),
|
||||
vec![
|
||||
Token::Error(Error {
|
||||
span: sp(0..3),
|
||||
error: SyntaxError::LegacyCommentInModule,
|
||||
})
|
||||
.span(0..3)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Error(Error {
|
||||
span: sp(0..3),
|
||||
error: SyntaxError::LegacyCommentInModule,
|
||||
})
|
||||
.span(0..3)
|
||||
.lb(),]
|
||||
)
|
||||
}
|
||||
|
||||
@ -238,14 +231,12 @@ fn ident_escape_unicode_2() {
|
||||
fn str_escape_hex() {
|
||||
assert_eq!(
|
||||
lex(r#"'\x61'"#),
|
||||
vec![
|
||||
Token::Str {
|
||||
value: "a".into(),
|
||||
has_escape: true,
|
||||
}
|
||||
.span(0..6)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Str {
|
||||
value: "a".into(),
|
||||
has_escape: true,
|
||||
}
|
||||
.span(0..6)
|
||||
.lb(),]
|
||||
);
|
||||
}
|
||||
|
||||
@ -253,14 +244,12 @@ fn str_escape_hex() {
|
||||
fn str_escape_octal() {
|
||||
assert_eq!(
|
||||
lex(r#"'Hello\012World'"#),
|
||||
vec![
|
||||
Token::Str {
|
||||
value: "Hello\nWorld".into(),
|
||||
has_escape: true,
|
||||
}
|
||||
.span(0..16)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Str {
|
||||
value: "Hello\nWorld".into(),
|
||||
has_escape: true,
|
||||
}
|
||||
.span(0..16)
|
||||
.lb(),]
|
||||
)
|
||||
}
|
||||
|
||||
@ -268,14 +257,12 @@ fn str_escape_octal() {
|
||||
fn str_escape_unicode_long() {
|
||||
assert_eq!(
|
||||
lex(r#"'\u{00000000034}'"#),
|
||||
vec![
|
||||
Token::Str {
|
||||
value: "4".into(),
|
||||
has_escape: true,
|
||||
}
|
||||
.span(0..17)
|
||||
.lb(),
|
||||
]
|
||||
vec![Token::Str {
|
||||
value: "4".into(),
|
||||
has_escape: true,
|
||||
}
|
||||
.span(0..17)
|
||||
.lb(),]
|
||||
);
|
||||
}
|
||||
|
||||
@ -394,18 +381,16 @@ fn simple_regex() {
|
||||
|
||||
assert_eq!(
|
||||
lex("/42/"),
|
||||
vec![
|
||||
Regex(
|
||||
Str {
|
||||
span: sp(1..3),
|
||||
value: "42".into(),
|
||||
has_escape: false,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.span(0..4)
|
||||
.lb(),
|
||||
]
|
||||
vec![Regex(
|
||||
Str {
|
||||
span: sp(1..3),
|
||||
value: "42".into(),
|
||||
has_escape: false,
|
||||
},
|
||||
None,
|
||||
)
|
||||
.span(0..4)
|
||||
.lb(),]
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -73,31 +73,22 @@ pub struct Session<'a> {
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn with_test_sess<F, Ret>(src: &'static str, f: F) -> Ret
|
||||
fn with_test_sess<F, Ret>(src: &'static str, f: F) -> Result<Ret, ::testing::StdErr>
|
||||
where
|
||||
F: FnOnce(Session, SourceFileInput) -> Ret,
|
||||
F: FnOnce(Session, SourceFileInput) -> Result<Ret, ()>,
|
||||
{
|
||||
use std::rc::Rc;
|
||||
use swc_common::{FileName, FilePathMapping, SourceMap};
|
||||
use swc_common::FileName;
|
||||
|
||||
let cm = Rc::new(SourceMap::new(FilePathMapping::empty()));
|
||||
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
|
||||
::testing::run_test(|logger, cm, handler| {
|
||||
let fm = cm.new_source_file(FileName::Real("testing".into()), src.into());
|
||||
|
||||
let handler = ::swc_common::errors::Handler::with_tty_emitter(
|
||||
::swc_common::errors::ColorConfig::Auto,
|
||||
true,
|
||||
false,
|
||||
Some(cm),
|
||||
);
|
||||
|
||||
let logger = ::testing::logger().new(o!("src" => src));
|
||||
|
||||
f(
|
||||
Session {
|
||||
handler: &handler,
|
||||
logger: &logger,
|
||||
cfg: Default::default(),
|
||||
},
|
||||
(&*fm).into(),
|
||||
)
|
||||
f(
|
||||
Session {
|
||||
handler: &handler,
|
||||
logger: &logger,
|
||||
cfg: Default::default(),
|
||||
},
|
||||
(&*fm).into(),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
@ -123,10 +123,9 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
}
|
||||
|
||||
// Handle async function expression
|
||||
if { is!("async") }
|
||||
&& { peeked_is!("function") }
|
||||
&& { !self.input.has_linebreak_between_cur_and_peeked() }
|
||||
{
|
||||
if { is!("async") } && { peeked_is!("function") } && {
|
||||
!self.input.has_linebreak_between_cur_and_peeked()
|
||||
} {
|
||||
return self.parse_async_fn_expr();
|
||||
}
|
||||
|
||||
@ -498,7 +497,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
tail,
|
||||
|
||||
// FIXME
|
||||
cooked: false,
|
||||
cooked: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_common::DUMMY_SP as span;
|
||||
|
||||
fn lhs(s: &'static str) -> Box<Expr> {
|
||||
test_parser(s, |p| {
|
||||
@ -37,9 +37,6 @@ fn expr(s: &'static str) -> Box<Expr> {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const span: Span = DUMMY_SP;
|
||||
|
||||
#[test]
|
||||
fn arrow_assign() {
|
||||
assert_eq_ignore_span!(
|
||||
@ -59,6 +56,39 @@ fn arrow_assign() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn object_rest() {
|
||||
assert_eq_ignore_span!(
|
||||
expr("{a, ...foo, b}"),
|
||||
box Expr::Object(ObjectLit {
|
||||
span,
|
||||
props: vec![
|
||||
PropOrSpread::Prop(
|
||||
box Ident {
|
||||
span,
|
||||
sym: "a".into()
|
||||
}
|
||||
.into()
|
||||
),
|
||||
PropOrSpread::Spread(SpreadElement {
|
||||
dot3_token: span,
|
||||
expr: box Expr::Ident(Ident {
|
||||
span,
|
||||
sym: "foo".into(),
|
||||
})
|
||||
}),
|
||||
PropOrSpread::Prop(
|
||||
box Ident {
|
||||
span,
|
||||
sym: "b".into()
|
||||
}
|
||||
.into()
|
||||
),
|
||||
]
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn new_expr_should_not_eat_too_much() {
|
||||
assert_eq_ignore_span!(
|
||||
@ -138,7 +168,7 @@ fn arrow_fn_rest() {
|
||||
is_generator: false,
|
||||
params: vec![Pat::Rest(RestPat {
|
||||
dot3_token: span,
|
||||
pat: box Pat::Ident(Ident {
|
||||
arg: box Pat::Ident(Ident {
|
||||
span,
|
||||
sym: "a".into(),
|
||||
}),
|
||||
|
@ -86,7 +86,7 @@ pub fn test_parser<F, Ret>(s: &'static str, f: F) -> Ret
|
||||
where
|
||||
F: for<'a> FnOnce(&'a mut Parser<'a, ::SourceFileInput>) -> Ret,
|
||||
{
|
||||
::with_test_sess(s, |sess, input| f(&mut Parser::new(sess, input)))
|
||||
::with_test_sess(s, |sess, input| Ok(f(&mut Parser::new(sess, input)))).unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -78,7 +78,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
|
||||
#[parser]
|
||||
impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
type Prop = Prop;
|
||||
type Prop = PropOrSpread;
|
||||
|
||||
fn make_object(span: Span, props: Vec<Self::Prop>) -> Box<Expr> {
|
||||
box Expr::Object(ObjectLit { span, props })
|
||||
@ -89,6 +89,15 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
let start = cur_pos!();
|
||||
// Parse as 'MethodDefinition'
|
||||
|
||||
if eat!("...") {
|
||||
// spread elemnent
|
||||
let dot3_token = span!(start);
|
||||
|
||||
let expr = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
|
||||
return Ok(PropOrSpread::Spread(SpreadElement { dot3_token, expr }));
|
||||
}
|
||||
|
||||
if eat!('*') {
|
||||
let span_of_gen = span!(start);
|
||||
|
||||
@ -101,10 +110,10 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
Some(span_of_gen),
|
||||
)
|
||||
.map(|function| {
|
||||
Prop::Method(MethodProp {
|
||||
PropOrSpread::Prop(box Prop::Method(MethodProp {
|
||||
key: name,
|
||||
function,
|
||||
})
|
||||
}))
|
||||
});
|
||||
}
|
||||
|
||||
@ -116,14 +125,18 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
// { a: expr, }
|
||||
if eat!(':') {
|
||||
let value = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
return Ok(Prop::KeyValue(KeyValueProp { key, value }));
|
||||
return Ok(PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp {
|
||||
key,
|
||||
value,
|
||||
})));
|
||||
}
|
||||
|
||||
// Handle `a(){}` (and async(){} / get(){} / set(){})
|
||||
if is!('(') {
|
||||
return self
|
||||
.parse_fn_args_body(start, Parser::parse_unique_formal_params, None, None)
|
||||
.map(|function| Prop::Method(MethodProp { key, function }));
|
||||
.map(|function| box Prop::Method(MethodProp { key, function }))
|
||||
.map(PropOrSpread::Prop);
|
||||
}
|
||||
|
||||
let ident = match key {
|
||||
@ -141,9 +154,12 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
|
||||
if eat!('=') {
|
||||
let value = self.include_in_expr(true).parse_assignment_expr()?;
|
||||
return Ok(Prop::Assign(AssignProp { key: ident, value }));
|
||||
return Ok(PropOrSpread::Prop(box Prop::Assign(AssignProp {
|
||||
key: ident,
|
||||
value,
|
||||
})));
|
||||
}
|
||||
return Ok(ident.into());
|
||||
return Ok(PropOrSpread::Prop(box Prop::from(ident)));
|
||||
}
|
||||
|
||||
// get a(){}
|
||||
@ -158,11 +174,11 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
js_word!("get") => self
|
||||
.parse_fn_args_body(start, |_| Ok(vec![]), None, None)
|
||||
.map(|Function { body, .. }| {
|
||||
Prop::Getter(GetterProp {
|
||||
PropOrSpread::Prop(box Prop::Getter(GetterProp {
|
||||
span: span!(start),
|
||||
key,
|
||||
body,
|
||||
})
|
||||
}))
|
||||
}),
|
||||
js_word!("set") => self
|
||||
.parse_fn_args_body(
|
||||
@ -173,12 +189,12 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
)
|
||||
.map(|Function { params, body, .. }| {
|
||||
assert_eq!(params.len(), 1);
|
||||
Prop::Setter(SetterProp {
|
||||
PropOrSpread::Prop(box Prop::Setter(SetterProp {
|
||||
span: span!(start),
|
||||
key,
|
||||
body,
|
||||
param: params.into_iter().next().unwrap(),
|
||||
})
|
||||
}))
|
||||
}),
|
||||
js_word!("async") => self
|
||||
.parse_fn_args_body(
|
||||
@ -187,7 +203,9 @@ impl<'a, I: Input> ParseObject<'a, (Box<Expr>)> for Parser<'a, I> {
|
||||
Some(ident.span),
|
||||
None,
|
||||
)
|
||||
.map(|function| Prop::Method(MethodProp { key, function })),
|
||||
.map(|function| {
|
||||
PropOrSpread::Prop(box Prop::Method(MethodProp { key, function }))
|
||||
}),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
@ -90,7 +90,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
let pat = self.parse_binding_pat_or_ident()?;
|
||||
let pat = Pat::Rest(RestPat {
|
||||
dot3_token,
|
||||
pat: box pat,
|
||||
arg: box pat,
|
||||
});
|
||||
elems.push(Some(pat));
|
||||
// Trailing comma isn't allowed
|
||||
@ -138,7 +138,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
let pat = self.parse_binding_pat_or_ident()?;
|
||||
let pat = Pat::Rest(RestPat {
|
||||
dot3_token,
|
||||
pat: box pat,
|
||||
arg: box pat,
|
||||
});
|
||||
params.push(pat);
|
||||
break;
|
||||
@ -277,23 +277,33 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
.map(|prop| {
|
||||
let span = prop.span();
|
||||
match prop {
|
||||
Prop::Shorthand(id) => Ok(ObjectPatProp::Assign(AssignPatProp {
|
||||
span: id.span(),
|
||||
key: id.into(),
|
||||
value: None,
|
||||
})),
|
||||
Prop::KeyValue(KeyValueProp { key, value }) => {
|
||||
Ok(ObjectPatProp::KeyValue(KeyValuePatProp {
|
||||
key,
|
||||
value: box self
|
||||
.reparse_expr_as_pat(pat_ty.element(), value)?,
|
||||
PropOrSpread::Prop(box Prop::Shorthand(id)) => {
|
||||
Ok(ObjectPatProp::Assign(AssignPatProp {
|
||||
span: id.span(),
|
||||
key: id.into(),
|
||||
value: None,
|
||||
}))
|
||||
}
|
||||
Prop::Assign(AssignProp { key, value }) => {
|
||||
PropOrSpread::Prop(box Prop::KeyValue(kv_prop)) => {
|
||||
Ok(ObjectPatProp::KeyValue(KeyValuePatProp {
|
||||
key: kv_prop.key,
|
||||
value: box self
|
||||
.reparse_expr_as_pat(pat_ty.element(), kv_prop.value)?,
|
||||
}))
|
||||
}
|
||||
PropOrSpread::Prop(box Prop::Assign(assign_prop)) => {
|
||||
Ok(ObjectPatProp::Assign(AssignPatProp {
|
||||
span,
|
||||
key,
|
||||
value: Some(value),
|
||||
key: assign_prop.key,
|
||||
value: Some(assign_prop.value),
|
||||
}))
|
||||
}
|
||||
PropOrSpread::Spread(SpreadElement { dot3_token, expr }) => {
|
||||
Ok(ObjectPatProp::Rest(RestPat {
|
||||
dot3_token,
|
||||
// FIXME: is BindingPat correct?
|
||||
arg: box self
|
||||
.reparse_expr_as_pat(PatType::BindingPat, expr)?,
|
||||
}))
|
||||
}
|
||||
|
||||
@ -356,7 +366,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
.map(|pat| {
|
||||
Pat::Rest(RestPat {
|
||||
dot3_token,
|
||||
pat: box pat,
|
||||
arg: box pat,
|
||||
})
|
||||
})
|
||||
.map(Some)?
|
||||
@ -430,7 +440,7 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
} => self.reparse_expr_as_pat(pat_ty, expr).map(|pat| {
|
||||
Pat::Rest(RestPat {
|
||||
dot3_token,
|
||||
pat: box pat,
|
||||
arg: box pat,
|
||||
})
|
||||
})?,
|
||||
ExprOrSpread { expr, .. } => self.reparse_expr_as_pat(pat_ty, expr)?,
|
||||
|
@ -393,11 +393,15 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
}))
|
||||
}
|
||||
|
||||
fn parse_catch_param(&mut self) -> PResult<'a, Pat> {
|
||||
expect!('(');
|
||||
let pat = self.parse_binding_pat_or_ident()?;
|
||||
expect!(')');
|
||||
Ok(pat)
|
||||
/// It's optinal since es2019
|
||||
fn parse_catch_param(&mut self) -> PResult<'a, Option<Pat>> {
|
||||
if eat!('(') {
|
||||
let pat = self.parse_binding_pat_or_ident()?;
|
||||
expect!(')');
|
||||
Ok(Some(pat))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_var_stmt(&mut self, for_loop: bool) -> PResult<'a, VarDecl> {
|
||||
@ -555,6 +559,11 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
let start = cur_pos!();
|
||||
|
||||
assert_and_bump!("for");
|
||||
let await_token = if eat!("await") {
|
||||
Some(span!(start))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
expect!('(');
|
||||
let head = self.parse_for_head()?;
|
||||
expect!(')');
|
||||
@ -562,21 +571,34 @@ impl<'a, I: Input> Parser<'a, I> {
|
||||
|
||||
let span = span!(start);
|
||||
Ok(match head {
|
||||
ForHead::For { init, test, update } => Stmt::For(ForStmt {
|
||||
span,
|
||||
init,
|
||||
test,
|
||||
update,
|
||||
body,
|
||||
}),
|
||||
ForHead::ForIn { left, right } => Stmt::ForIn(ForInStmt {
|
||||
span,
|
||||
left,
|
||||
right,
|
||||
body,
|
||||
}),
|
||||
ForHead::For { init, test, update } => {
|
||||
if let Some(await_token) = await_token {
|
||||
syntax_error!(await_token, SyntaxError::AwaitForStmt);
|
||||
}
|
||||
|
||||
Stmt::For(ForStmt {
|
||||
span,
|
||||
init,
|
||||
test,
|
||||
update,
|
||||
body,
|
||||
})
|
||||
}
|
||||
ForHead::ForIn { left, right } => {
|
||||
if let Some(await_token) = await_token {
|
||||
syntax_error!(await_token, SyntaxError::AwaitForStmt);
|
||||
}
|
||||
|
||||
Stmt::ForIn(ForInStmt {
|
||||
span,
|
||||
left,
|
||||
right,
|
||||
body,
|
||||
})
|
||||
}
|
||||
ForHead::ForOf { left, right } => Stmt::ForOf(ForOfStmt {
|
||||
span,
|
||||
await_token,
|
||||
left,
|
||||
right,
|
||||
body,
|
||||
@ -705,7 +727,7 @@ impl<'a, I: Input> StmtLikeParser<'a, Stmt> for Parser<'a, I> {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use swc_common::DUMMY_SP;
|
||||
use swc_common::DUMMY_SP as span;
|
||||
|
||||
fn stmt(s: &'static str) -> Stmt {
|
||||
test_parser(s, |p| {
|
||||
@ -724,9 +746,6 @@ mod tests {
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
const span: Span = DUMMY_SP;
|
||||
|
||||
#[test]
|
||||
fn expr_stmt() {
|
||||
assert_eq_ignore_span!(stmt("a + b + c"), Stmt::Expr(expr("a + b + c")))
|
||||
@ -742,6 +761,35 @@ mod tests {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn await_for_of() {
|
||||
assert_eq_ignore_span!(
|
||||
stmt("for await (const a of b) ;"),
|
||||
Stmt::ForOf(ForOfStmt {
|
||||
span,
|
||||
await_token: Some(span),
|
||||
left: VarDeclOrPat::VarDecl(VarDecl {
|
||||
span,
|
||||
kind: VarDeclKind::Const,
|
||||
decls: vec![VarDeclarator {
|
||||
span,
|
||||
init: None,
|
||||
name: Pat::Ident(Ident {
|
||||
span,
|
||||
sym: "a".into()
|
||||
})
|
||||
}],
|
||||
}),
|
||||
right: box Expr::Ident(Ident {
|
||||
span,
|
||||
sym: "b".into()
|
||||
}),
|
||||
|
||||
body: box Stmt::Empty(EmptyStmt { span })
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn no_empty_without_semi() {
|
||||
assert_eq_ignore_span!(
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
extern crate slog;
|
||||
extern crate swc_common;
|
||||
extern crate swc_ecma_ast;
|
||||
extern crate swc_ecma_parser;
|
||||
extern crate test;
|
||||
extern crate testing;
|
||||
@ -14,7 +15,8 @@ use std::{
|
||||
path::Path,
|
||||
};
|
||||
use swc_common::{FileName, Fold, FoldWith, Span};
|
||||
use swc_ecma_parser::{ast::*, PResult, Parser, Session, SourceFileInput};
|
||||
use swc_ecma_ast::*;
|
||||
use swc_ecma_parser::{PResult, Parser, Session, SourceFileInput};
|
||||
use test::{test_main, Options, ShouldPanic::No, TestDesc, TestDescAndFn, TestFn, TestName};
|
||||
use testing::{NormalizedOutput, StdErr};
|
||||
|
||||
@ -85,6 +87,8 @@ fn add_test<F: FnOnce() + Send + 'static>(
|
||||
|
||||
fn error_tests(tests: &mut Vec<TestDescAndFn>) -> Result<(), io::Error> {
|
||||
const IGNORED_ERROR_TESTS: &[&str] = &[
|
||||
// Old (wrong) tests
|
||||
"569a2c1bad3beeb2.js",
|
||||
// Wrong tests
|
||||
"0d5e450f1da8a92a.js",
|
||||
"346316bef54d805a.js",
|
||||
|
@ -409,11 +409,11 @@ impl Classes {
|
||||
append_to.push(Expr::Object(ObjectLit {
|
||||
span: DUMMY_SP,
|
||||
props: vec![
|
||||
mk_prop_key(&m.key),
|
||||
Prop::KeyValue(KeyValueProp {
|
||||
PropOrSpread::Prop(box mk_prop_key(&m.key)),
|
||||
PropOrSpread::Prop(box Prop::KeyValue(KeyValueProp {
|
||||
key: PropName::Ident(quote_ident!("value")),
|
||||
value,
|
||||
}),
|
||||
})),
|
||||
],
|
||||
}));
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
pub use self::{
|
||||
arrow::Arrow, classes::Classes, shorthand_property::Shorthand, spread::SpreadElement,
|
||||
arrow::Arrow, classes::Classes, shorthand_property::Shorthand, spread::Spread,
|
||||
sticky_regex::StickyRegex, template_literal::TemplateLiteral,
|
||||
};
|
||||
|
||||
@ -20,7 +20,7 @@ pub fn es2015(helpers: Arc<Helpers>) -> impl Fold<Module> {
|
||||
Classes {
|
||||
helpers: helpers.clone(),
|
||||
}
|
||||
.then(SpreadElement {
|
||||
.then(Spread {
|
||||
helpers: helpers.clone(),
|
||||
})
|
||||
.then(StickyRegex)
|
||||
|
@ -8,11 +8,11 @@ use swc_ecma_ast::*;
|
||||
|
||||
/// es2015 - `SpreadElement`
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct SpreadElement {
|
||||
pub struct Spread {
|
||||
pub helpers: Arc<Helpers>,
|
||||
}
|
||||
|
||||
impl Fold<Expr> for SpreadElement {
|
||||
impl Fold<Expr> for Spread {
|
||||
fn fold(&mut self, e: Expr) -> Expr {
|
||||
let e = e.fold_children(self);
|
||||
|
||||
@ -175,28 +175,28 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
test!(
|
||||
SpreadElement::default(),
|
||||
Spread::default(),
|
||||
call,
|
||||
"ca(a, b, c, ...d, e)",
|
||||
"ca.apply(undefined, [a, b, c].concat(_toConsumableArray(d), [e]));"
|
||||
);
|
||||
|
||||
test!(
|
||||
SpreadElement::default(),
|
||||
Spread::default(),
|
||||
call_multi_spread,
|
||||
"ca(a, b, ...d, e, f, ...h)",
|
||||
"ca.apply(undefined, [a, b].concat(_toConsumableArray(d), [e, f], _toConsumableArray(h)));"
|
||||
);
|
||||
|
||||
test!(
|
||||
SpreadElement::default(),
|
||||
Spread::default(),
|
||||
call_noop,
|
||||
"ca(a, b, c, d, e)",
|
||||
"ca(a, b, c, d, e);"
|
||||
);
|
||||
|
||||
test!(
|
||||
SpreadElement::default(),
|
||||
Spread::default(),
|
||||
new,
|
||||
"new C(a, b, c, ...d, e)",
|
||||
"new (Function.prototype.bind.apply(C, [null, a, b, c].concat(_toConsumableArray(d), \
|
||||
@ -204,7 +204,7 @@ mod tests {
|
||||
);
|
||||
|
||||
test!(
|
||||
SpreadElement::default(),
|
||||
Spread::default(),
|
||||
new_noop,
|
||||
"new C(a, b, c, c, d, e)",
|
||||
"new C(a, b, c, c, d, e);"
|
||||
|
@ -973,24 +973,27 @@ where
|
||||
|
||||
Expr::Object(ObjectLit { props, .. }) => {
|
||||
props.into_iter().for_each(|node| match node {
|
||||
Prop::Shorthand(..) => return,
|
||||
Prop::KeyValue(KeyValueProp { key, value }) => {
|
||||
match key {
|
||||
PropOrSpread::Prop(box node) => match node {
|
||||
Prop::Shorthand(..) => return,
|
||||
Prop::KeyValue(KeyValueProp { key, value }) => {
|
||||
match key {
|
||||
PropName::Computed(e) => add_effects(v, e),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
add_effects(v, value)
|
||||
}
|
||||
Prop::Getter(GetterProp { key, .. })
|
||||
| Prop::Setter(SetterProp { key, .. })
|
||||
| Prop::Method(MethodProp { key, .. }) => match key {
|
||||
PropName::Computed(e) => add_effects(v, e),
|
||||
_ => {}
|
||||
},
|
||||
Prop::Assign(..) => {
|
||||
unreachable!("assign property in object literal is not a valid syntax")
|
||||
}
|
||||
|
||||
add_effects(v, value)
|
||||
}
|
||||
Prop::Getter(GetterProp { key, .. })
|
||||
| Prop::Setter(SetterProp { key, .. })
|
||||
| Prop::Method(MethodProp { key, .. }) => match key {
|
||||
PropName::Computed(e) => add_effects(v, e),
|
||||
_ => {}
|
||||
},
|
||||
Prop::Assign(..) => {
|
||||
unreachable!("assign property in object literal is not a valid syntax")
|
||||
}
|
||||
PropOrSpread::Spread(SpreadElement { expr, .. }) => add_effects(v, expr),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -522,17 +522,20 @@ pub trait ExprExt {
|
||||
|| alt.may_have_side_effects()
|
||||
}
|
||||
|
||||
Expr::Object(ObjectLit { ref props, .. }) => props.iter().any(|node| match *node {
|
||||
Prop::Shorthand(..) => false,
|
||||
Prop::KeyValue(KeyValueProp { ref key, ref value }) => {
|
||||
let k = match *key {
|
||||
PropName::Computed(ref e) => e.may_have_side_effects(),
|
||||
_ => false,
|
||||
};
|
||||
Expr::Object(ObjectLit { ref props, .. }) => props.iter().any(|node| match node {
|
||||
PropOrSpread::Prop(box node) => match *node {
|
||||
Prop::Shorthand(..) => false,
|
||||
Prop::KeyValue(KeyValueProp { ref key, ref value }) => {
|
||||
let k = match *key {
|
||||
PropName::Computed(ref e) => e.may_have_side_effects(),
|
||||
_ => false,
|
||||
};
|
||||
|
||||
k || value.may_have_side_effects()
|
||||
}
|
||||
_ => true,
|
||||
k || value.may_have_side_effects()
|
||||
}
|
||||
_ => true,
|
||||
},
|
||||
PropOrSpread::Spread(SpreadElement { expr, .. }) => expr.may_have_side_effects(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
@ -34,12 +34,18 @@ pub struct Diff {
|
||||
/// slashes (/) (for Windows) - All CR LF newlines are converted to LF
|
||||
///
|
||||
/// - `normalize-stdout` is not implemented (yet?).
|
||||
#[derive(Debug, Clone, Ord, PartialOrd, PartialEq, Eq, Default, Hash)]
|
||||
#[derive(Clone, Ord, PartialOrd, PartialEq, Eq, Default, Hash)]
|
||||
pub struct NormalizedOutput(String);
|
||||
|
||||
impl fmt::Display for NormalizedOutput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for NormalizedOutput {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
fmt::Display::fmt(&self.0, f)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user