Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(js_semantic): identify bindings with indexes #582

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 51 additions & 35 deletions crates/biome_js_semantic/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::collections::VecDeque;
use std::mem;
use JsSyntaxKind::*;

use crate::ScopeId;
use crate::{BindingId, ScopeId};

/// Events emitted by the [SemanticEventExtractor].
/// These events are later made into the Semantic Model.
Expand All @@ -36,7 +36,7 @@ pub enum SemanticEvent {
/// - All reference identifiers
Read {
range: TextRange,
declaration_at: TextSize,
declaration_id: BindingId,
scope_id: ScopeId,
},

Expand All @@ -45,7 +45,7 @@ pub enum SemanticEvent {
/// - All reference identifiers
HoistedRead {
range: TextRange,
declaration_at: TextSize,
declaration_id: BindingId,
scope_id: ScopeId,
},

Expand All @@ -54,7 +54,7 @@ pub enum SemanticEvent {
/// - All identifier assignments
Write {
range: TextRange,
declaration_at: TextSize,
declaration_id: BindingId,
scope_id: ScopeId,
},

Expand All @@ -64,7 +64,7 @@ pub enum SemanticEvent {
/// - All identifier assignments
HoistedWrite {
range: TextRange,
declaration_at: TextSize,
declaration_id: BindingId,
scope_id: ScopeId,
},

Expand Down Expand Up @@ -93,7 +93,8 @@ pub enum SemanticEvent {
/// Tracks where a symbol is exported.
Export {
range: TextRange,
declaration_at: TextSize,
declaration_id: BindingId,
declaration_start: TextSize,
},
}

Expand Down Expand Up @@ -153,10 +154,13 @@ pub struct SemanticEventExtractor {
/// Number of generated scopes
/// This allows assigning a unique id to every scope.
scope_count: usize,
/// Number of declarations.
/// This allows assigning a unique id to every declaration.
declaration_count: usize,
/// At any point this is the set of available bindings and their range in the current scope
bindings: FxHashMap<BindingName, BindingInfo>,
/// Type parameters bound in a `infer T` clause.
infers: Vec<TsTypeParameterName>,
infers: Vec<(TsTypeParameterName, BindingId)>,
}

/// A binding name is either a type or a value.
Expand Down Expand Up @@ -184,19 +188,13 @@ impl BindingName {
struct BindingInfo {
/// range of the name
range: TextRange,
declaration_id: BindingId,
/// Kind of the declaration,
/// or in the case of a bogus declaration, the kind of the name
declaration_kind: JsSyntaxKind,
}

impl BindingInfo {
fn new(range: TextRange, declaration_kind: JsSyntaxKind) -> Self {
Self {
range,
declaration_kind,
}
}

fn is_imported(&self) -> bool {
matches!(
self.declaration_kind,
Expand Down Expand Up @@ -452,11 +450,16 @@ impl SemanticEventExtractor {

fn enter_identifier_binding(&mut self, node: &AnyJsIdentifierBinding) {
let mut hoisted_scope_id = None;
let declaration_id = BindingId::new(self.declaration_count);
self.declaration_count += 1;
let is_exported = if let Ok(name_token) = node.name_token() {
let name = name_token.token_text_trimmed();
if let Some(declaration) = node.declaration() {
let info =
BindingInfo::new(name_token.text_trimmed_range(), declaration.syntax().kind());
let info = BindingInfo {
range: name_token.text_trimmed_range(),
declaration_id,
declaration_kind: declaration.syntax().kind(),
};
let is_exported = declaration.export().is_some();
match declaration {
AnyJsBindingDeclaration::JsArrayBindingPatternElement(_)
Expand Down Expand Up @@ -594,15 +597,21 @@ impl SemanticEventExtractor {
AnyJsBindingDeclaration::TsInferType(_) => {
// Delay the declaration of parameter types that are inferred.
// Their scope corresponds to the true branch of the conditional type.
self.infers
.push(TsTypeParameterName::unwrap_cast(node.syntax().clone()));
self.infers.push((
TsTypeParameterName::unwrap_cast(node.syntax().clone()),
declaration_id,
));
return;
}
}
is_exported
} else {
// Handle identifiers in bogus nodes
let info = BindingInfo::new(name_token.text_trimmed_range(), node.syntax().kind());
let info = BindingInfo {
range: name_token.text_trimmed_range(),
declaration_id,
declaration_kind: node.syntax().kind(),
};
self.push_binding(None, BindingName::Value(name), info);
false
}
Expand All @@ -620,7 +629,8 @@ impl SemanticEventExtractor {
if is_exported {
self.stash.push_back(SemanticEvent::Export {
range,
declaration_at: range.start(),
declaration_id,
declaration_start: range.start(),
});
}
}
Expand Down Expand Up @@ -833,11 +843,15 @@ impl SemanticEventExtractor {

fn push_infers_in_scope(&mut self) {
let infers = mem::take(&mut self.infers);
for infer in infers {
for (infer, declaration_id) in infers {
if let Ok(name_token) = infer.ident_token() {
let name = name_token.token_text_trimmed();
let name_range = name_token.text_trimmed_range();
let binding_info = BindingInfo::new(name_range, JsSyntaxKind::TS_INFER_TYPE);
let binding_info = BindingInfo {
range: name_range,
declaration_id,
declaration_kind: JsSyntaxKind::TS_INFER_TYPE,
};
self.push_binding(None, BindingName::Type(name), binding_info);
let scope_id = self.current_scope_mut().scope_id;
self.stash.push_back(SemanticEvent::DeclarationFound {
Expand Down Expand Up @@ -892,28 +906,30 @@ impl SemanticEventExtractor {
if let Some(&BindingInfo {
range: declaration_range,
declaration_kind,
declaration_id,
}) = self.bindings.get(&name)
{
let declaration_at = declaration_range.start();
// We know the declaration of these reference.
for reference in references {
let declaration_before_reference = declaration_at < reference.range().start();
let declaration_before_reference =
declaration_range.start() < reference.range().start();
let event = match reference {
Reference::Export(range) => {
self.stash.push_back(SemanticEvent::Export {
range,
declaration_at,
declaration_id,
declaration_start: declaration_range.start(),
});
if declaration_before_reference {
SemanticEvent::Read {
range,
declaration_at,
declaration_id,
scope_id,
}
} else {
SemanticEvent::HoistedRead {
range,
declaration_at,
declaration_id,
scope_id,
}
}
Expand All @@ -936,13 +952,13 @@ impl SemanticEventExtractor {
if declaration_before_reference {
SemanticEvent::Read {
range,
declaration_at,
declaration_id,
scope_id,
}
} else {
SemanticEvent::HoistedRead {
range,
declaration_at,
declaration_id,
scope_id,
}
}
Expand All @@ -951,13 +967,13 @@ impl SemanticEventExtractor {
if declaration_before_reference {
SemanticEvent::Write {
range,
declaration_at,
declaration_id,
scope_id,
}
} else {
SemanticEvent::HoistedWrite {
range,
declaration_at,
declaration_id,
scope_id,
}
}
Expand Down Expand Up @@ -986,19 +1002,19 @@ impl SemanticEventExtractor {
Reference::AmbientRead(range) if info.is_imported() => {
// An ambient read can only read a value,
// but also an imported value as a type (with the `type` modifier)
let declaration_at = info.range.start();
let declaration_id = info.declaration_id;
let declaration_before_reference =
declaration_at < reference.range().start();
info.range.start() < reference.range().start();
let event = if declaration_before_reference {
SemanticEvent::Read {
range,
declaration_at,
declaration_id,
scope_id: ScopeId::new(0),
}
} else {
SemanticEvent::HoistedRead {
range,
declaration_at,
declaration_id,
scope_id: ScopeId::new(0),
}
};
Expand Down
47 changes: 25 additions & 22 deletions crates/biome_js_semantic/src/semantic_model/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,12 +198,11 @@ impl SemanticModelBuilder {
}
Read {
range,
declaration_at,
declaration_id,
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at];
let binding = &mut self.bindings[binding_id.index()];
let reference_id = ReferenceId::new(binding_id, binding.references.len());
let binding = &mut self.bindings[declaration_id.index()];
let reference_id = ReferenceId::new(declaration_id, binding.references.len());
binding.references.push(SemanticModelReference {
range_start: range.start(),
ty: SemanticModelReferenceType::Read { hoisted: false },
Expand All @@ -212,16 +211,16 @@ impl SemanticModelBuilder {
let scope = &mut self.scopes[scope_id.index()];
scope.read_references.push(reference_id);

self.declared_at_by_start.insert(range.start(), binding_id);
self.declared_at_by_start
.insert(range.start(), declaration_id);
}
HoistedRead {
range,
declaration_at,
declaration_id,
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at];
let binding = &mut self.bindings[binding_id.index()];
let reference_id = ReferenceId::new(binding_id, binding.references.len());
let binding = &mut self.bindings[declaration_id.index()];
let reference_id = ReferenceId::new(declaration_id, binding.references.len());
binding.references.push(SemanticModelReference {
range_start: range.start(),
ty: SemanticModelReferenceType::Read { hoisted: true },
Expand All @@ -230,16 +229,16 @@ impl SemanticModelBuilder {
let scope = &mut self.scopes[scope_id.index()];
scope.read_references.push(reference_id);

self.declared_at_by_start.insert(range.start(), binding_id);
self.declared_at_by_start
.insert(range.start(), declaration_id);
}
Write {
range,
declaration_at,
declaration_id,
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at];
let binding = &mut self.bindings[binding_id.index()];
let reference_id = ReferenceId::new(binding_id, binding.references.len());
let binding = &mut self.bindings[declaration_id.index()];
let reference_id = ReferenceId::new(declaration_id, binding.references.len());
binding.references.push(SemanticModelReference {
range_start: range.start(),
ty: SemanticModelReferenceType::Write { hoisted: false },
Expand All @@ -248,16 +247,16 @@ impl SemanticModelBuilder {
let scope = &mut self.scopes[scope_id.index()];
scope.read_references.push(reference_id);

self.declared_at_by_start.insert(range.start(), binding_id);
self.declared_at_by_start
.insert(range.start(), declaration_id);
}
HoistedWrite {
range,
declaration_at,
declaration_id,
scope_id,
} => {
let binding_id = self.bindings_by_start[&declaration_at];
let binding = &mut self.bindings[binding_id.index()];
let reference_id = ReferenceId::new(binding_id, binding.references.len());
let binding = &mut self.bindings[declaration_id.index()];
let reference_id = ReferenceId::new(declaration_id, binding.references.len());
binding.references.push(SemanticModelReference {
range_start: range.start(),
ty: SemanticModelReferenceType::Write { hoisted: true },
Expand All @@ -266,7 +265,8 @@ impl SemanticModelBuilder {
let scope = &mut self.scopes[scope_id.index()];
scope.read_references.push(reference_id);

self.declared_at_by_start.insert(range.start(), binding_id);
self.declared_at_by_start
.insert(range.start(), declaration_id);
}
UnresolvedReference { is_read, range } => {
let ty = if is_read {
Expand Down Expand Up @@ -307,8 +307,11 @@ impl SemanticModelBuilder {
.push(SemanticModelUnresolvedReference { range }),
}
}
Export { declaration_at, .. } => {
self.exported.insert(declaration_at);
Export {
declaration_start: declaration_range_start,
..
} => {
self.exported.insert(declaration_range_start);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_js_semantic/src/semantic_model/model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::*;
use biome_js_syntax::{AnyJsFunction, AnyJsRoot};

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
pub(crate) struct BindingId(u32);
pub struct BindingId(u32);

impl BindingId {
pub(crate) fn new(index: usize) -> Self {
Expand Down
Loading
Loading