From 427b4e1051983d6ebbf7ba72a006504776c092c4 Mon Sep 17 00:00:00 2001 From: Victorien Elvinger Date: Wed, 10 Jul 2024 19:24:12 +0200 Subject: [PATCH] refcator(js_semantic): identify declaration with indexes --- crates/biome_js_semantic/src/events.rs | 84 +++++++++++-------- .../src/semantic_model/builder.rs | 59 +++++++------ .../src/semantic_model/model.rs | 17 ++-- .../biome_js_semantic/src/tests/assertions.rs | 37 ++++---- 4 files changed, 103 insertions(+), 94 deletions(-) diff --git a/crates/biome_js_semantic/src/events.rs b/crates/biome_js_semantic/src/events.rs index cef9cfc99038..7eeeb40dbb9e 100644 --- a/crates/biome_js_semantic/src/events.rs +++ b/crates/biome_js_semantic/src/events.rs @@ -6,7 +6,6 @@ use biome_js_syntax::{ TsTypeParameterName, }; use biome_js_syntax::{AnyJsImportClause, AnyJsNamedImportSpecifier, AnyTsType}; -use biome_rowan::TextSize; use biome_rowan::{syntax::Preorder, AstNode, SyntaxNodeOptionExt, TokenText}; use rustc_hash::FxHashMap; use std::collections::VecDeque; @@ -34,7 +33,7 @@ pub enum SemanticEvent { /// - All reference identifiers Read { range: TextRange, - declaration_at: TextSize, + declaration_id: u32, scope_id: u32, }, @@ -43,7 +42,7 @@ pub enum SemanticEvent { /// - All reference identifiers HoistedRead { range: TextRange, - declaration_at: TextSize, + declaration_id: u32, scope_id: u32, }, @@ -52,7 +51,7 @@ pub enum SemanticEvent { /// - All identifier assignments Write { range: TextRange, - declaration_at: TextSize, + declaration_id: u32, scope_id: u32, }, @@ -62,7 +61,7 @@ pub enum SemanticEvent { /// - All identifier assignments HoistedWrite { range: TextRange, - declaration_at: TextSize, + declaration_id: u32, scope_id: u32, }, @@ -78,7 +77,6 @@ pub enum SemanticEvent { ScopeStarted { /// Scope range range: TextRange, - scope_id: u32, parent_scope_id: Option, is_closure: bool, }, @@ -97,7 +95,7 @@ pub enum SemanticEvent { /// The range points to the binding that is being exported. Export { range: TextRange, - declaration_at: TextSize, + declaration_id: u32, }, } @@ -157,10 +155,13 @@ pub struct SemanticEventExtractor { /// Number of generated scopes /// This allows assigning a unique id to every scope. scope_count: u32, + /// Number of declarations. + /// This allows assigning a unique id to every declaration. + declaration_count: u32, /// 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, u32)>, } /// A binding name is either a type or a value. @@ -188,19 +189,13 @@ impl BindingName { struct BindingInfo { /// range of the name range: TextRange, + declaration_id: u32, /// Kind of the declaration, /// or in the acse 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, @@ -393,11 +388,16 @@ impl SemanticEventExtractor { fn enter_identifier_binding(&mut self, node: &AnyJsIdentifierBinding) { let mut hoisted_scope_id = None; + let declaration_id = 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(_) @@ -524,15 +524,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 } @@ -550,7 +556,7 @@ impl SemanticEventExtractor { if is_exported { self.stash.push_back(SemanticEvent::Export { range, - declaration_at: range.start(), + declaration_id, }); } } @@ -763,11 +769,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 { @@ -784,7 +794,6 @@ impl SemanticEventExtractor { self.scope_count += 1; self.stash.push_back(SemanticEvent::ScopeStarted { range, - scope_id, parent_scope_id: self.scopes.iter().last().map(|x| x.scope_id), is_closure, }); @@ -812,28 +821,29 @@ 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, }); if declaration_before_reference { SemanticEvent::Read { range, - declaration_at, + declaration_id, scope_id, } } else { SemanticEvent::HoistedRead { range, - declaration_at, + declaration_id, scope_id, } } @@ -856,13 +866,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, } } @@ -871,13 +881,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, } } @@ -906,19 +916,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: 0, } } else { SemanticEvent::HoistedRead { range, - declaration_at, + declaration_id, scope_id: 0, } }; diff --git a/crates/biome_js_semantic/src/semantic_model/builder.rs b/crates/biome_js_semantic/src/semantic_model/builder.rs index 44a00e39f4f4..c6a0399720b8 100644 --- a/crates/biome_js_semantic/src/semantic_model/builder.rs +++ b/crates/biome_js_semantic/src/semantic_model/builder.rs @@ -23,7 +23,7 @@ pub struct SemanticModelBuilder { bindings_by_start: FxHashMap, /// maps a reference range start to its bindings. u32 points to SemanticModelBuilder::bindings vec declared_at_by_start: FxHashMap, - exported: FxHashSet, + exported: FxHashSet, unresolved_references: Vec, } @@ -128,11 +128,10 @@ impl SemanticModelBuilder { ScopeStarted { range, parent_scope_id, - scope_id, is_closure, } => { // Scopes will be raised in order - debug_assert!((scope_id as usize) == self.scopes.len()); + let scope_id = self.scopes.len() as u32; self.scopes.push(SemanticModelScopeData { range, @@ -173,23 +172,23 @@ impl SemanticModelBuilder { // event extractor debug_assert!((binding_scope_id as usize) < self.scopes.len()); - let binding_id = self.bindings.len() as u32; + let declaration_id = self.bindings.len() as u32; self.bindings.push(SemanticModelBindingData { - id: binding_id.into(), + id: declaration_id.into(), range, references: vec![], }); - self.bindings_by_start.insert(range.start(), binding_id); + self.bindings_by_start.insert(range.start(), declaration_id); let scope = &mut self.scopes[binding_scope_id as usize]; - scope.bindings.push(binding_id); + scope.bindings.push(declaration_id); // Handle bindings with a bogus name if let Some(node) = self.binding_node_by_start.get(&range.start()) { if let Some(node) = JsIdentifierBinding::cast_ref(node) { if let Ok(name_token) = node.name_token() { let name = name_token.token_text_trimmed(); - scope.bindings_by_name.insert(name, binding_id); + scope.bindings_by_name.insert(name, declaration_id); } } } @@ -201,11 +200,10 @@ 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 as usize]; + let binding = &mut self.bindings[declaration_id as usize]; let reference_index = binding.references.len() as u32; binding.references.push(SemanticModelReference { @@ -216,19 +214,19 @@ impl SemanticModelBuilder { let scope = &mut self.scopes[scope_id as usize]; scope.read_references.push(SemanticModelScopeReference { - binding_id, + binding_id: declaration_id, reference_id: reference_index, }); - 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 as usize]; + let binding = &mut self.bindings[declaration_id as usize]; let reference_index = binding.references.len() as u32; binding.references.push(SemanticModelReference { @@ -239,19 +237,19 @@ impl SemanticModelBuilder { let scope = &mut self.scopes[scope_id as usize]; scope.read_references.push(SemanticModelScopeReference { - binding_id, + binding_id: declaration_id, reference_id: reference_index, }); - 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 as usize]; + let binding = &mut self.bindings[declaration_id as usize]; let reference_index = binding.references.len() as u32; binding.references.push(SemanticModelReference { @@ -262,19 +260,19 @@ impl SemanticModelBuilder { let scope = &mut self.scopes[scope_id as usize]; scope.read_references.push(SemanticModelScopeReference { - binding_id, + binding_id: declaration_id, reference_id: reference_index, }); - 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 as usize]; + let binding = &mut self.bindings[declaration_id as usize]; let reference_index = binding.references.len() as u32; binding.references.push(SemanticModelReference { @@ -285,11 +283,12 @@ impl SemanticModelBuilder { let scope = &mut self.scopes[scope_id as usize]; scope.read_references.push(SemanticModelScopeReference { - binding_id, + binding_id: declaration_id, reference_id: reference_index, }); - 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 { @@ -327,8 +326,8 @@ impl SemanticModelBuilder { .push(SemanticModelUnresolvedReference { range }), } } - Export { declaration_at, .. } => { - self.exported.insert(declaration_at); + Export { declaration_id, .. } => { + self.exported.insert(declaration_id); } } } diff --git a/crates/biome_js_semantic/src/semantic_model/model.rs b/crates/biome_js_semantic/src/semantic_model/model.rs index c99ffac49b63..2c0455c091de 100644 --- a/crates/biome_js_semantic/src/semantic_model/model.rs +++ b/crates/biome_js_semantic/src/semantic_model/model.rs @@ -6,22 +6,22 @@ pub(crate) struct BindingIndex(u32); impl From for BindingIndex { fn from(v: u32) -> Self { - BindingIndex(v) + Self(v) } } #[derive(Copy, Clone, Debug)] -pub(crate) struct ReferenceIndex(u32, u32); +pub(crate) struct ReferenceIndex(BindingIndex, u32); impl ReferenceIndex { pub(crate) fn binding(&self) -> BindingIndex { - BindingIndex(self.0) + self.0 } } impl From<(BindingIndex, u32)> for ReferenceIndex { fn from((binding_index, index): (BindingIndex, u32)) -> Self { - ReferenceIndex(binding_index.0, index) + ReferenceIndex(binding_index, index) } } @@ -47,7 +47,7 @@ pub(crate) struct SemanticModelData { // Index bindings by range start pub(crate) bindings_by_start: FxHashMap, // All bindings that were exported - pub(crate) exported: FxHashSet, + pub(crate) exported: FxHashSet, /// All references that could not be resolved pub(crate) unresolved_references: Vec, /// All globals references @@ -60,12 +60,12 @@ impl SemanticModelData { } pub(crate) fn reference(&self, index: ReferenceIndex) -> &SemanticModelReference { - let binding = &self.bindings[index.0 as usize]; + let binding = &self.bindings[index.binding().0 as usize]; &binding.references[index.1 as usize] } pub(crate) fn next_reference(&self, index: ReferenceIndex) -> Option<&SemanticModelReference> { - let binding = &self.bindings[index.0 as usize]; + let binding = &self.bindings[index.binding().0 as usize]; binding.references.get(index.1 as usize + 1) } @@ -90,7 +90,8 @@ impl SemanticModelData { } pub fn is_exported(&self, range: TextRange) -> bool { - self.exported.contains(&range.start()) + self.exported + .contains(&self.bindings_by_start[&range.start()]) } } diff --git a/crates/biome_js_semantic/src/tests/assertions.rs b/crates/biome_js_semantic/src/tests/assertions.rs index 19d4ed4668ab..bfe32575dbf6 100644 --- a/crates/biome_js_semantic/src/tests/assertions.rs +++ b/crates/biome_js_semantic/src/tests/assertions.rs @@ -124,18 +124,16 @@ 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 scope_range_by_id: 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, scope_id, .. - } => { - scope_range_by_id.insert(scope_id, range); + SemanticEvent::ScopeStarted { range, .. } => { + scope_ranges.push(range); range.start() } SemanticEvent::Read { range, .. } @@ -158,8 +156,8 @@ pub fn assert(code: &str, test_name: &str) { code, test_name, events_by_pos, - declaration_range_by_start, - scope_range_by_id, + declaration_ranges, + scope_ranges, ); } @@ -464,8 +462,8 @@ impl SemanticAssertions { code: &str, test_name: &str, events_by_pos: FxHashMap>, - declaration_range_by_start: FxHashMap, - scope_range_by_id: FxHashMap, + declaration_ranges: Vec, + scope_ranges: Vec, ) { // Check every declaration assertion is ok @@ -521,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 as usize) } _ => None, }; @@ -581,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 as usize) } _ => None, }; @@ -623,8 +621,9 @@ impl SemanticAssertions { .get(&at_scope_assertion.scope_name) { Some(scope_start_assertion) => { - let scope_started_at = - &scope_range_by_id[&hoisted_scope_id.unwrap_or(*scope_id)].start(); + let scope_started_at = &scope_ranges + [hoisted_scope_id.unwrap_or(*scope_id) as usize] + .start(); if scope_start_assertion.range.start() != *scope_started_at { show_all_events(test_name, code, events_by_pos, is_scope_event); show_unmatched_assertion(