fix(parser): parse assert keyword in TSImportAttributes (#4610)

closes #4601
This commit is contained in:
Boshen 2024-08-04 01:41:31 +00:00
parent 6ff200d072
commit a40a217de4
12 changed files with 122 additions and 79 deletions

View file

@ -1062,6 +1062,7 @@ pub struct TSImportType<'a> {
pub struct TSImportAttributes<'a> {
#[serde(flatten)]
pub span: Span,
pub attributes_keyword: IdentifierName<'a>, // `with` or `assert`
pub elements: Vec<'a, TSImportAttribute<'a>>,
}
@ -1251,7 +1252,7 @@ pub struct TSNonNullExpression<'a> {
/// @LogParam x: number // parameter decorator
/// ) {
/// // ...
/// }
/// }
/// }
/// ```
///

View file

@ -930,18 +930,19 @@ const _: () = {
assert!(offset_of!(TSTypeQuery, type_parameters) == 24usize);
assert!(size_of::<TSTypeQueryExprName>() == 16usize);
assert!(align_of::<TSTypeQueryExprName>() == 8usize);
assert!(size_of::<TSImportType>() == 96usize);
assert!(size_of::<TSImportType>() == 120usize);
assert!(align_of::<TSImportType>() == 8usize);
assert!(offset_of!(TSImportType, span) == 0usize);
assert!(offset_of!(TSImportType, is_type_of) == 8usize);
assert!(offset_of!(TSImportType, parameter) == 16usize);
assert!(offset_of!(TSImportType, qualifier) == 32usize);
assert!(offset_of!(TSImportType, attributes) == 48usize);
assert!(offset_of!(TSImportType, type_parameters) == 88usize);
assert!(size_of::<TSImportAttributes>() == 40usize);
assert!(offset_of!(TSImportType, type_parameters) == 112usize);
assert!(size_of::<TSImportAttributes>() == 64usize);
assert!(align_of::<TSImportAttributes>() == 8usize);
assert!(offset_of!(TSImportAttributes, span) == 0usize);
assert!(offset_of!(TSImportAttributes, elements) == 8usize);
assert!(offset_of!(TSImportAttributes, attributes_keyword) == 8usize);
assert!(offset_of!(TSImportAttributes, elements) == 32usize);
assert!(size_of::<TSImportAttribute>() == 56usize);
assert!(align_of::<TSImportAttribute>() == 8usize);
assert!(offset_of!(TSImportAttribute, span) == 0usize);
@ -2050,18 +2051,19 @@ const _: () = {
assert!(offset_of!(TSTypeQuery, type_parameters) == 16usize);
assert!(size_of::<TSTypeQueryExprName>() == 8usize);
assert!(align_of::<TSTypeQueryExprName>() == 4usize);
assert!(size_of::<TSImportType>() == 56usize);
assert!(size_of::<TSImportType>() == 72usize);
assert!(align_of::<TSImportType>() == 4usize);
assert!(offset_of!(TSImportType, span) == 0usize);
assert!(offset_of!(TSImportType, is_type_of) == 8usize);
assert!(offset_of!(TSImportType, parameter) == 12usize);
assert!(offset_of!(TSImportType, qualifier) == 20usize);
assert!(offset_of!(TSImportType, attributes) == 28usize);
assert!(offset_of!(TSImportType, type_parameters) == 52usize);
assert!(size_of::<TSImportAttributes>() == 24usize);
assert!(offset_of!(TSImportType, type_parameters) == 68usize);
assert!(size_of::<TSImportAttributes>() == 40usize);
assert!(align_of::<TSImportAttributes>() == 4usize);
assert!(offset_of!(TSImportAttributes, span) == 0usize);
assert!(offset_of!(TSImportAttributes, elements) == 8usize);
assert!(offset_of!(TSImportAttributes, attributes_keyword) == 8usize);
assert!(offset_of!(TSImportAttributes, elements) == 24usize);
assert!(size_of::<TSImportAttribute>() == 36usize);
assert!(align_of::<TSImportAttribute>() == 4usize);
assert!(offset_of!(TSImportAttribute, span) == 0usize);

View file

@ -11832,14 +11832,16 @@ impl<'a> AstBuilder<'a> {
///
/// ## Parameters
/// - span: The [`Span`] covering this node
/// - attributes_keyword
/// - elements
#[inline]
pub fn ts_import_attributes(
self,
span: Span,
attributes_keyword: IdentifierName<'a>,
elements: Vec<'a, TSImportAttribute<'a>>,
) -> TSImportAttributes<'a> {
TSImportAttributes { span, elements }
TSImportAttributes { span, attributes_keyword, elements }
}
/// Builds a [`TSImportAttributes`] and stores it in the memory arena.
@ -11848,14 +11850,16 @@ impl<'a> AstBuilder<'a> {
///
/// ## Parameters
/// - span: The [`Span`] covering this node
/// - attributes_keyword
/// - elements
#[inline]
pub fn alloc_ts_import_attributes(
self,
span: Span,
attributes_keyword: IdentifierName<'a>,
elements: Vec<'a, TSImportAttribute<'a>>,
) -> Box<'a, TSImportAttributes<'a>> {
Box::new_in(self.ts_import_attributes(span, elements), self.allocator)
Box::new_in(self.ts_import_attributes(span, attributes_keyword, elements), self.allocator)
}
/// Builds a [`TSImportAttribute`]

View file

@ -2157,6 +2157,7 @@ pub mod walk {
it: &TSImportAttributes<'a>,
) {
// NOTE: AstKind doesn't exists!
visitor.visit_identifier_name(&it.attributes_keyword);
visitor.visit_ts_import_attribute_list(&it.elements);
}

View file

@ -2230,6 +2230,7 @@ pub mod walk_mut {
it: &mut TSImportAttributes<'a>,
) {
// NOTE: AstType doesn't exists!
visitor.visit_identifier_name(&mut it.attributes_keyword);
visitor.visit_ts_import_attribute_list(&mut it.elements);
}

View file

@ -21,6 +21,6 @@ proc-macro = true
doctest = false
[dependencies]
quote = { workspace = true }
syn = { workspace = true, features = ["full"] }
quote = { workspace = true }
syn = { workspace = true, features = ["full"] }
proc-macro2 = { workspace = true }

View file

@ -3286,10 +3286,18 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for TSImportType<'a> {
impl<'a, const MINIFY: bool> Gen<MINIFY> for TSImportAttributes<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
// { with: { ... } }
p.print_str("{ with: { ");
p.print_char(b'{');
p.print_soft_space();
self.attributes_keyword.gen(p, ctx);
p.print_str(":");
p.print_soft_space();
p.print_char(b'{');
p.print_soft_space();
p.print_list(&self.elements, ctx);
p.print_str(" }}");
p.print_soft_space();
p.print_char(b'}');
p.print_soft_space();
p.print_char(b'}');
}
}

View file

@ -1001,9 +1001,14 @@ impl<'a> ParserImpl<'a> {
fn parse_ts_import_attributes(&mut self) -> Result<TSImportAttributes<'a>> {
let span = self.start_span();
// { with:
self.expect(Kind::LCurly)?;
self.expect(Kind::With)?;
let attributes_keyword = match self.cur_kind() {
Kind::Assert if !self.cur_token().is_on_new_line => self.parse_identifier_name()?,
Kind::With => self.parse_identifier_name()?,
_ => {
return Err(self.unexpected());
}
};
self.expect(Kind::Colon)?;
self.expect(Kind::LCurly)?;
let elements = self.parse_delimited_list(
@ -1014,7 +1019,7 @@ impl<'a> ParserImpl<'a> {
)?;
self.expect(Kind::RCurly)?;
self.expect(Kind::RCurly)?;
Ok(self.ast.ts_import_attributes(span, elements))
Ok(self.ast.ts_import_attributes(self.end_span(span), attributes_keyword, elements))
}
fn parse_ts_import_attribute(&mut self) -> Result<TSImportAttribute<'a>> {

View file

@ -292,38 +292,39 @@ pub(crate) enum AncestorType {
TSImportTypeQualifier = 260,
TSImportTypeAttributes = 261,
TSImportTypeTypeParameters = 262,
TSImportAttributesElements = 263,
TSImportAttributeName = 264,
TSImportAttributeValue = 265,
TSFunctionTypeThisParam = 266,
TSFunctionTypeParams = 267,
TSFunctionTypeReturnType = 268,
TSFunctionTypeTypeParameters = 269,
TSConstructorTypeParams = 270,
TSConstructorTypeReturnType = 271,
TSConstructorTypeTypeParameters = 272,
TSMappedTypeTypeParameter = 273,
TSMappedTypeNameType = 274,
TSMappedTypeTypeAnnotation = 275,
TSTemplateLiteralTypeQuasis = 276,
TSTemplateLiteralTypeTypes = 277,
TSAsExpressionExpression = 278,
TSAsExpressionTypeAnnotation = 279,
TSSatisfiesExpressionExpression = 280,
TSSatisfiesExpressionTypeAnnotation = 281,
TSTypeAssertionExpression = 282,
TSTypeAssertionTypeAnnotation = 283,
TSImportEqualsDeclarationId = 284,
TSImportEqualsDeclarationModuleReference = 285,
TSExternalModuleReferenceExpression = 286,
TSNonNullExpressionExpression = 287,
DecoratorExpression = 288,
TSExportAssignmentExpression = 289,
TSNamespaceExportDeclarationId = 290,
TSInstantiationExpressionExpression = 291,
TSInstantiationExpressionTypeParameters = 292,
JSDocNullableTypeTypeAnnotation = 293,
JSDocNonNullableTypeTypeAnnotation = 294,
TSImportAttributesAttributesKeyword = 263,
TSImportAttributesElements = 264,
TSImportAttributeName = 265,
TSImportAttributeValue = 266,
TSFunctionTypeThisParam = 267,
TSFunctionTypeParams = 268,
TSFunctionTypeReturnType = 269,
TSFunctionTypeTypeParameters = 270,
TSConstructorTypeParams = 271,
TSConstructorTypeReturnType = 272,
TSConstructorTypeTypeParameters = 273,
TSMappedTypeTypeParameter = 274,
TSMappedTypeNameType = 275,
TSMappedTypeTypeAnnotation = 276,
TSTemplateLiteralTypeQuasis = 277,
TSTemplateLiteralTypeTypes = 278,
TSAsExpressionExpression = 279,
TSAsExpressionTypeAnnotation = 280,
TSSatisfiesExpressionExpression = 281,
TSSatisfiesExpressionTypeAnnotation = 282,
TSTypeAssertionExpression = 283,
TSTypeAssertionTypeAnnotation = 284,
TSImportEqualsDeclarationId = 285,
TSImportEqualsDeclarationModuleReference = 286,
TSExternalModuleReferenceExpression = 287,
TSNonNullExpressionExpression = 288,
DecoratorExpression = 289,
TSExportAssignmentExpression = 290,
TSNamespaceExportDeclarationId = 291,
TSInstantiationExpressionExpression = 292,
TSInstantiationExpressionTypeParameters = 293,
JSDocNullableTypeTypeAnnotation = 294,
JSDocNonNullableTypeTypeAnnotation = 295,
}
/// Ancestor type used in AST traversal.
@ -804,6 +805,8 @@ pub enum Ancestor<'a> {
AncestorType::TSImportTypeAttributes as u16,
TSImportTypeTypeParameters(TSImportTypeWithoutTypeParameters<'a>) =
AncestorType::TSImportTypeTypeParameters as u16,
TSImportAttributesAttributesKeyword(TSImportAttributesWithoutAttributesKeyword<'a>) =
AncestorType::TSImportAttributesAttributesKeyword as u16,
TSImportAttributesElements(TSImportAttributesWithoutElements<'a>) =
AncestorType::TSImportAttributesElements as u16,
TSImportAttributeName(TSImportAttributeWithoutName<'a>) =
@ -1726,7 +1729,10 @@ impl<'a> Ancestor<'a> {
#[inline]
pub fn is_ts_import_attributes(&self) -> bool {
matches!(self, Self::TSImportAttributesElements(_))
matches!(
self,
Self::TSImportAttributesAttributesKeyword(_) | Self::TSImportAttributesElements(_)
)
}
#[inline]
@ -11231,9 +11237,30 @@ impl<'a> TSImportTypeWithoutTypeParameters<'a> {
}
pub(crate) const OFFSET_TS_IMPORT_ATTRIBUTES_SPAN: usize = offset_of!(TSImportAttributes, span);
pub(crate) const OFFSET_TS_IMPORT_ATTRIBUTES_ATTRIBUTES_KEYWORD: usize =
offset_of!(TSImportAttributes, attributes_keyword);
pub(crate) const OFFSET_TS_IMPORT_ATTRIBUTES_ELEMENTS: usize =
offset_of!(TSImportAttributes, elements);
#[repr(transparent)]
#[derive(Debug)]
pub struct TSImportAttributesWithoutAttributesKeyword<'a>(pub(crate) *const TSImportAttributes<'a>);
impl<'a> TSImportAttributesWithoutAttributesKeyword<'a> {
#[inline]
pub fn span(&self) -> &Span {
unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_ATTRIBUTES_SPAN) as *const Span) }
}
#[inline]
pub fn elements(&self) -> &Vec<'a, TSImportAttribute<'a>> {
unsafe {
&*((self.0 as *const u8).add(OFFSET_TS_IMPORT_ATTRIBUTES_ELEMENTS)
as *const Vec<'a, TSImportAttribute<'a>>)
}
}
}
#[repr(transparent)]
#[derive(Debug)]
pub struct TSImportAttributesWithoutElements<'a>(pub(crate) *const TSImportAttributes<'a>);
@ -11243,6 +11270,14 @@ impl<'a> TSImportAttributesWithoutElements<'a> {
pub fn span(&self) -> &Span {
unsafe { &*((self.0 as *const u8).add(OFFSET_TS_IMPORT_ATTRIBUTES_SPAN) as *const Span) }
}
#[inline]
pub fn attributes_keyword(&self) -> &IdentifierName<'a> {
unsafe {
&*((self.0 as *const u8).add(OFFSET_TS_IMPORT_ATTRIBUTES_ATTRIBUTES_KEYWORD)
as *const IdentifierName<'a>)
}
}
}
pub(crate) const OFFSET_TS_IMPORT_ATTRIBUTE_SPAN: usize = offset_of!(TSImportAttribute, span);

View file

@ -5125,9 +5125,16 @@ pub(crate) unsafe fn walk_ts_import_attributes<'a, Tr: Traverse<'a>>(
ctx: &mut TraverseCtx<'a>,
) {
traverser.enter_ts_import_attributes(&mut *node, ctx);
ctx.push_stack(Ancestor::TSImportAttributesElements(
ancestor::TSImportAttributesWithoutElements(node),
ctx.push_stack(Ancestor::TSImportAttributesAttributesKeyword(
ancestor::TSImportAttributesWithoutAttributesKeyword(node),
));
walk_identifier_name(
traverser,
(node as *mut u8).add(ancestor::OFFSET_TS_IMPORT_ATTRIBUTES_ATTRIBUTES_KEYWORD)
as *mut IdentifierName,
ctx,
);
ctx.retag_stack(AncestorType::TSImportAttributesElements);
for item in (*((node as *mut u8).add(ancestor::OFFSET_TS_IMPORT_ATTRIBUTES_ELEMENTS)
as *mut Vec<TSImportAttribute>))
.iter_mut()

View file

@ -90,12 +90,11 @@ Negative Passed: 14/14 (100.00%)
· ─────────
╰────
× Expected `with` but found `string`
× Unexpected token
╭─[fail/oxc-2394.ts:20:22]
19 │ export type LocalInterface =
20 │ & import("pkg", {"resolution-mode": "require"}).RequireInterface
· ────────┬────────
· ╰── `with` expected
· ─────────────────
21 │ & import("pkg", {"resolution-mode": "import"}).ImportInterface;
╰────

View file

@ -1,8 +1,8 @@
commit: d8086f14
parser_typescript Summary:
AST Parsed : 6444/6456 (99.81%)
Positive Passed: 6421/6456 (99.46%)
AST Parsed : 6446/6456 (99.85%)
Positive Passed: 6423/6456 (99.49%)
Negative Passed: 1167/5653 (20.64%)
Expect Syntax Error: "compiler/ClassDeclaration10.ts"
Expect Syntax Error: "compiler/ClassDeclaration11.ts"
@ -4817,16 +4817,6 @@ Expect to Parse: "conformance/jsdoc/overloadTag3.ts"
· ──────
5 │ /**
╰────
Expect to Parse: "conformance/moduleResolution/resolutionModeImportType1.ts"
× Expected `with` but found `assert`
╭─[conformance/moduleResolution/resolutionModeImportType1.ts:2:38]
1 │ type Default = typeof import("foo").x;
2 │ type Import = typeof import("foo", { assert: { "resolution-mode": "import" } }).x;
· ───┬──
· ╰── `with` expected
3 │ type Require = typeof import("foo", { assert: { "resolution-mode": "require" } }).x;
╰────
Expect to Parse: "conformance/moduleResolution/untypedModuleImport.ts"
× Expected a semicolon or an implicit semicolon after a statement, but found none
@ -4845,16 +4835,6 @@ Expect to Parse: "conformance/moduleResolution/untypedModuleImport_vsAmbient.ts"
· ▲
╰────
help: Try insert a semicolon here
Expect to Parse: "conformance/node/nodeModulesImportTypeModeDeclarationEmit1.ts"
× Expected `with` but found `assert`
╭─[conformance/node/nodeModulesImportTypeModeDeclarationEmit1.ts:2:23]
1 │ export type LocalInterface =
2 │ & import("pkg", { assert: {"resolution-mode": "require"} }).RequireInterface
· ───┬──
· ╰── `with` expected
3 │ & import("pkg", { assert: {"resolution-mode": "import"} }).ImportInterface;
╰────
Expect to Parse: "conformance/salsa/annotatedThisPropertyInitializerDoesntNarrow.ts"
× Cannot use export statement outside a module