fix(rslint_parser): Avoid parsing assignment expr three times
Reduces the time an assignment pattern is parsed to two:
1. Parse it as an expression to determine if it is a member expression or a single assignment
2. Re-Parse it only if it is a complex array or object pattern
MODIFY
crates/rslint_parser/src/event.rs
crates/rslint_parser/src/event.rs
@@ -157,16 +157,19 @@ pub trait RewriteParseEvents {
}
}
-/// Allows to rewrite the parse events by visiting each event emitted after the checkpoint.
+/// Allows rewriting a super grammar to a sub grammar by visiting each event emitted after the checkpoint.
/// Useful if a node turned out to be of a different kind its subtree must be re-shaped
-/// (adding new nodes, dropping sub nodes, etc).
+/// (adding new nodes, dropping sub nodes, etc.).
pub fn rewrite_events<T: RewriteParseEvents>(
rewriter: &mut T,
checkpoint: Checkpoint,
p: &mut Parser,
) {
+ // Only rewind the events and token position but do not reset the parser errors nor parser state.
+ // The current parsed grammar is a super-set of the grammar that gets re-parsed. Thus, any
+ // error that applied to the old grammar also applies to the sub-grammar.
let events: Vec<_> = p.events.split_off(checkpoint.event_pos + 1);
- p.rewind(checkpoint);
+ p.tokens.rewind(checkpoint.token_pos);
let mut sink = RewriteParseEventsTreeSink {
parser: p,

MODIFY
crates/rslint_parser/src/parser.rs
crates/rslint_parser/src/parser.rs
@@ -128,8 +128,8 @@ impl ParserProgress {
/// ```
pub struct Parser<'t> {
pub file_id: usize,
- tokens: TokenSource<'t>,
- pub(crate) events: Vec<Event>,
+ pub(super) tokens: TokenSource<'t>,
+ pub(super) events: Vec<Event>,
pub state: ParserState,
pub syntax: Syntax,
pub errors: Vec<ParserError>,

MODIFY
crates/rslint_parser/src/syntax/assignment.rs
crates/rslint_parser/src/syntax/assignment.rs
use crate::event::{rewrite_events, RewriteParseEvents};
use crate::parser::{expected_any, ParsedSyntax, ToDiagnostic};
use crate::syntax::class::parse_initializer_clause;
-use crate::syntax::expr::{
- is_at_identifier, parse_conditional_expr, parse_expression, parse_unary_expr,
-};
+use crate::syntax::expr::{is_at_identifier, parse_conditional_expr, parse_unary_expr};
use crate::syntax::js_parse_error::{
expected_assignment_target, expected_identifier, expected_object_member_name,
};
@@ -37,18 +35,17 @@ pub(crate) fn expression_to_assignment_pattern(
p: &mut Parser,
target: CompletedMarker,
checkpoint: Checkpoint,
- expr_kind: AssignmentExprPrecedence,
) -> CompletedMarker {
- if let Ok(assignment_target) = try_expression_to_assignment(p, target, checkpoint) {
- return assignment_target;
+ match target.kind() {
+ JS_OBJECT_EXPRESSION => {
+ p.rewind(checkpoint);
+ ObjectAssignmentPattern.parse_object_pattern(p).unwrap()
}
-
- let expression_end = p.token_pos();
+ JS_ARRAY_EXPRESSION => {
p.rewind(checkpoint);
-
- match parse_assignment_pattern(p, expr_kind) {
- Present(target) => target,
- Absent => wrap_expression_in_invalid_assignment(p, expression_end),
+ ArrayAssignmentPattern.parse_array_pattern(p).unwrap()
+ }
+ _ => expression_to_assignment(p, target, checkpoint),
}
}
@@ -76,17 +73,8 @@ pub(crate) fn parse_assignment_pattern(
let checkpoint = p.checkpoint();
let assignment_expression = expression_kind.parse_expression(p);
- assignment_expression.and_then(|expr| match expr.kind() {
- JS_OBJECT_EXPRESSION => {
- p.rewind(checkpoint);
- ObjectAssignmentPattern.parse_object_pattern(p)
- }
- JS_ARRAY_EXPRESSION => {
- p.rewind(checkpoint);
- ArrayAssignmentPattern.parse_array_pattern(p)
- }
- _ => Present(expression_to_assignment(p, expr, checkpoint)),
- })
+ assignment_expression
+ .map(|expression| expression_to_assignment_pattern(p, expression, checkpoint))
}
/// Re-parses an expression as an assignment.
@@ -108,7 +96,6 @@ pub(crate) fn expression_to_assignment(
pub(crate) enum AssignmentExprPrecedence {
Unary,
Conditional,
- Any,
}
impl AssignmentExprPrecedence {
@@ -116,7 +103,6 @@ impl AssignmentExprPrecedence {
match self {
AssignmentExprPrecedence::Unary => parse_unary_expr(p),
AssignmentExprPrecedence::Conditional => parse_conditional_expr(p),
- AssignmentExprPrecedence::Any => parse_expression(p),
}
}
}

MODIFY
crates/rslint_parser/src/syntax/expr.rs
crates/rslint_parser/src/syntax/expr.rs
@@ -217,13 +217,7 @@ fn parse_assign_expr_recursive(
checkpoint: Checkpoint,
) -> ParsedSyntax {
if p.at_ts(ASSIGN_TOKENS) {
- let target = expression_to_assignment_pattern(
- p,
- target,
- checkpoint,
- AssignmentExprPrecedence::Conditional,
- );
-
+ let target = expression_to_assignment_pattern(p, target, checkpoint);
let m = target.precede(p);
p.bump_any(); // operator
parse_expr_or_assignment(p)

MODIFY
crates/rslint_parser/src/syntax/stmt.rs
crates/rslint_parser/src/syntax/stmt.rs
@@ -12,7 +12,7 @@ use crate::state::{
EnableStrictModeSnapshot, EnterBreakable, IncludeIn, LabelledItem,
StrictMode as StrictModeState,
};
-use crate::syntax::assignment::{expression_to_assignment_pattern, AssignmentExprPrecedence};
+use crate::syntax::assignment::expression_to_assignment_pattern;
use crate::syntax::class::{parse_class_statement, parse_initializer_clause};
use crate::syntax::expr::{
is_at_expression, is_at_identifier, is_nth_at_name, parse_expr_or_assignment,
@@ -1304,12 +1304,8 @@ fn parse_for_head(p: &mut Parser) -> JsSyntaxKind {
if p.at(T![in]) || p.cur_src() == "of" {
// for (assignment_pattern in ...
if let Present(assignment_expr) = init_expr {
- let mut assignment = expression_to_assignment_pattern(
- p,
- assignment_expr,
- checkpoint,
- AssignmentExprPrecedence::Any,
- );
+ let mut assignment =
+ expression_to_assignment_pattern(p, assignment_expr, checkpoint);
if p.typescript()
&& p.at(T![in])

fix(rslint_parser): Avoid parsing assignment expr three times (#1948)

Reduces the time an assignment pattern is parsed to two:

1. Parse it as an expression to determine if it is a member expression or a single assignment
2. Re-Parse it only if it is a complex array or object pattern