fix(es): Fix easy bugs (#2178)

swc_ecma_parser:
 - Fix parsing of complex arrow expressions in a conditional expression. (#2174)
 - Report an error for wrong jsx, instead of `panic!`. (#2173)

swc_ecma_transforms_react:
 - `jsx`: Handle fragment with single child correctly. (#2177)

swc:
 - Ensure that #2170 is an invalid issue. (#2170)
This commit is contained in:
강동윤 2021-08-30 05:40:52 +09:00 committed by GitHub
parent a10118c90f
commit c0b0337d1d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 57401 additions and 58 deletions

6
Cargo.lock generated
View File

@ -2287,7 +2287,7 @@ checksum = "6446ced80d6c486436db5c078dde11a9f73d42b57fb273121e160b84f63d894c"
[[package]]
name = "swc"
version = "0.47.0"
version = "0.46.1"
dependencies = [
"ahash",
"anyhow",
@ -2660,7 +2660,7 @@ dependencies = [
[[package]]
name = "swc_ecma_parser"
version = "0.68.0"
version = "0.68.1"
dependencies = [
"either",
"enum_kind",
@ -2876,7 +2876,7 @@ dependencies = [
[[package]]
name = "swc_ecma_transforms_react"
version = "0.36.0"
version = "0.36.1"
dependencies = [
"base64 0.13.0",
"dashmap",

View File

@ -20,7 +20,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc"
repository = "https://github.com/swc-project/swc.git"
version = "0.47.0"
version = "0.46.1"
[lib]
name = "swc"

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "examples/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_parser"
repository = "https://github.com/swc-project/swc.git"
version = "0.68.0"
version = "0.68.1"
[package.metadata.docs.rs]
all-features = true

View File

@ -409,6 +409,7 @@ pub struct Context {
/// If true, `:` should not be treated as a type annotation.
in_cond_expr: bool,
is_direct_child_of_cond: bool,
in_function: bool,

View File

@ -80,18 +80,21 @@ impl<'a, I: Tokens> Parser<I> {
&& (is_one_of!(self, '<', JSXTagStart))
&& (peeked_is!(self, IdentName) || peeked_is!(self, JSXName))
{
let res = self.try_parse_ts(|p| {
let ctx = Context {
is_direct_child_of_cond: false,
..self.ctx()
};
let res = self.with_ctx(ctx).try_parse_ts(|p| {
if is!(p, JSXTagStart) {
debug_assert_eq!(
p.input.token_context().current(),
Some(TokenContext::JSXOpeningTag)
);
p.input.token_context_mut().pop();
debug_assert_eq!(
p.input.token_context().current(),
Some(TokenContext::JSXExpr)
);
p.input.token_context_mut().pop();
if let Some(TokenContext::JSXOpeningTag) = p.input.token_context().current() {
p.input.token_context_mut().pop();
debug_assert_eq!(
p.input.token_context().current(),
Some(TokenContext::JSXExpr)
);
p.input.token_context_mut().pop();
}
}
let type_parameters = p.parse_ts_type_params()?;
@ -197,6 +200,7 @@ impl<'a, I: Tokens> Parser<I> {
if eat!(self, '?') {
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
include_in_expr: true,
..self.ctx()
};
@ -204,6 +208,7 @@ impl<'a, I: Tokens> Parser<I> {
expect!(self, ':');
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
..self.ctx()
};
let alt = self.with_ctx(ctx).parse_assignment_expr()?;
@ -550,36 +555,43 @@ impl<'a, I: Tokens> Parser<I> {
pub(super) fn parse_args(&mut self, is_dynamic_import: bool) -> PResult<Vec<ExprOrSpread>> {
trace_cur!(self, parse_args);
let start = cur_pos!(self);
expect!(self, '(');
let ctx = Context {
is_direct_child_of_cond: false,
..self.ctx()
};
let mut first = true;
let mut expr_or_spreads = vec![];
self.with_ctx(ctx).parse_with(|p| {
let start = cur_pos!(p);
expect!(p, '(');
while !eof!(self) && !is!(self, ')') {
if first {
first = false;
} else {
expect!(self, ',');
// Handle trailing comma.
if is!(self, ')') {
if is_dynamic_import {
syntax_error!(
self,
span!(self, start),
SyntaxError::TrailingCommaInsideImport
)
let mut first = true;
let mut expr_or_spreads = vec![];
while !eof!(p) && !is!(p, ')') {
if first {
first = false;
} else {
expect!(p, ',');
// Handle trailing comma.
if is!(p, ')') {
if is_dynamic_import {
syntax_error!(
p,
span!(p, start),
SyntaxError::TrailingCommaInsideImport
)
}
break;
}
break;
}
expr_or_spreads.push(p.include_in_expr(true).parse_expr_or_spread()?);
}
expr_or_spreads.push(self.include_in_expr(true).parse_expr_or_spread()?);
}
expect!(self, ')');
Ok(expr_or_spreads)
expect!(p, ')');
Ok(expr_or_spreads)
})
}
/// AssignmentExpression[+In, ?Yield, ?Await]
@ -615,12 +627,21 @@ impl<'a, I: Tokens> Parser<I> {
// But as all patterns of javascript is subset of
// expressions, we can parse both as expression.
let paren_items = self.include_in_expr(true).parse_args_or_pats()?;
let ctx = Context {
is_direct_child_of_cond: false,
..self.ctx()
};
let paren_items = self
.with_ctx(ctx)
.include_in_expr(true)
.parse_args_or_pats()?;
let has_pattern = paren_items.iter().any(|item| match item {
PatOrExprOrSpread::Pat(..) => true,
_ => false,
});
let is_direct_child_of_cond = self.ctx().is_direct_child_of_cond;
// This is slow path. We handle arrow in conditional expression.
if self.syntax().typescript() && self.ctx().in_cond_expr && is!(self, ':') {
// TODO: Remove clone
@ -637,6 +658,13 @@ impl<'a, I: Tokens> Parser<I> {
let body: BlockStmtOrExpr = p.parse_fn_body(async_span.is_some(), false)?;
if is_direct_child_of_cond {
if !is_one_of!(p, ':', ';') {
trace_cur!(p, parse_arrow_in_cond__fail);
unexpected!(p, "fail")
}
}
Ok(Some(Box::new(Expr::Arrow(ArrowExpr {
span: span!(p, expr_start),
is_async: async_span.is_some(),
@ -1308,6 +1336,7 @@ impl<'a, I: Tokens> Parser<I> {
let test = arg.expr;
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
include_in_expr: true,
..self.ctx()
};
@ -1315,6 +1344,7 @@ impl<'a, I: Tokens> Parser<I> {
expect!(self, ':');
let ctx = Context {
in_cond_expr: true,
is_direct_child_of_cond: true,
..self.ctx()
};
let alt = self.with_ctx(ctx).parse_assignment_expr()?;

View File

@ -0,0 +1,5 @@
const setAction = (fields) =>
setCurrentAction({
...(<HeaderAction>getCurrentAction()),
...fields,
});

View File

@ -0,0 +1,6 @@
error: Unexpected token `HeaderAction`. Expected jsx identifier
--> $DIR/tests/typescript-errors/issue-2173/case1/input.tsx:3:14
|
3 | ...(<HeaderAction>getCurrentAction()),
| ^^^^^^^^^^^^

View File

@ -193,6 +193,7 @@ where
}
#[testing::fixture("tests/typescript-errors/**/*.ts")]
#[testing::fixture("tests/typescript-errors/**/*.tsx")]
fn errors(file: PathBuf) {
let file_name = file.display().to_string();

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
const x = {
prop: isCorrect
? fn => ({ })
: fn => true,
};

View File

@ -0,0 +1,160 @@
{
"type": "Script",
"span": {
"start": 0,
"end": 82,
"ctxt": 0
},
"body": [
{
"type": "VariableDeclaration",
"span": {
"start": 0,
"end": 82,
"ctxt": 0
},
"kind": "const",
"declare": false,
"declarations": [
{
"type": "VariableDeclarator",
"span": {
"start": 6,
"end": 81,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 6,
"end": 7,
"ctxt": 0
},
"value": "x",
"optional": false,
"typeAnnotation": null
},
"init": {
"type": "ObjectExpression",
"span": {
"start": 10,
"end": 81,
"ctxt": 0
},
"properties": [
{
"type": "KeyValueProperty",
"key": {
"type": "Identifier",
"span": {
"start": 16,
"end": 20,
"ctxt": 0
},
"value": "prop",
"optional": false
},
"value": {
"type": "ConditionalExpression",
"span": {
"start": 22,
"end": 78,
"ctxt": 0
},
"test": {
"type": "Identifier",
"span": {
"start": 22,
"end": 31,
"ctxt": 0
},
"value": "isCorrect",
"optional": false
},
"consequent": {
"type": "ArrowFunctionExpression",
"span": {
"start": 42,
"end": 53,
"ctxt": 0
},
"params": [
{
"type": "Identifier",
"span": {
"start": 42,
"end": 44,
"ctxt": 0
},
"value": "fn",
"optional": false,
"typeAnnotation": null
}
],
"body": {
"type": "ParenthesisExpression",
"span": {
"start": 48,
"end": 53,
"ctxt": 0
},
"expression": {
"type": "ObjectExpression",
"span": {
"start": 49,
"end": 52,
"ctxt": 0
},
"properties": []
}
},
"async": false,
"generator": false,
"typeParameters": null,
"returnType": null
},
"alternate": {
"type": "ArrowFunctionExpression",
"span": {
"start": 68,
"end": 78,
"ctxt": 0
},
"params": [
{
"type": "Identifier",
"span": {
"start": 68,
"end": 70,
"ctxt": 0
},
"value": "fn",
"optional": false,
"typeAnnotation": null
}
],
"body": {
"type": "BooleanLiteral",
"span": {
"start": 74,
"end": 78,
"ctxt": 0
},
"value": true
},
"async": false,
"generator": false,
"typeParameters": null,
"returnType": null
}
}
}
]
},
"definite": false
}
]
}
],
"interpreter": null
}

View File

@ -0,0 +1,5 @@
const x = {
prop: isCorrect
? fn => ({ })
: fn => true,
};

View File

@ -0,0 +1,160 @@
{
"type": "Script",
"span": {
"start": 0,
"end": 82,
"ctxt": 0
},
"body": [
{
"type": "VariableDeclaration",
"span": {
"start": 0,
"end": 82,
"ctxt": 0
},
"kind": "const",
"declare": false,
"declarations": [
{
"type": "VariableDeclarator",
"span": {
"start": 6,
"end": 81,
"ctxt": 0
},
"id": {
"type": "Identifier",
"span": {
"start": 6,
"end": 7,
"ctxt": 0
},
"value": "x",
"optional": false,
"typeAnnotation": null
},
"init": {
"type": "ObjectExpression",
"span": {
"start": 10,
"end": 81,
"ctxt": 0
},
"properties": [
{
"type": "KeyValueProperty",
"key": {
"type": "Identifier",
"span": {
"start": 16,
"end": 20,
"ctxt": 0
},
"value": "prop",
"optional": false
},
"value": {
"type": "ConditionalExpression",
"span": {
"start": 22,
"end": 78,
"ctxt": 0
},
"test": {
"type": "Identifier",
"span": {
"start": 22,
"end": 31,
"ctxt": 0
},
"value": "isCorrect",
"optional": false
},
"consequent": {
"type": "ArrowFunctionExpression",
"span": {
"start": 42,
"end": 53,
"ctxt": 0
},
"params": [
{
"type": "Identifier",
"span": {
"start": 42,
"end": 44,
"ctxt": 0
},
"value": "fn",
"optional": false,
"typeAnnotation": null
}
],
"body": {
"type": "ParenthesisExpression",
"span": {
"start": 48,
"end": 53,
"ctxt": 0
},
"expression": {
"type": "ObjectExpression",
"span": {
"start": 49,
"end": 52,
"ctxt": 0
},
"properties": []
}
},
"async": false,
"generator": false,
"typeParameters": null,
"returnType": null
},
"alternate": {
"type": "ArrowFunctionExpression",
"span": {
"start": 68,
"end": 78,
"ctxt": 0
},
"params": [
{
"type": "Identifier",
"span": {
"start": 68,
"end": 70,
"ctxt": 0
},
"value": "fn",
"optional": false,
"typeAnnotation": null
}
],
"body": {
"type": "BooleanLiteral",
"span": {
"start": 74,
"end": 78,
"ctxt": 0
},
"value": true
},
"async": false,
"generator": false,
"typeParameters": null,
"returnType": null
}
}
}
]
},
"definite": false
}
]
}
],
"interpreter": null
}

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms_react"
repository = "https://github.com/swc-project/swc.git"
version = "0.36.0"
version = "0.36.1"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -245,18 +245,7 @@ where
match children.len() {
0 => {}
1 if children[0].as_ref().unwrap().spread.is_none()
&& match &*children[0].as_ref().unwrap().expr {
Expr::Call(CallExpr {
callee: ExprOrSuper::Expr(callee),
..
}) => match &**callee {
Expr::Ident(Ident { sym, .. }) => *sym != *"_jsx",
_ => true,
},
_ => true,
} =>
{
1 if children[0].as_ref().unwrap().spread.is_none() => {
props_obj
.props
.push(PropOrSpread::Prop(Box::new(Prop::KeyValue(KeyValueProp {

View File

@ -0,0 +1,7 @@
export var App = function () {
return (
<>
<div>1</div>
</>
);
};

View File

@ -0,0 +1,3 @@
{
"runtime": "automatic"
}

View File

@ -0,0 +1,8 @@
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
export var App = function() {
return _jsx(_Fragment, {
children: _jsx("div", {
children: "1"
})
});
};

View File

@ -1,7 +1,5 @@
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
var x = _jsx(_Fragment, {
children: [
_jsx("div", {
})
]
children: _jsx("div", {
})
});

View File

@ -0,0 +1 @@
function foo(arg1 = 1, arg2 = 1) { }

View File

@ -0,0 +1,3 @@
function foo(param, param1) {
var arg1 = param === void 0 ? 1 : param, arg2 = param1 === void 0 ? 1 : param1;
}