mirror of
https://github.com/swc-project/swc.git
synced 2024-12-22 13:11:31 +03:00
Nullish coalescing / optional chaining / comments (#529)
swc_ecma_ast: - rename `TsOptChain` to `OptChainExpr` (Fixes #525) - add `BinOp::NullishCoalescing` swc_ecma_parser: - parse `??` (Fixes #526) swc_ecma_transforms: - remap comments from fixer (Fixes #528)
This commit is contained in:
parent
d8541c4f63
commit
b1e4122b02
12
README.md
12
README.md
@ -133,6 +133,18 @@ New generation javascript to old-days javascript.
|
|||||||
- [x] optional-catch-binding
|
- [x] optional-catch-binding
|
||||||
- [ ] unicode-property-regex
|
- [ ] unicode-property-regex
|
||||||
|
|
||||||
|
- es2019 (stage 4)
|
||||||
|
- [x] optional catch binding
|
||||||
|
- [x] big int literal
|
||||||
|
|
||||||
|
- stage 3
|
||||||
|
- [x] class fields
|
||||||
|
- [x] numeric separator
|
||||||
|
- [ ] nullish coalescing
|
||||||
|
|
||||||
|
- stage 2
|
||||||
|
- [x] decorators
|
||||||
|
|
||||||
- react
|
- react
|
||||||
- [x] jsx
|
- [x] jsx
|
||||||
|
|
||||||
|
@ -31,6 +31,34 @@ impl Comments {
|
|||||||
pub fn leading_comments(&self, pos: BytePos) -> Option<ReadGuard<'_, BytePos, Vec<Comment>>> {
|
pub fn leading_comments(&self, pos: BytePos) -> Option<ReadGuard<'_, BytePos, Vec<Comment>>> {
|
||||||
self.leading.get(&pos)
|
self.leading.get(&pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_leading(&self, from: BytePos, to: BytePos) {
|
||||||
|
let cmt = self.leading.remove(&from);
|
||||||
|
|
||||||
|
if let Some(cmt) = cmt {
|
||||||
|
self.leading.alter(to, |v| match v {
|
||||||
|
Some(mut value) => {
|
||||||
|
value.extend(cmt);
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
None => Some(cmt),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn move_trailing(&self, from: BytePos, to: BytePos) {
|
||||||
|
let cmt = self.trailing.remove(&from);
|
||||||
|
|
||||||
|
if let Some(cmt) = cmt {
|
||||||
|
self.trailing.alter(to, |v| match v {
|
||||||
|
Some(mut value) => {
|
||||||
|
value.extend(cmt);
|
||||||
|
Some(value)
|
||||||
|
}
|
||||||
|
None => Some(cmt),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "swc_ecma_ast"
|
name = "swc_ecma_ast"
|
||||||
version = "0.12.1"
|
version = "0.13.0"
|
||||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
|
@ -10,8 +10,8 @@ use crate::{
|
|||||||
prop::Prop,
|
prop::Prop,
|
||||||
stmt::BlockStmt,
|
stmt::BlockStmt,
|
||||||
typescript::{
|
typescript::{
|
||||||
TsAsExpr, TsConstAssertion, TsNonNullExpr, TsOptChain, TsTypeAnn, TsTypeAssertion,
|
TsAsExpr, TsConstAssertion, TsNonNullExpr, TsTypeAnn, TsTypeAssertion, TsTypeCastExpr,
|
||||||
TsTypeCastExpr, TsTypeParamDecl, TsTypeParamInstantiation,
|
TsTypeParamDecl, TsTypeParamInstantiation,
|
||||||
},
|
},
|
||||||
Invalid,
|
Invalid,
|
||||||
};
|
};
|
||||||
@ -144,8 +144,8 @@ pub enum Expr {
|
|||||||
#[tag("PrivateName")]
|
#[tag("PrivateName")]
|
||||||
PrivateName(PrivateName),
|
PrivateName(PrivateName),
|
||||||
|
|
||||||
#[tag("TsOptionalChainingExpression")]
|
#[tag("OptionalChainingExpression")]
|
||||||
TsOptChain(TsOptChain),
|
OptChain(OptChainExpr),
|
||||||
|
|
||||||
#[tag("Invalid")]
|
#[tag("Invalid")]
|
||||||
Invalid(Invalid),
|
Invalid(Invalid),
|
||||||
@ -544,6 +544,12 @@ impl From<Str> for Expr {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[ast_node("OptionalChainingExpression")]
|
||||||
|
pub struct OptChainExpr {
|
||||||
|
pub span: Span,
|
||||||
|
pub expr: Box<Expr>,
|
||||||
|
}
|
||||||
|
|
||||||
test_de!(
|
test_de!(
|
||||||
jsx_element,
|
jsx_element,
|
||||||
JSXElement,
|
JSXElement,
|
||||||
|
@ -15,8 +15,8 @@ pub use self::{
|
|||||||
expr::{
|
expr::{
|
||||||
ArrayLit, ArrowExpr, AssignExpr, AwaitExpr, BinExpr, BlockStmtOrExpr, CallExpr, ClassExpr,
|
ArrayLit, ArrowExpr, AssignExpr, AwaitExpr, BinExpr, BlockStmtOrExpr, CallExpr, ClassExpr,
|
||||||
CondExpr, Expr, ExprOrSpread, ExprOrSuper, FnExpr, MemberExpr, MetaPropExpr, NewExpr,
|
CondExpr, Expr, ExprOrSpread, ExprOrSuper, FnExpr, MemberExpr, MetaPropExpr, NewExpr,
|
||||||
ObjectLit, ParenExpr, PatOrExpr, PropOrSpread, SeqExpr, SpreadElement, Super, TaggedTpl,
|
ObjectLit, OptChainExpr, ParenExpr, PatOrExpr, PropOrSpread, SeqExpr, SpreadElement, Super,
|
||||||
ThisExpr, Tpl, TplElement, UnaryExpr, UpdateExpr, YieldExpr,
|
TaggedTpl, ThisExpr, Tpl, TplElement, UnaryExpr, UpdateExpr, YieldExpr,
|
||||||
},
|
},
|
||||||
function::{Function, PatOrTsParamProp},
|
function::{Function, PatOrTsParamProp},
|
||||||
ident::{Ident, IdentExt, PrivateName},
|
ident::{Ident, IdentExt, PrivateName},
|
||||||
@ -56,12 +56,12 @@ pub use self::{
|
|||||||
TsInterfaceBody, TsInterfaceDecl, TsIntersectionType, TsKeywordType, TsKeywordTypeKind,
|
TsInterfaceBody, TsInterfaceDecl, TsIntersectionType, TsKeywordType, TsKeywordTypeKind,
|
||||||
TsLit, TsLitType, TsMappedType, TsMethodSignature, TsModuleBlock, TsModuleDecl,
|
TsLit, TsLitType, TsMappedType, TsMethodSignature, TsModuleBlock, TsModuleDecl,
|
||||||
TsModuleName, TsModuleRef, TsNamespaceBody, TsNamespaceDecl, TsNamespaceExportDecl,
|
TsModuleName, TsModuleRef, TsNamespaceBody, TsNamespaceDecl, TsNamespaceExportDecl,
|
||||||
TsNonNullExpr, TsOptChain, TsOptionalType, TsParamProp, TsParamPropParam,
|
TsNonNullExpr, TsOptionalType, TsParamProp, TsParamPropParam, TsParenthesizedType,
|
||||||
TsParenthesizedType, TsPropertySignature, TsQualifiedName, TsRestType, TsSignatureDecl,
|
TsPropertySignature, TsQualifiedName, TsRestType, TsSignatureDecl, TsThisType,
|
||||||
TsThisType, TsThisTypeOrIdent, TsTupleType, TsType, TsTypeAliasDecl, TsTypeAnn,
|
TsThisTypeOrIdent, TsTupleType, TsType, TsTypeAliasDecl, TsTypeAnn, TsTypeAssertion,
|
||||||
TsTypeAssertion, TsTypeCastExpr, TsTypeElement, TsTypeLit, TsTypeOperator,
|
TsTypeCastExpr, TsTypeElement, TsTypeLit, TsTypeOperator, TsTypeOperatorOp, TsTypeParam,
|
||||||
TsTypeOperatorOp, TsTypeParam, TsTypeParamDecl, TsTypeParamInstantiation, TsTypePredicate,
|
TsTypeParamDecl, TsTypeParamInstantiation, TsTypePredicate, TsTypeQuery, TsTypeQueryExpr,
|
||||||
TsTypeQuery, TsTypeQueryExpr, TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
|
TsTypeRef, TsUnionOrIntersectionType, TsUnionType,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use swc_common::{ast_node, Span};
|
use swc_common::{ast_node, Span};
|
||||||
|
@ -106,6 +106,9 @@ macro_rules! op {
|
|||||||
("**") => {
|
("**") => {
|
||||||
$crate::BinaryOp::Exp
|
$crate::BinaryOp::Exp
|
||||||
};
|
};
|
||||||
|
("??") => {
|
||||||
|
$crate::BinaryOp::NullishCoalescing
|
||||||
|
};
|
||||||
|
|
||||||
("=") => {
|
("=") => {
|
||||||
$crate::AssignOp::Assign
|
$crate::AssignOp::Assign
|
||||||
|
@ -85,6 +85,10 @@ pub enum BinaryOp {
|
|||||||
/// `**`
|
/// `**`
|
||||||
#[kind(precedence = "11")]
|
#[kind(precedence = "11")]
|
||||||
Exp,
|
Exp,
|
||||||
|
|
||||||
|
/// `??`
|
||||||
|
#[kind(precedence = "1")]
|
||||||
|
NullishCoalescing,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(StringEnum, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
#[derive(StringEnum, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -837,9 +837,3 @@ pub struct TsConstAssertion {
|
|||||||
pub span: Span,
|
pub span: Span,
|
||||||
pub expr: Box<Expr>,
|
pub expr: Box<Expr>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[ast_node("TsOptionalChainingExpression")]
|
|
||||||
pub struct TsOptChain {
|
|
||||||
pub span: Span,
|
|
||||||
pub expr: Box<Expr>,
|
|
||||||
}
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "swc_ecma_codegen"
|
name = "swc_ecma_codegen"
|
||||||
version = "0.10.0"
|
version = "0.11.0"
|
||||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
@ -13,11 +13,11 @@ bitflags = "1"
|
|||||||
hashbrown = "0.6"
|
hashbrown = "0.6"
|
||||||
swc_atoms = { version = "0.2", path ="../../atoms" }
|
swc_atoms = { version = "0.2", path ="../../atoms" }
|
||||||
swc_common = { version = "0.4.0", path ="../../common" }
|
swc_common = { version = "0.4.0", path ="../../common" }
|
||||||
swc_ecma_ast = { version = "0.12.0", path ="../ast" }
|
swc_ecma_ast = { version = "0.13.0", path ="../ast" }
|
||||||
swc_ecma_codegen_macros = { version = "0.4", path ="./macros" }
|
swc_ecma_codegen_macros = { version = "0.4", path ="./macros" }
|
||||||
sourcemap = "4.1.1"
|
sourcemap = "4.1.1"
|
||||||
num-bigint = { version = "0.2", features = ["serde"] }
|
num-bigint = { version = "0.2", features = ["serde"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
testing = { version = "0.4", path ="../../testing" }
|
testing = { version = "0.4", path ="../../testing" }
|
||||||
swc_ecma_parser = { version = "0.14", path ="../parser" }
|
swc_ecma_parser = { version = "0.15", path ="../parser" }
|
@ -480,11 +480,40 @@ impl<'a> Emitter<'a> {
|
|||||||
Expr::TsTypeAssertion(ref n) => emit!(n),
|
Expr::TsTypeAssertion(ref n) => emit!(n),
|
||||||
Expr::TsConstAssertion(ref n) => emit!(n),
|
Expr::TsConstAssertion(ref n) => emit!(n),
|
||||||
Expr::TsTypeCast(ref n) => emit!(n),
|
Expr::TsTypeCast(ref n) => emit!(n),
|
||||||
Expr::TsOptChain(ref n) => emit!(n),
|
Expr::OptChain(ref n) => emit!(n),
|
||||||
Expr::Invalid(..) => unimplemented!("emit Expr::Invalid"),
|
Expr::Invalid(..) => unimplemented!("emit Expr::Invalid"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[emitter]
|
||||||
|
pub fn emit_opt_chain(&mut self, n: &OptChainExpr) -> Result {
|
||||||
|
self.emit_leading_comments_of_pos(n.span().lo())?;
|
||||||
|
|
||||||
|
match *n.expr {
|
||||||
|
Expr::Member(ref e) => {
|
||||||
|
emit!(e.obj);
|
||||||
|
self.wr.write_operator("?.")?;
|
||||||
|
|
||||||
|
if e.computed {
|
||||||
|
punct!("[");
|
||||||
|
emit!(e.prop);
|
||||||
|
punct!("]");
|
||||||
|
} else {
|
||||||
|
emit!(e.prop);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Expr::Call(ref e) => {
|
||||||
|
emit!(e.callee);
|
||||||
|
self.wr.write_operator("?.")?;
|
||||||
|
|
||||||
|
punct!("(");
|
||||||
|
self.emit_expr_or_spreads(n.span(), &e.args, ListFormat::CallExpressionArguments)?;
|
||||||
|
punct!(")");
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[emitter]
|
#[emitter]
|
||||||
pub fn emit_call_expr(&mut self, node: &CallExpr) -> Result {
|
pub fn emit_call_expr(&mut self, node: &CallExpr) -> Result {
|
||||||
self.emit_leading_comments_of_pos(node.span().lo())?;
|
self.emit_leading_comments_of_pos(node.span().lo())?;
|
||||||
|
@ -16,11 +16,6 @@ impl<'a> Emitter<'a> {
|
|||||||
unimplemented!("emit_ts_array_type")
|
unimplemented!("emit_ts_array_type")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[emitter]
|
|
||||||
pub fn emit_ts_opt_chain(&mut self, n: &TsOptChain) -> Result {
|
|
||||||
unimplemented!("emit_ts_opt_chain")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[emitter]
|
#[emitter]
|
||||||
pub fn emit_ts_as_expr(&mut self, n: &TsAsExpr) -> Result {
|
pub fn emit_ts_as_expr(&mut self, n: &TsAsExpr) -> Result {
|
||||||
unimplemented!("emit_ts_as_expr")
|
unimplemented!("emit_ts_as_expr")
|
||||||
|
@ -210,7 +210,7 @@ impl StartsWithAlphaNum for Expr {
|
|||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
Expr::TsTypeCast(..) => true,
|
Expr::TsTypeCast(..) => true,
|
||||||
Expr::TsOptChain(ref e) => e.expr.starts_with_alpha_num(),
|
Expr::OptChain(ref e) => e.expr.starts_with_alpha_num(),
|
||||||
|
|
||||||
Expr::Invalid(..) => true,
|
Expr::Invalid(..) => true,
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "swc_ecma_parser"
|
name = "swc_ecma_parser"
|
||||||
version = "0.14.0"
|
version = "0.15.0"
|
||||||
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
authors = ["강동윤 <kdy1997.dev@gmail.com>"]
|
||||||
license = "Apache-2.0/MIT"
|
license = "Apache-2.0/MIT"
|
||||||
repository = "https://github.com/swc-project/swc.git"
|
repository = "https://github.com/swc-project/swc.git"
|
||||||
@ -18,7 +18,7 @@ verify = ["fold"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
swc_atoms = { version = "0.2", path ="../../atoms" }
|
swc_atoms = { version = "0.2", path ="../../atoms" }
|
||||||
swc_common = { version = "0.4.0", path ="../../common" }
|
swc_common = { version = "0.4.0", path ="../../common" }
|
||||||
swc_ecma_ast = { version = "0.12.0", path ="../ast" }
|
swc_ecma_ast = { version = "0.13.0", path ="../ast" }
|
||||||
swc_ecma_parser_macros = { package = "swc_ecma_parser_macros", version = "0.4", path ="./macros" }
|
swc_ecma_parser_macros = { package = "swc_ecma_parser_macros", version = "0.4", path ="./macros" }
|
||||||
enum_kind = { version = "0.2", path ="../../macros/enum_kind" }
|
enum_kind = { version = "0.2", path ="../../macros/enum_kind" }
|
||||||
unicode-xid = "0.2"
|
unicode-xid = "0.2"
|
||||||
|
@ -103,6 +103,9 @@ pub enum SyntaxError {
|
|||||||
AwaitStar,
|
AwaitStar,
|
||||||
ReservedWordInObjShorthandOrPat,
|
ReservedWordInObjShorthandOrPat,
|
||||||
|
|
||||||
|
NullishCoalescingWithLogicalOp,
|
||||||
|
NullishCoalescingNotEnabled,
|
||||||
|
|
||||||
MultipleDefault {
|
MultipleDefault {
|
||||||
/// Span of the previous default case
|
/// Span of the previous default case
|
||||||
previous: Span,
|
previous: Span,
|
||||||
@ -359,6 +362,13 @@ impl<'a> From<ErrorToDiag<'a>> for DiagnosticBuilder<'a> {
|
|||||||
"A numeric separator is only allowed between two digits".into()
|
"A numeric separator is only allowed between two digits".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NullishCoalescingWithLogicalOp => "Nullish coalescing operator(??) requires parens \
|
||||||
|
when mixing with logical operators"
|
||||||
|
.into(),
|
||||||
|
NullishCoalescingNotEnabled => {
|
||||||
|
"Nullish coalescing operator(??) requires jsc.parser.Coalescing".into()
|
||||||
|
}
|
||||||
|
|
||||||
TS1056 => "jsc.taraget should be es5 or upper to use getter / setter".into(),
|
TS1056 => "jsc.taraget should be es5 or upper to use getter / setter".into(),
|
||||||
TS1141 => "literal in an import type should be string literal".into(),
|
TS1141 => "literal in an import type should be string literal".into(),
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ impl<'a, I: Input> Lexer<'a, I> {
|
|||||||
return Ok(Some(tok!('.')));
|
return Ok(Some(tok!('.')));
|
||||||
}
|
}
|
||||||
|
|
||||||
'(' | ')' | ';' | ',' | '[' | ']' | '{' | '}' | '@' | '?' => {
|
'(' | ')' | ';' | ',' | '[' | ']' | '{' | '}' | '@' => {
|
||||||
// These tokens are emitted directly.
|
// These tokens are emitted directly.
|
||||||
self.input.bump();
|
self.input.bump();
|
||||||
return Ok(Some(match c {
|
return Ok(Some(match c {
|
||||||
@ -197,6 +197,18 @@ impl<'a, I: Input> Lexer<'a, I> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
'?' => match self.input.peek() {
|
||||||
|
Some('?') => {
|
||||||
|
self.input.bump();
|
||||||
|
self.input.bump();
|
||||||
|
return Ok(Some(tok!("??")));
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.input.bump();
|
||||||
|
return Ok(Some(tok!('?')));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
'`' => {
|
'`' => {
|
||||||
self.bump();
|
self.bump();
|
||||||
return Ok(Some(tok!('`')));
|
return Ok(Some(tok!('`')));
|
||||||
|
@ -247,6 +247,18 @@ impl Syntax {
|
|||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn nullish_coalescing(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Syntax::Es(EsConfig {
|
||||||
|
nullish_coalescing: true,
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| Syntax::Typescript(..) => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
|
||||||
@ -292,6 +304,7 @@ pub struct EsConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub jsx: bool,
|
pub jsx: bool,
|
||||||
/// Support numeric separator.
|
/// Support numeric separator.
|
||||||
|
/// Stage 3.
|
||||||
#[serde(rename = "numericSeparator")]
|
#[serde(rename = "numericSeparator")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub num_sep: bool,
|
pub num_sep: bool,
|
||||||
@ -332,6 +345,10 @@ pub struct EsConfig {
|
|||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub dynamic_import: bool,
|
pub dynamic_import: bool,
|
||||||
|
|
||||||
|
/// Stage 3.
|
||||||
|
#[serde(default)]
|
||||||
|
pub nullish_coalescing: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Syntactic context.
|
/// Syntactic context.
|
||||||
|
@ -19,6 +19,9 @@ macro_rules! tok {
|
|||||||
('-') => {
|
('-') => {
|
||||||
crate::token::Token::BinOp(crate::token::BinOpToken::Sub)
|
crate::token::Token::BinOp(crate::token::BinOpToken::Sub)
|
||||||
};
|
};
|
||||||
|
("??") => {
|
||||||
|
crate::token::Token::BinOp(crate::token::BinOpToken::NullishCoalescing)
|
||||||
|
};
|
||||||
('~') => {
|
('~') => {
|
||||||
crate::token::Token::Tilde
|
crate::token::Token::Tilde
|
||||||
};
|
};
|
||||||
|
@ -869,7 +869,7 @@ impl<'a, I: Tokens> Parser<'a, I> {
|
|||||||
macro_rules! wrap {
|
macro_rules! wrap {
|
||||||
($e:expr) => {{
|
($e:expr) => {{
|
||||||
if is_optional_chaining {
|
if is_optional_chaining {
|
||||||
Expr::TsOptChain(TsOptChain {
|
Expr::OptChain(OptChainExpr {
|
||||||
span: span!(self, start),
|
span: span!(self, start),
|
||||||
expr: Box::new($e),
|
expr: Box::new($e),
|
||||||
})
|
})
|
||||||
|
@ -107,6 +107,10 @@ impl<'a, I: Tokens> Parser<'a, I> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !self.syntax().nullish_coalescing() && op == op!("??") {
|
||||||
|
syntax_error!(left.span(), SyntaxError::NullishCoalescingNotEnabled)
|
||||||
|
}
|
||||||
|
|
||||||
if op.precedence() <= min_prec {
|
if op.precedence() <= min_prec {
|
||||||
trace!(
|
trace!(
|
||||||
"returning {:?} without parsing {:?} because min_prec={}, prec={}",
|
"returning {:?} without parsing {:?} because min_prec={}, prec={}",
|
||||||
@ -154,6 +158,33 @@ impl<'a, I: Tokens> Parser<'a, I> {
|
|||||||
},
|
},
|
||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
/* this check is for all ?? operators
|
||||||
|
* a ?? b && c for this example
|
||||||
|
* b && c => This is considered as a logical expression in the ast tree
|
||||||
|
* a => Identifier
|
||||||
|
* so for ?? operator we need to check in this case the right expression to
|
||||||
|
* have parenthesis second case a && b ?? c
|
||||||
|
* here a && b => This is considered as a logical expression in the ast tree
|
||||||
|
* c => identifier
|
||||||
|
* so now here for ?? operator we need to check the left expression to have
|
||||||
|
* parenthesis if the parenthesis is missing we raise an error and
|
||||||
|
* throw it
|
||||||
|
*/
|
||||||
|
if op == op!("??") {
|
||||||
|
match *left {
|
||||||
|
Expr::Bin(BinExpr { span, op, .. }) if op == op!("&&") || op == op!("||") => {
|
||||||
|
syntax_error!(span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match *right {
|
||||||
|
Expr::Bin(BinExpr { span, op, .. }) if op == op!("&&") || op == op!("||") => {
|
||||||
|
syntax_error!(span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let node = Box::new(Expr::Bin(BinExpr {
|
let node = Box::new(Expr::Bin(BinExpr {
|
||||||
span: Span::new(left.span().lo(), right.span().hi(), Default::default()),
|
span: Span::new(left.span().lo(), right.span().hi(), Default::default()),
|
||||||
@ -163,6 +194,16 @@ impl<'a, I: Tokens> Parser<'a, I> {
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
let expr = self.parse_bin_op_recursively(node, min_prec)?;
|
let expr = self.parse_bin_op_recursively(node, min_prec)?;
|
||||||
|
|
||||||
|
if op == op!("??") {
|
||||||
|
match *expr {
|
||||||
|
Expr::Bin(BinExpr { span, op, .. }) if op == op!("&&") || op == op!("||") => {
|
||||||
|
syntax_error!(span, SyntaxError::NullishCoalescingWithLogicalOp);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -243,7 +243,7 @@ pub(super) trait ExprExt {
|
|||||||
| Expr::JSXFragment(..) => false,
|
| Expr::JSXFragment(..) => false,
|
||||||
|
|
||||||
// typescript
|
// typescript
|
||||||
Expr::TsOptChain(TsOptChain { ref expr, .. })
|
Expr::OptChain(OptChainExpr { ref expr, .. })
|
||||||
| Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
|
| Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
|
||||||
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
||||||
| Expr::TsTypeCast(TsTypeCastExpr { ref expr, .. })
|
| Expr::TsTypeCast(TsTypeCastExpr { ref expr, .. })
|
||||||
|
@ -203,6 +203,9 @@ pub enum BinOpToken {
|
|||||||
LogicalOr,
|
LogicalOr,
|
||||||
/// `&&`
|
/// `&&`
|
||||||
LogicalAnd,
|
LogicalAnd,
|
||||||
|
|
||||||
|
/// `??`
|
||||||
|
NullishCoalescing,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BinOpToken {
|
impl BinOpToken {
|
||||||
@ -539,6 +542,7 @@ impl From<BinOpToken> for BinaryOp {
|
|||||||
BinOpToken::LogicalOr => LogicalOr,
|
BinOpToken::LogicalOr => LogicalOr,
|
||||||
BinOpToken::LogicalAnd => LogicalAnd,
|
BinOpToken::LogicalAnd => LogicalAnd,
|
||||||
BinOpToken::Exp => Exp,
|
BinOpToken::Exp => Exp,
|
||||||
|
BinOpToken::NullishCoalescing => NullishCoalescing,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
c && d ?? e;
|
@ -0,0 +1,6 @@
|
|||||||
|
error: Nullish coalescing operator(??) requires parens when mixing with logical operators
|
||||||
|
--> $DIR/tests/typescript-errors/nullish-coalescing-operator/no-paren-and-nullish/input.ts:1:1
|
||||||
|
|
|
||||||
|
1 | c && d ?? e;
|
||||||
|
| ^^^^^^
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
a ?? b && c;
|
@ -0,0 +1,6 @@
|
|||||||
|
error: Nullish coalescing operator(??) requires parens when mixing with logical operators
|
||||||
|
--> $DIR/tests/typescript-errors/nullish-coalescing-operator/no-paren-nullish-and/input.ts:1:6
|
||||||
|
|
|
||||||
|
1 | a ?? b && c;
|
||||||
|
| ^^^^^^
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
e ?? f ?? g || h;
|
@ -0,0 +1,6 @@
|
|||||||
|
error: Nullish coalescing operator(??) requires parens when mixing with logical operators
|
||||||
|
--> $DIR/tests/typescript-errors/nullish-coalescing-operator/no-paren-nullish-or/input.ts:1:1
|
||||||
|
|
|
||||||
|
1 | e ?? f ?? g || h;
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
h || i ?? j;
|
@ -0,0 +1,6 @@
|
|||||||
|
error: Nullish coalescing operator(??) requires parens when mixing with logical operators
|
||||||
|
--> $DIR/tests/typescript-errors/nullish-coalescing-operator/no-paren-or-nullish/input.ts:1:1
|
||||||
|
|
|
||||||
|
1 | h || i ?? j;
|
||||||
|
| ^^^^^^
|
||||||
|
|
@ -0,0 +1 @@
|
|||||||
|
(a && b) ?? c;
|
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "ParenthesisExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 8,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 1,
|
||||||
|
"end": 7,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "&&",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 1,
|
||||||
|
"end": 2,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "a",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 6,
|
||||||
|
"end": 7,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "b",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 12,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "c",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
a ?? b ?? c;
|
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 12,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 12,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 11,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 6,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 1,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "a",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 5,
|
||||||
|
"end": 6,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "b",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 10,
|
||||||
|
"end": 11,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "c",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
foo ?? 1;
|
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 9,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 9,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 8,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 3,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "foo",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "NumericLiteral",
|
||||||
|
"span": {
|
||||||
|
"start": 7,
|
||||||
|
"end": 8,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": 1.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
a
|
||||||
|
?? b
|
||||||
|
?? c;
|
@ -0,0 +1,70 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 17,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 17,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 16,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 9,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 1,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "a",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 8,
|
||||||
|
"end": 9,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "b",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 15,
|
||||||
|
"end": 16,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "c",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
a ?? (b && c);
|
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 1,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "a",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "ParenthesisExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 5,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 6,
|
||||||
|
"end": 12,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "&&",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 6,
|
||||||
|
"end": 7,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "b",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 11,
|
||||||
|
"end": 12,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "c",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
a ?? (b || c);
|
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 1,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "a",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "ParenthesisExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 5,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 6,
|
||||||
|
"end": 12,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "||",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 6,
|
||||||
|
"end": 7,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "b",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 11,
|
||||||
|
"end": 12,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "c",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
(a || b) ?? c;
|
@ -0,0 +1,78 @@
|
|||||||
|
{
|
||||||
|
"type": "Module",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"type": "ExpressionStatement",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 14,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "??",
|
||||||
|
"left": {
|
||||||
|
"type": "ParenthesisExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 0,
|
||||||
|
"end": 8,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "BinaryExpression",
|
||||||
|
"span": {
|
||||||
|
"start": 1,
|
||||||
|
"end": 7,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"operator": "||",
|
||||||
|
"left": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 1,
|
||||||
|
"end": 2,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "a",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 6,
|
||||||
|
"end": 7,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "b",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"right": {
|
||||||
|
"type": "Identifier",
|
||||||
|
"span": {
|
||||||
|
"start": 12,
|
||||||
|
"end": 13,
|
||||||
|
"ctxt": 0
|
||||||
|
},
|
||||||
|
"value": "c",
|
||||||
|
"typeAnnotation": null,
|
||||||
|
"optional": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"interpreter": null
|
||||||
|
}
|
@ -21,7 +21,7 @@
|
|||||||
"ctxt": 0
|
"ctxt": 0
|
||||||
},
|
},
|
||||||
"callee": {
|
"callee": {
|
||||||
"type": "TsOptionalChainingExpression",
|
"type": "OptionalChainingExpression",
|
||||||
"span": {
|
"span": {
|
||||||
"start": 0,
|
"start": 0,
|
||||||
"end": 20,
|
"end": 20,
|
||||||
|
@ -11,8 +11,8 @@ edition = "2018"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
swc_atoms = { version = "0.2.0", path ="../../atoms" }
|
swc_atoms = { version = "0.2.0", path ="../../atoms" }
|
||||||
swc_common = { version = "0.4.2", path ="../../common" }
|
swc_common = { version = "0.4.2", path ="../../common" }
|
||||||
ast = { package = "swc_ecma_ast", version = "0.12.0", path ="../ast" }
|
ast = { package = "swc_ecma_ast", version = "0.13.0", path ="../ast" }
|
||||||
swc_ecma_parser = { version = "0.14", path ="../parser", features = ["verify"] }
|
swc_ecma_parser = { version = "0.15", path ="../parser", features = ["verify"] }
|
||||||
chashmap = "2.2.0"
|
chashmap = "2.2.0"
|
||||||
either = "1.5"
|
either = "1.5"
|
||||||
fxhash = "0.2"
|
fxhash = "0.2"
|
||||||
@ -31,7 +31,7 @@ smallvec = "1"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
testing = { version = "0.4", path ="../../testing" }
|
testing = { version = "0.4", path ="../../testing" }
|
||||||
swc_ecma_codegen = { version = "0.10.0", path ="../codegen" }
|
swc_ecma_codegen = { version = "0.11.0", path ="../codegen" }
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
pretty_assertions = "0.6"
|
pretty_assertions = "0.6"
|
||||||
sourcemap = "4.1.1"
|
sourcemap = "4.1.1"
|
@ -1053,7 +1053,7 @@ fn can_be_null(e: &Expr) -> bool {
|
|||||||
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
||||||
| Expr::TsTypeCast(TsTypeCastExpr { ref expr, .. })
|
| Expr::TsTypeCast(TsTypeCastExpr { ref expr, .. })
|
||||||
| Expr::TsConstAssertion(TsConstAssertion { ref expr, .. }) => can_be_null(expr),
|
| Expr::TsConstAssertion(TsConstAssertion { ref expr, .. }) => can_be_null(expr),
|
||||||
Expr::TsOptChain(ref e) => can_be_null(&e.expr),
|
Expr::OptChain(ref e) => can_be_null(&e.expr),
|
||||||
|
|
||||||
Expr::Invalid(..) => unreachable!(),
|
Expr::Invalid(..) => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,29 @@
|
|||||||
use crate::{pass::Pass, util::ExprFactory};
|
use crate::{
|
||||||
|
pass::Pass,
|
||||||
|
util::{ExprFactory, COMMENTS},
|
||||||
|
};
|
||||||
use ast::*;
|
use ast::*;
|
||||||
|
use fxhash::FxHashMap;
|
||||||
use swc_common::{
|
use swc_common::{
|
||||||
util::{map::Map, move_map::MoveMap},
|
util::{map::Map, move_map::MoveMap},
|
||||||
Fold, FoldWith,
|
Fold, FoldWith, Span, Spanned,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn fixer() -> impl Pass {
|
pub fn fixer() -> impl Pass {
|
||||||
Fixer {
|
Fixer {
|
||||||
ctx: Default::default(),
|
ctx: Default::default(),
|
||||||
|
span_map: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct Fixer {
|
struct Fixer {
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
|
/// A hash map to preserve original span.
|
||||||
|
///
|
||||||
|
/// Key is span of inner expression, and value is span of the paren
|
||||||
|
/// expression.
|
||||||
|
span_map: FxHashMap<Span, Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
@ -33,12 +44,32 @@ enum Context {
|
|||||||
is_var_decl: bool,
|
is_var_decl: bool,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Context {
|
impl Default for Context {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Context::Default
|
Context::Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Fold<Program> for Fixer {
|
||||||
|
fn fold(&mut self, p: Program) -> Program {
|
||||||
|
debug_assert!(self.span_map.is_empty());
|
||||||
|
self.span_map.clear();
|
||||||
|
|
||||||
|
let p = p.fold_children(self);
|
||||||
|
|
||||||
|
COMMENTS.with(|c| {
|
||||||
|
for (to, from) in self.span_map.drain() {
|
||||||
|
let (from, to) = (from.data(), to.data());
|
||||||
|
c.move_leading(from.lo, to.lo);
|
||||||
|
c.move_trailing(from.hi, to.hi);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
p
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Fold<KeyValuePatProp> for Fixer {
|
impl Fold<KeyValuePatProp> for Fixer {
|
||||||
fn fold(&mut self, node: KeyValuePatProp) -> KeyValuePatProp {
|
fn fold(&mut self, node: KeyValuePatProp) -> KeyValuePatProp {
|
||||||
let old = self.ctx;
|
let old = self.ctx;
|
||||||
@ -84,7 +115,7 @@ impl Fold<BlockStmtOrExpr> for Fixer {
|
|||||||
|
|
||||||
match body {
|
match body {
|
||||||
BlockStmtOrExpr::Expr(box expr @ Expr::Object(..)) => {
|
BlockStmtOrExpr::Expr(box expr @ Expr::Object(..)) => {
|
||||||
BlockStmtOrExpr::Expr(box expr.wrap_with_paren())
|
BlockStmtOrExpr::Expr(box self.wrap(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => body,
|
_ => body,
|
||||||
@ -108,7 +139,7 @@ impl Fold<Stmt> for Fixer {
|
|||||||
let stmt = match stmt {
|
let stmt = match stmt {
|
||||||
Stmt::Expr(ExprStmt { span, expr }) => Stmt::Expr(ExprStmt {
|
Stmt::Expr(ExprStmt { span, expr }) => Stmt::Expr(ExprStmt {
|
||||||
span,
|
span,
|
||||||
expr: expr.map(handle_expr_stmt),
|
expr: expr.map(|e| self.handle_expr_stmt(e)),
|
||||||
}),
|
}),
|
||||||
|
|
||||||
_ => stmt,
|
_ => stmt,
|
||||||
@ -175,7 +206,7 @@ impl Fold<KeyValueProp> for Fixer {
|
|||||||
|
|
||||||
match *prop.value {
|
match *prop.value {
|
||||||
Expr::Seq(..) => KeyValueProp {
|
Expr::Seq(..) => KeyValueProp {
|
||||||
value: box (*prop.value).wrap_with_paren(),
|
value: box self.wrap(*prop.value),
|
||||||
..prop
|
..prop
|
||||||
},
|
},
|
||||||
_ => prop,
|
_ => prop,
|
||||||
@ -183,14 +214,86 @@ impl Fold<KeyValueProp> for Fixer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes paren
|
impl Fixer {
|
||||||
fn unwrap_expr(mut e: Expr) -> Expr {
|
fn wrap<T>(&mut self, e: T) -> Expr
|
||||||
match e {
|
where
|
||||||
Expr::Seq(SeqExpr { ref mut exprs, .. }) if exprs.len() == 1 => {
|
T: Into<Expr>,
|
||||||
unwrap_expr(*exprs.pop().unwrap())
|
{
|
||||||
|
let expr = box e.into();
|
||||||
|
let span = expr.span();
|
||||||
|
|
||||||
|
let span = if let Some(span) = self.span_map.remove(&span) {
|
||||||
|
span
|
||||||
|
} else {
|
||||||
|
span
|
||||||
|
};
|
||||||
|
|
||||||
|
Expr::Paren(ParenExpr { expr, span })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes paren
|
||||||
|
fn unwrap_expr(&mut self, mut e: Expr) -> Expr {
|
||||||
|
match e {
|
||||||
|
Expr::Seq(SeqExpr { ref mut exprs, .. }) if exprs.len() == 1 => {
|
||||||
|
self.unwrap_expr(*exprs.pop().unwrap())
|
||||||
|
}
|
||||||
|
Expr::Paren(ParenExpr {
|
||||||
|
span: paren_span,
|
||||||
|
expr,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
let e = self.unwrap_expr(*expr);
|
||||||
|
|
||||||
|
self.span_map.insert(e.span(), paren_span);
|
||||||
|
e
|
||||||
|
}
|
||||||
|
_ => validate!(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_expr_stmt(&mut self, expr: Expr) -> Expr {
|
||||||
|
match expr {
|
||||||
|
// It's important for arrow pass to work properly.
|
||||||
|
Expr::Object(..) | Expr::Class(..) | Expr::Fn(..) => self.wrap(expr),
|
||||||
|
|
||||||
|
// ({ a } = foo)
|
||||||
|
Expr::Assign(AssignExpr {
|
||||||
|
span,
|
||||||
|
left: PatOrExpr::Pat(left @ box Pat::Object(..)),
|
||||||
|
op,
|
||||||
|
right,
|
||||||
|
}) => self.wrap(AssignExpr {
|
||||||
|
span,
|
||||||
|
left: PatOrExpr::Pat(left),
|
||||||
|
op,
|
||||||
|
right,
|
||||||
|
}),
|
||||||
|
|
||||||
|
Expr::Seq(SeqExpr { span, exprs }) => {
|
||||||
|
debug_assert!(
|
||||||
|
exprs.len() != 1,
|
||||||
|
"SeqExpr should be unwrapped if exprs.len() == 1, but length is 1"
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
let len = exprs.len();
|
||||||
|
Expr::Seq(SeqExpr {
|
||||||
|
span,
|
||||||
|
exprs: exprs.move_map(|expr| {
|
||||||
|
i += 1;
|
||||||
|
let is_last = len == i;
|
||||||
|
|
||||||
|
if !is_last {
|
||||||
|
expr.map(|e| self.handle_expr_stmt(e))
|
||||||
|
} else {
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => expr,
|
||||||
}
|
}
|
||||||
Expr::Paren(ParenExpr { expr, .. }) => unwrap_expr(*expr),
|
|
||||||
_ => validate!(e),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,7 +302,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
let expr = validate!(expr);
|
let expr = validate!(expr);
|
||||||
let expr = expr.fold_children(self);
|
let expr = expr.fold_children(self);
|
||||||
let expr = validate!(expr);
|
let expr = validate!(expr);
|
||||||
let expr = unwrap_expr(expr);
|
let expr = self.unwrap_expr(expr);
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Member(MemberExpr {
|
Expr::Member(MemberExpr {
|
||||||
@ -301,7 +404,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
}) => validate!(MemberExpr {
|
}) => validate!(MemberExpr {
|
||||||
span,
|
span,
|
||||||
computed,
|
computed,
|
||||||
obj: obj.wrap_with_paren().as_obj(),
|
obj: self.wrap(*obj).as_obj(),
|
||||||
prop,
|
prop,
|
||||||
})
|
})
|
||||||
.into(),
|
.into(),
|
||||||
@ -381,10 +484,10 @@ impl Fold<Expr> for Fixer {
|
|||||||
| e @ Expr::Seq(..)
|
| e @ Expr::Seq(..)
|
||||||
| e @ Expr::Yield(..)
|
| e @ Expr::Yield(..)
|
||||||
| e @ Expr::Cond(..)
|
| e @ Expr::Cond(..)
|
||||||
| e @ Expr::Arrow(..) => box validate!(e).wrap_with_paren(),
|
| e @ Expr::Arrow(..) => box self.wrap(e),
|
||||||
Expr::Bin(BinExpr { op: op_of_rhs, .. }) => {
|
Expr::Bin(BinExpr { op: op_of_rhs, .. }) => {
|
||||||
if op_of_rhs.precedence() <= expr.op.precedence() {
|
if op_of_rhs.precedence() <= expr.op.precedence() {
|
||||||
box expr.right.wrap_with_paren()
|
box self.wrap(*expr.right)
|
||||||
} else {
|
} else {
|
||||||
validate!(expr.right)
|
validate!(expr.right)
|
||||||
}
|
}
|
||||||
@ -398,7 +501,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
Expr::Bin(BinExpr { op: op_of_lhs, .. }) => {
|
Expr::Bin(BinExpr { op: op_of_lhs, .. }) => {
|
||||||
if op_of_lhs.precedence() < expr.op.precedence() {
|
if op_of_lhs.precedence() < expr.op.precedence() {
|
||||||
Expr::Bin(validate!(BinExpr {
|
Expr::Bin(validate!(BinExpr {
|
||||||
left: box expr.left.wrap_with_paren(),
|
left: box self.wrap(*expr.left),
|
||||||
..expr
|
..expr
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
@ -411,7 +514,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
| e @ Expr::Cond(..)
|
| e @ Expr::Cond(..)
|
||||||
| e @ Expr::Assign(..)
|
| e @ Expr::Assign(..)
|
||||||
| e @ Expr::Arrow(..) => validate!(Expr::Bin(BinExpr {
|
| e @ Expr::Arrow(..) => validate!(Expr::Bin(BinExpr {
|
||||||
left: box e.wrap_with_paren(),
|
left: box self.wrap(e),
|
||||||
..expr
|
..expr
|
||||||
})),
|
})),
|
||||||
_ => validate!(Expr::Bin(expr)),
|
_ => validate!(Expr::Bin(expr)),
|
||||||
@ -423,11 +526,11 @@ impl Fold<Expr> for Fixer {
|
|||||||
e @ Expr::Seq(..)
|
e @ Expr::Seq(..)
|
||||||
| e @ Expr::Assign(..)
|
| e @ Expr::Assign(..)
|
||||||
| e @ Expr::Cond(..)
|
| e @ Expr::Cond(..)
|
||||||
| e @ Expr::Arrow(..) => box e.wrap_with_paren(),
|
| e @ Expr::Arrow(..) => box self.wrap(e),
|
||||||
|
|
||||||
e @ Expr::Object(..) | e @ Expr::Fn(..) | e @ Expr::Class(..) => {
|
e @ Expr::Object(..) | e @ Expr::Fn(..) | e @ Expr::Class(..) => {
|
||||||
if self.ctx == Context::Default {
|
if self.ctx == Context::Default {
|
||||||
box e.wrap_with_paren()
|
box self.wrap(e)
|
||||||
} else {
|
} else {
|
||||||
box e
|
box e
|
||||||
}
|
}
|
||||||
@ -436,12 +539,12 @@ impl Fold<Expr> for Fixer {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let cons = match *expr.cons {
|
let cons = match *expr.cons {
|
||||||
e @ Expr::Seq(..) => box e.wrap_with_paren(),
|
e @ Expr::Seq(..) => box self.wrap(e),
|
||||||
_ => expr.cons,
|
_ => expr.cons,
|
||||||
};
|
};
|
||||||
|
|
||||||
let alt = match *expr.alt {
|
let alt = match *expr.alt {
|
||||||
e @ Expr::Seq(..) => box e.wrap_with_paren(),
|
e @ Expr::Seq(..) => box self.wrap(e),
|
||||||
_ => expr.alt,
|
_ => expr.alt,
|
||||||
};
|
};
|
||||||
validate!(Expr::Cond(CondExpr {
|
validate!(Expr::Cond(CondExpr {
|
||||||
@ -459,7 +562,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
| e @ Expr::Seq(..)
|
| e @ Expr::Seq(..)
|
||||||
| e @ Expr::Cond(..)
|
| e @ Expr::Cond(..)
|
||||||
| e @ Expr::Arrow(..)
|
| e @ Expr::Arrow(..)
|
||||||
| e @ Expr::Yield(..) => box e.wrap_with_paren(),
|
| e @ Expr::Yield(..) => box self.wrap(e),
|
||||||
_ => expr.arg,
|
_ => expr.arg,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -479,7 +582,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
}) => expr.right,
|
}) => expr.right,
|
||||||
|
|
||||||
// Handle `foo = bar = init()
|
// Handle `foo = bar = init()
|
||||||
Expr::Seq(right) => box right.wrap_with_paren(),
|
Expr::Seq(right) => box self.wrap(right),
|
||||||
_ => expr.right,
|
_ => expr.right,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -493,7 +596,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
type_args,
|
type_args,
|
||||||
}) => validate!(Expr::Call(CallExpr {
|
}) => validate!(Expr::Call(CallExpr {
|
||||||
span,
|
span,
|
||||||
callee: callee.wrap_with_paren().as_callee(),
|
callee: self.wrap(*callee).as_callee(),
|
||||||
args,
|
args,
|
||||||
type_args,
|
type_args,
|
||||||
})),
|
})),
|
||||||
@ -512,17 +615,16 @@ impl Fold<Expr> for Fixer {
|
|||||||
type_args,
|
type_args,
|
||||||
})),
|
})),
|
||||||
|
|
||||||
Context::Callee { is_new: true } => validate!(Expr::Call(CallExpr {
|
Context::Callee { is_new: true } => self.wrap(CallExpr {
|
||||||
span,
|
span,
|
||||||
callee: callee.as_callee(),
|
callee: callee.as_callee(),
|
||||||
args,
|
args,
|
||||||
type_args,
|
type_args,
|
||||||
}))
|
}),
|
||||||
.wrap_with_paren(),
|
|
||||||
|
|
||||||
_ => validate!(Expr::Call(CallExpr {
|
_ => validate!(Expr::Call(CallExpr {
|
||||||
span,
|
span,
|
||||||
callee: callee.wrap_with_paren().as_callee(),
|
callee: self.wrap(*callee).as_callee(),
|
||||||
args,
|
args,
|
||||||
type_args,
|
type_args,
|
||||||
})),
|
})),
|
||||||
@ -534,7 +636,7 @@ impl Fold<Expr> for Fixer {
|
|||||||
type_args,
|
type_args,
|
||||||
}) => validate!(Expr::Call(CallExpr {
|
}) => validate!(Expr::Call(CallExpr {
|
||||||
span,
|
span,
|
||||||
callee: callee.wrap_with_paren().as_callee(),
|
callee: self.wrap(*callee).as_callee(),
|
||||||
args,
|
args,
|
||||||
type_args,
|
type_args,
|
||||||
})),
|
})),
|
||||||
@ -552,7 +654,7 @@ impl Fold<ExprOrSpread> for Fixer {
|
|||||||
Expr::Yield(..) => {
|
Expr::Yield(..) => {
|
||||||
return ExprOrSpread {
|
return ExprOrSpread {
|
||||||
spread: None,
|
spread: None,
|
||||||
expr: box e.expr.wrap_with_paren(),
|
expr: box self.wrap(*e.expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@ -569,7 +671,7 @@ impl Fold<ExportDefaultExpr> for Fixer {
|
|||||||
self.ctx = Context::Default;
|
self.ctx = Context::Default;
|
||||||
let mut node = node.fold_children(self);
|
let mut node = node.fold_children(self);
|
||||||
node.expr = match *node.expr {
|
node.expr = match *node.expr {
|
||||||
Expr::Arrow(..) | Expr::Seq(..) => box node.expr.wrap_with_paren(),
|
Expr::Arrow(..) | Expr::Seq(..) => box self.wrap(*node.expr),
|
||||||
_ => node.expr,
|
_ => node.expr,
|
||||||
};
|
};
|
||||||
self.ctx = old;
|
self.ctx = old;
|
||||||
@ -584,7 +686,7 @@ impl Fold<ArrowExpr> for Fixer {
|
|||||||
let mut node = node.fold_children(self);
|
let mut node = node.fold_children(self);
|
||||||
node.body = match node.body {
|
node.body = match node.body {
|
||||||
BlockStmtOrExpr::Expr(e @ box Expr::Seq(..)) => {
|
BlockStmtOrExpr::Expr(e @ box Expr::Seq(..)) => {
|
||||||
BlockStmtOrExpr::Expr(box e.wrap_with_paren())
|
BlockStmtOrExpr::Expr(box self.wrap(*e))
|
||||||
}
|
}
|
||||||
_ => node.body,
|
_ => node.body,
|
||||||
};
|
};
|
||||||
@ -599,7 +701,7 @@ impl Fold<Class> for Fixer {
|
|||||||
self.ctx = Context::Default;
|
self.ctx = Context::Default;
|
||||||
let mut node = node.fold_children(self);
|
let mut node = node.fold_children(self);
|
||||||
node.super_class = match node.super_class {
|
node.super_class = match node.super_class {
|
||||||
Some(e @ box Expr::Seq(..)) => Some(box e.wrap_with_paren()),
|
Some(e @ box Expr::Seq(..)) => Some(box self.wrap(*e)),
|
||||||
_ => node.super_class,
|
_ => node.super_class,
|
||||||
};
|
};
|
||||||
self.ctx = old;
|
self.ctx = old;
|
||||||
@ -619,52 +721,6 @@ fn ignore_return_value(expr: Box<Expr>) -> Option<Box<Expr>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_expr_stmt(expr: Expr) -> Expr {
|
|
||||||
match expr {
|
|
||||||
// It's important for arrow pass to work properly.
|
|
||||||
Expr::Object(..) | Expr::Class(..) | Expr::Fn(..) => expr.wrap_with_paren(),
|
|
||||||
|
|
||||||
// ({ a } = foo)
|
|
||||||
Expr::Assign(AssignExpr {
|
|
||||||
span,
|
|
||||||
left: PatOrExpr::Pat(left @ box Pat::Object(..)),
|
|
||||||
op,
|
|
||||||
right,
|
|
||||||
}) => AssignExpr {
|
|
||||||
span,
|
|
||||||
left: PatOrExpr::Pat(left),
|
|
||||||
op,
|
|
||||||
right,
|
|
||||||
}
|
|
||||||
.wrap_with_paren(),
|
|
||||||
|
|
||||||
Expr::Seq(SeqExpr { span, exprs }) => {
|
|
||||||
debug_assert!(
|
|
||||||
exprs.len() != 1,
|
|
||||||
"SeqExpr should be unwrapped if exprs.len() == 1, but length is 1"
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut i = 0;
|
|
||||||
let len = exprs.len();
|
|
||||||
Expr::Seq(SeqExpr {
|
|
||||||
span,
|
|
||||||
exprs: exprs.move_map(|expr| {
|
|
||||||
i += 1;
|
|
||||||
let is_last = len == i;
|
|
||||||
|
|
||||||
if !is_last {
|
|
||||||
expr.map(handle_expr_stmt)
|
|
||||||
} else {
|
|
||||||
expr
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => expr,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
struct Noop;
|
struct Noop;
|
||||||
|
@ -993,7 +993,7 @@ where
|
|||||||
| Expr::TsTypeCast(TsTypeCastExpr { expr, .. })
|
| Expr::TsTypeCast(TsTypeCastExpr { expr, .. })
|
||||||
| Expr::TsAs(TsAsExpr { expr, .. })
|
| Expr::TsAs(TsAsExpr { expr, .. })
|
||||||
| Expr::TsConstAssertion(TsConstAssertion { expr, .. }) => add_effects(v, expr),
|
| Expr::TsConstAssertion(TsConstAssertion { expr, .. }) => add_effects(v, expr),
|
||||||
Expr::TsOptChain(e) => add_effects(v, e.expr),
|
Expr::OptChain(e) => add_effects(v, e.expr),
|
||||||
|
|
||||||
Expr::Invalid(..) => unreachable!(),
|
Expr::Invalid(..) => unreachable!(),
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
pub use self::{class_properties::class_properties, decorators::decorators, export::export};
|
pub use self::{
|
||||||
|
class_properties::class_properties, decorators::decorators, export::export,
|
||||||
|
opt_chaining::optional_chaining,
|
||||||
|
};
|
||||||
|
|
||||||
mod class_properties;
|
mod class_properties;
|
||||||
pub mod decorators;
|
pub mod decorators;
|
||||||
mod export;
|
mod export;
|
||||||
|
mod opt_chaining;
|
||||||
|
@ -48,7 +48,7 @@ where
|
|||||||
impl Fold<Expr> for OptChaining {
|
impl Fold<Expr> for OptChaining {
|
||||||
fn fold(&mut self, e: Expr) -> Expr {
|
fn fold(&mut self, e: Expr) -> Expr {
|
||||||
let e = match e {
|
let e = match e {
|
||||||
Expr::TsOptChain(e) => Expr::Cond(validate!(self.unwrap(e))),
|
Expr::OptChain(e) => Expr::Cond(validate!(self.unwrap(e))),
|
||||||
Expr::Unary(e) => validate!(self.handle_unary(e)),
|
Expr::Unary(e) => validate!(self.handle_unary(e)),
|
||||||
Expr::Member(e) => validate!(self.handle_member(e)),
|
Expr::Member(e) => validate!(self.handle_member(e)),
|
||||||
Expr::Call(e) => validate!(self.handle_call(e)),
|
Expr::Call(e) => validate!(self.handle_call(e)),
|
||||||
@ -66,7 +66,7 @@ impl OptChaining {
|
|||||||
|
|
||||||
if let op!("delete") = e.op {
|
if let op!("delete") = e.op {
|
||||||
match *e.arg {
|
match *e.arg {
|
||||||
Expr::TsOptChain(o) => {
|
Expr::OptChain(o) => {
|
||||||
let expr = self.unwrap(o);
|
let expr = self.unwrap(o);
|
||||||
|
|
||||||
return CondExpr {
|
return CondExpr {
|
||||||
@ -83,7 +83,7 @@ impl OptChaining {
|
|||||||
|
|
||||||
Expr::Member(MemberExpr {
|
Expr::Member(MemberExpr {
|
||||||
span,
|
span,
|
||||||
obj: ExprOrSuper::Expr(box Expr::TsOptChain(o)),
|
obj: ExprOrSuper::Expr(box Expr::OptChain(o)),
|
||||||
prop,
|
prop,
|
||||||
computed,
|
computed,
|
||||||
}) => {
|
}) => {
|
||||||
@ -114,7 +114,7 @@ impl OptChaining {
|
|||||||
|
|
||||||
/// Only called from [Fold<Expr>].
|
/// Only called from [Fold<Expr>].
|
||||||
fn handle_call(&mut self, e: CallExpr) -> Expr {
|
fn handle_call(&mut self, e: CallExpr) -> Expr {
|
||||||
if let ExprOrSuper::Expr(box Expr::TsOptChain(o)) = e.callee {
|
if let ExprOrSuper::Expr(box Expr::OptChain(o)) = e.callee {
|
||||||
let expr = self.unwrap(o);
|
let expr = self.unwrap(o);
|
||||||
|
|
||||||
return CondExpr {
|
return CondExpr {
|
||||||
@ -133,7 +133,7 @@ impl OptChaining {
|
|||||||
|
|
||||||
/// Only called from `[Fold<Expr>].
|
/// Only called from `[Fold<Expr>].
|
||||||
fn handle_member(&mut self, e: MemberExpr) -> Expr {
|
fn handle_member(&mut self, e: MemberExpr) -> Expr {
|
||||||
if let ExprOrSuper::Expr(box Expr::TsOptChain(o)) = e.obj {
|
if let ExprOrSuper::Expr(box Expr::OptChain(o)) = e.obj {
|
||||||
let expr = self.unwrap(o);
|
let expr = self.unwrap(o);
|
||||||
|
|
||||||
return CondExpr {
|
return CondExpr {
|
||||||
@ -150,13 +150,13 @@ impl OptChaining {
|
|||||||
Expr::Member(e)
|
Expr::Member(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unwrap(&mut self, e: TsOptChain) -> CondExpr {
|
fn unwrap(&mut self, e: OptChainExpr) -> CondExpr {
|
||||||
let span = e.span;
|
let span = e.span;
|
||||||
let cons = undefined(span);
|
let cons = undefined(span);
|
||||||
|
|
||||||
match *e.expr {
|
match *e.expr {
|
||||||
Expr::Member(MemberExpr {
|
Expr::Member(MemberExpr {
|
||||||
obj: ExprOrSuper::Expr(box Expr::TsOptChain(o)),
|
obj: ExprOrSuper::Expr(box Expr::OptChain(o)),
|
||||||
prop,
|
prop,
|
||||||
computed,
|
computed,
|
||||||
span: m_span,
|
span: m_span,
|
||||||
@ -170,7 +170,7 @@ impl OptChaining {
|
|||||||
prop,
|
prop,
|
||||||
computed,
|
computed,
|
||||||
});
|
});
|
||||||
let alt = box Expr::TsOptChain(TsOptChain {
|
let alt = box Expr::OptChain(OptChainExpr {
|
||||||
span: o_span,
|
span: o_span,
|
||||||
expr: alt,
|
expr: alt,
|
||||||
});
|
});
|
||||||
@ -180,7 +180,7 @@ impl OptChaining {
|
|||||||
|
|
||||||
Expr::Call(CallExpr {
|
Expr::Call(CallExpr {
|
||||||
span,
|
span,
|
||||||
callee: ExprOrSuper::Expr(box Expr::TsOptChain(o)),
|
callee: ExprOrSuper::Expr(box Expr::OptChain(o)),
|
||||||
args,
|
args,
|
||||||
type_args,
|
type_args,
|
||||||
}) => {
|
}) => {
|
||||||
@ -192,7 +192,7 @@ impl OptChaining {
|
|||||||
args,
|
args,
|
||||||
type_args,
|
type_args,
|
||||||
});
|
});
|
||||||
let alt = box Expr::TsOptChain(TsOptChain { span, expr: alt });
|
let alt = box Expr::OptChain(OptChainExpr { span, expr: alt });
|
||||||
|
|
||||||
return validate!(CondExpr {
|
return validate!(CondExpr {
|
||||||
span: DUMMY_SP,
|
span: DUMMY_SP,
|
@ -1,4 +1,3 @@
|
|||||||
pub use self::opt_chaining::optional_chaining;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
pass::Pass,
|
pass::Pass,
|
||||||
util::{prepend_stmts, var::VarCollector, ExprFactory},
|
util::{prepend_stmts, var::VarCollector, ExprFactory},
|
||||||
@ -10,7 +9,6 @@ use swc_common::{
|
|||||||
util::move_map::MoveMap, Fold, FoldWith, Spanned, SyntaxContext, Visit, VisitWith, DUMMY_SP,
|
util::move_map::MoveMap, Fold, FoldWith, Spanned, SyntaxContext, Visit, VisitWith, DUMMY_SP,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod opt_chaining;
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
@ -19,7 +19,8 @@ use std::{
|
|||||||
};
|
};
|
||||||
use swc_atoms::{js_word, JsWord};
|
use swc_atoms::{js_word, JsWord};
|
||||||
use swc_common::{
|
use swc_common::{
|
||||||
errors::Handler, Fold, FoldWith, Mark, Span, Spanned, Visit, VisitWith, DUMMY_SP,
|
comments::Comments, errors::Handler, Fold, FoldWith, Mark, Span, Spanned, Visit, VisitWith,
|
||||||
|
DUMMY_SP,
|
||||||
};
|
};
|
||||||
use unicode_xid::UnicodeXID;
|
use unicode_xid::UnicodeXID;
|
||||||
|
|
||||||
@ -693,7 +694,7 @@ pub trait ExprExt {
|
|||||||
| Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
|
| Expr::TsNonNull(TsNonNullExpr { ref expr, .. })
|
||||||
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
| Expr::TsTypeAssertion(TsTypeAssertion { ref expr, .. })
|
||||||
| Expr::TsTypeCast(TsTypeCastExpr { ref expr, .. }) => expr.may_have_side_effects(),
|
| Expr::TsTypeCast(TsTypeCastExpr { ref expr, .. }) => expr.may_have_side_effects(),
|
||||||
Expr::TsOptChain(ref e) => e.expr.may_have_side_effects(),
|
Expr::OptChain(ref e) => e.expr.may_have_side_effects(),
|
||||||
|
|
||||||
Expr::Invalid(..) => unreachable!(),
|
Expr::Invalid(..) => unreachable!(),
|
||||||
}
|
}
|
||||||
@ -923,7 +924,7 @@ not_lit!(TsTypeAssertion);
|
|||||||
not_lit!(TsConstAssertion);
|
not_lit!(TsConstAssertion);
|
||||||
|
|
||||||
not_lit!(PrivateName);
|
not_lit!(PrivateName);
|
||||||
not_lit!(TsOptChain);
|
not_lit!(OptChainExpr);
|
||||||
|
|
||||||
not_lit!(SpreadElement);
|
not_lit!(SpreadElement);
|
||||||
not_lit!(Invalid);
|
not_lit!(Invalid);
|
||||||
@ -1283,3 +1284,4 @@ impl<'a> UsageFinder<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
scoped_thread_local!(pub static HANDLER: Handler);
|
scoped_thread_local!(pub static HANDLER: Handler);
|
||||||
|
scoped_thread_local!(pub static COMMENTS: Comments);
|
||||||
|
@ -10,7 +10,7 @@ use ecmascript::{
|
|||||||
chain_at, const_modules, modules,
|
chain_at, const_modules, modules,
|
||||||
optimization::{simplifier, InlineGlobals, JsonParse},
|
optimization::{simplifier, InlineGlobals, JsonParse},
|
||||||
pass::{noop, Optional, Pass},
|
pass::{noop, Optional, Pass},
|
||||||
proposals::{class_properties, decorators, export},
|
proposals::{class_properties, decorators, export, optional_chaining},
|
||||||
react, resolver, typescript,
|
react, resolver, typescript,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -181,7 +181,7 @@ impl Options {
|
|||||||
// handle jsx
|
// handle jsx
|
||||||
Optional::new(react::react(cm.clone(), transform.react), syntax.jsx()),
|
Optional::new(react::react(cm.clone(), transform.react), syntax.jsx()),
|
||||||
Optional::new(typescript::strip(), syntax.typescript()),
|
Optional::new(typescript::strip(), syntax.typescript()),
|
||||||
Optional::new(typescript::optional_chaining(), syntax.typescript()),
|
Optional::new(optional_chaining(), syntax.typescript()),
|
||||||
Optional::new(class_properties(), syntax.typescript()),
|
Optional::new(class_properties(), syntax.typescript()),
|
||||||
resolver(),
|
resolver(),
|
||||||
const_modules,
|
const_modules,
|
||||||
|
34
src/lib.rs
34
src/lib.rs
@ -25,6 +25,7 @@ use ecmascript::{
|
|||||||
transforms::{
|
transforms::{
|
||||||
helpers::{self, Helpers},
|
helpers::{self, Helpers},
|
||||||
util,
|
util,
|
||||||
|
util::COMMENTS,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
pub use ecmascript::{
|
pub use ecmascript::{
|
||||||
@ -43,6 +44,7 @@ pub struct Compiler {
|
|||||||
/// CodeMap
|
/// CodeMap
|
||||||
pub cm: Arc<SourceMap>,
|
pub cm: Arc<SourceMap>,
|
||||||
pub handler: Handler,
|
pub handler: Handler,
|
||||||
|
comments: Comments,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
@ -54,6 +56,10 @@ pub struct TransformOutput {
|
|||||||
|
|
||||||
/// These are **low-level** apis.
|
/// These are **low-level** apis.
|
||||||
impl Compiler {
|
impl Compiler {
|
||||||
|
pub fn comments(&self) -> &Comments {
|
||||||
|
&self.comments
|
||||||
|
}
|
||||||
|
|
||||||
/// Runs `op` in current compiler's context.
|
/// Runs `op` in current compiler's context.
|
||||||
///
|
///
|
||||||
/// Note: Other methods of `Compiler` already uses this internally.
|
/// Note: Other methods of `Compiler` already uses this internally.
|
||||||
@ -61,7 +67,13 @@ impl Compiler {
|
|||||||
where
|
where
|
||||||
F: FnOnce() -> R,
|
F: FnOnce() -> R,
|
||||||
{
|
{
|
||||||
GLOBALS.set(&self.globals, || op())
|
GLOBALS.set(&self.globals, || {
|
||||||
|
//
|
||||||
|
COMMENTS.set(&self.comments, || {
|
||||||
|
//
|
||||||
|
op()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method parses a javascript / typescript file
|
/// This method parses a javascript / typescript file
|
||||||
@ -71,7 +83,7 @@ impl Compiler {
|
|||||||
target: JscTarget,
|
target: JscTarget,
|
||||||
syntax: Syntax,
|
syntax: Syntax,
|
||||||
is_module: bool,
|
is_module: bool,
|
||||||
comments: Option<&Comments>,
|
parse_comments: bool,
|
||||||
) -> Result<Program, Error> {
|
) -> Result<Program, Error> {
|
||||||
self.run(|| {
|
self.run(|| {
|
||||||
let session = ParseSess {
|
let session = ParseSess {
|
||||||
@ -82,7 +94,11 @@ impl Compiler {
|
|||||||
syntax,
|
syntax,
|
||||||
target,
|
target,
|
||||||
SourceFileInput::from(&*fm),
|
SourceFileInput::from(&*fm),
|
||||||
comments,
|
if parse_comments {
|
||||||
|
Some(&self.comments)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
);
|
);
|
||||||
let mut parser = Parser::new_from(session, lexer);
|
let mut parser = Parser::new_from(session, lexer);
|
||||||
let program = if is_module {
|
let program = if is_module {
|
||||||
@ -181,6 +197,7 @@ impl Compiler {
|
|||||||
cm,
|
cm,
|
||||||
handler,
|
handler,
|
||||||
globals: Globals::new(),
|
globals: Globals::new(),
|
||||||
|
comments: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -290,13 +307,12 @@ impl Compiler {
|
|||||||
eprintln!("processing js file: {:?}", fm)
|
eprintln!("processing js file: {:?}", fm)
|
||||||
}
|
}
|
||||||
|
|
||||||
let comments = Default::default();
|
|
||||||
let module = self.parse_js(
|
let module = self.parse_js(
|
||||||
fm.clone(),
|
fm.clone(),
|
||||||
config.target,
|
config.target,
|
||||||
config.syntax,
|
config.syntax,
|
||||||
config.is_module,
|
config.is_module,
|
||||||
if config.minify { None } else { Some(&comments) },
|
!config.minify,
|
||||||
)?;
|
)?;
|
||||||
let mut pass = config.pass;
|
let mut pass = config.pass;
|
||||||
let module = helpers::HELPERS.set(&Helpers::new(config.external_helpers), || {
|
let module = helpers::HELPERS.set(&Helpers::new(config.external_helpers), || {
|
||||||
@ -306,7 +322,13 @@ impl Compiler {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
self.print(&module, fm, &comments, config.source_maps, config.minify)
|
self.print(
|
||||||
|
&module,
|
||||||
|
fm,
|
||||||
|
&self.comments,
|
||||||
|
config.source_maps,
|
||||||
|
config.minify,
|
||||||
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,3 +228,25 @@ fn issue_467() {
|
|||||||
fn issue_468() {
|
fn issue_468() {
|
||||||
file("tests/projects/issue-468/input.ts").expect("failed to parse typescript");
|
file("tests/projects/issue-468/input.ts").expect("failed to parse typescript");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_528() {
|
||||||
|
let f = file("tests/projects/issue-528/input.js")
|
||||||
|
.unwrap()
|
||||||
|
.replace(" ", "");
|
||||||
|
let f = f.trim();
|
||||||
|
|
||||||
|
println!("{}", f);
|
||||||
|
assert_eq!(
|
||||||
|
f,
|
||||||
|
"\
|
||||||
|
//bar
|
||||||
|
[
|
||||||
|
//foo
|
||||||
|
a,
|
||||||
|
//baz
|
||||||
|
//bar
|
||||||
|
b
|
||||||
|
];"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
11
tests/projects/issue-528/input.js
Normal file
11
tests/projects/issue-528/input.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
// bar
|
||||||
|
[
|
||||||
|
// foo
|
||||||
|
a,
|
||||||
|
|
||||||
|
//bar
|
||||||
|
(
|
||||||
|
//baz
|
||||||
|
b
|
||||||
|
)
|
||||||
|
]
|
Loading…
Reference in New Issue
Block a user