From c205b0df518889f85243d25d038b702e6b636eae Mon Sep 17 00:00:00 2001 From: Boshen Date: Sat, 24 Jan 2026 15:25:10 +0000 Subject: [PATCH] fix(ast): remove `ThisExpression` from `TSModuleReference` (#18489) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Removes `ThisExpression` variant from `TSModuleReference` since `import x = this` is invalid TypeScript (error TS1359) - Adds parser error when `this` is used in import equals declaration - Updates codegen, transformer, and linter rules to handle the new enum structure Closes #16841 ๐Ÿค– Generated with [Claude Code](https://claude.com/claude-code) --- apps/oxlint/src-js/generated/deserialize.js | 8 +-- apps/oxlint/src-js/generated/types.d.ts | 2 +- crates/oxc_ast/src/ast/ts.rs | 18 +++--- crates/oxc_ast/src/generated/ast_builder.rs | 62 +++++++++++++++++++ .../oxc_ast/src/generated/derive_clone_in.rs | 6 -- .../src/generated/derive_content_eq.rs | 1 - crates/oxc_ast/src/generated/derive_dummy.rs | 6 +- crates/oxc_ast/src/generated/derive_estree.rs | 1 - .../src/generated/derive_get_address.rs | 1 - .../oxc_ast/src/generated/derive_get_span.rs | 1 - .../src/generated/derive_get_span_mut.rs | 1 - crates/oxc_ast_visit/src/generated/visit.rs | 5 +- .../oxc_ast_visit/src/generated/visit_mut.rs | 5 +- crates/oxc_codegen/src/gen.rs | 3 +- .../src/ast_nodes/generated/ast_nodes.rs | 25 +++++--- .../src/ast_nodes/generated/format.rs | 15 ++++- crates/oxc_linter/src/rules/import/first.rs | 3 +- .../rules/typescript/no_require_imports.rs | 5 +- .../typescript/triple_slash_reference.rs | 3 +- crates/oxc_parser/src/ts/statement.rs | 36 ++++++++++- .../oxc_transformer/src/typescript/module.rs | 35 ++++++++--- crates/oxc_traverse/src/generated/walk.rs | 9 ++- .../parser/src-js/generated/deserialize/js.js | 8 +-- .../src-js/generated/deserialize/js_parent.js | 8 +-- .../src-js/generated/deserialize/js_range.js | 8 +-- .../generated/deserialize/js_range_parent.js | 8 +-- .../parser/src-js/generated/deserialize/ts.js | 8 +-- .../src-js/generated/deserialize/ts_parent.js | 8 +-- .../src-js/generated/deserialize/ts_range.js | 8 +-- .../generated/deserialize/ts_range_parent.js | 8 +-- .../src-js/generated/lazy/constructors.js | 8 +-- napi/parser/src-js/generated/lazy/walk.js | 9 +-- npm/oxc-types/types.d.ts | 2 +- .../coverage/misc/fail/import-equals-this.ts | 2 + tasks/coverage/snapshots/parser_babel.snap | 19 +++++- tasks/coverage/snapshots/parser_misc.snap | 9 ++- 36 files changed, 241 insertions(+), 123 deletions(-) create mode 100644 tasks/coverage/misc/fail/import-equals-this.ts diff --git a/apps/oxlint/src-js/generated/deserialize.js b/apps/oxlint/src-js/generated/deserialize.js index c0789590fef98..3cfdda66786f5 100644 --- a/apps/oxlint/src-js/generated/deserialize.js +++ b/apps/oxlint/src-js/generated/deserialize.js @@ -5558,13 +5558,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/apps/oxlint/src-js/generated/types.d.ts b/apps/oxlint/src-js/generated/types.d.ts index e9f47f4781b78..96109abfff3f8 100644 --- a/apps/oxlint/src-js/generated/types.d.ts +++ b/apps/oxlint/src-js/generated/types.d.ts @@ -1617,7 +1617,7 @@ export interface TSImportEqualsDeclaration extends Span { parent: Node; } -export type TSModuleReference = TSExternalModuleReference | TSTypeName; +export type TSModuleReference = TSExternalModuleReference | IdentifierReference | TSQualifiedName; export interface TSExternalModuleReference extends Span { type: "TSExternalModuleReference"; diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 3f81b1a79f604..37ddc31ce2e48 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -1628,20 +1628,24 @@ pub struct TSImportEqualsDeclaration<'a> { pub import_kind: ImportOrExportKind, } -inherit_variants! { /// TS Module Reference /// -/// Inherits variants from [`TSTypeName`]. See [`ast` module docs] for explanation of inheritance. +/// The right-hand side of an `import x = ...` declaration. /// -/// [`ast` module docs]: `super` +/// ## Examples +/// +/// ```ts +/// import x = foo; // IdentifierReference +/// import x = foo.bar; // QualifiedName +/// import x = require("x"); // ExternalModuleReference +/// ``` #[ast(visit)] #[derive(Debug)] #[generate_derive(CloneIn, Dummy, TakeIn, GetSpan, GetSpanMut, GetAddress, ContentEq, ESTree)] pub enum TSModuleReference<'a> { - ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>) = 3, - // `TSTypeName` variants added here by `inherit_variants!` macro - @inherit TSTypeName -} + ExternalModuleReference(Box<'a, TSExternalModuleReference<'a>>) = 0, + IdentifierReference(Box<'a, IdentifierReference<'a>>) = 1, + QualifiedName(Box<'a, TSQualifiedName<'a>>) = 2, } #[ast(visit)] diff --git a/crates/oxc_ast/src/generated/ast_builder.rs b/crates/oxc_ast/src/generated/ast_builder.rs index 34f20d85f8c8a..40b13195bf698 100644 --- a/crates/oxc_ast/src/generated/ast_builder.rs +++ b/crates/oxc_ast/src/generated/ast_builder.rs @@ -14857,6 +14857,68 @@ impl<'a> AstBuilder<'a> { ) } + /// Build a [`TSModuleReference::IdentifierReference`]. + /// + /// This node contains an [`IdentifierReference`] that will be stored in the memory arena. + /// + /// ## Parameters + /// * `span`: The [`Span`] covering this node + /// * `name`: The name of the identifier being referenced. + #[inline] + pub fn ts_module_reference_identifier_reference( + self, + span: Span, + name: A1, + ) -> TSModuleReference<'a> + where + A1: Into>, + { + TSModuleReference::IdentifierReference(self.alloc_identifier_reference(span, name)) + } + + /// Build a [`TSModuleReference::IdentifierReference`] with `reference_id`. + /// + /// This node contains an [`IdentifierReference`] that will be stored in the memory arena. + /// + /// ## Parameters + /// * `span`: The [`Span`] covering this node + /// * `name`: The name of the identifier being referenced. + /// * `reference_id`: Reference ID + #[inline] + pub fn ts_module_reference_identifier_reference_with_reference_id( + self, + span: Span, + name: A1, + reference_id: ReferenceId, + ) -> TSModuleReference<'a> + where + A1: Into>, + { + TSModuleReference::IdentifierReference(self.alloc_identifier_reference_with_reference_id( + span, + name, + reference_id, + )) + } + + /// Build a [`TSModuleReference::QualifiedName`]. + /// + /// This node contains a [`TSQualifiedName`] that will be stored in the memory arena. + /// + /// ## Parameters + /// * `span`: The [`Span`] covering this node + /// * `left` + /// * `right` + #[inline] + pub fn ts_module_reference_qualified_name( + self, + span: Span, + left: TSTypeName<'a>, + right: IdentifierName<'a>, + ) -> TSModuleReference<'a> { + TSModuleReference::QualifiedName(self.alloc_ts_qualified_name(span, left, right)) + } + /// Build a [`TSExternalModuleReference`]. /// /// If you want the built node to be allocated in the memory arena, diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index 49677db77fd21..cb978aac8dcf5 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -7757,9 +7757,6 @@ impl<'new_alloc> CloneIn<'new_alloc> for TSModuleReference<'_> { Self::QualifiedName(it) => { TSModuleReference::QualifiedName(CloneIn::clone_in(it, allocator)) } - Self::ThisExpression(it) => { - TSModuleReference::ThisExpression(CloneIn::clone_in(it, allocator)) - } } } @@ -7774,9 +7771,6 @@ impl<'new_alloc> CloneIn<'new_alloc> for TSModuleReference<'_> { Self::QualifiedName(it) => { TSModuleReference::QualifiedName(CloneIn::clone_in_with_semantic_ids(it, allocator)) } - Self::ThisExpression(it) => TSModuleReference::ThisExpression( - CloneIn::clone_in_with_semantic_ids(it, allocator), - ), } } } diff --git a/crates/oxc_ast/src/generated/derive_content_eq.rs b/crates/oxc_ast/src/generated/derive_content_eq.rs index a61a4431942ea..753c00634a7ff 100644 --- a/crates/oxc_ast/src/generated/derive_content_eq.rs +++ b/crates/oxc_ast/src/generated/derive_content_eq.rs @@ -2434,7 +2434,6 @@ impl ContentEq for TSModuleReference<'_> { (Self::ExternalModuleReference(a), Self::ExternalModuleReference(b)) => a.content_eq(b), (Self::IdentifierReference(a), Self::IdentifierReference(b)) => a.content_eq(b), (Self::QualifiedName(a), Self::QualifiedName(b)) => a.content_eq(b), - (Self::ThisExpression(a), Self::ThisExpression(b)) => a.content_eq(b), _ => false, } } diff --git a/crates/oxc_ast/src/generated/derive_dummy.rs b/crates/oxc_ast/src/generated/derive_dummy.rs index 8aeed811a4a2c..636f9f939ed28 100644 --- a/crates/oxc_ast/src/generated/derive_dummy.rs +++ b/crates/oxc_ast/src/generated/derive_dummy.rs @@ -2798,7 +2798,7 @@ impl<'a> Dummy<'a> for TSTypeAssertion<'a> { impl<'a> Dummy<'a> for TSImportEqualsDeclaration<'a> { /// Create a dummy [`TSImportEqualsDeclaration`]. /// - /// Has cost of making 1 allocation (8 bytes). + /// Has cost of making 1 allocation (32 bytes). fn dummy(allocator: &'a Allocator) -> Self { Self { span: Dummy::dummy(allocator), @@ -2812,9 +2812,9 @@ impl<'a> Dummy<'a> for TSImportEqualsDeclaration<'a> { impl<'a> Dummy<'a> for TSModuleReference<'a> { /// Create a dummy [`TSModuleReference`]. /// - /// Has cost of making 1 allocation (8 bytes). + /// Has cost of making 1 allocation (32 bytes). fn dummy(allocator: &'a Allocator) -> Self { - Self::ThisExpression(Dummy::dummy(allocator)) + Self::IdentifierReference(Dummy::dummy(allocator)) } } diff --git a/crates/oxc_ast/src/generated/derive_estree.rs b/crates/oxc_ast/src/generated/derive_estree.rs index 4fbd3b080a76b..2dd94e6dd9976 100644 --- a/crates/oxc_ast/src/generated/derive_estree.rs +++ b/crates/oxc_ast/src/generated/derive_estree.rs @@ -3111,7 +3111,6 @@ impl ESTree for TSModuleReference<'_> { Self::ExternalModuleReference(it) => it.serialize(serializer), Self::IdentifierReference(it) => it.serialize(serializer), Self::QualifiedName(it) => it.serialize(serializer), - Self::ThisExpression(it) => it.serialize(serializer), } } } diff --git a/crates/oxc_ast/src/generated/derive_get_address.rs b/crates/oxc_ast/src/generated/derive_get_address.rs index 3c458fa256ddb..8db63e5746eba 100644 --- a/crates/oxc_ast/src/generated/derive_get_address.rs +++ b/crates/oxc_ast/src/generated/derive_get_address.rs @@ -791,7 +791,6 @@ impl GetAddress for TSModuleReference<'_> { Self::ExternalModuleReference(it) => GetAddress::address(it), Self::IdentifierReference(it) => GetAddress::address(it), Self::QualifiedName(it) => GetAddress::address(it), - Self::ThisExpression(it) => GetAddress::address(it), } } } diff --git a/crates/oxc_ast/src/generated/derive_get_span.rs b/crates/oxc_ast/src/generated/derive_get_span.rs index 7c1e5e97c784f..52996f824fddc 100644 --- a/crates/oxc_ast/src/generated/derive_get_span.rs +++ b/crates/oxc_ast/src/generated/derive_get_span.rs @@ -2113,7 +2113,6 @@ impl GetSpan for TSModuleReference<'_> { Self::ExternalModuleReference(it) => GetSpan::span(&**it), Self::IdentifierReference(it) => GetSpan::span(&**it), Self::QualifiedName(it) => GetSpan::span(&**it), - Self::ThisExpression(it) => GetSpan::span(&**it), } } } diff --git a/crates/oxc_ast/src/generated/derive_get_span_mut.rs b/crates/oxc_ast/src/generated/derive_get_span_mut.rs index 1ccd4889d8efc..690e9987b7956 100644 --- a/crates/oxc_ast/src/generated/derive_get_span_mut.rs +++ b/crates/oxc_ast/src/generated/derive_get_span_mut.rs @@ -2113,7 +2113,6 @@ impl GetSpanMut for TSModuleReference<'_> { Self::ExternalModuleReference(it) => GetSpanMut::span_mut(&mut **it), Self::IdentifierReference(it) => GetSpanMut::span_mut(&mut **it), Self::QualifiedName(it) => GetSpanMut::span_mut(&mut **it), - Self::ThisExpression(it) => GetSpanMut::span_mut(&mut **it), } } } diff --git a/crates/oxc_ast_visit/src/generated/visit.rs b/crates/oxc_ast_visit/src/generated/visit.rs index 4803b370cf2ed..72ba0a8e66a77 100644 --- a/crates/oxc_ast_visit/src/generated/visit.rs +++ b/crates/oxc_ast_visit/src/generated/visit.rs @@ -4127,9 +4127,8 @@ pub mod walk { TSModuleReference::ExternalModuleReference(it) => { visitor.visit_ts_external_module_reference(it) } - match_ts_type_name!(TSModuleReference) => { - visitor.visit_ts_type_name(it.to_ts_type_name()) - } + TSModuleReference::IdentifierReference(it) => visitor.visit_identifier_reference(it), + TSModuleReference::QualifiedName(it) => visitor.visit_ts_qualified_name(it), } } diff --git a/crates/oxc_ast_visit/src/generated/visit_mut.rs b/crates/oxc_ast_visit/src/generated/visit_mut.rs index d55e349986e82..2d3139e0388eb 100644 --- a/crates/oxc_ast_visit/src/generated/visit_mut.rs +++ b/crates/oxc_ast_visit/src/generated/visit_mut.rs @@ -4351,9 +4351,8 @@ pub mod walk_mut { TSModuleReference::ExternalModuleReference(it) => { visitor.visit_ts_external_module_reference(it) } - match_ts_type_name!(TSModuleReference) => { - visitor.visit_ts_type_name(it.to_ts_type_name_mut()) - } + TSModuleReference::IdentifierReference(it) => visitor.visit_identifier_reference(it), + TSModuleReference::QualifiedName(it) => visitor.visit_ts_qualified_name(it), } } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index ff326c68eb1db..ef7322e8cd1b5 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -3844,7 +3844,8 @@ impl Gen for TSModuleReference<'_> { p.print_string_literal(&decl.expression, false); p.print_ascii_byte(b')'); } - match_ts_type_name!(Self) => self.to_ts_type_name().print(p, ctx), + Self::IdentifierReference(ident) => ident.print(p, ctx), + Self::QualifiedName(qualified) => qualified.print(p, ctx), } } } diff --git a/crates/oxc_formatter/src/ast_nodes/generated/ast_nodes.rs b/crates/oxc_formatter/src/ast_nodes/generated/ast_nodes.rs index d3a54e0c35d01..5de415e3b7c95 100644 --- a/crates/oxc_formatter/src/ast_nodes/generated/ast_nodes.rs +++ b/crates/oxc_formatter/src/ast_nodes/generated/ast_nodes.rs @@ -9433,16 +9433,21 @@ impl<'a> AstNode<'a, TSModuleReference<'a>> { following_span_start: self.following_span_start, })) } - it @ match_ts_type_name!(TSModuleReference) => { - return self - .allocator - .alloc(AstNode { - inner: it.to_ts_type_name(), - parent, - allocator: self.allocator, - following_span_start: self.following_span_start, - }) - .as_ast_nodes(); + TSModuleReference::IdentifierReference(s) => { + AstNodes::IdentifierReference(self.allocator.alloc(AstNode { + inner: s.as_ref(), + parent, + allocator: self.allocator, + following_span_start: self.following_span_start, + })) + } + TSModuleReference::QualifiedName(s) => { + AstNodes::TSQualifiedName(self.allocator.alloc(AstNode { + inner: s.as_ref(), + parent, + allocator: self.allocator, + following_span_start: self.following_span_start, + })) } }; self.allocator.alloc(node) diff --git a/crates/oxc_formatter/src/ast_nodes/generated/format.rs b/crates/oxc_formatter/src/ast_nodes/generated/format.rs index d816e284229cf..ee400d014c712 100644 --- a/crates/oxc_formatter/src/ast_nodes/generated/format.rs +++ b/crates/oxc_formatter/src/ast_nodes/generated/format.rs @@ -5519,10 +5519,19 @@ impl<'a> Format<'a> for AstNode<'a, TSModuleReference<'a>> { }) .fmt(f); } - it @ match_ts_type_name!(TSModuleReference) => { - let inner = it.to_ts_type_name(); + TSModuleReference::IdentifierReference(inner) => { allocator - .alloc(AstNode::<'a, TSTypeName> { + .alloc(AstNode:: { + inner, + parent, + allocator, + following_span_start: self.following_span_start, + }) + .fmt(f); + } + TSModuleReference::QualifiedName(inner) => { + allocator + .alloc(AstNode:: { inner, parent, allocator, diff --git a/crates/oxc_linter/src/rules/import/first.rs b/crates/oxc_linter/src/rules/import/first.rs index 0fb33b006c137..cde8628d93d66 100644 --- a/crates/oxc_linter/src/rules/import/first.rs +++ b/crates/oxc_linter/src/rules/import/first.rs @@ -116,8 +116,7 @@ impl Rule for First { } } TSModuleReference::IdentifierReference(_) - | TSModuleReference::QualifiedName(_) - | TSModuleReference::ThisExpression(_) => {} + | TSModuleReference::QualifiedName(_) => {} }, Statement::ImportDeclaration(decl) => { if matches!(self.0, AbsoluteFirst::AbsoluteFirst) { diff --git a/crates/oxc_linter/src/rules/typescript/no_require_imports.rs b/crates/oxc_linter/src/rules/typescript/no_require_imports.rs index 8d62fff577cff..1129fd239322c 100644 --- a/crates/oxc_linter/src/rules/typescript/no_require_imports.rs +++ b/crates/oxc_linter/src/rules/typescript/no_require_imports.rs @@ -192,9 +192,8 @@ impl Rule for NoRequireImports { ctx.diagnostic(no_require_imports_diagnostic(decl.span)); } - TSModuleReference::IdentifierReference(_) - | TSModuleReference::QualifiedName(_) - | TSModuleReference::ThisExpression(_) => {} + TSModuleReference::IdentifierReference(_) | TSModuleReference::QualifiedName(_) => { + } }, _ => {} } diff --git a/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs b/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs index 2dd1b879bb729..5f1de31aef547 100644 --- a/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs +++ b/crates/oxc_linter/src/rules/typescript/triple_slash_reference.rs @@ -143,8 +143,7 @@ impl Rule for TripleSlashReference { } } TSModuleReference::IdentifierReference(_) - | TSModuleReference::QualifiedName(_) - | TSModuleReference::ThisExpression(_) => {} + | TSModuleReference::QualifiedName(_) => {} }, Statement::ImportDeclaration(decl) => { if let Some(v) = refs_for_import.get(decl.source.value.as_str()) { diff --git a/crates/oxc_parser/src/ts/statement.rs b/crates/oxc_parser/src/ts/statement.rs index 988ef19aab938..da7f0d4a18110 100644 --- a/crates/oxc_parser/src/ts/statement.rs +++ b/crates/oxc_parser/src/ts/statement.rs @@ -573,8 +573,7 @@ impl<'a> ParserImpl<'a> { expression, ) } else { - let type_name = self.parse_ts_type_name(); - TSModuleReference::from(type_name) + self.parse_ts_module_reference(reference_span) }; self.asi(); @@ -588,6 +587,39 @@ impl<'a> ParserImpl<'a> { self.ast.declaration_ts_import_equals(span, identifier, module_reference, import_kind) } + /// Parse `TSModuleReference` for `import x = foo` or `import x = foo.bar`. + /// + /// Unlike `parse_ts_type_name`, this does not allow `this` as the identifier. + fn parse_ts_module_reference(&mut self, span: u32) -> TSModuleReference<'a> { + // Check for invalid `this` keyword + if self.at(Kind::This) { + let this_span = self.cur_token().span(); + self.error(diagnostics::identifier_reserved_word(this_span, "this")); + self.bump_any(); + // Recover by creating a dummy identifier + let ident = self.ast.alloc_identifier_reference(this_span, "this"); + return TSModuleReference::IdentifierReference(ident); + } + + let ident = self.parse_identifier_name(); + let left = self.ast.ts_type_name_identifier_reference(ident.span, ident.name); + + // Parse qualified name: foo.bar.baz + let type_name = + if self.at(Kind::Dot) { self.parse_ts_qualified_type_name(span, left) } else { left }; + + // Convert TSTypeName to TSModuleReference + match type_name { + TSTypeName::IdentifierReference(ident) => TSModuleReference::IdentifierReference(ident), + TSTypeName::QualifiedName(qualified) => TSModuleReference::QualifiedName(qualified), + TSTypeName::ThisExpression(_) => { + // This shouldn't happen since we check for `this` above, + // but handle it for completeness + unreachable!("ThisExpression should have been caught earlier") + } + } + } + pub(crate) fn parse_ts_this_parameter(&mut self) -> TSThisParameter<'a> { let span = self.start_span(); self.bump_any(); diff --git a/crates/oxc_transformer/src/typescript/module.rs b/crates/oxc_transformer/src/typescript/module.rs index 50cbb38e35d58..729b9451e93cf 100644 --- a/crates/oxc_transformer/src/typescript/module.rs +++ b/crates/oxc_transformer/src/typescript/module.rs @@ -120,13 +120,27 @@ impl<'a> TypeScriptModule<'a, '_> { flags.remove(SymbolFlags::Import); let (kind, init) = match &mut decl.module_reference { - type_name @ match_ts_type_name!(TSModuleReference) => { + TSModuleReference::IdentifierReference(ident) => { flags.insert(SymbolFlags::FunctionScopedVariable); - ( - VariableDeclarationKind::Var, - self.transform_ts_type_name(&mut *type_name.to_ts_type_name_mut(), ctx), - ) + let ident = ident.clone(); + let reference = ctx.scoping_mut().get_reference_mut(ident.reference_id()); + *reference.flags_mut() = ReferenceFlags::Read; + (VariableDeclarationKind::Var, Expression::Identifier(ctx.alloc(ident))) + } + TSModuleReference::QualifiedName(qualified_name) => { + flags.insert(SymbolFlags::FunctionScopedVariable); + + let init = ctx + .ast + .member_expression_static( + SPAN, + self.transform_ts_type_name(&mut qualified_name.left, ctx), + qualified_name.right.clone(), + false, + ) + .into(); + (VariableDeclarationKind::Var, init) } TSModuleReference::ExternalModuleReference(reference) => { flags.insert(SymbolFlags::BlockScopedVariable | SymbolFlags::ConstVariable); @@ -237,9 +251,14 @@ impl<'a> TypeScriptModule<'a, '_> { // For `import foo = bar.baz`, change the reference to `bar` from Read to Type. // For `import foo = require('module')`, there's no identifier reference to change. - if let module_reference @ match_ts_type_name!(TSModuleReference) = &decl.module_reference - && let Some(ident) = module_reference.to_ts_type_name().get_identifier_reference() - { + let ident = match &decl.module_reference { + TSModuleReference::IdentifierReference(ident) => Some(ident.as_ref()), + TSModuleReference::QualifiedName(qualified) => { + qualified.left.get_identifier_reference() + } + TSModuleReference::ExternalModuleReference(_) => None, + }; + if let Some(ident) = ident { let reference = ctx.scoping_mut().get_reference_mut(ident.reference_id()); // The binding of TSImportEqualsDeclaration will be treated as unused // because there is no value reference, so it will be removed. diff --git a/crates/oxc_traverse/src/generated/walk.rs b/crates/oxc_traverse/src/generated/walk.rs index f3b23e25254dc..9ccdc0cf98d7a 100644 --- a/crates/oxc_traverse/src/generated/walk.rs +++ b/crates/oxc_traverse/src/generated/walk.rs @@ -5548,9 +5548,12 @@ unsafe fn walk_ts_module_reference<'a, State, Tr: Traverse<'a, State>>( TSModuleReference::ExternalModuleReference(node) => { walk_ts_external_module_reference(traverser, (&mut **node) as *mut _, ctx) } - TSModuleReference::IdentifierReference(_) - | TSModuleReference::QualifiedName(_) - | TSModuleReference::ThisExpression(_) => walk_ts_type_name(traverser, node as *mut _, ctx), + TSModuleReference::IdentifierReference(node) => { + walk_identifier_reference(traverser, (&mut **node) as *mut _, ctx) + } + TSModuleReference::QualifiedName(node) => { + walk_ts_qualified_name(traverser, (&mut **node) as *mut _, ctx) + } } traverser.exit_ts_module_reference(&mut *node, ctx); } diff --git a/napi/parser/src-js/generated/deserialize/js.js b/napi/parser/src-js/generated/deserialize/js.js index 3e55d0021297c..70fa40ce1abb7 100644 --- a/napi/parser/src-js/generated/deserialize/js.js +++ b/napi/parser/src-js/generated/deserialize/js.js @@ -3978,13 +3978,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/js_parent.js b/napi/parser/src-js/generated/deserialize/js_parent.js index 90193d3bbfc55..bfae6949c21c6 100644 --- a/napi/parser/src-js/generated/deserialize/js_parent.js +++ b/napi/parser/src-js/generated/deserialize/js_parent.js @@ -4489,13 +4489,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/js_range.js b/napi/parser/src-js/generated/deserialize/js_range.js index 90bc3f2d531e7..bbd2bf4ba0b57 100644 --- a/napi/parser/src-js/generated/deserialize/js_range.js +++ b/napi/parser/src-js/generated/deserialize/js_range.js @@ -4474,13 +4474,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/js_range_parent.js b/napi/parser/src-js/generated/deserialize/js_range_parent.js index 1a48739b92cef..8a9521bc997b2 100644 --- a/napi/parser/src-js/generated/deserialize/js_range_parent.js +++ b/napi/parser/src-js/generated/deserialize/js_range_parent.js @@ -4988,13 +4988,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/ts.js b/napi/parser/src-js/generated/deserialize/ts.js index 65cd4e26fb20f..177e4138454ae 100644 --- a/napi/parser/src-js/generated/deserialize/ts.js +++ b/napi/parser/src-js/generated/deserialize/ts.js @@ -4285,13 +4285,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/ts_parent.js b/napi/parser/src-js/generated/deserialize/ts_parent.js index a58ae0e2588c0..48c6f7bc5dd86 100644 --- a/napi/parser/src-js/generated/deserialize/ts_parent.js +++ b/napi/parser/src-js/generated/deserialize/ts_parent.js @@ -4822,13 +4822,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/ts_range.js b/napi/parser/src-js/generated/deserialize/ts_range.js index a9990d8ca1110..233ad87a3f756 100644 --- a/napi/parser/src-js/generated/deserialize/ts_range.js +++ b/napi/parser/src-js/generated/deserialize/ts_range.js @@ -4815,13 +4815,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/deserialize/ts_range_parent.js b/napi/parser/src-js/generated/deserialize/ts_range_parent.js index e9dcf325c2891..b34079aa2da53 100644 --- a/napi/parser/src-js/generated/deserialize/ts_range_parent.js +++ b/napi/parser/src-js/generated/deserialize/ts_range_parent.js @@ -5352,13 +5352,11 @@ function deserializeTSImportEqualsDeclaration(pos) { function deserializeTSModuleReference(pos) { switch (uint8[pos]) { case 0: - return deserializeBoxIdentifierReference(pos + 8); + return deserializeBoxTSExternalModuleReference(pos + 8); case 1: - return deserializeBoxTSQualifiedName(pos + 8); + return deserializeBoxIdentifierReference(pos + 8); case 2: - return deserializeBoxThisExpression(pos + 8); - case 3: - return deserializeBoxTSExternalModuleReference(pos + 8); + return deserializeBoxTSQualifiedName(pos + 8); default: throw Error(`Unexpected discriminant ${uint8[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/lazy/constructors.js b/napi/parser/src-js/generated/lazy/constructors.js index debec7dd5fb9a..4cb56cc36e224 100644 --- a/napi/parser/src-js/generated/lazy/constructors.js +++ b/napi/parser/src-js/generated/lazy/constructors.js @@ -11274,13 +11274,11 @@ const DebugTSImportEqualsDeclaration = class TSImportEqualsDeclaration {}; function constructTSModuleReference(pos, ast) { switch (ast.buffer[pos]) { case 0: - return constructBoxIdentifierReference(pos + 8, ast); + return constructBoxTSExternalModuleReference(pos + 8, ast); case 1: - return constructBoxTSQualifiedName(pos + 8, ast); + return constructBoxIdentifierReference(pos + 8, ast); case 2: - return constructBoxThisExpression(pos + 8, ast); - case 3: - return constructBoxTSExternalModuleReference(pos + 8, ast); + return constructBoxTSQualifiedName(pos + 8, ast); default: throw new Error(`Unexpected discriminant ${ast.buffer[pos]} for TSModuleReference`); } diff --git a/napi/parser/src-js/generated/lazy/walk.js b/napi/parser/src-js/generated/lazy/walk.js index 42625d2fc9c6e..45832c19eba3b 100644 --- a/napi/parser/src-js/generated/lazy/walk.js +++ b/napi/parser/src-js/generated/lazy/walk.js @@ -4572,16 +4572,13 @@ function walkTSImportEqualsDeclaration(pos, ast, visitors) { function walkTSModuleReference(pos, ast, visitors) { switch (ast.buffer[pos]) { case 0: - walkBoxIdentifierReference(pos + 8, ast, visitors); + walkBoxTSExternalModuleReference(pos + 8, ast, visitors); return; case 1: - walkBoxTSQualifiedName(pos + 8, ast, visitors); + walkBoxIdentifierReference(pos + 8, ast, visitors); return; case 2: - walkBoxThisExpression(pos + 8, ast, visitors); - return; - case 3: - walkBoxTSExternalModuleReference(pos + 8, ast, visitors); + walkBoxTSQualifiedName(pos + 8, ast, visitors); return; default: throw new Error(`Unexpected discriminant ${ast.buffer[pos]} for TSModuleReference`); diff --git a/npm/oxc-types/types.d.ts b/npm/oxc-types/types.d.ts index 17256f16ccdc2..22279168777a3 100644 --- a/npm/oxc-types/types.d.ts +++ b/npm/oxc-types/types.d.ts @@ -1610,7 +1610,7 @@ export interface TSImportEqualsDeclaration extends Span { parent?: Node; } -export type TSModuleReference = TSExternalModuleReference | TSTypeName; +export type TSModuleReference = TSExternalModuleReference | IdentifierReference | TSQualifiedName; export interface TSExternalModuleReference extends Span { type: "TSExternalModuleReference"; diff --git a/tasks/coverage/misc/fail/import-equals-this.ts b/tasks/coverage/misc/fail/import-equals-this.ts new file mode 100644 index 0000000000000..c450d4d88bee7 --- /dev/null +++ b/tasks/coverage/misc/fail/import-equals-this.ts @@ -0,0 +1,2 @@ +// `this` is not valid in import equals declaration +import x = this; diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 8896cdd12ae0c..3429c3a2e0fca 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -3,7 +3,7 @@ commit: 6ef16ca4 parser_babel Summary: AST Parsed : 2217/2221 (99.82%) Positive Passed: 2204/2221 (99.23%) -Negative Passed: 1654/1689 (97.93%) +Negative Passed: 1655/1689 (97.99%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2026/explicit-resource-management/invalid-for-using-of-no-initializer/input.js Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/cast/unparenthesized-assert-and-assign/input.ts @@ -60,8 +60,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-2/input.ts -Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-disallowed/input.ts - Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/type-arguments/instantiation-expression-property-access/input.ts Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/const-type-parameters-invalid/input.ts @@ -13631,6 +13629,21 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc โ•ฐโ”€โ”€โ”€โ”€ help: Either remove this `await` or add the `async` keyword to the enclosing function + ร— Identifier expected. 'this' is a reserved word that cannot be used here. + โ•ญโ”€[babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-disallowed/input.ts:2:12] + 1 โ”‚ import X = Y.if; + 2 โ”‚ import A = this.A; + ยท โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€ + + ร— Expected a semicolon or an implicit semicolon after a statement, but found none + โ•ญโ”€[babel/packages/babel-parser/test/fixtures/typescript/regression/keyword-qualified-type-disallowed/input.ts:2:16] + 1 โ”‚ import X = Y.if; + 2 โ”‚ import A = this.A; + ยท โ–ฒ + โ•ฐโ”€โ”€โ”€โ”€ + help: Try inserting a semicolon here + ร— Identifier `A` has already been declared โ•ญโ”€[babel/packages/babel-parser/test/fixtures/typescript/scope/redeclaration-class-class/input.ts:1:7] 1 โ”‚ class A {} diff --git a/tasks/coverage/snapshots/parser_misc.snap b/tasks/coverage/snapshots/parser_misc.snap index 8bc04a79738f2..03c7fff470fe1 100644 --- a/tasks/coverage/snapshots/parser_misc.snap +++ b/tasks/coverage/snapshots/parser_misc.snap @@ -1,7 +1,7 @@ parser_misc Summary: AST Parsed : 64/64 (100.00%) Positive Passed: 64/64 (100.00%) -Negative Passed: 133/133 (100.00%) +Negative Passed: 134/134 (100.00%) ร— Cannot assign to 'arguments' in strict mode โ•ญโ”€[misc/fail/arguments-eval.ts:1:10] @@ -254,6 +254,13 @@ Negative Passed: 133/133 (100.00%) ยท โ•ฐโ”€โ”€ `from` expected โ•ฐโ”€โ”€โ”€โ”€ + ร— Identifier expected. 'this' is a reserved word that cannot be used here. + โ•ญโ”€[misc/fail/import-equals-this.ts:2:12] + 1 โ”‚ // `this` is not valid in import equals declaration + 2 โ”‚ import x = this; + ยท โ”€โ”€โ”€โ”€ + โ•ฐโ”€โ”€โ”€โ”€ + ร— Expected `from` but found `string` โ•ญโ”€[misc/fail/import-from-str.js:1:13] 1 โ”‚ import from 'module';