diff --git a/crates/rome_js_parser/src/state.rs b/crates/rome_js_parser/src/state.rs index 389885ad457..07839dca0f8 100644 --- a/crates/rome_js_parser/src/state.rs +++ b/crates/rome_js_parser/src/state.rs @@ -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, - - /// TODO - pub(crate) allow_conditional_type: bool, } #[derive(Debug, Clone, PartialEq, Eq)] @@ -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() { @@ -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() } @@ -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; @@ -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 @@ -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, diff --git a/crates/rome_js_parser/src/syntax/typescript/types.rs b/crates/rome_js_parser/src/syntax/typescript/types.rs index 3c3acaaa991..e2b3f467b37 100644 --- a/crates/rome_js_parser/src/syntax/typescript/types.rs +++ b/crates/rome_js_parser/src/syntax/typescript/types.rs @@ -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, @@ -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); @@ -214,7 +209,7 @@ fn parse_ts_type_impl(p: &mut JsParser) -> ParsedSyntax { // test ts ts_conditional_type_call_signature_lhs // type X = V extends (...args: any[]) => any ? (...args: Parameters) => 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; @@ -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 @@ -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)); } @@ -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 { @@ -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