From 95e15b6dc53c0ce9a718e4aeb07065a61a387c0d Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Wed, 17 Jul 2024 09:52:58 +0000 Subject: [PATCH] fix(semantic): incorrect resolve references for `ExportSpecifier` (#4320) ```ts type A = any; const B = 0; export { A, B } ^^^^^^^^ ExportSpecifiers export { A } ^^^^^ type-only ExportSpecifiers ``` non-type-only `ExportSpecifier` can reference value and type symbols. but currently, `IdentifierReference` in ExportSpecifier only has a `ReferenceFlags::Read` --- crates/oxc_semantic/src/builder.rs | 38 +++++++++---------- .../typescript-eslint/export/named-dual.snap | 2 +- .../typescript-eslint/export/named2-type.snap | 9 ++++- .../typescript-eslint/export/named3-type.snap | 9 ++++- crates/oxc_syntax/src/reference.rs | 4 ++ crates/oxc_syntax/src/symbol.rs | 6 ++- 6 files changed, 45 insertions(+), 23 deletions(-) diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index a6defb7ba98d5..da7a1b47b9d68 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -399,9 +399,15 @@ impl<'a> SemanticBuilder<'a> { resolved_references.reserve(references.len()); references.retain(|(id, flag)| { - if flag.is_type() && symbol_flag.is_type() + if flag.is_type() && symbol_flag.is_can_be_referenced_by_type() || flag.is_value() && symbol_flag.is_value() { + // The non type-only ExportSpecifier can reference a type, + // If the reference is not a type, remove the type flag from the reference + if !symbol_flag.is_type() && !flag.is_type_only() { + *self.symbols.references[*id].flag_mut() -= ReferenceFlag::Type; + } + self.symbols.references[*id].set_symbol_id(symbol_id); resolved_references.push(*id); false @@ -1619,11 +1625,12 @@ impl<'a> SemanticBuilder<'a> { self.current_reference_flag = ReferenceFlag::Type; } } - AstKind::ExportAllDeclaration(s) if s.export_kind.is_type() => { - self.current_reference_flag = ReferenceFlag::Type; - } - AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { - self.current_reference_flag = ReferenceFlag::Type; + AstKind::ExportSpecifier(s) => { + if self.current_reference_flag.is_type() || s.export_kind.is_type() { + self.current_reference_flag = ReferenceFlag::Type; + } else { + self.current_reference_flag = ReferenceFlag::Read | ReferenceFlag::Type; + } } AstKind::ImportSpecifier(specifier) => { specifier.bind(self); @@ -1709,9 +1716,6 @@ impl<'a> SemanticBuilder<'a> { AstKind::TSTypeParameter(type_parameter) => { type_parameter.bind(self); } - AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { - self.current_reference_flag = ReferenceFlag::Type; - } AstKind::TSInterfaceHeritage(_) => { self.current_reference_flag = ReferenceFlag::Type; } @@ -1798,17 +1802,11 @@ impl<'a> SemanticBuilder<'a> { AstKind::BindingIdentifier(_) => { self.current_symbol_flags -= SymbolFlags::Export; } - AstKind::ExportNamedDeclaration(decl) => { - if decl.export_kind.is_type() { - self.current_reference_flag -= ReferenceFlag::Type; + AstKind::ExportSpecifier(_) => { + if !self.current_reference_flag.is_type_only() { + self.current_reference_flag = ReferenceFlag::empty(); } } - AstKind::ExportAllDeclaration(s) if s.export_kind.is_type() => { - self.current_reference_flag -= ReferenceFlag::Type; - } - AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { - self.current_reference_flag -= ReferenceFlag::Type; - } AstKind::LabeledStatement(_) => self.label_builder.leave(), AstKind::StaticBlock(_) => { self.label_builder.leave_function_or_static_block(); @@ -1851,7 +1849,9 @@ impl<'a> SemanticBuilder<'a> { self.current_reference_flag -= ReferenceFlag::Read; } } - AstKind::MemberExpression(_) | AstKind::TSTypeQuery(_) => { + AstKind::MemberExpression(_) + | AstKind::TSTypeQuery(_) + | AstKind::ExportNamedDeclaration(_) => { self.current_reference_flag = ReferenceFlag::empty(); } AstKind::AssignmentTarget(_) => self.current_reference_flag -= ReferenceFlag::Write, diff --git a/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named-dual.snap b/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named-dual.snap index 5590723c7434d..e36a88fe05e15 100644 --- a/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named-dual.snap +++ b/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named-dual.snap @@ -24,7 +24,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named-du "node": "VariableDeclarator", "references": [ { - "flag": "ReferenceFlag(Read)", + "flag": "ReferenceFlag(Read | Type)", "id": 0, "name": "T", "node_id": 12 diff --git a/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named2-type.snap b/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named2-type.snap index 5a755841e73a7..0933fd799ccee 100644 --- a/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named2-type.snap +++ b/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named2-type.snap @@ -22,7 +22,14 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named2-t "id": 0, "name": "A", "node": "TSTypeAliasDeclaration", - "references": [] + "references": [ + { + "flag": "ReferenceFlag(Read | Type)", + "id": 0, + "name": "A", + "node_id": 8 + } + ] } ] } diff --git a/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named3-type.snap b/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named3-type.snap index 28b89fdc04785..f65c9f2d9c7ff 100644 --- a/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named3-type.snap +++ b/crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named3-type.snap @@ -22,7 +22,14 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named3-t "id": 0, "name": "V", "node": "TSTypeAliasDeclaration", - "references": [] + "references": [ + { + "flag": "ReferenceFlag(Read | Type)", + "id": 0, + "name": "V", + "node_id": 8 + } + ] } ] } diff --git a/crates/oxc_syntax/src/reference.rs b/crates/oxc_syntax/src/reference.rs index ce036ad993771..61a72bcb9b54b 100644 --- a/crates/oxc_syntax/src/reference.rs +++ b/crates/oxc_syntax/src/reference.rs @@ -99,6 +99,10 @@ impl ReferenceFlag { self.contains(Self::Type) } + pub const fn is_type_only(self) -> bool { + matches!(self, Self::Type) + } + pub const fn is_value(&self) -> bool { self.intersects(Self::Value) } diff --git a/crates/oxc_syntax/src/symbol.rs b/crates/oxc_syntax/src/symbol.rs index 7f782011c0377..57ad2db74cb67 100644 --- a/crates/oxc_syntax/src/symbol.rs +++ b/crates/oxc_syntax/src/symbol.rs @@ -95,7 +95,11 @@ impl SymbolFlags { } pub fn is_type(&self) -> bool { - self.intersects(Self::Type | Self::TypeImport | Self::Import) + self.intersects(Self::Type | Self::TypeImport) + } + + pub fn is_can_be_referenced_by_type(&self) -> bool { + self.is_type() || self.contains(Self::Import) } pub fn is_value(&self) -> bool {