diff --git a/crates/biome_js_semantic/src/events.rs b/crates/biome_js_semantic/src/events.rs index bed7ce3f0efb..35b79c20fc8d 100644 --- a/crates/biome_js_semantic/src/events.rs +++ b/crates/biome_js_semantic/src/events.rs @@ -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. @@ -36,7 +36,7 @@ pub enum SemanticEvent { /// - All reference identifiers Read { range: TextRange, - declaration_at: TextSize, + declaration_id: BindingId, scope_id: ScopeId, }, @@ -45,7 +45,7 @@ pub enum SemanticEvent { /// - All reference identifiers HoistedRead { range: TextRange, - declaration_at: TextSize, + declaration_id: BindingId, scope_id: ScopeId, }, @@ -54,7 +54,7 @@ pub enum SemanticEvent { /// - All identifier assignments Write { range: TextRange, - declaration_at: TextSize, + declaration_id: BindingId, scope_id: ScopeId, }, @@ -64,7 +64,7 @@ pub enum SemanticEvent { /// - All identifier assignments HoistedWrite { range: TextRange, - declaration_at: TextSize, + declaration_id: BindingId, scope_id: ScopeId, }, @@ -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, }, } @@ -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, /// Type parameters bound in a `infer T` clause. - infers: Vec, + infers: Vec<(TsTypeParameterName, BindingId)>, } /// A binding name is either a type or a value. @@ -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, @@ -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(_) @@ -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 } @@ -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(), }); } } @@ -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 { @@ -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, } } @@ -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, } } @@ -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, } } @@ -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), } }; diff --git a/crates/biome_js_semantic/src/semantic_model/builder.rs b/crates/biome_js_semantic/src/semantic_model/builder.rs index ac8c76dba880..bf27b60cc686 100644 --- a/crates/biome_js_semantic/src/semantic_model/builder.rs +++ b/crates/biome_js_semantic/src/semantic_model/builder.rs @@ -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 }, @@ -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 }, @@ -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 }, @@ -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 }, @@ -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 { @@ -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); } } } diff --git a/crates/biome_js_semantic/src/semantic_model/model.rs b/crates/biome_js_semantic/src/semantic_model/model.rs index 436aad7771f4..8695bc1466af 100644 --- a/crates/biome_js_semantic/src/semantic_model/model.rs +++ b/crates/biome_js_semantic/src/semantic_model/model.rs @@ -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 { diff --git a/crates/biome_js_semantic/src/tests/assertions.rs b/crates/biome_js_semantic/src/tests/assertions.rs index 0a39654ded5a..08a0ba14317f 100644 --- a/crates/biome_js_semantic/src/tests/assertions.rs +++ b/crates/biome_js_semantic/src/tests/assertions.rs @@ -124,12 +124,12 @@ pub fn assert(code: &str, test_name: &str) { // Extract semantic events and index by range let mut events_by_pos: FxHashMap> = FxHashMap::default(); - let mut declaration_range_by_start: FxHashMap = FxHashMap::default(); + let mut declaration_ranges: Vec = vec![]; let mut scope_ranges: Vec = vec![]; for event in semantic_events(r.syntax()) { let pos = match event { SemanticEvent::DeclarationFound { range, .. } => { - declaration_range_by_start.insert(range.start(), range); + declaration_ranges.push(range); range.start() } SemanticEvent::ScopeStarted { range, .. } => { @@ -156,7 +156,7 @@ pub fn assert(code: &str, test_name: &str) { code, test_name, events_by_pos, - declaration_range_by_start, + declaration_ranges, scope_ranges, ); } @@ -462,7 +462,7 @@ impl SemanticAssertions { code: &str, test_name: &str, events_by_pos: FxHashMap>, - declaration_range_by_start: FxHashMap, + declaration_ranges: Vec, scope_ranges: Vec, ) { // Check every declaration assertion is ok @@ -519,9 +519,9 @@ impl SemanticAssertions { let mut unused_match = None; let at_least_one_match = events.iter().any(|e| { let declaration_at_range = match &e { - SemanticEvent::Read { declaration_at, .. } - | SemanticEvent::HoistedRead { declaration_at, .. } => { - declaration_range_by_start.get(declaration_at) + SemanticEvent::Read { declaration_id, .. } + | SemanticEvent::HoistedRead { declaration_id, .. } => { + declaration_ranges.get(declaration_id.index()) } _ => None, }; @@ -579,9 +579,9 @@ impl SemanticAssertions { let at_least_one_match = events.iter().any(|e| { let declaration_at_range = match &e { - SemanticEvent::Write { declaration_at, .. } - | SemanticEvent::HoistedWrite { declaration_at, .. } => { - declaration_range_by_start.get(declaration_at) + SemanticEvent::Write { declaration_id, .. } + | SemanticEvent::HoistedWrite { declaration_id, .. } => { + declaration_ranges.get(declaration_id.index()) } _ => None, };