Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

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
3 changes: 2 additions & 1 deletion crates/oxc_ast/src/ast/ts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

q: isn't better to use enum in here?

pub elements: Vec<'a, TSImportAttribute<'a>>,
}

Expand Down Expand Up @@ -1251,7 +1252,7 @@ pub struct TSNonNullExpression<'a> {
/// @LogParam x: number // parameter decorator
/// ) {
/// // ...
/// }
/// }
/// }
/// ```
///
Expand Down
18 changes: 10 additions & 8 deletions crates/oxc_ast/src/generated/assert_layouts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
8 changes: 6 additions & 2 deletions crates/oxc_ast/src/generated/ast_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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`]
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_ast/src/generated/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
1 change: 1 addition & 0 deletions crates/oxc_ast/src/generated/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_ast_macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
14 changes: 11 additions & 3 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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'}');
}
}

Expand Down
11 changes: 8 additions & 3 deletions crates/oxc_parser/src/ts/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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>> {
Expand Down
101 changes: 68 additions & 33 deletions crates/oxc_traverse/src/ancestor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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>) =
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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>);
Expand All @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions crates/oxc_traverse/src/walk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
5 changes: 2 additions & 3 deletions tasks/coverage/parser_misc.snap
Original file line number Diff line number Diff line change
Expand Up @@ -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;
╰────

Expand Down
24 changes: 2 additions & 22 deletions tasks/coverage/parser_typescript.snap
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading