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());
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,

View file

@ -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

View file

@ -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
}
]
}
]
}

View file

@ -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
}
]
}
]
}

View file

@ -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)
}

View file

@ -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 {