Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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