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:
강동윤 2018-11-17 12:30:49 +09:00 committed by GitHub
parent 63ee25f0c5
commit fb08c18f7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 361 additions and 220 deletions

View File

@ -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,
}

View File

@ -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,

View File

@ -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}`

View File

@ -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,
}

View File

@ -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,
}
}

View File

@ -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;

View File

@ -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);

View File

@ -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 {

View File

@ -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(),]
);
}

View File

@ -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(),
)
})
}

View File

@ -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,
})
}

View File

@ -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(),
}),

View File

@ -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]

View File

@ -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!(),
};
}

View File

@ -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)?,

View File

@ -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!(

View File

@ -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",

View File

@ -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,
}),
})),
],
}));
}

View File

@ -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)

View File

@ -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);"

View File

@ -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),
})
}

View File

@ -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(),
}),
}
}

View File

@ -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)
}
}