Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(ecma/parser): parse types in CallExpression inside templates #6611

Merged
merged 4 commits into from Dec 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 0 additions & 3 deletions crates/swc_ecma_parser/src/lib.rs
Expand Up @@ -369,9 +369,6 @@ pub struct Context {

in_forced_jsx_context: bool,

/// If true, `:` should not be treated as a type annotation.
dont_parse_colon_as_type_ann: bool,

// If true, allow super.x and super[x]
allow_direct_super: bool,

Expand Down
17 changes: 12 additions & 5 deletions crates/swc_ecma_parser/src/parser/expr.rs
Expand Up @@ -218,7 +218,6 @@ impl<I: Tokens> Parser<I> {
let ctx = Context {
in_cond_expr: true,
will_expect_colon_for_cond: false,
dont_parse_colon_as_type_ann: false,
..self.ctx()
};
let alt = self.with_ctx(ctx).parse_assignment_expr()?;
Expand Down Expand Up @@ -306,7 +305,6 @@ impl<I: Tokens> Parser<I> {
tok!('[') => {
let ctx = Context {
will_expect_colon_for_cond: false,
dont_parse_colon_as_type_ann: false,
..self.ctx()
};
return self.with_ctx(ctx).parse_array_lit();
Expand Down Expand Up @@ -378,8 +376,13 @@ impl<I: Tokens> Parser<I> {
}

tok!('`') => {
let ctx = Context {
will_expect_colon_for_cond: false,
..self.ctx()
};

// parse template literal
return Ok(Box::new(Expr::Tpl(self.parse_tpl(false)?)));
return Ok(Box::new(Expr::Tpl(self.with_ctx(ctx).parse_tpl(false)?)));
}

tok!('(') => {
Expand Down Expand Up @@ -841,7 +844,6 @@ impl<I: Tokens> Parser<I> {
let return_type = if !self.ctx().will_expect_colon_for_cond
&& self.input.syntax().typescript()
&& is!(self, ':')
&& !self.ctx().dont_parse_colon_as_type_ann
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we don't need dont_parse_colon_as_type_ann, because will_expect_colon_for_cond is used to check should we parse : as type or not, also I found we duplicate logic, i.e. we set will_expect_colon_for_cond and dont_parse_colon_as_type_ann to the same value (except switch logic, but I found it is not used there plus I added test cases)

{
self.try_parse_ts(|p| {
let return_type = p.parse_ts_type_or_type_predicate_ann(&tok!(':'))?;
Expand Down Expand Up @@ -1470,7 +1472,12 @@ impl<I: Tokens> Parser<I> {

// MemberExpression[?Yield, ?Await] TemplateLiteral[?Yield, ?Await, +Tagged]
if is!(self, '`') {
let tpl = self.parse_tagged_tpl(expr, None)?;
let ctx = Context {
will_expect_colon_for_cond: false,
..self.ctx()
};

let tpl = self.with_ctx(ctx).parse_tagged_tpl(expr, None)?;
return Ok((Box::new(Expr::TaggedTpl(tpl)), true));
}

Expand Down
1 change: 0 additions & 1 deletion crates/swc_ecma_parser/src/parser/object.rs
Expand Up @@ -14,7 +14,6 @@ impl<I: Tokens> Parser<I> {
{
let ctx = Context {
will_expect_colon_for_cond: false,
dont_parse_colon_as_type_ann: false,
..self.ctx()
};
self.with_ctx(ctx).parse_with(|p| {
Expand Down
9 changes: 1 addition & 8 deletions crates/swc_ecma_parser/src/parser/stmt.rs
Expand Up @@ -610,15 +610,8 @@ impl<'a, I: Tokens> Parser<I> {
let is_case = is!(p, "case");
let case_start = cur_pos!(p);
bump!(p);
let ctx = Context {
dont_parse_colon_as_type_ann: true,
..p.ctx()
};
let test = if is_case {
p.with_ctx(ctx)
.include_in_expr(true)
.parse_expr()
.map(Some)?
p.include_in_expr(true).parse_expr().map(Some)?
} else {
if let Some(previous) = span_of_previous_default {
syntax_error!(p, SyntaxError::MultipleDefault { previous });
Expand Down
104 changes: 104 additions & 0 deletions crates/swc_ecma_parser/tests/typescript/issue-6601/index.tsx
@@ -0,0 +1,104 @@
function exampleFunction1() {
return Math.random() > 0.5
? `<button
@click="${(): void => console.log('this line causes a syntax error')}"
></button>`
: `<button
@click="${(): void => console.log('this line does NOT causes a syntax error')}"
></button>`;
}

function exampleFunction2() {
return Math.random() > 0.5
? `<bar></bar>` + `<button
@click="${(): void => console.log('this line causes a syntax error')}"
></button>`
: `<bar></bar>` + `<button
@click="${(): void => console.log('this line does NOT causes a syntax error')}"
></button>`;
}

function exampleFunction3() {
return Math.random() > 0.5
? (): void => console.log('this line causes a syntax error')
: (): void => console.log('this line does NOT causes a syntax error');
}

function exampleFunction4() {
return Math.random() > 0.5
? function (): void { console.log('this line causes a syntax error') }
: function (): void { console.log('this line does NOT causes a syntax error') };
}

function exampleFunction5() {
return Math.random() > 0.5
? (function (): void { console.log('this line causes a syntax error') })
: (function (): void { console.log('this line does NOT causes a syntax error') });
}

function exampleFunction6() {
return Math.random() > 0.5
? "test" == "test"
? `<button @click="${(): void => console.log('this line causes a syntax error')}"></button>`
: "bar"
: `<button
@click="${(): void => console.log('this line does NOT causes a syntax error')}"
></button>`;
}


function exampleFunction6() {
return Math.random() > 0.5
? `<button @click="${(): void => console.log('this line causes a syntax error')}"></button>`
: "test" == "test"
? `<button @click="${(): void => console.log('this line causes a syntax error')}"></button>`
: "bar";
}

function exampleFunction7() {
return Math.random() > 0.5
? foo`<button @click="${(): void => console.log('this line causes a syntax error')}"></button>`
: bar`<button @click="${(): void => console.log('this line does NOT causes a syntax error')}"></button>`;
}

function exampleFunction8() {
return Math.random() > 0.5
? ((): void => console.log('this line causes a syntax error'))
: ((): void => console.log('this line does NOT causes a syntax error'));
}

function exampleFunction9() {
return Math.random() > 0.5
? async (): Promise<void> => console.log('this line causes a syntax error')
: async (): Promise<void> => console.log('this line causes a syntax error');
}

function exampleFunction10() {
const foo = "Oranges";

switch (foo) {
case 'Oranges': {
return `<button @click="${(): void => console.log('this line causes a syntax error')}" ></button>`;
}
default:
console.log(`Sorry, we are out of test.`);
}
}

function exampleFunction11() {
switch (true) {
case ((): boolean => true)(): {
console.log('This shape is a square.');
break;
}
}
}

function exampleFunction12() {
switch (((): boolean => true)()) {
case ((): boolean => true)(): {
console.log('This shape is a square.');
break;
}
}
}