diff --git a/Cargo.toml b/Cargo.toml index 23c368ed1..1d5f6736c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -185,7 +185,7 @@ criterion2 = { version = "0.11.0", default-features = false } daachorse = { version = "1.0.0" } [workspace.metadata.cargo-shear] -ignored = ["napi", "oxc_traverse", "oxc_ast_codegen", "prettyplease"] +ignored = ["napi", "oxc_ast_codegen", "oxc_traverse", "prettyplease"] [profile.dev] # Disabling debug info speeds up local and CI builds, diff --git a/crates/oxc_ast/src/ast/macros.rs b/crates/oxc_ast/src/ast/macros.rs index 698b1a2db..b5ea9b761 100644 --- a/crates/oxc_ast/src/ast/macros.rs +++ b/crates/oxc_ast/src/ast/macros.rs @@ -621,7 +621,9 @@ macro_rules! inherit_variants { /// Inherited from [`TSType`] JSDocNullableType(Box<'a, JSDocNullableType<'a>>) = 34, /// Inherited from [`TSType`] - JSDocUnknownType(Box<'a, JSDocUnknownType>) = 35, + JSDocNonNullableType(Box<'a, JSDocNonNullableType<'a>>) = 35, + /// Inherited from [`TSType`] + JSDocUnknownType(Box<'a, JSDocUnknownType>) = 36, $($rest)* } @@ -671,6 +673,7 @@ macro_rules! inherit_variants { TSTypeReference, TSUnionType, JSDocNullableType, + JSDocNonNullableType, JSDocUnknownType, ] ); diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 7fd5962e8..d5a9c47f9 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -174,7 +174,8 @@ pub enum TSType<'a> { TSUnionType(Box<'a, TSUnionType<'a>>) = 33, // JSDoc JSDocNullableType(Box<'a, JSDocNullableType<'a>>) = 34, - JSDocUnknownType(Box<'a, JSDocUnknownType>) = 35, + JSDocNonNullableType(Box<'a, JSDocNonNullableType<'a>>) = 35, + JSDocUnknownType(Box<'a, JSDocUnknownType>) = 36, } /// Macro for matching `TSType`'s variants. @@ -216,6 +217,7 @@ macro_rules! match_ts_type { | $ty::TSTypeReference(_) | $ty::TSUnionType(_) | $ty::JSDocNullableType(_) + | $ty::JSDocNonNullableType(_) | $ty::JSDocUnknownType(_) }; } @@ -333,7 +335,7 @@ pub struct TSTupleType<'a> { pub struct TSNamedTupleMember<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, - pub element_type: TSType<'a>, + pub element_type: TSTupleElement<'a>, pub label: IdentifierName<'a>, pub optional: bool, } @@ -909,7 +911,8 @@ pub enum TSTypeQueryExprName<'a> { pub struct TSImportType<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, - pub argument: TSType<'a>, + pub is_type_of: bool, // `typeof import("foo")` + pub parameter: TSType<'a>, pub qualifier: Option>, pub attributes: Option>, pub type_parameters: Option>>, @@ -1160,6 +1163,18 @@ pub struct JSDocNullableType<'a> { pub postfix: bool, } +/// `type foo = ty!` or `type foo = !ty` +#[visited_node] +#[derive(Debug, Hash)] +#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] +#[cfg_attr(feature = "serialize", serde(tag = "type", rename_all = "camelCase"))] +pub struct JSDocNonNullableType<'a> { + #[cfg_attr(feature = "serialize", serde(flatten))] + pub span: Span, + pub type_annotation: TSType<'a>, + pub postfix: bool, +} + #[visited_node] #[derive(Debug, Hash)] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index 90b3dc781..bd7d65bd1 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -1915,7 +1915,7 @@ impl<'a> AstBuilder<'a> { } #[inline] - pub fn ts_this_keyword(self, span: Span) -> TSType<'a> { + pub fn ts_this_type(self, span: Span) -> TSType<'a> { TSType::TSThisType(self.alloc(TSThisType { span })) } @@ -2041,14 +2041,16 @@ impl<'a> AstBuilder<'a> { pub fn ts_import_type( self, span: Span, - argument: TSType<'a>, + is_type_of: bool, + parameter: TSType<'a>, qualifier: Option>, attributes: Option>, type_parameters: Option>>, ) -> TSType<'a> { TSType::TSImportType(self.alloc(TSImportType { span, - argument, + is_type_of, + parameter, qualifier, attributes, type_parameters, @@ -2195,6 +2197,20 @@ impl<'a> AstBuilder<'a> { TSType::JSDocNullableType(self.alloc(JSDocNullableType { span, type_annotation, postfix })) } + #[inline] + pub fn js_doc_non_nullable_type( + self, + span: Span, + type_annotation: TSType<'a>, + postfix: bool, + ) -> TSType<'a> { + TSType::JSDocNonNullableType(self.alloc(JSDocNonNullableType { + span, + type_annotation, + postfix, + })) + } + #[inline] pub fn js_doc_unknown_type(self, span: Span) -> TSType<'a> { TSType::JSDocUnknownType(self.alloc(JSDocUnknownType { span })) diff --git a/crates/oxc_ast/src/generated/span.rs b/crates/oxc_ast/src/generated/span.rs index 60f3e3ff4..3e7e5eccf 100644 --- a/crates/oxc_ast/src/generated/span.rs +++ b/crates/oxc_ast/src/generated/span.rs @@ -1385,6 +1385,7 @@ impl<'a> GetSpan for TSType<'a> { Self::TSTypeReference(it) => it.span(), Self::TSUnionType(it) => it.span(), Self::JSDocNullableType(it) => it.span(), + Self::JSDocNonNullableType(it) => it.span(), Self::JSDocUnknownType(it) => it.span(), } } @@ -1500,6 +1501,7 @@ impl<'a> GetSpan for TSTupleElement<'a> { Self::TSTypeReference(it) => it.span(), Self::TSUnionType(it) => it.span(), Self::JSDocNullableType(it) => it.span(), + Self::JSDocNonNullableType(it) => it.span(), Self::JSDocUnknownType(it) => it.span(), } } @@ -1960,6 +1962,13 @@ impl<'a> GetSpan for JSDocNullableType<'a> { } } +impl<'a> GetSpan for JSDocNonNullableType<'a> { + #[inline] + fn span(&self) -> Span { + self.span + } +} + impl GetSpan for JSDocUnknownType { #[inline] fn span(&self) -> Span { diff --git a/crates/oxc_ast/src/visit/visit.rs b/crates/oxc_ast/src/visit/visit.rs index 532357f73..7642617f1 100644 --- a/crates/oxc_ast/src/visit/visit.rs +++ b/crates/oxc_ast/src/visit/visit.rs @@ -2816,7 +2816,9 @@ pub mod walk { TSType::TSTypeReference(ty) => visitor.visit_ts_type_reference(ty), TSType::TSUnionType(ty) => visitor.visit_ts_union_type(ty), // JSDoc - TSType::JSDocNullableType(_) | TSType::JSDocUnknownType(_) => { /* TODO */ } + TSType::JSDocNullableType(_) + | TSType::JSDocNonNullableType(_) + | TSType::JSDocUnknownType(_) => { /* TODO */ } } } @@ -3124,7 +3126,7 @@ pub mod walk { pub fn walk_ts_import_type<'a, V: Visit<'a>>(visitor: &mut V, ty: &TSImportType<'a>) { let kind = AstKind::TSImportType(visitor.alloc(ty)); visitor.enter_node(kind); - visitor.visit_ts_type(&ty.argument); + visitor.visit_ts_type(&ty.parameter); if let Some(name) = &ty.qualifier { visitor.visit_ts_type_name(name); } @@ -3244,7 +3246,7 @@ pub mod walk { let kind = AstKind::TSNamedTupleMember(visitor.alloc(ty)); visitor.enter_node(kind); visitor.visit_identifier_name(&ty.label); - visitor.visit_ts_type(&ty.element_type); + visitor.visit_ts_tuple_element(&ty.element_type); visitor.leave_node(kind); } diff --git a/crates/oxc_ast/src/visit/visit_mut.rs b/crates/oxc_ast/src/visit/visit_mut.rs index 507af2405..de7e72730 100644 --- a/crates/oxc_ast/src/visit/visit_mut.rs +++ b/crates/oxc_ast/src/visit/visit_mut.rs @@ -3202,7 +3202,7 @@ pub mod walk_mut { ) { let kind = AstType::TSImportType; visitor.enter_node(kind); - visitor.visit_ts_type(&mut ty.argument); + visitor.visit_ts_type(&mut ty.parameter); if let Some(name) = &mut ty.qualifier { visitor.visit_ts_type_name(name); } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 2730be555..c0b4d4dd3 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -2697,6 +2697,7 @@ impl<'a, const MINIFY: bool> Gen for TSType<'a> { Self::TSTypeQuery(ty) => ty.gen(p, ctx), Self::TSTypeReference(ty) => ty.gen(p, ctx), Self::JSDocNullableType(ty) => ty.gen(p, ctx), + Self::JSDocNonNullableType(ty) => ty.gen(p, ctx), Self::JSDocUnknownType(_ty) => p.print_str(b"unknown"), } } @@ -2904,6 +2905,18 @@ impl<'a, const MINIFY: bool> Gen for JSDocNullableType<'a> { } } +impl<'a, const MINIFY: bool> Gen for JSDocNonNullableType<'a> { + fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { + if self.postfix { + self.type_annotation.gen(p, ctx); + p.print_str(b"!"); + } else { + p.print_str(b"!"); + self.type_annotation.gen(p, ctx); + } + } +} + impl<'a, const MINIFY: bool> Gen for TSTemplateLiteralType<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { p.print_str(b"`"); @@ -3145,8 +3158,11 @@ impl<'a, const MINIFY: bool> Gen for TSTypeQueryExprName<'a> { impl<'a, const MINIFY: bool> Gen for TSImportType<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { + if self.is_type_of { + p.print_str(b"typeof "); + } p.print_str(b"import("); - self.argument.gen(p, ctx); + self.parameter.gen(p, ctx); if let Some(attributes) = &self.attributes { p.print_str(", "); attributes.gen(p, ctx); diff --git a/crates/oxc_parser/src/js/arrow.rs b/crates/oxc_parser/src/js/arrow.rs index 299876614..66db265cc 100644 --- a/crates/oxc_parser/src/js/arrow.rs +++ b/crates/oxc_parser/src/js/arrow.rs @@ -258,7 +258,7 @@ impl<'a> ParserImpl<'a> { self.error(diagnostics::ts_arrow_function_this_parameter(this_param.span)); } - let return_type = self.parse_ts_return_type_annotation()?; + let return_type = self.parse_ts_return_type_annotation(Kind::Arrow, false)?; self.ctx = self.ctx.and_await(has_await); diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index e50fa89ee..ec3ccb72e 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -414,7 +414,10 @@ impl<'a> ParserImpl<'a> { Ok(TemplateLiteral { span: self.end_span(span), quasis, expressions }) } - fn parse_template_literal_expression(&mut self, tagged: bool) -> Result> { + pub(crate) fn parse_template_literal_expression( + &mut self, + tagged: bool, + ) -> Result> { self.parse_template_literal(tagged) .map(|template_literal| self.ast.template_literal_expression(template_literal)) } diff --git a/crates/oxc_parser/src/js/function.rs b/crates/oxc_parser/src/js/function.rs index 8a268f97a..1f549fb01 100644 --- a/crates/oxc_parser/src/js/function.rs +++ b/crates/oxc_parser/src/js/function.rs @@ -73,7 +73,8 @@ impl<'a> ParserImpl<'a> { let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::FormalParameter)?; - let return_type = self.parse_ts_return_type_annotation()?; + let return_type = + self.parse_ts_return_type_annotation(Kind::Colon, /* is_type */ true)?; let body = if self.at(Kind::LCurly) { Some(self.parse_function_body()?) } else { None }; diff --git a/crates/oxc_parser/src/lexer/kind.rs b/crates/oxc_parser/src/lexer/kind.rs index f043985ac..3b2c1eeb5 100644 --- a/crates/oxc_parser/src/lexer/kind.rs +++ b/crates/oxc_parser/src/lexer/kind.rs @@ -76,7 +76,7 @@ pub enum Kind { Out, Readonly, Require, - Number, + Number, // the "number" keyword for TypeScript Object, Satisfies, String, // the "string" keyword for TypeScript @@ -87,7 +87,7 @@ pub enum Kind { Using, Unknown, Global, - BigInt, + BigInt, // the "bigint" keyword for TypeScript Override, // Future keywords (strict mode reserved words) Implements, @@ -104,7 +104,7 @@ pub enum Kind { Amp2, Amp2Eq, AmpEq, - Bang, + Bang, // ! Caret, CaretEq, Colon, @@ -367,6 +367,10 @@ impl Kind { | Export | In | Out | Public | Private | Protected | Readonly | Static | Override) } + pub fn is_binding_identifier_or_private_identifier_or_pattern(self) -> bool { + matches!(self, LCurly | LBrack | PrivateIdentifier) || self.is_binding_identifier() + } + pub fn match_keyword(s: &str) -> Self { let len = s.len(); if len <= 1 || len >= 12 || !s.as_bytes()[0].is_ascii_lowercase() { diff --git a/crates/oxc_parser/src/ts/list.rs b/crates/oxc_parser/src/ts/list.rs index a0dfd8bb2..fae36a904 100644 --- a/crates/oxc_parser/src/ts/list.rs +++ b/crates/oxc_parser/src/ts/list.rs @@ -50,54 +50,8 @@ impl<'a> SeparatedList<'a> for TSTupleElementList<'a> { } fn parse_element(&mut self, p: &mut ParserImpl<'a>) -> Result<()> { - let span = p.start_span(); - if p.is_at_named_tuple_element() { - if p.eat(Kind::Dot3) { - let member_span = p.start_span(); - let label = p.parse_identifier_name()?; - p.expect(Kind::Colon)?; - let element_type = p.parse_ts_type()?; - self.elements.push(TSTupleElement::TSRestType(p.ast.alloc(TSRestType { - span: p.end_span(span), - type_annotation: TSType::TSNamedTupleMember(p.ast.alloc(TSNamedTupleMember { - span: p.end_span(member_span), - element_type, - label, - optional: false, // A tuple member cannot be both optional and rest. (TS5085) - })), - }))); - return Ok(()); - } - - let label = p.parse_identifier_name()?; - let optional = p.eat(Kind::Question); - p.expect(Kind::Colon)?; - - let element_type = p.parse_ts_type()?; - self.elements.push(TSTupleElement::TSNamedTupleMember(p.ast.alloc( - TSNamedTupleMember { span: p.end_span(span), element_type, label, optional }, - ))); - - return Ok(()); - } - - if p.eat(Kind::Dot3) { - let type_annotation = p.parse_ts_type()?; - self.elements.push(TSTupleElement::TSRestType( - p.ast.alloc(TSRestType { span: p.end_span(span), type_annotation }), - )); - return Ok(()); - } - - let type_annotation = p.parse_ts_type()?; - if p.eat(Kind::Question) { - self.elements.push(TSTupleElement::TSOptionalType( - p.ast.alloc(TSOptionalType { span: p.end_span(span), type_annotation }), - )); - } else { - self.elements.push(TSTupleElement::from(type_annotation)); - } - + let ty = p.parse_tuple_element_name_or_tuple_element_type()?; + self.elements.push(ty); Ok(()) } } diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index dc2a39ee2..9b740c35c 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -1,7 +1,7 @@ use oxc_allocator::{Box, Vec}; use oxc_ast::ast::*; use oxc_diagnostics::Result; -use oxc_span::Span; +use oxc_span::GetSpan; use oxc_syntax::operator::UnaryOperator; use super::list::{ @@ -21,9 +21,38 @@ impl<'a> ParserImpl<'a> { if self.is_start_of_function_type_or_constructor_type() { return self.parse_function_or_constructor_type(); } - let left_span = self.start_span(); - let left = self.parse_ts_union_type()?; - self.parse_ts_conditional_type(left_span, left) + let span = self.start_span(); + let ty = self.parse_union_type_or_higher()?; + if !self.ctx.has_disallow_conditional_types() + && !self.cur_token().is_on_new_line + && self.eat(Kind::Extends) + { + let extends_type = self.context( + Context::DisallowConditionalTypes, + Context::empty(), + Self::parse_ts_type, + )?; + self.expect(Kind::Question)?; + let true_type = self.context( + Context::empty(), + Context::DisallowConditionalTypes, + Self::parse_ts_type, + )?; + self.expect(Kind::Colon)?; + let false_type = self.context( + Context::empty(), + Context::DisallowConditionalTypes, + Self::parse_ts_type, + )?; + return Ok(self.ast.ts_conditional_type( + self.end_span(span), + ty, + extends_type, + true_type, + false_type, + )); + } + Ok(ty) } fn parse_function_or_constructor_type(&mut self) -> Result> { @@ -32,10 +61,13 @@ impl<'a> ParserImpl<'a> { let is_constructor_type = self.eat(Kind::New); let type_parameters = self.parse_ts_type_parameters()?; let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?; - self.expect(Kind::Arrow)?; let return_type = { let return_type_span = self.start_span(); - let return_type = self.parse_ts_return_type()?; + let Some(return_type) = + self.parse_return_type(Kind::Arrow, /* is_type */ false)? + else { + return Err(self.unexpected()); + }; self.ast.ts_type_annotation(self.end_span(return_type_span), return_type) }; @@ -175,196 +207,174 @@ impl<'a> ParserImpl<'a> { )) } - fn parse_ts_type_constraint(&mut self) -> Result>> { - if !self.at(Kind::Extends) { - return Ok(None); - } - self.bump_any(); - Ok(Some(self.parse_ts_type()?)) + fn parse_intersection_type_or_higher(&mut self) -> Result> { + self.parse_union_type_or_intersection_type(Kind::Amp, Self::parse_type_operator_or_higher) } - fn parse_ts_default_type(&mut self) -> Result>> { - if !self.at(Kind::Eq) { - return Ok(None); - } - self.bump_any(); - Ok(Some(self.parse_ts_type()?)) + fn parse_union_type_or_higher(&mut self) -> Result> { + self.parse_union_type_or_intersection_type( + Kind::Pipe, + Self::parse_intersection_type_or_higher, + ) } - fn parse_ts_conditional_type( + fn parse_union_type_or_intersection_type( &mut self, - left_span: Span, - left: TSType<'a>, - ) -> Result> { - if !self.ctx.has_disallow_conditional_types() - && !self.cur_token().is_on_new_line - && self.eat(Kind::Extends) - { - let extends_type = self.context( - Context::DisallowConditionalTypes, - Context::empty(), - Self::parse_ts_type, - )?; - - self.expect(Kind::Question)?; - - let true_type = self.context( - Context::empty(), - Context::DisallowConditionalTypes, - Self::parse_ts_type, - )?; - - self.expect(Kind::Colon)?; - - let false_type = self.context( - Context::empty(), - Context::DisallowConditionalTypes, - Self::parse_ts_type, - )?; - - return Ok(self.ast.ts_conditional_type( - self.end_span(left_span), - left, - extends_type, - true_type, - false_type, - )); - } - - Ok(left) - } - - // test ts ts_union_type - // type A = string | number; - // type B = | A | void | null; - // type C = A & C | C; - fn parse_ts_union_type(&mut self) -> Result> { + kind: Kind, + parse_constituent_type: F, + ) -> Result> + where + F: Fn(&mut Self) -> Result>, + { let span = self.start_span(); - if self.at(Kind::Pipe) { - let mut types = self.ast.new_vec(); - while self.eat(Kind::Pipe) { - types.push(self.parse_ts_intersection_type()?); - } - Ok(self.ast.ts_union_type(self.end_span(span), types)) - } else { - let first = self.parse_ts_intersection_type()?; - if self.at(Kind::Pipe) { - let mut types = self.ast.new_vec(); - types.push(first); - while self.eat(Kind::Pipe) { - types.push(self.parse_ts_intersection_type()?); - } - Ok(self.ast.ts_union_type(self.end_span(span), types)) - } else { - Ok(first) + // let is_union_type = kind == Kind::Pipe; + let has_leading_operator = self.eat(kind); + /* hasLeadingOperator && parseFunctionOrConstructorTypeToError(isUnionType) ||*/ + let mut ty = parse_constituent_type(self)?; + if self.at(kind) || has_leading_operator { + let mut types = self.ast.new_vec_single(ty); + while self.eat(kind) { + types.push( + /*parseFunctionOrConstructorTypeToError(isUnionType) || */ + parse_constituent_type(self)?, + ); } + let span = self.end_span(span); + ty = match kind { + Kind::Pipe => self.ast.ts_union_type(span, types), + Kind::Amp => self.ast.ts_intersection_type(span, types), + _ => unreachable!(), + }; } + Ok(ty) } - // test ts ts_intersection_type - // type A = string & number; - // type B = & A & void & null; - fn parse_ts_intersection_type(&mut self) -> Result> { - let span = self.start_span(); - if self.at(Kind::Amp) { - let mut types = self.ast.new_vec(); - while self.eat(Kind::Amp) { - types.push(self.parse_ts_primary_type()?); - } - Ok(self.ast.ts_intersection_type(self.end_span(span), types)) - } else { - let first = self.parse_ts_primary_type()?; - if self.at(Kind::Amp) { - let mut types = self.ast.new_vec(); - types.push(first); - while self.eat(Kind::Amp) { - types.push(self.parse_ts_primary_type()?); - } - Ok(self.ast.ts_intersection_type(self.end_span(span), types)) - } else { - Ok(first) - } - } - } - - fn parse_ts_primary_type(&mut self) -> Result> { - let span = self.start_span(); - if self.at(Kind::Infer) { - return self.parse_ts_infer_type(); - } - - let operator = match self.cur_kind() { - Kind::KeyOf => Some(TSTypeOperatorOperator::Keyof), - Kind::Unique => Some(TSTypeOperatorOperator::Unique), - Kind::Readonly => Some(TSTypeOperatorOperator::Readonly), - _ => None, - }; - - // test ts ts_type_operator - // type B = keyof A; - // type C = readonly string[]; - // const d: unique symbol = Symbol(); - if let Some(operator) = operator { - self.bump_any(); // bump operator - let type_annotation = self.parse_ts_primary_type()?; - return Ok(self.ast.ts_type_operator_type( - self.end_span(span), - operator, - type_annotation, - )); - } - - let mut left = self.context( - Context::empty(), - Context::DisallowConditionalTypes, - ParserImpl::parse_ts_basic_type, - )?; - - while !self.cur_token().is_on_new_line && self.eat(Kind::LBrack) { - if self.eat(Kind::RBrack) { - // test ts ts_array_type - // type A = string[]; - // type B = { a: number } []; - left = self.ast.ts_array_type(self.end_span(span), left); - } else { - // test ts ts_indexed_access_type - // type A = string[number]; - // type B = string[number][number][number][]; - let index_type = self.parse_ts_type()?; - self.expect(Kind::RBrack)?; - left = self.ast.ts_indexed_access_type(self.end_span(span), left, index_type); - } - } - - Ok(left) - } - - // test ts ts_predefined_type - // type A = any - // type B = number; - // type C = object; - // type D = boolean; - // type E = bigint; - // type F = string; - // type G = symbol; - // type H = void; - // type I = undefined; - // type J = null; - // type K = never - fn parse_ts_basic_type(&mut self) -> Result> { + fn parse_type_operator_or_higher(&mut self) -> Result> { match self.cur_kind() { - Kind::LParen => { - self.bump_any(); - let result = self.parse_ts_type(); - self.expect(Kind::RParen)?; - result + Kind::KeyOf => self.parse_type_operator(TSTypeOperatorOperator::Keyof), + Kind::Unique => self.parse_type_operator(TSTypeOperatorOperator::Unique), + Kind::Readonly => self.parse_type_operator(TSTypeOperatorOperator::Readonly), + Kind::Infer => self.parse_infer_type(), + _ => self.context( + Context::empty(), + Context::DisallowConditionalTypes, + Self::parse_postfix_type_or_higher, + ), + } + } + + fn parse_type_operator(&mut self, operator: TSTypeOperatorOperator) -> Result> { + let span = self.start_span(); + self.bump_any(); // bump operator + let type_annotation = self.parse_type_operator_or_higher()?; + Ok(self.ast.ts_type_operator_type(self.end_span(span), operator, type_annotation)) + } + + fn parse_infer_type(&mut self) -> Result> { + let span = self.start_span(); + self.bump_any(); // bump `infer` + let type_parameter = self.parse_type_parameter_of_infer_type()?; + Ok(self.ast.ts_infer_type(self.end_span(span), type_parameter)) + } + + fn parse_type_parameter_of_infer_type(&mut self) -> Result>> { + let span = self.start_span(); + let name = self.parse_binding_identifier()?; + let constraint = self.try_parse(Self::try_parse_constraint_of_infer_type).unwrap_or(None); + let span = self.end_span(span); + let ts_type_parameter = + self.ast.ts_type_parameter(span, name, constraint, None, false, false, false); + Ok(self.ast.alloc(ts_type_parameter)) + } + + fn parse_postfix_type_or_higher(&mut self) -> Result> { + let span = self.start_span(); + let mut ty = self.parse_non_array_type()?; + + while !self.cur_token().is_on_new_line { + match self.cur_kind() { + Kind::Bang => { + self.bump_any(); + ty = self.ast.js_doc_non_nullable_type( + self.end_span(span), + ty, + /* postfix */ true, + ); + } + Kind::Question => { + // If next token is start of a type we have a conditional type + if self.lookahead(Self::next_token_is_start_of_type) { + return Ok(ty); + } + self.bump_any(); + ty = self.ast.js_doc_nullable_type( + self.end_span(span), + ty, + /* postfix */ true, + ); + } + Kind::LBrack => { + self.bump_any(); + if self.is_start_of_type(/* in_start_of_parameter */ false) { + let index_type = self.parse_ts_type()?; + self.expect(Kind::RBrack)?; + ty = self.ast.ts_indexed_access_type(self.end_span(span), ty, index_type); + } else { + self.expect(Kind::RBrack)?; + ty = self.ast.ts_array_type(self.end_span(span), ty); + } + } + _ => return Ok(ty), } - Kind::LBrack => self.parse_ts_tuple_type(), - Kind::LCurly => { - if self.is_at_mapped_type() { - self.parse_ts_mapped_type() + } + Ok(ty) + } + + fn parse_non_array_type(&mut self) -> Result> { + match self.cur_kind() { + Kind::Any + | Kind::Unknown + | Kind::String + | Kind::Number + | Kind::BigInt + | Kind::Symbol + | Kind::Boolean + | Kind::Undefined + | Kind::Never + | Kind::Object => { + if let Ok(Some(ty)) = self.try_parse(Self::parse_keyword_and_no_dot) { + Ok(ty) } else { - self.parse_ts_object_ype() + self.parse_type_reference() + } + } + // TODO: js doc types: `JSDocAllType`, `JSDocFunctionType` + // Kind::StarEq => { + // scanner.reScanAsteriskEqualsToken(); + // falls through + // } + // Kind::Star => { + // return parseJSDocAllType(); + // } + // case SyntaxKind.QuestionQuestionToken: + // // If there is '??', treat it as prefix-'?' in JSDoc type. + // scanner.reScanQuestionToken(); + // // falls through + // case SyntaxKind.FunctionKeyword: + // return parseJSDocFunctionType(); + Kind::Question => self.parse_js_doc_unknown_or_nullable_type(), + Kind::Bang => self.parse_js_doc_non_nullable_type(), + Kind::NoSubstitutionTemplate | Kind::Str | Kind::False | Kind::Null => { + self.parse_literal_type_node(/* negative */ false) + } + kind if kind.is_number() => { + self.parse_literal_type_node(/* negative */ false) + } + Kind::Minus => { + if self.peek_kind().is_number() { + self.parse_literal_type_node(/* negative */ true) + } else { + self.parse_type_reference() } } Kind::Void => { @@ -374,58 +384,74 @@ impl<'a> ParserImpl<'a> { } Kind::This => { let span = self.start_span(); - self.bump_any(); - Ok(self.ast.ts_this_keyword(self.end_span(span))) - } - Kind::NoSubstitutionTemplate | Kind::TemplateHead => { - self.parse_ts_template_literal_type(false) - } - Kind::Typeof => self.parse_ts_typeof_type(), - Kind::Import => { - let node = self.parse_ts_import_type()?; - Ok(self.ast.ts_import_type( - node.span, - node.argument, - node.qualifier, - node.attributes, - node.type_parameters, - )) - } - Kind::Minus if self.peek_kind().is_number() => self.parse_ts_literal_type(), - Kind::Question => self.parse_js_doc_unknown_or_nullable_type(), - // null should not be parsed as a literal type - kind if kind.is_literal() && kind != Kind::Null => self.parse_ts_literal_type(), - _ => { - if !self.peek_at(Kind::Dot) { - let keyword = self.parse_ts_keyword_type(); - if let Some(keyword) = keyword { - return Ok(keyword); - } + self.bump_any(); // bump `this` + let this_type = TSThisType { span: self.end_span(span) }; + if self.peek_at(Kind::Is) && !self.peek_token().is_on_new_line { + return self.parse_this_type_predicate(this_type); } - self.parse_ts_reference_type() + Ok(TSType::TSThisType(self.ast.alloc(this_type))) } + Kind::Typeof => { + if self.peek_at(Kind::Import) { + self.parse_ts_import_type() + } else { + self.parse_type_query() + } + } + Kind::LCurly => { + if self.lookahead(Self::is_start_of_mapped_type) { + self.parse_mapped_type() + } else { + self.parse_type_literal() + } + } + Kind::LBrack => self.parse_tuple_type(), + Kind::LParen => self.parse_parenthesized_type(), + Kind::Import => self.parse_ts_import_type(), + Kind::Asserts => { + let peek_token = self.peek_token(); + if peek_token.kind.is_identifier_name() && !peek_token.is_on_new_line { + self.parse_asserts_type_predicate() + } else { + self.parse_type_reference() + } + } + Kind::TemplateHead => self.parse_template_type(false), + _ => self.parse_type_reference(), } } - fn parse_ts_keyword_type(&mut self) -> Option> { + fn parse_keyword_and_no_dot(&mut self) -> Result>> { let span = self.start_span(); - match self.cur_kind() { + let ty = match self.cur_kind() { Kind::Any => { self.bump_any(); Some(self.ast.ts_any_keyword(self.end_span(span))) } - Kind::Unknown => { + Kind::BigInt => { self.bump_any(); - Some(self.ast.ts_unknown_keyword(self.end_span(span))) - } - Kind::Number => { - self.bump_any(); - Some(self.ast.ts_number_keyword(self.end_span(span))) + Some(self.ast.ts_bigint_keyword(self.end_span(span))) } Kind::Boolean => { self.bump_any(); Some(self.ast.ts_boolean_keyword(self.end_span(span))) } + Kind::Intrinsic => { + self.bump_any(); + Some(self.ast.ts_intrinsic_keyword(self.end_span(span))) + } + Kind::Never => { + self.bump_any(); + Some(self.ast.ts_never_keyword(self.end_span(span))) + } + Kind::Null => { + self.bump_any(); + Some(self.ast.ts_null_keyword(self.end_span(span))) + } + Kind::Number => { + self.bump_any(); + Some(self.ast.ts_number_keyword(self.end_span(span))) + } Kind::Object => { self.bump_any(); Some(self.ast.ts_object_keyword(self.end_span(span))) @@ -434,115 +460,78 @@ impl<'a> ParserImpl<'a> { self.bump_any(); Some(self.ast.ts_string_keyword(self.end_span(span))) } - Kind::BigInt => { - self.bump_any(); - Some(self.ast.ts_bigint_keyword(self.end_span(span))) - } Kind::Symbol => { self.bump_any(); Some(self.ast.ts_symbol_keyword(self.end_span(span))) } - Kind::Null => { - self.bump_any(); - Some(self.ast.ts_null_keyword(self.end_span(span))) - } Kind::Undefined => { self.bump_any(); Some(self.ast.ts_undefined_keyword(self.end_span(span))) } - Kind::Never => { + Kind::Unknown => { self.bump_any(); - Some(self.ast.ts_never_keyword(self.end_span(span))) + Some(self.ast.ts_unknown_keyword(self.end_span(span))) } - Kind::Intrinsic => { + Kind::Void => { self.bump_any(); - Some(self.ast.ts_intrinsic_keyword(self.end_span(span))) + Some(self.ast.ts_void_keyword(self.end_span(span))) } _ => None, + }; + if self.at(Kind::Dot) { + return Err(self.unexpected()); + } + Ok(ty) + } + + fn is_start_of_type(&mut self, in_start_of_parameter: bool) -> bool { + match self.cur_kind() { + kind if kind.is_number() => true, + Kind::Any + | Kind::Unknown + | Kind::String + | Kind::Number + | Kind::BigInt + | Kind::Boolean + | Kind::Readonly + | Kind::Symbol + | Kind::Unique + | Kind::Void + | Kind::Undefined + | Kind::Null + | Kind::This + | Kind::Typeof + | Kind::Never + | Kind::LCurly + | Kind::LBrack + | Kind::LAngle + | Kind::Pipe + | Kind::Amp + | Kind::New + | Kind::Str + | Kind::True + | Kind::False + | Kind::Object + | Kind::Star + | Kind::Question + | Kind::Break + | Kind::Dot3 + | Kind::Infer + | Kind::Import + | Kind::Asserts + | Kind::NoSubstitutionTemplate + | Kind::TemplateHead => true, + Kind::Function => !in_start_of_parameter, + Kind::Minus => !in_start_of_parameter && self.peek_kind().is_number(), + Kind::LParen => { + !in_start_of_parameter + && self.lookahead(Self::is_start_of_parenthesized_or_function_type) + } + kind => kind.is_identifier(), } } - // test ts ts_reference_type - // type C = A; - // type D = B.a; - // type E = D.c.b.a; - fn parse_ts_reference_type(&mut self) -> Result> { - let span = self.start_span(); - let type_name = self.parse_ts_type_name()?; - let type_parameters = - if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? }; - - Ok(self.ast.ts_type_reference(self.end_span(span), type_name, type_parameters)) - } - - fn parse_ts_implement_name(&mut self) -> Result> { - let span = self.start_span(); - let expression = self.parse_ts_type_name()?; - let type_parameters = - if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? }; - - Ok(self.ast.ts_type_implement(self.end_span(span), expression, type_parameters)) - } - - pub(crate) fn parse_ts_type_name(&mut self) -> Result> { - let span = self.start_span(); - let ident = self.parse_identifier_name()?; - let ident = IdentifierReference::new(ident.span, ident.name); - let mut left = TSTypeName::IdentifierReference(self.ast.alloc(ident)); - while self.eat(Kind::Dot) { - let right = self.parse_identifier_name()?; - left = TSTypeName::QualifiedName(self.ast.alloc(TSQualifiedName { - span: self.end_span(span), - left, - right, - })); - } - Ok(left) - } - - pub(crate) fn parse_ts_type_arguments( - &mut self, - ) -> Result>>> { - self.re_lex_ts_l_angle(); - if !self.at(Kind::LAngle) { - return Ok(None); - } - let span = self.start_span(); - let params = TSTypeArgumentList::parse(self, false)?.params; - Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))) - } - - pub(crate) fn parse_ts_type_arguments_in_expression( - &mut self, - ) -> Result>>> { - if !self.ts_enabled() { - return Ok(None); - } - - let span = self.start_span(); - self.re_lex_ts_l_angle(); - if !self.at(Kind::LAngle) { - return Ok(None); - } - - let params = TSTypeArgumentList::parse(self, /* in_expression */ true)?.params; - - let token = self.cur_token(); - - if token.is_on_new_line || token.kind.can_follow_type_arguments_in_expr() { - return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))); - } - - Err(self.unexpected()) - } - - fn parse_ts_tuple_type(&mut self) -> Result> { - let span = self.start_span(); - let elements = TSTupleElementList::parse(self)?.elements; - Ok(self.ast.ts_tuple_type(self.end_span(span), elements)) - } - - fn is_at_mapped_type(&mut self) -> bool { + fn is_start_of_mapped_type(&mut self) -> bool { if !self.at(Kind::LCurly) { return false; } @@ -562,7 +551,28 @@ impl<'a> ParserImpl<'a> { && self.nth_at(offset + 2, Kind::In) } - fn parse_ts_mapped_type(&mut self) -> Result> { + fn next_token_is_start_of_type(&mut self) -> bool { + self.bump_any(); + self.is_start_of_type(false) + } + + fn is_start_of_parenthesized_or_function_type(&mut self) -> bool { + self.bump_any(); + self.at(Kind::RParen) + || self.is_start_of_parameter(/* is_js_doc_parameter */ false) + || self.is_start_of_type(false) + } + + fn is_start_of_parameter(&mut self, is_js_doc_parameter: bool) -> bool { + let kind = self.cur_kind(); + kind == Kind::Dot3 + || kind.is_binding_identifier_or_private_identifier_or_pattern() + || kind.is_modifier_kind() + || kind == Kind::At + || self.is_start_of_type(!is_js_doc_parameter) + } + + fn parse_mapped_type(&mut self) -> Result> { let span = self.start_span(); self.expect(Kind::LCurly)?; let mut readonly = TSMappedTypeModifierOperator::None; @@ -627,52 +637,60 @@ impl<'a> ParserImpl<'a> { )) } - pub(crate) fn is_at_named_tuple_element(&mut self) -> bool { - let offset = u8::from(self.at(Kind::Dot3)); - let has_colon = self.nth_at(offset + 1, Kind::Colon); - let has_question_colon = - self.nth_at(offset + 1, Kind::Question) && self.nth_at(offset + 2, Kind::Colon); - - self.nth_kind(offset).is_identifier_name() && (has_colon || has_question_colon) - } - - fn parse_ts_object_ype(&mut self) -> Result> { + fn parse_type_literal(&mut self) -> Result> { let span = self.start_span(); let mut member_list = TSInterfaceOrObjectBodyList::new(self); member_list.parse(self)?; - Ok(self.ast.ts_type_literal(self.end_span(span), member_list.body)) } - fn parse_ts_literal_type(&mut self) -> Result> { + fn parse_type_query(&mut self) -> Result> { let span = self.start_span(); - let negative = self.eat(Kind::Minus); - - let expression = self.parse_literal_expression()?; - - let span = self.end_span(span); - let literal = if negative { - match self.ast.unary_expression(span, UnaryOperator::UnaryNegation, expression) { - Expression::UnaryExpression(unary_expr) => TSLiteral::UnaryExpression(unary_expr), - _ => unreachable!(), - } + self.bump_any(); // `bump `typeof` + let entity_name = self.parse_ts_type_name()?; // TODO: parseEntityName + let entity_name = self.ast.ts_type_query_expr_name_type_name(entity_name); + let type_arguments = if self.cur_token().is_on_new_line { + None } else { - match expression { - Expression::BooleanLiteral(literal) => TSLiteral::BooleanLiteral(literal), - Expression::NullLiteral(literal) => TSLiteral::NullLiteral(literal), - Expression::NumericLiteral(literal) => TSLiteral::NumericLiteral(literal), - Expression::BigIntLiteral(literal) => TSLiteral::BigIntLiteral(literal), - Expression::RegExpLiteral(literal) => TSLiteral::RegExpLiteral(literal), - Expression::StringLiteral(literal) => TSLiteral::StringLiteral(literal), - Expression::TemplateLiteral(literal) => TSLiteral::TemplateLiteral(literal), - _ => return Err(self.unexpected()), - } + // TODO: tryParseTypeArguments + self.parse_ts_type_arguments()? }; - - Ok(self.ast.ts_literal_type(span, literal)) + Ok(self.ast.ts_type_query_type(self.end_span(span), entity_name, type_arguments)) } - fn parse_ts_template_literal_type(&mut self, tagged: bool) -> Result> { + fn parse_this_type_predicate(&mut self, this_ty: TSThisType) -> Result> { + let span = this_ty.span; + self.bump_any(); // bump `is` + let parameter_name = self.ast.ts_type_predicate_name_this(this_ty); + let type_span = self.start_span(); + let ty = self.parse_ts_type()?; + let type_annotation = Some(self.ast.ts_type_annotation(self.end_span(type_span), ty)); + Ok(self.ast.ts_type_predicate(self.end_span(span), parameter_name, false, type_annotation)) + } + + fn parse_this_type_node(&mut self) -> TSThisType { + let span = self.start_span(); + self.bump_any(); // bump `this` + TSThisType { span: self.end_span(span) } + } + + fn parse_ts_type_constraint(&mut self) -> Result>> { + if !self.at(Kind::Extends) { + return Ok(None); + } + self.bump_any(); + Ok(Some(self.parse_ts_type()?)) + } + + fn parse_ts_default_type(&mut self) -> Result>> { + if !self.at(Kind::Eq) { + return Ok(None); + } + self.bump_any(); + Ok(Some(self.parse_ts_type()?)) + } + + fn parse_template_type(&mut self, tagged: bool) -> Result> { let span = self.start_span(); let mut types = self.ast.new_vec(); let mut quasis = self.ast.new_vec(); @@ -707,40 +725,241 @@ impl<'a> ParserImpl<'a> { Ok(self.ast.ts_template_literal_type(self.end_span(span), quasis, types)) } - fn parse_ts_typeof_type(&mut self) -> Result> { + fn parse_asserts_type_predicate(&mut self) -> Result> { let span = self.start_span(); - self.expect(Kind::Typeof)?; - let expr_name: TSTypeQueryExprName = if self.at(Kind::Import) { - let node = self.parse_ts_import_type()?; - self.ast.ts_type_query_expr_name_import_type(node) + self.bump_any(); // bump `asserts` + let parameter_name = if self.at(Kind::This) { + self.ast.ts_type_predicate_name_this(self.parse_this_type_node()) } else { - let node = self.parse_ts_type_name()?; - self.ast.ts_type_query_expr_name_type_name(node) + let node = self.parse_identifier_name()?; + self.ast.ts_type_predicate_name_identifier(node) }; - let type_parameters = self.parse_ts_type_arguments()?; - Ok(self.ast.ts_type_query_type(self.end_span(span), expr_name, type_parameters)) + let mut type_annotation = None; + if self.eat(Kind::Is) { + let type_span = self.start_span(); + let ty = self.parse_ts_type()?; + type_annotation = Some(self.ast.ts_type_annotation(self.end_span(type_span), ty)); + } + Ok(self.ast.ts_type_predicate( + self.end_span(span), + parameter_name, + /* asserts */ true, + type_annotation, + )) } - fn parse_ts_import_type(&mut self) -> Result> { + fn parse_type_reference(&mut self) -> Result> { let span = self.start_span(); + let type_name = self.parse_ts_type_name()?; + let type_parameters = + if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? }; + Ok(self.ast.ts_type_reference(self.end_span(span), type_name, type_parameters)) + } + + fn parse_ts_implement_name(&mut self) -> Result> { + let span = self.start_span(); + let expression = self.parse_ts_type_name()?; + let type_parameters = + if self.cur_token().is_on_new_line { None } else { self.parse_ts_type_arguments()? }; + + Ok(self.ast.ts_type_implement(self.end_span(span), expression, type_parameters)) + } + + pub(crate) fn parse_ts_type_name(&mut self) -> Result> { + let span = self.start_span(); + let ident = self.parse_identifier_name()?; + let ident = IdentifierReference::new(ident.span, ident.name); + let mut left = TSTypeName::IdentifierReference(self.ast.alloc(ident)); + while self.eat(Kind::Dot) { + let right = self.parse_identifier_name()?; + left = TSTypeName::QualifiedName(self.ast.alloc(TSQualifiedName { + span: self.end_span(span), + left, + right, + })); + } + Ok(left) + } + + pub(crate) fn parse_ts_type_arguments( + &mut self, + ) -> Result>>> { + self.re_lex_ts_l_angle(); + if !self.at(Kind::LAngle) { + return Ok(None); + } + let span = self.start_span(); + let params = TSTypeArgumentList::parse(self, false)?.params; + Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))) + } + + pub(crate) fn parse_ts_type_arguments_in_expression( + &mut self, + ) -> Result>>> { + if !self.ts_enabled() { + return Ok(None); + } + + let span = self.start_span(); + self.re_lex_ts_l_angle(); + if !self.at(Kind::LAngle) { + return Ok(None); + } + + let params = TSTypeArgumentList::parse(self, /* in_expression */ true)?.params; + + let token = self.cur_token(); + + if token.is_on_new_line || token.kind.can_follow_type_arguments_in_expr() { + return Ok(Some(self.ast.ts_type_arguments(self.end_span(span), params))); + } + + Err(self.unexpected()) + } + + fn parse_tuple_type(&mut self) -> Result> { + let span = self.start_span(); + let elements = TSTupleElementList::parse(self)?.elements; + Ok(self.ast.ts_tuple_type(self.end_span(span), elements)) + } + + pub(super) fn parse_tuple_element_name_or_tuple_element_type( + &mut self, + ) -> Result> { + if self.lookahead(Self::is_tuple_element_name) { + let span = self.start_span(); + let dotdotdot = self.eat(Kind::Dot3); + let member_span = self.start_span(); + let label = self.parse_identifier_name()?; + let optional = self.eat(Kind::Question); + self.expect(Kind::Colon)?; + let element_type = self.parse_tuple_element_type()?; + let span = self.end_span(span); + return Ok(if dotdotdot { + TSTupleElement::TSRestType(self.ast.alloc(TSRestType { + span, + type_annotation: TSType::TSNamedTupleMember(self.ast.alloc( + TSNamedTupleMember { + span: self.end_span(member_span), + element_type, + label, + // TODO: A tuple member cannot be both optional and rest. (TS5085) + // See typescript suite + optional, + }, + )), + })) + } else { + TSTupleElement::TSNamedTupleMember(self.ast.alloc(TSNamedTupleMember { + span, + element_type, + label, + optional, + })) + }); + } + self.parse_tuple_element_type() + } + + fn is_tuple_element_name(&mut self) -> bool { + if self.eat(Kind::Dot3) { + return self.cur_kind().is_identifier_name() + && self.is_next_token_colon_or_question_colon(); + } + self.cur_kind().is_identifier_name() && self.is_next_token_colon_or_question_colon() + } + + fn is_next_token_colon_or_question_colon(&mut self) -> bool { + self.bump_any(); + if self.at(Kind::Colon) { + return true; + } + self.at(Kind::Question) && self.peek_at(Kind::Colon) + } + + fn parse_tuple_element_type(&mut self) -> Result> { + let span = self.start_span(); + if self.eat(Kind::Dot3) { + let ty = self.parse_ts_type()?; + return Ok(TSTupleElement::TSRestType( + self.ast.alloc(TSRestType { span: self.end_span(span), type_annotation: ty }), + )); + } + let ty = self.parse_ts_type()?; + if let TSType::JSDocNullableType(ty) = ty { + if ty.span.start == ty.type_annotation.span().start { + Ok(TSTupleElement::TSOptionalType(self.ast.alloc(TSOptionalType { + span: ty.span, + type_annotation: ty.unbox().type_annotation, + }))) + } else { + Ok(TSTupleElement::JSDocNullableType(ty)) + } + } else { + Ok(TSTupleElement::from(ty)) + } + } + + fn parse_parenthesized_type(&mut self) -> Result> { + self.bump_any(); // bump `(` + let result = self.parse_ts_type()?; + self.expect(Kind::RParen)?; + Ok(result) + } + + fn parse_literal_type_node(&mut self, negative: bool) -> Result> { + let span = self.start_span(); + if negative { + self.bump_any(); // bump `-` + } + + let expression = if self.at(Kind::NoSubstitutionTemplate) { + self.parse_template_literal_expression(false) + } else { + self.parse_literal_expression() + }?; + + let span = self.end_span(span); + let literal = if negative { + match self.ast.unary_expression(span, UnaryOperator::UnaryNegation, expression) { + Expression::UnaryExpression(unary_expr) => TSLiteral::UnaryExpression(unary_expr), + _ => unreachable!(), + } + } else { + match expression { + Expression::BooleanLiteral(literal) => TSLiteral::BooleanLiteral(literal), + Expression::NullLiteral(literal) => TSLiteral::NullLiteral(literal), + Expression::NumericLiteral(literal) => TSLiteral::NumericLiteral(literal), + Expression::BigIntLiteral(literal) => TSLiteral::BigIntLiteral(literal), + Expression::RegExpLiteral(literal) => TSLiteral::RegExpLiteral(literal), + Expression::StringLiteral(literal) => TSLiteral::StringLiteral(literal), + Expression::TemplateLiteral(literal) => TSLiteral::TemplateLiteral(literal), + _ => return Err(self.unexpected()), + } + }; + + Ok(self.ast.ts_literal_type(span, literal)) + } + + fn parse_ts_import_type(&mut self) -> Result> { + let span = self.start_span(); + let is_type_of = self.eat(Kind::Typeof); self.expect(Kind::Import)?; self.expect(Kind::LParen)?; - let argument = self.parse_ts_type()?; + let parameter = self.parse_ts_type()?; let attributes = if self.eat(Kind::Comma) { Some(self.parse_ts_import_attributes()?) } else { None }; self.expect(Kind::RParen)?; - let qualifier = if self.eat(Kind::Dot) { Some(self.parse_ts_type_name()?) } else { None }; - let type_parameters = self.parse_ts_type_arguments()?; - - Ok(TSImportType { - span: self.end_span(span), - argument, + Ok(self.ast.ts_import_type( + self.end_span(span), + is_type_of, + parameter, qualifier, attributes, type_parameters, - }) + )) } fn parse_ts_import_attributes(&mut self) -> Result> { @@ -754,29 +973,7 @@ impl<'a> ParserImpl<'a> { Ok(TSImportAttributes { span, elements }) } - fn parse_ts_infer_type(&mut self) -> Result> { - let span = self.start_span(); - self.expect(Kind::Infer)?; - - let parameter_span = self.start_span(); - let name = self.parse_binding_identifier()?; - - let constraint = self.try_parse(ParserImpl::parse_constraint_of_infer_type).unwrap_or(None); - - let type_parameter = self.ast.alloc(self.ast.ts_type_parameter( - self.end_span(parameter_span), - name, - constraint, - None, - false, - false, - false, - )); - - Ok(self.ast.ts_infer_type(self.end_span(span), type_parameter)) - } - - fn parse_constraint_of_infer_type(&mut self) -> Result>> { + fn try_parse_constraint_of_infer_type(&mut self) -> Result>> { if self.eat(Kind::Extends) { let constraint = self.context( Context::DisallowConditionalTypes, @@ -792,6 +989,8 @@ impl<'a> ParserImpl<'a> { pub(crate) fn parse_ts_return_type_annotation( &mut self, + kind: Kind, + is_type: bool, ) -> Result>>> { if !self.ts_enabled() { return Ok(None); @@ -800,58 +999,76 @@ impl<'a> ParserImpl<'a> { return Ok(None); } let span = self.start_span(); - self.bump_any(); // bump colon - let return_type = self.parse_ts_return_type()?; - Ok(Some(self.ast.ts_type_annotation(self.end_span(span), return_type))) + Ok(self + .parse_return_type(kind, is_type)? + .map(|return_type| self.ast.ts_type_annotation(self.end_span(span), return_type))) } - fn parse_ts_type_predicate(&mut self) -> Result> { - let span = self.start_span(); - let asserts = self.eat(Kind::Asserts); + fn parse_return_type( + &mut self, + return_kind: Kind, + is_type: bool, + ) -> Result>> { + if self.should_parse_return_type(return_kind, is_type) { + return self + .context( + Context::empty(), + Context::DisallowConditionalTypes, + Self::parse_type_or_type_predicate, + ) + .map(Some); + } + Ok(None) + } - let parameter_name = if self.at(Kind::This) { - let span = self.start_span(); + fn should_parse_return_type(&mut self, return_kind: Kind, _is_type: bool) -> bool { + if return_kind == Kind::Arrow { self.bump_any(); - self.ast.ts_type_predicate_name_this(TSThisType { span: self.end_span(span) }) - } else { - let node = self.parse_identifier_name()?; - self.ast.ts_type_predicate_name_identifier(node) - }; + return true; + } + if self.eat(Kind::Colon) { + return true; + } + // TODO + // if (isType && token() === SyntaxKind.EqualsGreaterThanToken) { + // // This is easy to get backward, especially in type contexts, so parse the type anyway + // parseErrorAtCurrentToken(Diagnostics._0_expected, tokenToString(SyntaxKind.ColonToken)); + // nextToken(); + // return true; + // } + false + } - if !asserts { - self.expect(Kind::Is)?; - } else if !self.eat(Kind::Is) { + fn parse_type_or_type_predicate(&mut self) -> Result> { + let span = self.start_span(); + let type_predicate_variable = if self.cur_kind().is_identifier_name() { + self.try_parse(Self::parse_type_predicate_prefix) + } else { + Ok(None) + }; + let type_span = self.start_span(); + let ty = self.parse_ts_type()?; + if let Ok(Some(id)) = type_predicate_variable { + let type_annotation = Some(self.ast.ts_type_annotation(self.end_span(type_span), ty)); + let parameter_name = self.ast.ts_type_predicate_name_identifier(id); return Ok(self.ast.ts_type_predicate( self.end_span(span), parameter_name, - asserts, - None, + false, + type_annotation, )); } - - let type_span = self.start_span(); - let type_annotation = self.parse_ts_type()?; - let type_annotation = - Some(self.ast.ts_type_annotation(self.end_span(type_span), type_annotation)); - - Ok(self.ast.ts_type_predicate( - self.end_span(span), - parameter_name, - asserts, - type_annotation, - )) + Ok(ty) } - pub(crate) fn parse_ts_return_type(&mut self) -> Result> { - let asserts = self.at(Kind::Asserts) - && (self.peek_kind().is_identifier() || self.peek_at(Kind::This)); - let is_predicate = - (self.cur_kind().is_identifier() || self.at(Kind::This)) && self.peek_at(Kind::Is); - if !self.peek_token().is_on_new_line && (asserts || is_predicate) { - self.parse_ts_type_predicate() - } else { - self.context(Context::empty(), Context::DisallowConditionalTypes, Self::parse_ts_type) + fn parse_type_predicate_prefix(&mut self) -> Result>> { + let id = self.parse_identifier_name()?; + let token = self.cur_token(); + if token.kind == Kind::Is && !token.is_on_new_line { + self.bump_any(); + return Ok(Some(id)); } + Err(self.unexpected()) } pub(crate) fn is_next_at_type_member_name(&mut self) -> bool { @@ -862,7 +1079,7 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); let type_parameters = self.parse_ts_type_parameters()?; let (this_patam, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?; - let return_type = self.parse_ts_return_type_annotation()?; + let return_type = self.parse_ts_return_type_annotation(Kind::Colon, false)?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); Ok(self.ast.ts_call_signature_declaration( @@ -879,7 +1096,7 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::Get)?; let (key, computed) = self.parse_property_name()?; let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?; - let return_type = self.parse_ts_return_type_annotation()?; + let return_type = self.parse_ts_return_type_annotation(Kind::Colon, false)?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); Ok(self.ast.ts_method_signature( @@ -900,7 +1117,7 @@ impl<'a> ParserImpl<'a> { self.expect(Kind::Set)?; let (key, computed) = self.parse_property_name()?; let (this_param, params) = self.parse_formal_parameters(FormalParameterKind::Signature)?; - let return_type = self.parse_ts_return_type_annotation()?; + let return_type = self.parse_ts_return_type_annotation(Kind::Colon, false)?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); if let Some(return_type) = return_type.as_ref() { @@ -981,7 +1198,7 @@ impl<'a> ParserImpl<'a> { self.error(diagnostics::ts_constructor_this_parameter(this_param.span)); } - let return_type = self.parse_ts_return_type_annotation()?; + let return_type = self.parse_ts_return_type_annotation(Kind::Colon, false)?; self.bump(Kind::Comma); self.bump(Kind::Semicolon); @@ -1083,4 +1300,11 @@ impl<'a> ParserImpl<'a> { Ok(self.ast.js_doc_nullable_type(span, type_annotation, /* postfix */ false)) } } + + fn parse_js_doc_non_nullable_type(&mut self) -> Result> { + let span = self.start_span(); + self.bump_any(); // bump `!` + let ty = self.parse_non_array_type()?; + Ok(self.ast.js_doc_non_nullable_type(self.end_span(span), ty, /* postfix */ false)) + } } diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index b83cb6791..559dc7c04 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -687,6 +687,7 @@ impl<'a> Format<'a> for TSType<'a> { TSType::TSTypeReference(v) => v.format(p), TSType::TSUnionType(v) => v.format(p), TSType::JSDocNullableType(v) => v.format(p), + TSType::JSDocNonNullableType(v) => v.format(p), TSType::JSDocUnknownType(v) => v.format(p), } } @@ -931,6 +932,12 @@ impl<'a> Format<'a> for JSDocNullableType<'a> { } } +impl<'a> Format<'a> for JSDocNonNullableType<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + line!() + } +} + impl<'a> Format<'a> for JSDocUnknownType { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { line!() diff --git a/crates/oxc_traverse/src/ancestor.rs b/crates/oxc_traverse/src/ancestor.rs index 7eebf90da..d88c9012a 100644 --- a/crates/oxc_traverse/src/ancestor.rs +++ b/crates/oxc_traverse/src/ancestor.rs @@ -287,7 +287,7 @@ pub(crate) enum AncestorType { TSInferTypeTypeParameter = 255, TSTypeQueryExprName = 256, TSTypeQueryTypeParameters = 257, - TSImportTypeArgument = 258, + TSImportTypeParameter = 258, TSImportTypeQualifier = 259, TSImportTypeAttributes = 260, TSImportTypeTypeParameters = 261, @@ -322,6 +322,7 @@ pub(crate) enum AncestorType { TSInstantiationExpressionExpression = 290, TSInstantiationExpressionTypeParameters = 291, JSDocNullableTypeTypeAnnotation = 292, + JSDocNonNullableTypeTypeAnnotation = 293, } /// Ancestor type used in AST traversal. @@ -792,8 +793,8 @@ pub enum Ancestor<'a> { TSTypeQueryExprName(TSTypeQueryWithoutExprName<'a>) = AncestorType::TSTypeQueryExprName as u16, TSTypeQueryTypeParameters(TSTypeQueryWithoutTypeParameters<'a>) = AncestorType::TSTypeQueryTypeParameters as u16, - TSImportTypeArgument(TSImportTypeWithoutArgument<'a>) = - AncestorType::TSImportTypeArgument as u16, + TSImportTypeParameter(TSImportTypeWithoutParameter<'a>) = + AncestorType::TSImportTypeParameter as u16, TSImportTypeQualifier(TSImportTypeWithoutQualifier<'a>) = AncestorType::TSImportTypeQualifier as u16, TSImportTypeAttributes(TSImportTypeWithoutAttributes<'a>) = @@ -861,6 +862,8 @@ pub enum Ancestor<'a> { AncestorType::TSInstantiationExpressionTypeParameters as u16, JSDocNullableTypeTypeAnnotation(JSDocNullableTypeWithoutTypeAnnotation<'a>) = AncestorType::JSDocNullableTypeTypeAnnotation as u16, + JSDocNonNullableTypeTypeAnnotation(JSDocNonNullableTypeWithoutTypeAnnotation<'a>) = + AncestorType::JSDocNonNullableTypeTypeAnnotation as u16, } impl<'a> Ancestor<'a> { @@ -1706,7 +1709,7 @@ impl<'a> Ancestor<'a> { pub fn is_ts_import_type(&self) -> bool { matches!( self, - Self::TSImportTypeArgument(_) + Self::TSImportTypeParameter(_) | Self::TSImportTypeQualifier(_) | Self::TSImportTypeAttributes(_) | Self::TSImportTypeTypeParameters(_) @@ -1825,6 +1828,11 @@ impl<'a> Ancestor<'a> { matches!(self, Self::JSDocNullableTypeTypeAnnotation(_)) } + #[inline] + pub fn is_js_doc_non_nullable_type(&self) -> bool { + matches!(self, Self::JSDocNonNullableTypeTypeAnnotation(_)) + } + #[inline] pub fn is_via_statement(&self) -> bool { matches!( @@ -2084,14 +2092,13 @@ impl<'a> Ancestor<'a> { | Self::TSArrayTypeElementType(_) | Self::TSIndexedAccessTypeObjectType(_) | Self::TSIndexedAccessTypeIndexType(_) - | Self::TSNamedTupleMemberElementType(_) | Self::TSOptionalTypeTypeAnnotation(_) | Self::TSRestTypeTypeAnnotation(_) | Self::TSTypeParameterInstantiationParams(_) | Self::TSTypeParameterConstraint(_) | Self::TSTypeParameterDefault(_) | Self::TSTypeAliasDeclarationTypeAnnotation(_) - | Self::TSImportTypeArgument(_) + | Self::TSImportTypeParameter(_) | Self::TSMappedTypeNameType(_) | Self::TSMappedTypeTypeAnnotation(_) | Self::TSTemplateLiteralTypeTypes(_) @@ -2099,6 +2106,7 @@ impl<'a> Ancestor<'a> { | Self::TSSatisfiesExpressionTypeAnnotation(_) | Self::TSTypeAssertionTypeAnnotation(_) | Self::JSDocNullableTypeTypeAnnotation(_) + | Self::JSDocNonNullableTypeTypeAnnotation(_) ) } @@ -2109,7 +2117,7 @@ impl<'a> Ancestor<'a> { #[inline] pub fn is_via_ts_tuple_element(&self) -> bool { - matches!(self, Self::TSTupleTypeElementTypes(_)) + matches!(self, Self::TSTupleTypeElementTypes(_) | Self::TSNamedTupleMemberElementType(_)) } #[inline] @@ -9128,10 +9136,10 @@ impl<'a> TSNamedTupleMemberWithoutLabel<'a> { } #[inline] - pub fn element_type(&self) -> &TSType<'a> { + pub fn element_type(&self) -> &TSTupleElement<'a> { unsafe { &*((self.0 as *const u8).add(OFFSET_TS_NAMED_TUPLE_MEMBER_ELEMENT_TYPE) - as *const TSType<'a>) + as *const TSTupleElement<'a>) } } @@ -10897,7 +10905,8 @@ impl<'a> TSTypeQueryWithoutTypeParameters<'a> { } pub(crate) const OFFSET_TS_IMPORT_TYPE_SPAN: usize = offset_of!(TSImportType, span); -pub(crate) const OFFSET_TS_IMPORT_TYPE_ARGUMENT: usize = offset_of!(TSImportType, argument); +pub(crate) const OFFSET_TS_IMPORT_TYPE_IS_TYPE_OF: usize = offset_of!(TSImportType, is_type_of); +pub(crate) const OFFSET_TS_IMPORT_TYPE_PARAMETER: usize = offset_of!(TSImportType, parameter); pub(crate) const OFFSET_TS_IMPORT_TYPE_QUALIFIER: usize = offset_of!(TSImportType, qualifier); pub(crate) const OFFSET_TS_IMPORT_TYPE_ATTRIBUTES: usize = offset_of!(TSImportType, attributes); pub(crate) const OFFSET_TS_IMPORT_TYPE_TYPE_PARAMETERS: usize = @@ -10905,14 +10914,19 @@ pub(crate) const OFFSET_TS_IMPORT_TYPE_TYPE_PARAMETERS: usize = #[repr(transparent)] #[derive(Debug)] -pub struct TSImportTypeWithoutArgument<'a>(pub(crate) *const TSImportType<'a>); +pub struct TSImportTypeWithoutParameter<'a>(pub(crate) *const TSImportType<'a>); -impl<'a> TSImportTypeWithoutArgument<'a> { +impl<'a> TSImportTypeWithoutParameter<'a> { #[inline] pub fn span(&self) -> &Span { unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_SPAN) as *const Span) } } + #[inline] + pub fn is_type_of(&self) -> &bool { + unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_IS_TYPE_OF) as *const bool) } + } + #[inline] pub fn qualifier(&self) -> &Option> { unsafe { @@ -10949,9 +10963,14 @@ impl<'a> TSImportTypeWithoutQualifier<'a> { } #[inline] - pub fn argument(&self) -> &TSType<'a> { + pub fn is_type_of(&self) -> &bool { + unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_IS_TYPE_OF) as *const bool) } + } + + #[inline] + pub fn parameter(&self) -> &TSType<'a> { unsafe { - &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_ARGUMENT) as *const TSType<'a>) + &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_PARAMETER) as *const TSType<'a>) } } @@ -10983,9 +11002,14 @@ impl<'a> TSImportTypeWithoutAttributes<'a> { } #[inline] - pub fn argument(&self) -> &TSType<'a> { + pub fn is_type_of(&self) -> &bool { + unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_IS_TYPE_OF) as *const bool) } + } + + #[inline] + pub fn parameter(&self) -> &TSType<'a> { unsafe { - &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_ARGUMENT) as *const TSType<'a>) + &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_PARAMETER) as *const TSType<'a>) } } @@ -11017,9 +11041,14 @@ impl<'a> TSImportTypeWithoutTypeParameters<'a> { } #[inline] - pub fn argument(&self) -> &TSType<'a> { + pub fn is_type_of(&self) -> &bool { + unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_IS_TYPE_OF) as *const bool) } + } + + #[inline] + pub fn parameter(&self) -> &TSType<'a> { unsafe { - &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_ARGUMENT) as *const TSType<'a>) + &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_TYPE_PARAMETER) as *const TSType<'a>) } } @@ -11898,3 +11927,32 @@ impl<'a> JSDocNullableTypeWithoutTypeAnnotation<'a> { unsafe { &*((self.0 as *const u8).add(OFFSET_JS_DOC_NULLABLE_TYPE_POSTFIX) as *const bool) } } } + +pub(crate) const OFFSET_JS_DOC_NON_NULLABLE_TYPE_SPAN: usize = + offset_of!(JSDocNonNullableType, span); +pub(crate) const OFFSET_JS_DOC_NON_NULLABLE_TYPE_TYPE_ANNOTATION: usize = + offset_of!(JSDocNonNullableType, type_annotation); +pub(crate) const OFFSET_JS_DOC_NON_NULLABLE_TYPE_POSTFIX: usize = + offset_of!(JSDocNonNullableType, postfix); + +#[repr(transparent)] +#[derive(Debug)] +pub struct JSDocNonNullableTypeWithoutTypeAnnotation<'a>( + pub(crate) *const JSDocNonNullableType<'a>, +); + +impl<'a> JSDocNonNullableTypeWithoutTypeAnnotation<'a> { + #[inline] + pub fn span(&self) -> &Span { + unsafe { + &*((self.0 as *const u8).add(OFFSET_JS_DOC_NON_NULLABLE_TYPE_SPAN) as *const Span) + } + } + + #[inline] + pub fn postfix(&self) -> &bool { + unsafe { + &*((self.0 as *const u8).add(OFFSET_JS_DOC_NON_NULLABLE_TYPE_POSTFIX) as *const bool) + } + } +} diff --git a/crates/oxc_traverse/src/traverse.rs b/crates/oxc_traverse/src/traverse.rs index c76d5d52f..0d7d0d5a9 100644 --- a/crates/oxc_traverse/src/traverse.rs +++ b/crates/oxc_traverse/src/traverse.rs @@ -2405,6 +2405,21 @@ pub trait Traverse<'a> { ) { } + #[inline] + fn enter_js_doc_non_nullable_type( + &mut self, + node: &mut JSDocNonNullableType<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + } + #[inline] + fn exit_js_doc_non_nullable_type( + &mut self, + node: &mut JSDocNonNullableType<'a>, + ctx: &mut TraverseCtx<'a>, + ) { + } + #[inline] fn enter_js_doc_unknown_type( &mut self, diff --git a/crates/oxc_traverse/src/walk.rs b/crates/oxc_traverse/src/walk.rs index c77ca99a7..452389b95 100644 --- a/crates/oxc_traverse/src/walk.rs +++ b/crates/oxc_traverse/src/walk.rs @@ -3899,6 +3899,9 @@ pub(crate) unsafe fn walk_ts_type<'a, Tr: Traverse<'a>>( TSType::JSDocNullableType(node) => { walk_js_doc_nullable_type(traverser, (&mut **node) as *mut _, ctx) } + TSType::JSDocNonNullableType(node) => { + walk_js_doc_non_nullable_type(traverser, (&mut **node) as *mut _, ctx) + } TSType::JSDocUnknownType(node) => { walk_js_doc_unknown_type(traverser, (&mut **node) as *mut _, ctx) } @@ -4063,9 +4066,10 @@ pub(crate) unsafe fn walk_ts_named_tuple_member<'a, Tr: Traverse<'a>>( ctx.push_stack(Ancestor::TSNamedTupleMemberElementType( ancestor::TSNamedTupleMemberWithoutElementType(node), )); - walk_ts_type( + walk_ts_tuple_element( traverser, - (node as *mut u8).add(ancestor::OFFSET_TS_NAMED_TUPLE_MEMBER_ELEMENT_TYPE) as *mut TSType, + (node as *mut u8).add(ancestor::OFFSET_TS_NAMED_TUPLE_MEMBER_ELEMENT_TYPE) + as *mut TSTupleElement, ctx, ); ctx.retag_stack(AncestorType::TSNamedTupleMemberLabel); @@ -4162,6 +4166,7 @@ pub(crate) unsafe fn walk_ts_tuple_element<'a, Tr: Traverse<'a>>( | TSTupleElement::TSTypeReference(_) | TSTupleElement::TSUnionType(_) | TSTupleElement::JSDocNullableType(_) + | TSTupleElement::JSDocNonNullableType(_) | TSTupleElement::JSDocUnknownType(_) => walk_ts_type(traverser, node as *mut _, ctx), } traverser.exit_ts_tuple_element(&mut *node, ctx); @@ -5004,10 +5009,10 @@ pub(crate) unsafe fn walk_ts_import_type<'a, Tr: Traverse<'a>>( ctx: &mut TraverseCtx<'a>, ) { traverser.enter_ts_import_type(&mut *node, ctx); - ctx.push_stack(Ancestor::TSImportTypeArgument(ancestor::TSImportTypeWithoutArgument(node))); + ctx.push_stack(Ancestor::TSImportTypeParameter(ancestor::TSImportTypeWithoutParameter(node))); walk_ts_type( traverser, - (node as *mut u8).add(ancestor::OFFSET_TS_IMPORT_TYPE_ARGUMENT) as *mut TSType, + (node as *mut u8).add(ancestor::OFFSET_TS_IMPORT_TYPE_PARAMETER) as *mut TSType, ctx, ); if let Some(field) = &mut *((node as *mut u8).add(ancestor::OFFSET_TS_IMPORT_TYPE_QUALIFIER) @@ -5474,6 +5479,25 @@ pub(crate) unsafe fn walk_js_doc_nullable_type<'a, Tr: Traverse<'a>>( traverser.exit_js_doc_nullable_type(&mut *node, ctx); } +pub(crate) unsafe fn walk_js_doc_non_nullable_type<'a, Tr: Traverse<'a>>( + traverser: &mut Tr, + node: *mut JSDocNonNullableType<'a>, + ctx: &mut TraverseCtx<'a>, +) { + traverser.enter_js_doc_non_nullable_type(&mut *node, ctx); + ctx.push_stack(Ancestor::JSDocNonNullableTypeTypeAnnotation( + ancestor::JSDocNonNullableTypeWithoutTypeAnnotation(node), + )); + walk_ts_type( + traverser, + (node as *mut u8).add(ancestor::OFFSET_JS_DOC_NON_NULLABLE_TYPE_TYPE_ANNOTATION) + as *mut TSType, + ctx, + ); + ctx.pop_stack(); + traverser.exit_js_doc_non_nullable_type(&mut *node, ctx); +} + pub(crate) unsafe fn walk_js_doc_unknown_type<'a, Tr: Traverse<'a>>( traverser: &mut Tr, node: *mut JSDocUnknownType, diff --git a/tasks/ast_codegen/Cargo.toml b/tasks/ast_codegen/Cargo.toml index 87c5c18cc..dd70f3f01 100644 --- a/tasks/ast_codegen/Cargo.toml +++ b/tasks/ast_codegen/Cargo.toml @@ -14,14 +14,22 @@ name = "oxc_ast_codegen" test = false [dependencies] -syn = { workspace = true, features = ["full", "extra-traits", "clone-impls", "derive", "parsing", "printing", "proc-macro"] } -quote = { workspace = true } -proc-macro2 = { workspace = true } -itertools = { workspace = true } -serde = { workspace = true, features = ["derive"] } -regex = { workspace = true } +syn = { workspace = true, features = [ + "clone-impls", + "derive", + "extra-traits", + "full", + "parsing", + "printing", + "proc-macro", +] } +quote = { workspace = true } +proc-macro2 = { workspace = true } +itertools = { workspace = true } +serde = { workspace = true, features = ["derive"] } +regex = { workspace = true } prettyplease = { workspace = true } -lazy_static = { workspace = true } +lazy_static = { workspace = true } [package.metadata.cargo-shear] ignored = ["prettyplease"] diff --git a/tasks/ast_codegen/src/generators/ast_kind.rs b/tasks/ast_codegen/src/generators/ast_kind.rs index c1e05afbe..c0cea6da7 100644 --- a/tasks/ast_codegen/src/generators/ast_kind.rs +++ b/tasks/ast_codegen/src/generators/ast_kind.rs @@ -8,7 +8,7 @@ use super::generated_header; pub struct AstKindGenerator; -const BLACK_LIST: [&str; 68] = [ +const BLACK_LIST: [&str; 69] = [ "Expression", "ObjectPropertyKind", "TemplateElement", @@ -69,6 +69,7 @@ const BLACK_LIST: [&str; 68] = [ "TSExportAssignment", "TSNamespaceExportDeclaration", "JSDocNullableType", + "JSDocNonNullableType", "JSDocUnknownType", "JSXExpression", "JSXEmptyExpression", diff --git a/tasks/coverage/parser_babel.snap b/tasks/coverage/parser_babel.snap index 72b8947dd..f154f8049 100644 --- a/tasks/coverage/parser_babel.snap +++ b/tasks/coverage/parser_babel.snap @@ -3,7 +3,7 @@ commit: 12619ffe parser_babel Summary: AST Parsed : 2095/2101 (99.71%) Positive Passed: 2087/2101 (99.33%) -Negative Passed: 1365/1501 (90.94%) +Negative Passed: 1364/1501 (90.87%) Expect Syntax Error: "annex-b/disabled/1.1-html-comments-close/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions/input.js" Expect Syntax Error: "annex-b/disabled/3.1-sloppy-labeled-functions-if-body/input.js" @@ -138,6 +138,7 @@ Expect Syntax Error: "typescript/types/read-only-1/input.ts" Expect Syntax Error: "typescript/types/read-only-2/input.ts" Expect Syntax Error: "typescript/types/read-only-3/input.ts" Expect Syntax Error: "typescript/types/read-only-4/input.ts" +Expect Syntax Error: "typescript/types/tuple-labeled-invalid-optional/input.ts" Expect Syntax Error: "typescript/types/tuple-optional-invalid/input.ts" Expect Syntax Error: "typescript/types/tuple-required-after-labeled-optional/input.ts" Expect to Parse: "core/opts/allowNewTargetOutsideFunction-true/input.js" @@ -10668,13 +10669,6 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" · ╰── `,` expected ╰──── - × Expected `,` but found `?` - ╭─[typescript/types/tuple-labeled-invalid-optional/input.ts:1:15] - 1 │ type T = [x: A?]; - · ┬ - · ╰── `,` expected - ╰──── - × Expected `,` but found `Identifier` ╭─[typescript/types/variance-annotations/input.ts:95:17] 94 │ @@ -10711,22 +10705,22 @@ Expect to Parse: "typescript/types/const-type-parameters-babel-7/input.ts" 99 │ type T21 = T; // Error ╰──── - × Expected `)` but found `EOF` + × Expected `]` but found `EOF` ╭─[typescript/types-arrow-function/invalid-incomplete-array-like/input.ts:2:1] 1 │ type F = ([ ╰──── - × Expected `)` but found `EOF` + × Expected `}` but found `EOF` ╭─[typescript/types-arrow-function/invalid-incomplete-object-like/input.ts:2:1] 1 │ type F = ({ ╰──── - × Expected `)` but found `EOF` + × Expected `]` but found `EOF` ╭─[typescript/types-arrow-function-babel-7/invalid-incomplete-array-like/input.ts:2:1] 1 │ type F = ([ ╰──── - × Expected `)` but found `EOF` + × Expected `}` but found `EOF` ╭─[typescript/types-arrow-function-babel-7/invalid-incomplete-object-like/input.ts:2:1] 1 │ type F = ({ ╰──── diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index aa0e4e073..f790605b2 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -3,7 +3,7 @@ commit: d8086f14 parser_typescript Summary: AST Parsed : 5280/5283 (99.94%) Positive Passed: 5273/5283 (99.81%) -Negative Passed: 1073/4875 (22.01%) +Negative Passed: 1069/4875 (21.93%) Expect Syntax Error: "compiler/ClassDeclaration10.ts" Expect Syntax Error: "compiler/ClassDeclaration11.ts" Expect Syntax Error: "compiler/ClassDeclaration13.ts" @@ -1391,6 +1391,8 @@ Expect Syntax Error: "compiler/paramterDestrcuturingDeclaration.ts" Expect Syntax Error: "compiler/parenthesizedJSDocCastDoesNotNarrow.ts" Expect Syntax Error: "compiler/parseCommaSeparatedNewlineNumber.ts" Expect Syntax Error: "compiler/parseCommaSeparatedNewlineString.ts" +Expect Syntax Error: "compiler/parseInvalidNonNullableTypes.ts" +Expect Syntax Error: "compiler/parseInvalidNullableTypes.ts" Expect Syntax Error: "compiler/parseJsxExtends2.ts" Expect Syntax Error: "compiler/parseTypes.ts" Expect Syntax Error: "compiler/parseUnaryExpressionNoTypeAssertionInJsx1.ts" @@ -3631,8 +3633,10 @@ Expect Syntax Error: "conformance/types/tuple/contextualTypeWithTuple.ts" Expect Syntax Error: "conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion01.ts" Expect Syntax Error: "conformance/types/tuple/emptyTuples/emptyTuplesTypeAssertion02.ts" Expect Syntax Error: "conformance/types/tuple/indexerWithTuple.ts" +Expect Syntax Error: "conformance/types/tuple/named/namedTupleMembersErrors.ts" Expect Syntax Error: "conformance/types/tuple/optionalTupleElements1.ts" Expect Syntax Error: "conformance/types/tuple/readonlyArraysAndTuples.ts" +Expect Syntax Error: "conformance/types/tuple/restTupleElements1.ts" Expect Syntax Error: "conformance/types/tuple/strictTupleLength.ts" Expect Syntax Error: "conformance/types/tuple/tupleLengthCheck.ts" Expect Syntax Error: "conformance/types/tuple/unionsOfTupleTypes1.ts" @@ -6712,13 +6716,13 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 3 │ function f() { ╰──── - × Expected `=>` but found `)` - ╭─[compiler/functionTypesLackingReturnTypes.ts:2:17] - 1 │ // Error (no '=>') - 2 │ function f(x: ()) { - · ┬ - · ╰── `=>` expected - 3 │ } + × Expected `,` but found `var` + ╭─[compiler/functionTypesLackingReturnTypes.ts:6:1] + 5 │ // Error (no '=>') + 6 │ var g: (param); + · ─┬─ + · ╰── `,` expected + 7 │ ╰──── × Identifier `total` has already been declared @@ -8540,23 +8544,6 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" ╰──── help: Try insert a semicolon here - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[compiler/parseInvalidNonNullableTypes.ts:1:36] - 1 │ function f1(a: string): a is string! { - · ▲ - 2 │ return true; - ╰──── - help: Try insert a semicolon here - - × Expected `,` but found `?` - ╭─[compiler/parseInvalidNullableTypes.ts:5:22] - 4 │ - 5 │ function f2(a: string?) {} - · ┬ - · ╰── `,` expected - 6 │ function f3(a: number?) {} - ╰──── - × Expected `>` but found `EOF` ╭─[compiler/parseJsxElementInUnaryExpressionNoCrash1.ts:1:5] 1 │ ~< < @@ -12073,11 +12060,12 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 25 │ a = b; // Error ╰──── - × Unexpected token + × Expected `]` but found `#identifier` ╭─[conformance/classes/members/privateNames/privateNamesAndIndexedAccess.ts:7:28] 6 │ // not supported yet, could support in future: 7 │ const badForNow: C[#bar] = 3; // Error - · ──── + · ──┬─ + · ╰── `]` expected 8 │ // will never use this syntax, already taken: ╰──── @@ -12397,14 +12385,13 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" ╰──── help: Try insert a semicolon here - × Expected a semicolon or an implicit semicolon after a statement, but found none - ╭─[conformance/decorators/decoratorMetadata-jsdoc.ts:5:15] - 4 │ @decorator() - 5 │ a?: string?; - · ▲ - 6 │ @decorator() - ╰──── - help: Try insert a semicolon here + × Unexpected token + ╭─[conformance/decorators/decoratorMetadata-jsdoc.ts:9:9] + 8 │ @decorator() + 9 │ c?: *; + · ─ + 10 │ } + ╰──── × Unexpected token ╭─[conformance/decorators/invalid/decoratorOnArrowFunction.ts:3:17] @@ -20820,24 +20807,6 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 169 │ function restParam(...this: C): number { return this.n; } ╰──── - × Expected `,` but found `?` - ╭─[conformance/types/tuple/named/namedTupleMembersErrors.ts:10:35] - 9 │ - 10 │ export type Opt = [element: string?]; // question mark on element disallowed - · ┬ - · ╰── `,` expected - 11 │ - ╰──── - - × Expected `,` but found `?` - ╭─[conformance/types/tuple/restTupleElements1.ts:10:22] - 9 │ type T08 = [...string]; // Error - 10 │ type T09 = [...string?]; // Error - · ┬ - · ╰── `,` expected - 11 │ type T10 = [string, ...[...string[]]]; - ╰──── - × Expected a semicolon or an implicit semicolon after a statement, but found none ╭─[conformance/types/typeAliases/reservedNamesInAliases.ts:6:5] 5 │ type string = I;