From 0a086860dab5bc9f5121350e27dcf91ae25c20c6 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Thu, 11 Jan 2024 23:29:29 +0800 Subject: [PATCH] feat(ast): visit TSModuleReference (#1998) ### Failed cases: * https://github.com/babel/babel/blob/7c29fbc4db7809d24789235b0597e7e8dd61c4ae/packages/babel-plugin-transform-typescript/test/fixtures/imports/elision-qualifiedname/input.ts * https://github.com/babel/babel/blob/7c29fbc4db7809d24789235b0597e7e8dd61c4ae/packages/babel-plugin-transform-typescript/test/fixtures/imports/elide-react/input.ts We need to distinguish whether a reference is a type or a js variable --- crates/oxc_ast/src/ast/ts.rs | 11 +++++- crates/oxc_ast/src/ast_kind.rs | 10 ++++++ crates/oxc_ast/src/visit.rs | 42 +++++++++++++++++++---- crates/oxc_ast/src/visit_mut.rs | 42 +++++++++++++++++++---- tasks/coverage/parser_typescript.snap | 40 +++++++++++++++++++++ tasks/transform_conformance/babel.snap.md | 4 +-- 6 files changed, 132 insertions(+), 17 deletions(-) diff --git a/crates/oxc_ast/src/ast/ts.rs b/crates/oxc_ast/src/ast/ts.rs index 9ad24a25b..b7ba7b6f6 100644 --- a/crates/oxc_ast/src/ast/ts.rs +++ b/crates/oxc_ast/src/ast/ts.rs @@ -4,7 +4,7 @@ //! [Archived TypeScript spec](https://github.com/microsoft/TypeScript/blob/3c99d50da5a579d9fa92d02664b1b66d4ff55944/doc/spec-ARCHIVED.md) use oxc_allocator::{Box, Vec}; -use oxc_span::{Atom, Span}; +use oxc_span::{Atom, GetSpan, Span}; #[cfg(feature = "serde")] use serde::Serialize; @@ -416,6 +416,15 @@ impl<'a> TSTypeName<'a> { } } +impl GetSpan for TSTypeName<'_> { + fn span(&self) -> Span { + match self { + TSTypeName::IdentifierReference(ident) => ident.span, + TSTypeName::QualifiedName(name) => name.span, + } + } +} + #[derive(Debug, Hash)] #[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type", rename_all = "camelCase"))] pub struct TSQualifiedName<'a> { diff --git a/crates/oxc_ast/src/ast_kind.rs b/crates/oxc_ast/src/ast_kind.rs index fb820d83a..ac9a2714c 100644 --- a/crates/oxc_ast/src/ast_kind.rs +++ b/crates/oxc_ast/src/ast_kind.rs @@ -152,6 +152,10 @@ pub enum AstKind<'a> { TSEnumBody(&'a TSEnumBody<'a>), TSImportEqualsDeclaration(&'a TSImportEqualsDeclaration<'a>), + TSTypeName(&'a TSTypeName<'a>), + TSExternalModuleReference(&'a TSExternalModuleReference), + TSQualifiedName(&'a TSQualifiedName<'a>), + TSInterfaceDeclaration(&'a TSInterfaceDeclaration<'a>), TSModuleDeclaration(&'a TSModuleDeclaration<'a>), TSTypeAliasDeclaration(&'a TSTypeAliasDeclaration<'a>), @@ -456,6 +460,9 @@ impl<'a> GetSpan for AstKind<'a> { Self::TSEnumBody(x) => x.span, Self::TSImportEqualsDeclaration(x) => x.span, + Self::TSTypeName(x) => x.span(), + Self::TSExternalModuleReference(x) => x.span, + Self::TSQualifiedName(x) => x.span, Self::TSInterfaceDeclaration(x) => x.span, Self::TSModuleDeclaration(x) => x.span, Self::TSTypeAliasDeclaration(x) => x.span, @@ -633,6 +640,9 @@ impl<'a> AstKind<'a> { Self::TSEnumMember(_) => "TSEnumMember".into(), Self::TSImportEqualsDeclaration(_) => "TSImportEqualsDeclaration".into(), + Self::TSTypeName(_) => "TSTypeName".into(), + Self::TSExternalModuleReference(_) => "TSExternalModuleReference".into(), + Self::TSQualifiedName(_) => "TSQualifiedName".into(), Self::TSInterfaceDeclaration(_) => "TSInterfaceDeclaration".into(), Self::TSModuleDeclaration(_) => "TSModuleDeclaration".into(), Self::TSTypeAliasDeclaration(_) => "TSTypeAliasDeclaration".into(), diff --git a/crates/oxc_ast/src/visit.rs b/crates/oxc_ast/src/visit.rs index 176f740fb..a95694aee 100644 --- a/crates/oxc_ast/src/visit.rs +++ b/crates/oxc_ast/src/visit.rs @@ -1451,6 +1451,41 @@ pub trait Visit<'a>: Sized { let kind = AstKind::TSImportEqualsDeclaration(self.alloc(decl)); self.enter_node(kind); self.visit_binding_identifier(&decl.id); + self.visit_ts_module_reference(&decl.module_reference); + self.leave_node(kind); + } + + fn visit_ts_module_reference(&mut self, reference: &TSModuleReference<'a>) { + match reference { + TSModuleReference::TypeName(name) => self.visit_ts_type_name(name), + TSModuleReference::ExternalModuleReference(reference) => { + self.visit_ts_external_module_reference(reference); + } + } + } + + fn visit_ts_type_name(&mut self, name: &TSTypeName<'a>) { + let kind = AstKind::TSTypeName(self.alloc(name)); + self.enter_node(kind); + match &name { + TSTypeName::IdentifierReference(ident) => self.visit_identifier_reference(ident), + TSTypeName::QualifiedName(name) => self.visit_ts_qualified_name(name), + } + self.leave_node(kind); + } + + fn visit_ts_external_module_reference(&mut self, reference: &TSExternalModuleReference) { + let kind = AstKind::TSExternalModuleReference(self.alloc(reference)); + self.enter_node(kind); + self.visit_string_literal(&reference.expression); + self.leave_node(kind); + } + + fn visit_ts_qualified_name(&mut self, name: &TSQualifiedName<'a>) { + let kind = AstKind::TSQualifiedName(self.alloc(name)); + self.enter_node(kind); + self.visit_ts_type_name(&name.left); + self.visit_identifier_name(&name.right); self.leave_node(kind); } @@ -1681,13 +1716,6 @@ pub trait Visit<'a>: Sized { self.visit_ts_type(&ty.element_type); } - fn visit_ts_type_name(&mut self, name: &TSTypeName<'a>) { - match &name { - TSTypeName::IdentifierReference(ident) => self.visit_identifier_reference(ident), - TSTypeName::QualifiedName(_) => {} - } - } - fn visit_ts_null_keyword(&mut self, ty: &TSNullKeyword) { let kind = AstKind::TSNullKeyword(self.alloc(ty)); self.enter_node(kind); diff --git a/crates/oxc_ast/src/visit_mut.rs b/crates/oxc_ast/src/visit_mut.rs index f8484b465..a15a72c24 100644 --- a/crates/oxc_ast/src/visit_mut.rs +++ b/crates/oxc_ast/src/visit_mut.rs @@ -1443,6 +1443,41 @@ pub trait VisitMut<'a>: Sized { let kind = AstKind::TSImportEqualsDeclaration(self.alloc(decl)); self.enter_node(kind); self.visit_binding_identifier(&mut decl.id); + self.visit_ts_module_reference(&mut decl.module_reference); + self.leave_node(kind); + } + + fn visit_ts_module_reference(&mut self, reference: &mut TSModuleReference<'a>) { + match reference { + TSModuleReference::TypeName(name) => self.visit_ts_type_name(name), + TSModuleReference::ExternalModuleReference(reference) => { + self.visit_ts_external_module_reference(reference); + } + } + } + + fn visit_ts_type_name(&mut self, name: &mut TSTypeName<'a>) { + let kind = AstKind::TSTypeName(self.alloc(name)); + self.enter_node(kind); + match name { + TSTypeName::IdentifierReference(ident) => self.visit_identifier_reference(ident), + TSTypeName::QualifiedName(name) => self.visit_ts_qualified_name(name), + } + self.leave_node(kind); + } + + fn visit_ts_external_module_reference(&mut self, reference: &mut TSExternalModuleReference) { + let kind = AstKind::TSExternalModuleReference(self.alloc(reference)); + self.enter_node(kind); + self.visit_string_literal(&mut reference.expression); + self.leave_node(kind); + } + + fn visit_ts_qualified_name(&mut self, name: &mut TSQualifiedName<'a>) { + let kind = AstKind::TSQualifiedName(self.alloc(name)); + self.enter_node(kind); + self.visit_ts_type_name(&mut name.left); + self.visit_identifier_name(&mut name.right); self.leave_node(kind); } @@ -1673,13 +1708,6 @@ pub trait VisitMut<'a>: Sized { self.visit_ts_type(&mut ty.element_type); } - fn visit_ts_type_name(&mut self, name: &mut TSTypeName<'a>) { - match name { - TSTypeName::IdentifierReference(ident) => self.visit_identifier_reference(ident), - TSTypeName::QualifiedName(_) => {} - } - } - fn visit_ts_null_keyword(&mut self, ty: &mut TSNullKeyword) { let kind = AstKind::TSNullKeyword(self.alloc(ty)); self.enter_node(kind); diff --git a/tasks/coverage/parser_typescript.snap b/tasks/coverage/parser_typescript.snap index 50236e5c6..d4cc1f081 100644 --- a/tasks/coverage/parser_typescript.snap +++ b/tasks/coverage/parser_typescript.snap @@ -8624,6 +8624,38 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 16 │ ╰──── + × The keyword 'public' is reserved + ╭─[compiler/strictModeReservedWord.ts:16:1] + 16 │ + 17 │ var b: public.bar; + · ────── + 18 │ + ╰──── + + × The keyword 'private' is reserved + ╭─[compiler/strictModeReservedWord.ts:18:1] + 18 │ + 19 │ function foo(x: private.x) { } + · ─────── + 20 │ function foo1(x: private.package.x) { } + ╰──── + + × The keyword 'private' is reserved + ╭─[compiler/strictModeReservedWord.ts:19:1] + 19 │ function foo(x: private.x) { } + 20 │ function foo1(x: private.package.x) { } + · ─────── + 21 │ function foo2(x: private.package.protected) { } + ╰──── + + × The keyword 'private' is reserved + ╭─[compiler/strictModeReservedWord.ts:20:1] + 20 │ function foo1(x: private.package.x) { } + 21 │ function foo2(x: private.package.protected) { } + · ─────── + 22 │ let b: interface.package.implements.B; + ╰──── + × Identifier `b` has already been declared ╭─[compiler/strictModeReservedWord.ts:16:1] 16 │ @@ -8640,6 +8672,14 @@ Expect to Parse: "conformance/salsa/plainJSRedeclare3.ts" 23 │ ublic(); ╰──── + × The keyword 'interface' is reserved + ╭─[compiler/strictModeReservedWord.ts:21:1] + 21 │ function foo2(x: private.package.protected) { } + 22 │ let b: interface.package.implements.B; + · ───────── + 23 │ ublic(); + ╰──── + × The keyword 'static' is reserved ╭─[compiler/strictModeReservedWord.ts:23:1] 23 │ ublic(); diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 4999bde49..ac7289288 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -863,9 +863,10 @@ Passed: 298/1179 * imports/elide-injected/input.ts * imports/elide-no-import-specifiers/input.ts * imports/elide-preact/input.ts -* imports/elide-type-referenced-in-imports-equal-no/input.ts +* imports/elide-react/input.ts * imports/elision/input.ts * imports/elision-locations/input.ts +* imports/elision-qualifiedname/input.ts * imports/elision-rename/input.ts * imports/enum-id/input.ts * imports/enum-value/input.ts @@ -881,7 +882,6 @@ Passed: 298/1179 * imports/type-only-export-specifier-2/input.ts * imports/type-only-import-specifier-3/input.ts * imports/type-only-import-specifier-4/input.ts -* namespace/alias/input.ts * namespace/ambient-module-nested/input.ts * namespace/ambient-module-nested-exported/input.ts * namespace/canonical/input.ts