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

Commit

Permalink
feat: unknown statements (#1794)
Browse files Browse the repository at this point in the history
  • Loading branch information
ematipico authored Nov 19, 2021
1 parent 4bf12dd commit 15072f0
Show file tree
Hide file tree
Showing 24 changed files with 371 additions and 128 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/test262.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
pull_request:
branches:
- main
# enable this job on this temporary branch
- feature/unknown-nodes-errors

env:
RUST_LOG: info
Expand Down
1 change: 1 addition & 0 deletions crates/rslint_parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ mod lossless_tree_sink;
mod lossy_tree_sink;
mod numbers;
mod parse;
pub(crate) mod parse_recoverer;
mod state;
mod syntax_node;
mod token_source;
Expand Down
2 changes: 1 addition & 1 deletion crates/rslint_parser/src/lossless_tree_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl<'a> TreeSink for LosslessTreeSink<'a> {
it.kind,
self.text
.get(start..end)
.unwrap_or_else(|| self.text.get(start - 1..end).unwrap()),
.unwrap_or_else(|| self.text.get(start - 1..end).unwrap_or("")),
)
});
n_attached_trivias(kind, leading_trivias.rev())
Expand Down
94 changes: 94 additions & 0 deletions crates/rslint_parser/src/parse_recoverer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
use crate::{Parser, ParserError, TokenSet};
use rslint_errors::Diagnostic;
use rslint_lexer::{SyntaxKind, T};

/// This struct contains the information needed to the parser to recover from a certain error
///
/// By default it doesn't check curly braces, use [with_braces_included] to turn opt-in the check
#[derive(Debug)]
pub struct ParseRecoverer {
/// The [Diagnostic] to emit
error: Option<ParserError>,
/// It tells the parser to recover if the position is inside a set of [tokens](TokenSet)
recovery: TokenSet,
/// It tells the parser to recover if the current token is a curly brace
include_braces: bool,
/// The kind of the unknown node the parser inserts if it isn't able to recover because
/// the current token is neither in the recovery set nor any of `{` or `}`.
unknown_node_kind: SyntaxKind,
}

impl ParseRecoverer {
pub fn new(recovery: TokenSet, unknown_node_kind: SyntaxKind) -> Self {
Self {
error: None,
recovery,
include_braces: false,
unknown_node_kind,
}
}

pub fn with_error<Err: Into<ParserError>>(
recovery: TokenSet,
unknown_node_kind: SyntaxKind,
error: Err,
) -> Self {
Self {
error: Some(error.into()),
recovery,
include_braces: false,
unknown_node_kind,
}
}

/// Enable check of curly braces as recovery tokens
pub fn enabled_braces_check(mut self) -> Self {
self.include_braces = true;
self
}

/// The main function that tells to the parser how to recover itself.
///
/// Recover from an error with a [recovery set](TokenSet) or by using a `{` or `}`.
///
/// If [ParserRecoverer] has an error, it gets tracked in the events.
pub fn recover(&self, p: &mut Parser) {
let error = self.get_error();
if let Some(error) = error {
p.error(error);
} else {
// the check on state should be done only when there's no error
if p.state.no_recovery {
return;
}
}
if !self.parsing_is_recoverable(p) {
let m = p.start();
p.bump_any();
m.complete(p, self.get_unknown_node_kind());
}
}

/// Checks if the parsing phase is recoverable by checking curly braces and [tokens set](TokenSet)
fn parsing_is_recoverable(&self, parser: &Parser) -> bool {
self.is_at_token_set(parser) || self.is_at_braces(parser)
}

/// It returns the diagnostic
fn get_error(&self) -> Option<Diagnostic> {
self.error.to_owned()
}

/// It returns the unknown node kind that will be used to complete the parsing
fn get_unknown_node_kind(&self) -> SyntaxKind {
self.unknown_node_kind
}

fn is_at_braces(&self, parser: &Parser) -> bool {
matches!(parser.cur(), T!['{'] | T!['}'] if self.include_braces)
}

fn is_at_token_set(&self, parser: &Parser) -> bool {
parser.at_ts(self.recovery)
}
}
69 changes: 0 additions & 69 deletions crates/rslint_parser/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,75 +179,6 @@ impl<'t> Parser<'t> {
true
}

/// Recover from an error with a recovery set or by using a `{` or `}`.
///
/// # Arguments
///
/// * `error` - the [Diagnostic] to emit
/// * `recovery` - it recovers the parser position is inside a set [tokens](TokenSet)
/// * `include_braces` - it recovers the parser if the current token is a curly brace
/// * `unknown_node` - The kind of the unknown node the parser inserts if it isn't able to recover because
/// the current token is neither in the recovery set nor any of `{` or `}`.
pub fn err_recover(
&mut self,
error: impl Into<ParserError>,
recovery: TokenSet,
include_braces: bool,
unknown_node: SyntaxKind,
) -> Option<()> {
if self.state.no_recovery {
return None;
}

match self.cur() {
T!['{'] | T!['}'] if include_braces => {
self.error(error);
return Some(());
}
_ => (),
}

if self.at_ts(recovery) {
self.error(error);
return Some(());
}

let m = self.start();
self.error(error);
self.bump_any();
m.complete(self, unknown_node);
Some(())
}

/// Recover from an error but don't add an error to the events
///
/// # Arguments
///
/// * `recovery` - it recovers the parser position is inside a set [tokens](TokenSet)
/// * `include_braces` - it recovers the parser if the current token is a curly brace
/// * `unknown_node` - The kind of the unknown node the parser inserts if it isn't able to recover because
/// the current token is neither in the recovery set nor any of `{` or `}`.
pub fn err_recover_no_err(
&mut self,
recovery: TokenSet,
include_braces: bool,
unknown_node: SyntaxKind,
) {
match self.cur() {
T!['{'] | T!['}'] if include_braces => {
return;
}
_ => (),
}

if self.at_ts(recovery) {
return;
}

let m = self.start();
self.bump_any();
m.complete(self, unknown_node);
}

/// Starts a new node in the syntax tree. All nodes and tokens
/// consumed between the `start` and the corresponding `Marker::complete`
Expand Down
24 changes: 13 additions & 11 deletions crates/rslint_parser/src/syntax/decl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use super::expr::{assign_expr, identifier_name};
use super::pat::{binding_identifier, pattern};
use super::typescript::*;
use crate::parse_recoverer::ParseRecoverer;
use crate::syntax::function::{args_body, function_body, function_body_or_declaration};
use crate::syntax::object::object_prop_name;
use crate::{SyntaxKind::*, *};
Expand Down Expand Up @@ -285,7 +286,7 @@ fn parameters_common(p: &mut Parser, constructor_params: bool) -> CompletedMarke
}
Some(res)
} else {
p.err_recover_no_err(
ParseRecoverer::new(
token_set![
T![ident],
T![await],
Expand All @@ -295,9 +296,10 @@ fn parameters_common(p: &mut Parser, constructor_params: bool) -> CompletedMarke
T![...],
T![')'],
],
true,
ERROR,
);
)
.enabled_braces_check()
.recover(p);
None
}
};
Expand Down Expand Up @@ -961,12 +963,12 @@ fn class_member_no_semi(p: &mut Parser) -> Option<CompletedMarker> {
let err = p
.err_builder("expected `;`, a property, or a method for a class body, but found none")
.primary(p.cur_tok().range, "");
p.err_recover(
err,
ParseRecoverer::with_error(
token_set![T![;], T![ident], T![async], T![yield], T!['}'], T![#]],
false,
ERROR,
);
err,
)
.recover(p);
None
}

Expand Down Expand Up @@ -1060,12 +1062,12 @@ pub fn method(
.err_builder("expected a method definition, but found none")
.primary(p.cur_tok().range, "");

p.err_recover(
err,
ParseRecoverer::with_error(
recovery_set.into().unwrap_or(BASE_METHOD_RECOVERY_SET),
false,
ERROR,
);
err,
)
.recover(p);
return None;
}
};
Expand Down
12 changes: 9 additions & 3 deletions crates/rslint_parser/src/syntax/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use super::decl::{arrow_body, class_decl, maybe_private_name, parameter_list};
use super::pat::pattern;
use super::typescript::*;
use super::util::*;
use crate::parse_recoverer::ParseRecoverer;
use crate::syntax::function::function_expression;
use crate::syntax::object::object_expr;
use crate::{SyntaxKind::*, *};
Expand Down Expand Up @@ -716,7 +717,8 @@ pub fn paren_or_arrow_expr(p: &mut Parser, can_be_arrow: bool) -> CompletedMarke
let err = temp.err_builder(&format!("expect a closing parenthesis after a spread element, but instead found `{}`", temp.cur_src()))
.primary(temp.cur_tok().range, "");

temp.err_recover(err, EXPR_RECOVERY_SET, false, ERROR);
ParseRecoverer::with_error(EXPR_RECOVERY_SET, ERROR, err)
.recover(&mut temp);
}
}
break;
Expand Down Expand Up @@ -1048,7 +1050,9 @@ pub fn primary_expr(p: &mut Parser) -> Option<CompletedMarker> {
let err = p
.err_builder("Expected an expression, but found none")
.primary(p.cur_tok().range, "Expected an expression here");
p.err_recover(err, p.state.expr_recovery_set, true, ERROR);
ParseRecoverer::with_error(p.state.expr_recovery_set, ERROR, err)
.enabled_braces_check()
.recover(p);
return None;
}
};
Expand All @@ -1068,7 +1072,9 @@ pub fn identifier_reference(p: &mut Parser) -> Option<CompletedMarker> {
.err_builder("Expected an identifier, but found none")
.primary(p.cur_tok().range, "");

p.err_recover(err, p.state.expr_recovery_set, true, ERROR);
ParseRecoverer::with_error(p.state.expr_recovery_set, ERROR, err)
.enabled_braces_check()
.recover(p);
None
}
}
Expand Down
5 changes: 3 additions & 2 deletions crates/rslint_parser/src/syntax/object.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::parse_recoverer::ParseRecoverer;
use crate::syntax::decl::{formal_param_pat, parameter_list, BASE_METHOD_RECOVERY_SET};
use crate::syntax::expr::{assign_expr, identifier_name, literal};
use crate::syntax::function::{function_body, ts_parameter_types, ts_return_type};
Expand Down Expand Up @@ -136,7 +137,7 @@ fn object_member(p: &mut Parser) -> Option<CompletedMarker> {
// test_err object_expr_non_ident_literal_prop
// let b = {5}

p.err_recover_no_err(token_set![T![:], T![,]], false, ERROR);
ParseRecoverer::new(token_set![T![:], T![,]], ERROR).recover(p);

if p.eat(T![:]) {
assign_expr(p);
Expand Down Expand Up @@ -277,7 +278,7 @@ fn method_object_member_body(p: &mut Parser) -> Result<(), ()> {
.err_builder("expected a method definition, but found none")
.primary(p.cur_tok().range, "");

p.err_recover(err, BASE_METHOD_RECOVERY_SET, false, ERROR);
ParseRecoverer::with_error(BASE_METHOD_RECOVERY_SET, ERROR, err).recover(p);
Err(())
};

Expand Down
16 changes: 8 additions & 8 deletions crates/rslint_parser/src/syntax/pat.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::expr::{assign_expr, identifier_name, identifier_reference, lhs_expr};
use crate::syntax::object::object_prop_name;
use crate::{SyntaxKind::*, *};
use crate::{parse_recoverer::ParseRecoverer, SyntaxKind::*, *};

pub fn pattern(p: &mut Parser, parameters: bool, assignment: bool) -> Option<CompletedMarker> {
Some(match p.cur() {
Expand Down Expand Up @@ -71,7 +71,7 @@ pub fn pattern(p: &mut Parser, parameters: bool, assignment: bool) -> Option<Com
if p.state.allow_object_expr {
ts = ts.union(token_set![T!['{']]);
}
p.err_recover(err, ts, false, ERROR);
ParseRecoverer::with_error(ts, ERROR, err).recover(p);
return None;
}
})
Expand Down Expand Up @@ -169,11 +169,11 @@ pub fn array_binding_pattern(
m.complete(p, REST_PATTERN);
break;
} else if binding_element(p, parameters, assignment).is_none() {
p.err_recover_no_err(
ParseRecoverer::new(
token_set![T![await], T![ident], T![yield], T![:], T![=], T![']']],
false,
ERROR,
);
)
.recover(p);
}
if !p.at(T![']']) {
p.expect(T![,]);
Expand Down Expand Up @@ -238,11 +238,11 @@ fn object_binding_prop(p: &mut Parser, parameters: bool) -> Option<CompletedMark
let name = if let Some(n) = name {
n
} else {
p.err_recover_no_err(
ParseRecoverer::new(
token_set![T![await], T![ident], T![yield], T![:], T![=], T!['}']],
false,
ERROR,
);
)
.recover(p);
return None;
};

Expand Down
Loading

0 comments on commit 15072f0

Please sign in to comment.