Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

fix(rome_js_parser): improve yield parsing in non generator function #3622

Merged
merged 2 commits into from
Nov 14, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 44 additions & 7 deletions crates/rome_js_parser/src/syntax/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,8 +244,10 @@ fn parse_assignment_expression_or_higher_base(
p: &mut Parser,
context: ExpressionContext,
) -> ParsedSyntax {
if p.state.in_generator() && p.at(T![yield]) {
return Present(parse_yield_expression(p, context));
if p.at(T![yield]) {
if let Some(m) = try_parse_yield_expression(p, context) {
return Present(m);
}
}

let checkpoint = p.checkpoint();
Expand Down Expand Up @@ -345,32 +347,67 @@ fn is_assign_token(kind: JsSyntaxKind) -> bool {
// yield
// yield
// }
fn parse_yield_expression(p: &mut Parser, context: ExpressionContext) -> CompletedMarker {
fn try_parse_yield_expression(
p: &mut Parser,
context: ExpressionContext,
) -> Option<CompletedMarker> {
let m = p.start();
let yield_range = p.cur_range();
95th marked this conversation as resolved.
Show resolved Hide resolved
let checkpoint = p.checkpoint();
p.expect(T![yield]);

// test reparse_yield_as_identifier
// // SCRIPT
// function foo() { yield *bar; }
// function bar() { yield; }
// function baz() { yield }
if !p.state.in_generator() && (p.at(T![*]) || !is_at_expression(p)) {
p.rewind(checkpoint);
m.abandon(p);
return None;
}

// test yield_in_generator_function
// function* foo() { yield 10; }
// function* foo() { yield *bar; }
// function* foo() { yield; }
if !is_semi(p, 0) && (p.at(T![*]) || is_at_expression(p)) {
let argument = p.start();
p.eat(T![*]);
parse_assignment_expression_or_higher(p, context.and_object_expression_allowed(true)).ok();

argument.complete(p, JS_YIELD_ARGUMENT);
}

let mut yield_expr = m.complete(p, JS_YIELD_EXPRESSION);

if !p.state.is_top_level() && !p.state.in_function() {
// test_err yield_at_top_level_module
// yield 10;

// test_err yield_at_top_level_script
// // SCRIPT
// yield 10;

// test_err yield_in_non_generator_function_script
// // SCRIPT
// function foo() { yield bar; }
// function foo() { yield 10; }

// test_err yield_in_non_generator_function_module
// function foo() { yield; }
// function foo() { yield foo; }
// function foo() { yield *foo; }
if !(p.state.in_generator() && p.state.in_function()) {
// test_err yield_expr_in_parameter_initializer
// function* test(a = yield "test") {}
// function test2(a = yield "test") {}
p.error(p.err_builder(
"`yield` is only allowed within generator functions.",
yield_expr.range(p),
yield_range,
));
yield_expr.change_to_unknown(p);
}

yield_expr
Some(yield_expr)
}

/// A conditional expression such as `foo ? bar : baz`
Expand Down
75 changes: 20 additions & 55 deletions crates/rome_js_parser/test_data/inline/err/function_decl_err.rast
Original file line number Diff line number Diff line change
Expand Up @@ -134,21 +134,17 @@ JsModule {
JsExpressionStatement {
expression: JsUnknownExpression {
items: [
JsUnknown {
items: [
IDENT@119..126 "yield" [Newline("\n")] [Whitespace(" ")],
],
YIELD_KW@119..126 "yield" [Newline("\n")] [Whitespace(" ")],
JsYieldArgument {
star_token: missing (optional),
expression: JsIdentifierExpression {
name: JsReferenceIdentifier {
value_token: IDENT@126..130 "foo3" [] [],
},
},
},
],
},
semicolon_token: missing (optional),
},
JsExpressionStatement {
expression: JsIdentifierExpression {
name: JsReferenceIdentifier {
value_token: IDENT@126..130 "foo3" [] [],
},
},
semicolon_token: SEMICOLON@130..131 ";" [] [],
},
JsUnknownStatement {
Expand Down Expand Up @@ -358,17 +354,16 @@ JsModule {
1: JS_DIRECTIVE_LIST@118..118
2: JS_STATEMENT_LIST@118..118
3: R_CURLY@118..119 "}" [] []
7: JS_EXPRESSION_STATEMENT@119..126
0: JS_UNKNOWN_EXPRESSION@119..126
0: JS_UNKNOWN@119..126
0: IDENT@119..126 "yield" [Newline("\n")] [Whitespace(" ")]
1: (empty)
8: JS_EXPRESSION_STATEMENT@126..131
0: JS_IDENTIFIER_EXPRESSION@126..130
0: JS_REFERENCE_IDENTIFIER@126..130
0: IDENT@126..130 "foo3" [] []
7: JS_EXPRESSION_STATEMENT@119..131
0: JS_UNKNOWN_EXPRESSION@119..130
0: YIELD_KW@119..126 "yield" [Newline("\n")] [Whitespace(" ")]
1: JS_YIELD_ARGUMENT@126..130
0: (empty)
1: JS_IDENTIFIER_EXPRESSION@126..130
0: JS_REFERENCE_IDENTIFIER@126..130
0: IDENT@126..130 "foo3" [] []
1: SEMICOLON@130..131 ";" [] []
9: JS_UNKNOWN_STATEMENT@131..159
8: JS_UNKNOWN_STATEMENT@131..159
0: FUNCTION_KW@131..141 "function" [Newline("\n")] [Whitespace(" ")]
1: JS_IDENTIFIER_BINDING@141..146
0: IDENT@141..146 "test2" [] []
Expand All @@ -385,7 +380,7 @@ JsModule {
1: JS_DIRECTIVE_LIST@158..158
2: JS_STATEMENT_LIST@158..158
3: R_CURLY@158..159 "}" [] []
10: JS_FUNCTION_DECLARATION@159..183
9: JS_FUNCTION_DECLARATION@159..183
0: (empty)
1: FUNCTION_KW@159..169 "function" [Newline("\n")] [Whitespace(" ")]
2: (empty)
Expand All @@ -408,7 +403,7 @@ JsModule {
1: JS_DIRECTIVE_LIST@182..182
2: JS_STATEMENT_LIST@182..182
3: R_CURLY@182..183 "}" [] []
11: JS_FUNCTION_DECLARATION@183..207
10: JS_FUNCTION_DECLARATION@183..207
0: (empty)
1: FUNCTION_KW@183..193 "function" [Newline("\n")] [Whitespace(" ")]
2: (empty)
Expand Down Expand Up @@ -512,7 +507,7 @@ function_decl_err.js:6:17 parse ━━━━━━━━━━━━━━━━
--
function_decl_err.js:8:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Illegal use of reserved keyword `yield` as an identifier in strict mode
× `yield` is only allowed within generator functions.

6 │ async function *() {}
7 │ function *foo2() {}
Expand All @@ -521,36 +516,6 @@ function_decl_err.js:8:1 parse ━━━━━━━━━━━━━━━━
9 │ function test2(): number {}
10 │ function foo4(await) {}

--
function_decl_err.js:8:7 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Expected a semicolon or an implicit semicolon after a statement, but found none

6 │ async function *() {}
7 │ function *foo2() {}
> 8 │ yield foo3;
│ ^^^^
9 │ function test2(): number {}
10 │ function foo4(await) {}

i An explicit or implicit semicolon is expected here...

6 │ async function *() {}
7 │ function *foo2() {}
> 8 │ yield foo3;
│ ^^^^
9 │ function test2(): number {}
10 │ function foo4(await) {}

i ...Which is required to end this statement

6 │ async function *() {}
7 │ function *foo2() {}
> 8 │ yield foo3;
│ ^^^^^^^^^^
9 │ function test2(): number {}
10 │ function foo4(await) {}

--
function_decl_err.js:9:17 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
yield 10;
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
JsModule {
interpreter_token: missing (optional),
directives: JsDirectiveList [],
items: JsModuleItemList [
JsExpressionStatement {
expression: JsUnknownExpression {
items: [
YIELD_KW@0..6 "yield" [] [Whitespace(" ")],
JsYieldArgument {
star_token: missing (optional),
expression: JsNumberLiteralExpression {
value_token: JS_NUMBER_LITERAL@6..8 "10" [] [],
},
},
],
},
semicolon_token: SEMICOLON@8..9 ";" [] [],
},
],
eof_token: EOF@9..10 "" [Newline("\n")] [],
}

0: JS_MODULE@0..10
0: (empty)
1: JS_DIRECTIVE_LIST@0..0
2: JS_MODULE_ITEM_LIST@0..9
0: JS_EXPRESSION_STATEMENT@0..9
0: JS_UNKNOWN_EXPRESSION@0..8
0: YIELD_KW@0..6 "yield" [] [Whitespace(" ")]
1: JS_YIELD_ARGUMENT@6..8
0: (empty)
1: JS_NUMBER_LITERAL_EXPRESSION@6..8
0: JS_NUMBER_LITERAL@6..8 "10" [] []
1: SEMICOLON@8..9 ";" [] []
3: EOF@9..10 "" [Newline("\n")] []
--
yield_at_top_level_module.js:1:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× `yield` is only allowed within generator functions.

> 1 │ yield 10;
│ ^^^^^
2 │

--
yield 10;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// SCRIPT
yield 10;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
JsScript {
interpreter_token: missing (optional),
directives: JsDirectiveList [],
statements: JsStatementList [
JsExpressionStatement {
expression: JsUnknownExpression {
items: [
YIELD_KW@0..16 "yield" [Comments("// SCRIPT"), Newline("\n")] [Whitespace(" ")],
JsYieldArgument {
star_token: missing (optional),
expression: JsNumberLiteralExpression {
value_token: JS_NUMBER_LITERAL@16..18 "10" [] [],
},
},
],
},
semicolon_token: SEMICOLON@18..19 ";" [] [],
},
],
eof_token: EOF@19..20 "" [Newline("\n")] [],
}

0: JS_SCRIPT@0..20
0: (empty)
1: JS_DIRECTIVE_LIST@0..0
2: JS_STATEMENT_LIST@0..19
0: JS_EXPRESSION_STATEMENT@0..19
0: JS_UNKNOWN_EXPRESSION@0..18
0: YIELD_KW@0..16 "yield" [Comments("// SCRIPT"), Newline("\n")] [Whitespace(" ")]
1: JS_YIELD_ARGUMENT@16..18
0: (empty)
1: JS_NUMBER_LITERAL_EXPRESSION@16..18
0: JS_NUMBER_LITERAL@16..18 "10" [] []
1: SEMICOLON@18..19 ";" [] []
3: EOF@19..20 "" [Newline("\n")] []
--
yield_at_top_level_script.js:2:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× `yield` is only allowed within generator functions.

1 │ // SCRIPT
> 2 │ yield 10;
│ ^^^^^
3 │

--
// SCRIPT
yield 10;
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,17 @@ JsModule {
eq_token: EQ@53..55 "=" [] [Whitespace(" ")],
expression: JsUnknownExpression {
items: [
JsUnknown {
items: [
IDENT@55..61 "yield" [] [Whitespace(" ")],
],
YIELD_KW@55..61 "yield" [] [Whitespace(" ")],
JsYieldArgument {
star_token: missing (optional),
expression: JsStringLiteralExpression {
value_token: JS_STRING_LITERAL@61..67 "\"test\"" [] [],
},
},
],
},
},
},
missing separator,
JsUnknownParameter {
items: [
JS_STRING_LITERAL@61..67 "\"test\"" [] [],
],
},
],
r_paren_token: R_PAREN@67..69 ")" [] [Whitespace(" ")],
},
Expand Down Expand Up @@ -140,19 +136,19 @@ JsModule {
5: JS_PARAMETERS@50..69
0: L_PAREN@50..51 "(" [] []
1: JS_PARAMETER_LIST@51..67
0: JS_FORMAL_PARAMETER@51..61
0: JS_FORMAL_PARAMETER@51..67
0: JS_IDENTIFIER_BINDING@51..53
0: IDENT@51..53 "a" [] [Whitespace(" ")]
1: (empty)
2: (empty)
3: JS_INITIALIZER_CLAUSE@53..61
3: JS_INITIALIZER_CLAUSE@53..67
0: EQ@53..55 "=" [] [Whitespace(" ")]
1: JS_UNKNOWN_EXPRESSION@55..61
0: JS_UNKNOWN@55..61
0: IDENT@55..61 "yield" [] [Whitespace(" ")]
1: (empty)
2: JS_UNKNOWN_PARAMETER@61..67
0: JS_STRING_LITERAL@61..67 "\"test\"" [] []
1: JS_UNKNOWN_EXPRESSION@55..67
0: YIELD_KW@55..61 "yield" [] [Whitespace(" ")]
1: JS_YIELD_ARGUMENT@61..67
0: (empty)
1: JS_STRING_LITERAL_EXPRESSION@61..67
0: JS_STRING_LITERAL@61..67 "\"test\"" [] []
2: R_PAREN@67..69 ")" [] [Whitespace(" ")]
6: (empty)
7: JS_FUNCTION_BODY@69..71
Expand All @@ -167,32 +163,20 @@ yield_expr_in_parameter_initializer.js:1:20 parse ━━━━━━━━━━
× `yield` is only allowed within generator functions.

> 1 │ function* test(a = yield "test") {}
│ ^^^^^^^^^^^^
│ ^^^^^
2 │ function test2(a = yield "test") {}
3 │

--
yield_expr_in_parameter_initializer.js:2:20 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× Illegal use of reserved keyword `yield` as an identifier in strict mode
× `yield` is only allowed within generator functions.

1 │ function* test(a = yield "test") {}
> 2 │ function test2(a = yield "test") {}
│ ^^^^^
3 │

--
yield_expr_in_parameter_initializer.js:2:26 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

× expected `,` but instead found `"test"`

1 │ function* test(a = yield "test") {}
> 2 │ function test2(a = yield "test") {}
│ ^^^^^^
3 │

i Remove "test"

--
function* test(a = yield "test") {}
function test2(a = yield "test") {}
Loading