Skip to content

Commit

Permalink
refactor(semantic): improve the logic of resolving references to be c…
Browse files Browse the repository at this point in the history
…leaner (#7829)

fine-tuning the logic, reducing unnecessary calls, and early return if it doesn't resolve. Also, add many comments to describe what code does.
  • Loading branch information
Dunqing committed Dec 13, 2024
1 parent 5710950 commit 0f367e5
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 27 deletions.
55 changes: 30 additions & 25 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -485,37 +485,42 @@ impl<'a> SemanticBuilder<'a> {
let bindings = self.scope.get_bindings(self.current_scope_id);
if let Some(symbol_id) = bindings.get(name.as_str()).copied() {
let symbol_flags = self.symbols.get_flags(symbol_id);

let resolved_references = &mut self.symbols.resolved_references[symbol_id];

references.retain(|&reference_id| {
let reference = &mut self.symbols.references[reference_id];

let flags = reference.flags();
if flags.is_type() && symbol_flags.can_be_referenced_by_type()
|| flags.is_value() && symbol_flags.can_be_referenced_by_value()
|| flags.is_value_as_type()
&& (symbol_flags.can_be_referenced_by_value()
|| symbol_flags.is_type_import())
{
if flags.is_value_as_type() {
// Resolve pending type references (e.g., from `typeof` expressions) to proper type references.
*reference.flags_mut() = ReferenceFlags::Type;
} else if symbol_flags.is_value() && !flags.is_type_only() {
// The non type-only ExportSpecifier can reference a type/value symbol,
// If the symbol is a value symbol and reference flag is not type-only, remove the type flag.
*reference.flags_mut() -= ReferenceFlags::Type;
} else {
// If the symbol is a type symbol and reference flag is not type-only, remove the value flag.
*reference.flags_mut() -= ReferenceFlags::Value;
}
let flags = reference.flags_mut();

reference.set_symbol_id(symbol_id);
resolved_references.push(reference_id);
false
// Determine the symbol whether can be referenced by this reference.
let resolved = (flags.is_value() && symbol_flags.can_be_referenced_by_value())
|| (flags.is_type() && symbol_flags.can_be_referenced_by_type())
|| (flags.is_value_as_type()
&& symbol_flags.can_be_referenced_by_value_as_type());

if !resolved {
return true;
}

if symbol_flags.is_value() && flags.is_value() {
// The non type-only ExportSpecifier can reference both type/value symbols,
// if the symbol is a value symbol and reference flag is not type-only,
// remove the type flag. For example: `const B = 1; export { B };`
*flags -= ReferenceFlags::Type;
} else {
true
// 1. ReferenceFlags::ValueAsType -> ReferenceFlags::Type
// `const ident = 0; typeof ident`
// ^^^^^ -> The ident is a value symbols,
// but it used as a type.
// 2. ReferenceFlags::Value | ReferenceFlags::Type -> ReferenceFlags::Type
// `type ident = string; export default ident;
// ^^^^^ We have confirmed the symbol is
// not a value symbol, so we need to
// make sure the reference is a type only.
*flags = ReferenceFlags::Type;
}
reference.set_symbol_id(symbol_id);
self.symbols.resolved_references[symbol_id].push(reference_id);

false
});

if references.is_empty() {
Expand Down
10 changes: 8 additions & 2 deletions crates/oxc_syntax/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,15 +206,21 @@ impl SymbolFlags {
self.contains(Self::TypeImport)
}

/// If true, then the symbol can be referenced by a type
/// If true, then the symbol can be referenced by a type reference
#[inline]
pub fn can_be_referenced_by_type(&self) -> bool {
self.intersects(Self::Type | Self::TypeImport | Self::Import)
}

/// If true, then the symbol can be referenced by a value
/// If true, then the symbol can be referenced by a value reference
#[inline]
pub fn can_be_referenced_by_value(&self) -> bool {
self.intersects(Self::Value | Self::Import | Self::Function)
}

/// If true, then the symbol can be referenced by a value_as_type reference
#[inline]
pub fn can_be_referenced_by_value_as_type(&self) -> bool {
self.intersects(Self::Value | Self::Import | Self::Function | Self::TypeImport)
}
}

0 comments on commit 0f367e5

Please sign in to comment.