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`
This commit is contained in:
Dunqing 2024-07-17 09:52:58 +00:00
parent a88d588a07
commit 95e15b6dc5
6 changed files with 45 additions and 23 deletions

View file

@ -399,9 +399,15 @@ impl<'a> SemanticBuilder<'a> {
resolved_references.reserve(references.len()); resolved_references.reserve(references.len());
references.retain(|(id, flag)| { 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() || 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); self.symbols.references[*id].set_symbol_id(symbol_id);
resolved_references.push(*id); resolved_references.push(*id);
false false
@ -1619,11 +1625,12 @@ impl<'a> SemanticBuilder<'a> {
self.current_reference_flag = ReferenceFlag::Type; self.current_reference_flag = ReferenceFlag::Type;
} }
} }
AstKind::ExportAllDeclaration(s) if s.export_kind.is_type() => { AstKind::ExportSpecifier(s) => {
self.current_reference_flag = ReferenceFlag::Type; if self.current_reference_flag.is_type() || s.export_kind.is_type() {
} self.current_reference_flag = ReferenceFlag::Type;
AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { } else {
self.current_reference_flag = ReferenceFlag::Type; self.current_reference_flag = ReferenceFlag::Read | ReferenceFlag::Type;
}
} }
AstKind::ImportSpecifier(specifier) => { AstKind::ImportSpecifier(specifier) => {
specifier.bind(self); specifier.bind(self);
@ -1709,9 +1716,6 @@ impl<'a> SemanticBuilder<'a> {
AstKind::TSTypeParameter(type_parameter) => { AstKind::TSTypeParameter(type_parameter) => {
type_parameter.bind(self); type_parameter.bind(self);
} }
AstKind::ExportSpecifier(s) if s.export_kind.is_type() => {
self.current_reference_flag = ReferenceFlag::Type;
}
AstKind::TSInterfaceHeritage(_) => { AstKind::TSInterfaceHeritage(_) => {
self.current_reference_flag = ReferenceFlag::Type; self.current_reference_flag = ReferenceFlag::Type;
} }
@ -1798,17 +1802,11 @@ impl<'a> SemanticBuilder<'a> {
AstKind::BindingIdentifier(_) => { AstKind::BindingIdentifier(_) => {
self.current_symbol_flags -= SymbolFlags::Export; self.current_symbol_flags -= SymbolFlags::Export;
} }
AstKind::ExportNamedDeclaration(decl) => { AstKind::ExportSpecifier(_) => {
if decl.export_kind.is_type() { if !self.current_reference_flag.is_type_only() {
self.current_reference_flag -= ReferenceFlag::Type; 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::LabeledStatement(_) => self.label_builder.leave(),
AstKind::StaticBlock(_) => { AstKind::StaticBlock(_) => {
self.label_builder.leave_function_or_static_block(); self.label_builder.leave_function_or_static_block();
@ -1851,7 +1849,9 @@ impl<'a> SemanticBuilder<'a> {
self.current_reference_flag -= ReferenceFlag::Read; self.current_reference_flag -= ReferenceFlag::Read;
} }
} }
AstKind::MemberExpression(_) | AstKind::TSTypeQuery(_) => { AstKind::MemberExpression(_)
| AstKind::TSTypeQuery(_)
| AstKind::ExportNamedDeclaration(_) => {
self.current_reference_flag = ReferenceFlag::empty(); self.current_reference_flag = ReferenceFlag::empty();
} }
AstKind::AssignmentTarget(_) => self.current_reference_flag -= ReferenceFlag::Write, AstKind::AssignmentTarget(_) => self.current_reference_flag -= ReferenceFlag::Write,

View file

@ -24,7 +24,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named-du
"node": "VariableDeclarator", "node": "VariableDeclarator",
"references": [ "references": [
{ {
"flag": "ReferenceFlag(Read)", "flag": "ReferenceFlag(Read | Type)",
"id": 0, "id": 0,
"name": "T", "name": "T",
"node_id": 12 "node_id": 12

View file

@ -22,7 +22,14 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named2-t
"id": 0, "id": 0,
"name": "A", "name": "A",
"node": "TSTypeAliasDeclaration", "node": "TSTypeAliasDeclaration",
"references": [] "references": [
{
"flag": "ReferenceFlag(Read | Type)",
"id": 0,
"name": "A",
"node_id": 8
}
]
} }
] ]
} }

View file

@ -22,7 +22,14 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/export/named3-t
"id": 0, "id": 0,
"name": "V", "name": "V",
"node": "TSTypeAliasDeclaration", "node": "TSTypeAliasDeclaration",
"references": [] "references": [
{
"flag": "ReferenceFlag(Read | Type)",
"id": 0,
"name": "V",
"node_id": 8
}
]
} }
] ]
} }

View file

@ -99,6 +99,10 @@ impl ReferenceFlag {
self.contains(Self::Type) self.contains(Self::Type)
} }
pub const fn is_type_only(self) -> bool {
matches!(self, Self::Type)
}
pub const fn is_value(&self) -> bool { pub const fn is_value(&self) -> bool {
self.intersects(Self::Value) self.intersects(Self::Value)
} }

View file

@ -95,7 +95,11 @@ impl SymbolFlags {
} }
pub fn is_type(&self) -> bool { 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 { pub fn is_value(&self) -> bool {