feat: parse lists
MODIFY
crates/rslint_parser/src/lib.rs
crates/rslint_parser/src/lib.rs
@@ -99,9 +99,9 @@ pub use rslint_syntax::*;
/// It also includes labels and possibly notes
pub type ParserError = rslint_errors::Diagnostic;
-use crate::parser::{ConditionalSyntax, ParsedSyntax};
-use crate::ConditionalSyntax::{Invalid, Valid};
-use crate::ParsedSyntax::{Absent, Present};
+pub use crate::parser::{ConditionalSyntax, ParseNodeList, ParseSeparatedList, ParsedSyntax};
+pub use crate::ConditionalSyntax::{Invalid, Valid};
+pub use crate::ParsedSyntax::{Absent, Present};
use rslint_errors::Diagnostic;
use std::ops::Range;
@@ -250,12 +250,12 @@ pub trait SyntaxFeature: Sized {
/// Creates a syntax that is only valid if this syntax feature is supported in the current
/// parsing context, adds a diagnostic if not.
///
- /// Returns [Present(Valid)] if this syntax feature is supported and the `syntax` is [Present]
+ /// Returns [Present(Valid)] if this syntax feature is supported and the `syntax` is [ParsedSyntax::Present]
///
/// Returns [Present(Invalid)], creates a diagnostic with the passed in error builder,
- /// and adds it to the parsing diagnostics if the syntax is [Present] and the `syntax` feature isn't supported
+ /// and adds it to the parsing diagnostics if the syntax is [ParsedSyntax::Present] and the `syntax` feature isn't supported
///
- /// Returns [Absent] if the `syntax` is [Absent]
+ /// Returns [ParsedSyntax::Absent] if the `syntax` is [ParsedSyntax::Absent]
fn exclusive_syntax<S, E>(
&self,
p: &mut Parser,
@@ -272,12 +272,12 @@ pub trait SyntaxFeature: Sized {
/// Parses a syntax that is only valid if this syntax feature is supported in the current parsing context
/// and ensures that any errors added while parsing the syntax are reverted if the syntax is not supported.
///
- /// Returns [Present(Valid)] if this syntax feature is supported and the `syntax` is [Present]
+ /// Returns [Present(Valid)] if this syntax feature is supported and the `syntax` is [ParsedSyntax::Present]
///
/// Returns [Present(Invalid)], creates a diagnostic with the passed in error builder,
- /// and adds it to the parsing diagnostics if the syntax is [Present] and the `syntax` feature isn't supported
+ /// and adds it to the parsing diagnostics if the syntax is [ParsedSyntax::Present] and the `syntax` feature isn't supported
///
- /// Returns [Absent] if the `syntax` is [Absent]
+ /// Returns [ParsedSyntax::Absent] if the `syntax` is [ParsedSyntax::Absent]
fn parse_exclusive_syntax<P, E>(
&self,
p: &mut Parser,
@@ -309,11 +309,11 @@ pub trait SyntaxFeature: Sized {
/// Creates a syntax that is only valid if this syntax feature is supported in the current
/// parsing context.
///
- /// Returns [Present(Valid)] if this syntax feature is supported and the `syntax` is [Present]
+ /// Returns [Present(Valid)] if this syntax feature is supported and the `syntax` is [ParsedSyntax::Present]
///
- /// Returns [Present(Invalid)] if the syntax is [Present] and the `syntax` feature isn't supported
+ /// Returns [Present(Invalid)] if the syntax is [ParsedSyntax::Present] and the `syntax` feature isn't supported
///
- /// Returns [Absent] if the `syntax` is [Absent]
+ /// Returns [ParsedSyntax::Absent] if the `syntax` is [ParsedSyntax::Absent]
fn exclusive_syntax_no_error<S>(&self, p: &Parser, syntax: S) -> ParsedSyntax<ConditionalSyntax>
where
S: Into<ParsedSyntax<CompletedMarker>>,
@@ -324,13 +324,13 @@ pub trait SyntaxFeature: Sized {
/// Creates a syntax that is only valid if the current parsing context doesn't support this syntax feature,
/// and adds a diagnostic if it does.
///
- /// Returns [Present(Valid)] if the parsing context doesn't support this syntax feature and `syntax` is [Present].
+ /// Returns [Present(Valid)] if the parsing context doesn't support this syntax feature and `syntax` is [ParsedSyntax::Present].
///
/// Returns [Present(Invalid)], calls the `error_builder` to create a diagnostic,
/// adds the diagnostic to the parsing diagnostics, and returns [Present(Invalid)]
- /// if the parsing context does support this syntax feature and the `syntax` is [Present].
+ /// if the parsing context does support this syntax feature and the `syntax` is [ParsedSyntax::Present].
///
- /// Returns [Absent] if the `syntax` is [Absent]
+ /// Returns [ParsedSyntax::Absent] if the `syntax` is [ParsedSyntax::Absent]
fn excluding_syntax<S, E>(
&self,
p: &mut Parser,
@@ -347,9 +347,9 @@ pub trait SyntaxFeature: Sized {
/// Creates a syntax that is only valid if this syntax feature isn't supported in the current
/// parsing context.
///
- /// * Returns [Valid(Present)] if this syntax feature isn't supported and the `syntax` is [Present]
- /// * Returns [Invalid(Present]) if this syntax feature is supported by the current parsing context and `syntax` is [Present]
- /// * Returns [Absent] if the `syntax` is [Absent].
+ /// * Returns [Valid(Present)] if this syntax feature isn't supported and the `syntax` is [ParsedSyntax::Present]
+ /// * Returns [Invalid(Present]) if this syntax feature is supported by the current parsing context and `syntax` is [ParsedSyntax::Present]
+ /// * Returns [ParsedSyntax::Absent] if the `syntax` is [ParsedSyntax::Absent].
fn excluding_syntax_no_error<S>(&self, p: &Parser, syntax: S) -> ParsedSyntax<ConditionalSyntax>
where
S: Into<ParsedSyntax<CompletedMarker>>,

MODIFY
crates/rslint_parser/src/parser.rs
crates/rslint_parser/src/parser.rs
//! These events are then applied to a `TreeSink`.
pub(crate) mod parse_error;
+mod parse_lists;
mod parse_recovery;
mod parsed_syntax;
pub(crate) mod single_token_parse_recovery;
@@ -15,6 +16,7 @@ use std::borrow::BorrowMut;
use std::ops::Range;
pub use parse_error::*;
+pub use parse_lists::{ParseNodeList, ParseSeparatedList};
pub use parsed_syntax::{ConditionalSyntax, InvalidSyntax, ParsedSyntax};
#[allow(deprecated)]
pub use single_token_parse_recovery::SingleTokenParseRecovery;

ADD
crates/rslint_parser/src/parser/parse_lists.rs
+///! A set of traits useful to parse various types of lists
+use super::{ParsedSyntax, ParserProgress, RecoveryResult};
+use crate::{Marker, Parser};
+use rslint_syntax::SyntaxKind;
+
+/// Use this trait to parse simple lists that don't have particular requirements.
+///
+/// In order to use this trait, you need to implement the [List] trait too.
+///
+/// ```rust,ignore
+/// use rslint_parser::{ParseSeparatedList};
+///
+/// struct MyList;
+///
+///
+/// impl ParseNormalList for MyList {
+/// // impl missing members
+/// }
+/// ```
+pub trait ParseNodeList {
+ /// The type returned when calling the function [Self::parse_element]
+ type ParsedElement;
+
+ /// Parses a single element of the list
+ fn parse_element(&mut self, p: &mut Parser) -> ParsedSyntax<Self::ParsedElement>;
+
+ /// It creates a marker just before starting a list
+ fn start_list(&mut self, p: &mut Parser) -> Marker {
+ p.start()
+ }
+
+ /// This method is used to check the current token inside the loop. When this method return [false],
+ /// the trait will exit from the loop.
+ ///
+ /// Usually here you want to check the current token.
+ fn is_at_list_end(&mut self, p: &mut Parser) -> bool;
+
+ /// This method is used to recover the parser in case [Self::parse_element] returns [ParsedSyntax::Absent]
+ fn recover(
+ &mut self,
+ p: &mut Parser,
+ parsed_element: ParsedSyntax<Self::ParsedElement>,
+ ) -> RecoveryResult;
+
+ /// It creates a [ParsedSyntax] that will contain the list
+ fn finish_list(&mut self, p: &mut Parser, m: Marker) {
+ m.complete(p, SyntaxKind::LIST);
+ }
+ /// Parses a simple list
+ ///
+ /// # Panics
+ ///
+ /// It panics if the parser doesn't advance at each cycle of the loop
+ fn parse_list(&mut self, p: &mut Parser) {
+ let elements = self.start_list(p);
+ let mut progress = ParserProgress::default();
+ while !p.at(SyntaxKind::EOF) && !self.is_at_list_end(p) {
+ progress.assert_progressing(p);
+
+ let parsed_element = self.parse_element(p);
+
+ if self.recover(p, parsed_element).is_err() {
+ break;
+ }
+ }
+ self.finish_list(p, elements);
+ }
+}
+
+/// A trait to parse lists that will be separated by a recurring element
+///
+/// In order to use this trait, you need to implement the [List] trait too.
+///
+/// ```rust,ignore
+/// use rslint_parser::{ParseSeparatedList};
+///
+/// struct MyList;
+///
+/// impl ParseSeparatedList for MyList {
+/// // impl missing members
+/// }
+/// ```
+pub trait ParseSeparatedList {
+ /// The type returned when calling the function [Self::parse_element]
+ type ParsedElement;
+
+ /// Parses a single element of the list
+ fn parse_element(&mut self, p: &mut Parser) -> ParsedSyntax<Self::ParsedElement>;
+
+ /// It creates a marker just before starting a list
+ fn start_list(&mut self, p: &mut Parser) -> Marker {
+ p.start()
+ }
+
+ /// This method is used to check the current token inside the loop. When this method return [false],
+ /// the trait will exit from the loop.
+ ///
+ /// Usually here you want to check the current token.
+ fn is_at_list_end(&mut self, p: &mut Parser) -> bool;
+
+ /// This method is used to recover the parser in case [Self::parse_element] returns [ParsedSyntax::Absent]
+ fn recover(
+ &mut self,
+ p: &mut Parser,
+ parsed_element: ParsedSyntax<Self::ParsedElement>,
+ ) -> RecoveryResult;
+
+ /// It creates a [ParsedSyntax] that will contain the list
+ fn finish_list(&mut self, p: &mut Parser, m: Marker) {
+ m.complete(p, SyntaxKind::LIST);
+ }
+ /// The [SyntaxKind] of the element that separates the elements of the list
+ fn separating_element_kind(&mut self) -> SyntaxKind;
+
+ /// Method called at each iteration of the the loop and checks if the expected
+ /// separator is present.
+ ///
+ /// If present, it [parses](Self::parse_separating_element) it and continues with loop.
+ /// If not present, it adds a missing marker.
+ fn expect_separator(&mut self, p: &mut Parser) -> bool {
+ p.expect_required(self.separating_element_kind())
+ }
+
+ /// Parses a list of elements separated by a recurring element
+ ///
+ /// # Panics
+ ///
+ /// It panics if the parser doesn't advance at each cycle of the loop
+ fn parse_list(&mut self, p: &mut Parser) {
+ let elements = self.start_list(p);
+ let mut progress = ParserProgress::default();
+ while !p.at(SyntaxKind::EOF) && !self.is_at_list_end(p) {
+ progress.assert_progressing(p);
+
+ if self.expect_separator(p) && self.is_at_list_end(p) {
+ break;
+ }
+
+ let parsed_element = self.parse_element(p);
+
+ if self.recover(p, parsed_element).is_err() {
+ break;
+ }
+ }
+ self.finish_list(p, elements);
+ }
+}

MODIFY
crates/rslint_parser/src/parser/parsed_syntax.rs
crates/rslint_parser/src/parser/parsed_syntax.rs
@@ -11,30 +11,30 @@ use std::ops::Range;
///
/// This type is commonly used as the return type of a parse function with the following types for `T`
///
-/// * [CompletedMarker]: Most commonly used type. Parse function that either returns [Absent]
-/// if the syntax isn't present or [Present] with a valid syntax.
+/// * [CompletedMarker]: Most commonly used type. Parse function that either returns [ParsedSyntax::Absent]
+/// if the syntax isn't present or [ParsedSyntax::Present] with a valid syntax.
/// * [ConditionalSyntax]: Used for parse functions where the parsed syntax may be invalid (a syntax error)
/// depending on the parse context. Examples are: 1) Use of a `with` statement in strict mode,
/// 2) use of `await` as an identifier inside of an `async` function, and so on. That's why these
-/// parse functions must differentiate between syntax that is [Absent] in the source text,
-/// syntax that is [Present] and [Valid], and syntax that is [Present] but [Invalid].
+/// parse functions must differentiate between syntax that is [ParsedSyntax::Absent] in the source text,
+/// syntax that is [ParsedSyntax::Present] and [Valid], and syntax that is [ParsedSyntax::Present] but [Invalid].
///
///
/// ## Parse Rule conventions
-/// * A parse rule must return [Present] if it is able to parse a node or at least parts of it. For example,
-/// the `parse_for_statement` should return [Present] for `for (` even tough many of the required children are missing
+/// * A parse rule must return [ParsedSyntax::Present] if it is able to parse a node or at least parts of it. For example,
+/// the `parse_for_statement` should return [ParsedSyntax::Present] for `for (` even tough many of the required children are missing
/// because it is still able to parse parts of the for statement.
-/// * A parse rule must return [Absent] if the expected node isn't present in the source code.
+/// * A parse rule must return [ParsedSyntax::Absent] if the expected node isn't present in the source code.
/// In most cases, this means if the first expected token isn't present, for example,
/// if the `for` keyword isn't present when parsing a for statement.
/// However, it can be possible for rules to recover even if the first token doesn't match. One example
/// is when parsing an assignment target that has an optional default. The rule can recover even
/// if the assignment target is missing as long as the cursor is then positioned at an `=` token.
-/// The rule must then return [Present] with the partial parsed node.
-/// * A parse rule must not eat any tokens when it returns [Absent]
-/// * A parse rule must not add any errors when it returns [Absent]
+/// The rule must then return [ParsedSyntax::Present] with the partial parsed node.
+/// * A parse rule must not eat any tokens when it returns [ParsedSyntax::Absent]
+/// * A parse rule must not add any errors when it returns [ParsedSyntax::Absent]
///
-/// This is a custom enum over using `Option` because [Absent] values must be handled by the caller.
+/// This is a custom enum over using `Option` because [ParsedSyntax::Absent] values must be handled by the caller.
#[derive(Debug, Clone, PartialEq, Eq)]
#[must_use = "this `ParsedSyntax` may be an `Absent` variant, which should be handled"]
pub enum ParsedSyntax<T> {
@@ -57,7 +57,7 @@ impl<T> ParsedSyntax<T> {
}
}
- /// Calls `op` if the syntax is present and otherwise returns [Absent]
+ /// Calls `op` if the syntax is present and otherwise returns [ParsedSyntax::Absent]
pub fn and_then<F>(self, op: F) -> ParsedSyntax<T>
where
F: FnOnce(T) -> T,
@@ -68,19 +68,19 @@ impl<T> ParsedSyntax<T> {
}
}
- /// Returns `true` if the parsed syntax is [Present]
+ /// Returns `true` if the parsed syntax is [ParsedSyntax::Present]
#[must_use]
pub fn is_present(&self) -> bool {
matches!(self, Present(_))
}
- /// Returns `true` if the parsed syntax is [Absent]
+ /// Returns `true` if the parsed syntax is [ParsedSyntax::Absent]
#[must_use]
pub fn is_absent(&self) -> bool {
matches!(self, Absent)
}
- /// It returns the contained [Present] value, consuming the `self` value
+ /// It returns the contained [ParsedSyntax::Present] value, consuming the `self` value
///
/// # Panics
///
@@ -94,8 +94,8 @@ impl<T> ParsedSyntax<T> {
}
}
- /// Maps a `ParsedSyntax<T>` to `ParsedSyntax<U>` by applying a function to a contained [Present] value,
- /// leaving an [Absent] value untouched.
+ /// Maps a `ParsedSyntax<T>` to `ParsedSyntax<U>` by applying a function to a contained [ParsedSyntax::Present] value,
+ /// leaving an [ParsedSyntax::Absent] value untouched.
///
/// This function can be used to compose the results of two functions.
pub fn map<F, U>(self, mapper: F) -> ParsedSyntax<U>
@@ -434,7 +434,7 @@ impl ConditionalSyntax {
///
/// # Panics
///
- /// Panics if the current syntax is [ConditionalParsedSyntax::Invalid]
+ /// Panics if the current syntax is [ConditionalSyntax::Invalid]
pub fn unwrap(self) -> CompletedMarker {
if let Valid(syntax) = self {
syntax

MODIFY
crates/rslint_parser/src/syntax/js_parse_error.rs
crates/rslint_parser/src/syntax/js_parse_error.rs
@@ -74,6 +74,10 @@ pub(crate) fn expected_case_or_default(p: &Parser, range: Range<usize>) -> Diagn
expected_any(&["default", "case"], range).to_diagnostic(p)
}
+pub(crate) fn expected_case(p: &Parser, range: Range<usize>) -> Diagnostic {
+ expected_node("case", range).to_diagnostic(p)
+}
+
pub(crate) fn expected_assignment_target(p: &Parser, range: Range<usize>) -> Diagnostic {
expected_any(&["identifier", "assignment target"], range).to_diagnostic(p)
}

MODIFY
crates/rslint_parser/src/syntax/stmt.rs
crates/rslint_parser/src/syntax/stmt.rs
@@ -8,7 +8,7 @@ use super::program::{export_decl, import_decl};
use super::typescript::*;
use super::util::{check_for_stmt_declaration, check_label_use};
#[allow(deprecated)]
-use crate::parser::{ParsedSyntax, ParserProgress};
+use crate::parser::{ParseNodeList, ParsedSyntax, ParserProgress};
use crate::syntax::assignment::{expression_to_assignment_pattern, AssignmentExprPrecedence};
use crate::syntax::class::{parse_class_declaration, parse_equal_value_clause};
use crate::syntax::expr::parse_identifier;
@@ -1054,6 +1054,35 @@ pub fn parse_for_statement(p: &mut Parser) -> ParsedSyntax<CompletedMarker> {
Present(m.complete(p, kind))
}
+struct SwitchClausesList;
+
+impl ParseNodeList for SwitchClausesList {
+ type ParsedElement = CompletedMarker;
+
+ fn parse_element(&mut self, p: &mut Parser) -> ParsedSyntax<CompletedMarker> {
+ match parse_statement(p, None) {
+ Some(stmt) => Present(stmt),
+ None => Absent,
+ }
+ }
+
+ fn is_at_list_end(&mut self, p: &mut Parser) -> bool {
+ p.at_ts(token_set![T![default], T![case], T!['}']])
+ }
+
+ fn recover(
+ &mut self,
+ p: &mut Parser,
+ parsed_element: ParsedSyntax<Self::ParsedElement>,
+ ) -> parser::RecoveryResult {
+ parsed_element.or_recover(
+ p,
+ &ParseRecovery::new(JS_UNKNOWN_STATEMENT, STMT_RECOVERY_SET),
+ js_parse_error::expected_case,
+ )
+ }
+}
+
// We return the range in case its a default clause so we can report multiple default clauses in a better way
fn parse_switch_clause(
p: &mut Parser,
@@ -1105,14 +1134,9 @@ fn parse_switch_clause(
p.bump_any();
expr(p);
p.expect_required(T![:]);
- let cons_list = p.start();
- let mut progress = ParserProgress::default();
- while !p.at_ts(token_set![T![default], T![case], T!['}'], EOF]) {
- progress.assert_progressing(p);
- parse_statement(p, None);
- }
- cons_list.complete(p, LIST);
+ SwitchClausesList.parse_list(p);
+
Present(m.complete(p, JS_CASE_CLAUSE))
}
_ => {

feat: parse lists (#1853)