Skip to content

Commit

Permalink
Use try parse for extends clause of infer (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
MichaReiser authored Dec 22, 2022
1 parent 1953d9c commit 76d7600
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 25 deletions.
35 changes: 30 additions & 5 deletions crates/rome_js_parser/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,6 @@ pub(crate) struct ParserState {
/// Stores the token positions of all syntax that looks like an arrow expressions but aren't one.
/// Optimization to reduce the back-tracking required when parsing parenthesized and arrow function expressions.
pub(crate) not_parenthesized_arrow: HashSet<TextSize>,

/// TODO
pub(crate) allow_conditional_type: bool,
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand All @@ -114,7 +111,6 @@ impl ParserState {
duplicate_binding_parent: None,
not_parenthesized_arrow: Default::default(),
speculative_parsing: false,
allow_conditional_type: true,
};

if source_type.module_kind().is_module() {
Expand Down Expand Up @@ -168,6 +164,12 @@ impl ParserState {
.contains(ParsingContextFlags::BREAK_ALLOWED)
}

pub fn allow_conditional_type(&self) -> bool {
!self
.parsing_context
.contains(ParsingContextFlags::DISALLOW_CONDITIONAL_TYPE)
}

pub fn strict(&self) -> Option<&StrictMode> {
self.strict.as_ref()
}
Expand Down Expand Up @@ -375,7 +377,7 @@ bitflags! {
/// snapshots each individual boolean field to allow restoring the previous state. With bitflags, all that
/// is needed is to copy away the flags field and restore it after.
#[derive(Default)]
pub(crate) struct ParsingContextFlags: u8 {
pub(crate) struct ParsingContextFlags: u16 {
/// Whether the parser is in a generator function like `function* a() {}`
/// Matches the `Yield` parameter in the ECMA spec
const IN_GENERATOR = 1 << 0;
Expand All @@ -401,6 +403,8 @@ bitflags! {
/// Whatever the parser is in a TypeScript ambient context
const AMBIENT_CONTEXT = 1 << 7;

const DISALLOW_CONDITIONAL_TYPE = 1 << 8;

const LOOP = Self::BREAK_ALLOWED.bits | Self::CONTINUE_ALLOWED.bits;

/// Bitmask of all the flags that must be reset (shouldn't be inherited) when the parser enters a function
Expand Down Expand Up @@ -584,6 +588,27 @@ impl ChangeParserStateFlags for EnterType {
}
}

pub(crate) struct EnterConditionalTypes(bool);

impl EnterConditionalTypes {
pub(crate) const fn allow() -> Self {
Self(true)
}
pub(crate) const fn disallow() -> Self {
Self(false)
}
}

impl ChangeParserStateFlags for EnterConditionalTypes {
fn compute_new_flags(&self, existing: ParsingContextFlags) -> ParsingContextFlags {
if self.0 {
existing - ParsingContextFlags::DISALLOW_CONDITIONAL_TYPE
} else {
existing | ParsingContextFlags::DISALLOW_CONDITIONAL_TYPE
}
}
}

#[derive(Default)]
pub(crate) struct EnterAmbientContextSnapshot {
flags: ParsingContextFlags,
Expand Down
62 changes: 42 additions & 20 deletions crates/rome_js_parser/src/syntax/typescript/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::parser::{RecoveryError, RecoveryResult};
use crate::prelude::*;
use crate::state::{EnterType, SignatureFlags};
use crate::state::{EnterConditionalTypes, EnterType, SignatureFlags};
use crate::syntax::expr::{
is_at_identifier, is_nth_at_identifier, is_nth_at_identifier_or_keyword,
parse_big_int_literal_expression, parse_identifier, parse_literal_expression, parse_name,
Expand Down Expand Up @@ -196,11 +196,6 @@ fn is_nth_at_ts_type_parameters(p: &mut JsParser, n: usize) -> bool {

#[inline(always)]
pub(crate) fn parse_ts_type(p: &mut JsParser) -> ParsedSyntax {
p.state_mut().allow_conditional_type = true;
parse_ts_type_impl(p)
}

fn parse_ts_type_impl(p: &mut JsParser) -> ParsedSyntax {
p.with_state(EnterType, |p| {
if is_at_constructor_type(p) {
return parse_ts_constructor_type(p);
Expand All @@ -214,7 +209,7 @@ fn parse_ts_type_impl(p: &mut JsParser) -> ParsedSyntax {

// test ts ts_conditional_type_call_signature_lhs
// type X<V> = V extends (...args: any[]) => any ? (...args: Parameters<V>) => void : Function;
if p.state().allow_conditional_type {
if p.state().allow_conditional_type() {
left.map(|left| {
// test ts ts_conditional_type
// type A = number;
Expand All @@ -226,12 +221,15 @@ fn parse_ts_type_impl(p: &mut JsParser) -> ParsedSyntax {
if !p.has_preceding_line_break() && p.at(T![extends]) {
let m = left.precede(p);
p.expect(T![extends]);
p.state_mut().allow_conditional_type = false;
parse_ts_type_impl(p).or_add_diagnostic(p, expected_ts_type);

p.with_state(EnterConditionalTypes::disallow(), parse_ts_type)
.or_add_diagnostic(p, expected_ts_type);
p.expect(T![?]);
parse_ts_type(p).or_add_diagnostic(p, expected_ts_type);
p.with_state(EnterConditionalTypes::allow(), parse_ts_type)
.or_add_diagnostic(p, expected_ts_type);
p.expect(T![:]);
parse_ts_type(p).or_add_diagnostic(p, expected_ts_type);
p.with_state(EnterConditionalTypes::allow(), parse_ts_type)
.or_add_diagnostic(p, expected_ts_type);
m.complete(p, TS_CONDITIONAL_TYPE)
} else {
left
Expand Down Expand Up @@ -354,9 +352,7 @@ fn parse_ts_primary_type(p: &mut JsParser) -> ParsedSyntax {
let m = p.start();
p.expect(T![infer]);
parse_ts_type_parameter_name(p).or_add_diagnostic(p, expected_identifier);
if !p.state().allow_conditional_type || !p.nth_at(2, T![?]) {
parse_ts_type_constraint_clause(p).ok();
}
try_parse_constraint_of_infer_type(p).ok();
return Present(m.complete(p, TS_INFER_TYPE));
}

Expand All @@ -373,7 +369,31 @@ fn parse_ts_primary_type(p: &mut JsParser) -> ParsedSyntax {
return Present(m.complete(p, TS_TYPE_OPERATOR_TYPE));
}

parse_postfix_type_or_higher(p)
p.with_state(EnterConditionalTypes::allow(), parse_postfix_type_or_higher)
}

fn try_parse_constraint_of_infer_type(p: &mut JsParser) -> ParsedSyntax {
if !p.at(T![extends]) {
return Absent;
}

try_parse(p, |p| {
let parsed = p
.with_state(
EnterConditionalTypes::disallow(),
parse_ts_type_constraint_clause,
)
.expect("Type constraint clause because parser is positioned at expect clause");

// Rewind if conditional types are allowed, and the parser is at the `?` token because
// this should instead be parsed as a conditional type.
if p.state.allow_conditional_type() && p.at(T![?]) {
Err(())
} else {
Ok(Present(parsed))
}
})
.unwrap_or(Absent)
}

fn parse_postfix_type_or_higher(p: &mut JsParser) -> ParsedSyntax {
Expand Down Expand Up @@ -1197,11 +1217,13 @@ fn parse_ts_return_type(p: &mut JsParser) -> ParsedSyntax {
p.at(T![asserts]) && (is_nth_at_identifier(p, 1) || p.nth_at(1, T![this]));
let is_is_predicate = (is_at_identifier(p) || p.at(T![this])) && p.nth_at(1, T![is]);

if !p.has_nth_preceding_line_break(1) && (is_asserts_predicate || is_is_predicate) {
parse_ts_type_predicate(p)
} else {
parse_ts_type(p)
}
p.with_state(EnterConditionalTypes::allow(), |p| {
if !p.has_nth_preceding_line_break(1) && (is_asserts_predicate || is_is_predicate) {
parse_ts_type_predicate(p)
} else {
parse_ts_type(p)
}
})
}

// test ts ts_type_predicate
Expand Down

0 comments on commit 76d7600

Please sign in to comment.