diff --git a/crates/rome_js_analyze/src/semantic_services.rs b/crates/rome_js_analyze/src/semantic_services.rs index 51a77d364bd6..60fc7560d4e9 100644 --- a/crates/rome_js_analyze/src/semantic_services.rs +++ b/crates/rome_js_analyze/src/semantic_services.rs @@ -3,6 +3,17 @@ use rome_analyze::{ RuleKey, ServiceBag, Visitor, VisitorContext, VisitorFinishContext, }; use rome_js_semantic::{SemanticEventExtractor, SemanticModel, SemanticModelBuilder}; +use rome_js_syntax::JsSyntaxKind::{ + JSX_REFERENCE_IDENTIFIER, JS_ARROW_FUNCTION_EXPRESSION, JS_BLOCK_STATEMENT, JS_CALL_EXPRESSION, + JS_CATCH_CLAUSE, JS_CLASS_DECLARATION, JS_CLASS_EXPORT_DEFAULT_DECLARATION, + JS_CLASS_EXPRESSION, JS_CONSTRUCTOR_CLASS_MEMBER, JS_FOR_IN_STATEMENT, JS_FOR_OF_STATEMENT, + JS_FOR_STATEMENT, JS_FUNCTION_BODY, JS_FUNCTION_DECLARATION, + JS_FUNCTION_EXPORT_DEFAULT_DECLARATION, JS_FUNCTION_EXPRESSION, JS_GETTER_CLASS_MEMBER, + JS_GETTER_OBJECT_MEMBER, JS_IDENTIFIER_ASSIGNMENT, JS_IDENTIFIER_BINDING, + JS_METHOD_CLASS_MEMBER, JS_METHOD_OBJECT_MEMBER, JS_MODULE, JS_REFERENCE_IDENTIFIER, JS_SCRIPT, + JS_SETTER_CLASS_MEMBER, JS_SETTER_OBJECT_MEMBER, JS_SWITCH_STATEMENT, TS_ENUM_DECLARATION, + TS_FUNCTION_TYPE, TS_IDENTIFIER_BINDING, TS_INTERFACE_DECLARATION, TS_TYPE_ALIAS_DECLARATION, +}; use rome_js_syntax::{JsAnyRoot, JsLanguage, WalkEvent}; use rome_rowan::{AstNode, SyntaxNode}; @@ -101,7 +112,50 @@ impl Visitor for SemanticModelBuilderVisitor { ) { match event { WalkEvent::Enter(node) => { - self.builder.push_node(node); + match node.kind() { + JS_IDENTIFIER_BINDING | TS_IDENTIFIER_BINDING => { + self.builder.push_node(node); + } + JS_REFERENCE_IDENTIFIER | JSX_REFERENCE_IDENTIFIER => { + self.builder.push_node(node); + } + JS_IDENTIFIER_ASSIGNMENT => { + self.builder.push_node(node); + } + JS_CALL_EXPRESSION => { + self.builder.push_node(node); + } + + JS_MODULE | JS_SCRIPT => self.builder.push_node(node), + JS_FUNCTION_DECLARATION + | JS_FUNCTION_EXPORT_DEFAULT_DECLARATION + | JS_FUNCTION_EXPRESSION + | JS_ARROW_FUNCTION_EXPRESSION + | JS_CLASS_DECLARATION + | JS_CLASS_EXPORT_DEFAULT_DECLARATION + | JS_CLASS_EXPRESSION + | JS_CONSTRUCTOR_CLASS_MEMBER + | JS_METHOD_CLASS_MEMBER + | JS_GETTER_CLASS_MEMBER + | JS_SETTER_CLASS_MEMBER + | JS_METHOD_OBJECT_MEMBER + | JS_GETTER_OBJECT_MEMBER + | JS_SETTER_OBJECT_MEMBER + | JS_FUNCTION_BODY + | TS_INTERFACE_DECLARATION + | TS_ENUM_DECLARATION + | TS_TYPE_ALIAS_DECLARATION + | TS_FUNCTION_TYPE => { + self.builder.push_node(node); + } + + JS_BLOCK_STATEMENT | JS_FOR_STATEMENT | JS_FOR_OF_STATEMENT + | JS_FOR_IN_STATEMENT | JS_SWITCH_STATEMENT | JS_CATCH_CLAUSE => { + self.builder.push_node(node); + } + _ => {} + } + self.extractor.enter(node); } WalkEvent::Leave(node) => { diff --git a/crates/rome_js_semantic/src/semantic_model.rs b/crates/rome_js_semantic/src/semantic_model.rs index 935c5e28e2d4..ec9de30bc4a6 100644 --- a/crates/rome_js_semantic/src/semantic_model.rs +++ b/crates/rome_js_semantic/src/semantic_model.rs @@ -12,7 +12,7 @@ use rome_rowan::{AstNode, SyntaxTokenText}; use rust_lapper::{Interval, Lapper}; use rustc_hash::{FxHashMap, FxHashSet}; use std::{ - collections::{BTreeSet, HashSet, VecDeque}, + collections::{BTreeSet, VecDeque}, iter::FusedIterator, sync::Arc, }; @@ -138,10 +138,6 @@ struct SemanticModelData { declared_at_by_range: FxHashMap, // Maps a declaration range to the range of its references declaration_all_references: FxHashMap>, - // Maps a declaration range to the range of its "reads" - declaration_all_reads: FxHashMap>, - // Maps a declaration range to the range of its "writes" - declaration_all_writes: FxHashMap>, // All bindings that were exported exported: FxHashSet, /// All references that could not be resolved @@ -182,25 +178,19 @@ impl SemanticModelData { } } - pub fn all_reads_iter( - &self, - range: &TextRange, - ) -> std::slice::Iter<'_, (ReferenceType, TextRange)> { - if let Some(v) = self.declaration_all_reads.get(range) { - v.iter() + pub fn all_reads_iter(&self, range: &TextRange) -> ReadsIter { + if let Some(v) = self.declaration_all_references.get(range) { + ReadsIter { iter: v.iter() } } else { - [].iter() + ReadsIter { iter: [].iter() } } } - pub fn all_writes_iter( - &self, - range: &TextRange, - ) -> std::slice::Iter<'_, (ReferenceType, TextRange)> { - if let Some(v) = self.declaration_all_writes.get(range) { - v.iter() + pub fn all_writes_iter(&self, range: &TextRange) -> WritesIter { + if let Some(v) = self.declaration_all_references.get(range) { + WritesIter { iter: v.iter() } } else { - [].iter() + WritesIter { iter: [].iter() } } } @@ -376,18 +366,18 @@ impl Binding { } /// Returns an iterator to all reads references of this binding. - pub fn all_reads(&self) -> ReferencesIter { + pub fn all_reads(&self) -> ReadReferencesIter { let range = self.node.text_range(); - ReferencesIter { + ReadReferencesIter { data: self.data.clone(), iter: self.data.all_reads_iter(&range), } } /// Returns an iterator to all write references of this binding. - pub fn all_writes(&self) -> ReferencesIter { + pub fn all_writes(&self) -> WriteReferencesIter { let range = self.node.text_range(); - ReferencesIter { + WriteReferencesIter { data: self.data.clone(), iter: self.data.all_writes_iter(&range), } @@ -448,6 +438,86 @@ impl Reference { } } +struct ReadsIter<'a> { + iter: std::slice::Iter<'a, (ReferenceType, TextRange)>, +} + +impl<'a> Iterator for ReadsIter<'a> { + type Item = (ReferenceType, TextRange); + + fn next(&mut self) -> Option { + let result = loop { + match self.iter.next()? { + (read @ ReferenceType::Read { .. }, range) => { + break (*read, *range); + } + _ => {} + } + }; + Some(result) + } +} + +pub struct ReadReferencesIter<'a> { + data: Arc, + iter: ReadsIter<'a>, +} + +impl<'a> Iterator for ReadReferencesIter<'a> { + type Item = Reference; + + fn next(&mut self) -> Option { + let (ty, range) = self.iter.next()?; + let node = self.data.node_by_range.get(&range)?; + Some(Reference { + data: self.data.clone(), + node: node.clone(), + range, + ty, + }) + } +} + +struct WritesIter<'a> { + iter: std::slice::Iter<'a, (ReferenceType, TextRange)>, +} + +impl<'a> Iterator for WritesIter<'a> { + type Item = (ReferenceType, TextRange); + + fn next(&mut self) -> Option { + let result = loop { + match self.iter.next()? { + (read @ ReferenceType::Write { .. }, range) => { + break (*read, *range); + } + _ => {} + } + }; + Some(result) + } +} + +pub struct WriteReferencesIter<'a> { + data: Arc, + iter: WritesIter<'a>, +} + +impl<'a> Iterator for WriteReferencesIter<'a> { + type Item = Reference; + + fn next(&mut self) -> Option { + let (ty, range) = self.iter.next()?; + let node = self.data.node_by_range.get(&range)?; + Some(Reference { + data: self.data.clone(), + node: node.clone(), + range, + ty, + }) + } +} + /// Iterate all references of a particular declaration. pub struct ReferencesIter<'a> { data: Arc, @@ -723,10 +793,10 @@ impl SemanticModel { /// Returns a list with all read [Reference] of the specified declaration. /// Can also be called from "all_reads" extension method. - pub fn all_reads<'a>(&'a self, declaration: &impl IsDeclarationAstNode) -> ReferencesIter<'a> { + pub fn all_reads(&self, declaration: &impl IsDeclarationAstNode) -> ReadReferencesIter { let node = declaration.node(); let range = node.syntax().text_range(); - ReferencesIter { + ReadReferencesIter { data: self.data.clone(), iter: self.data.all_reads_iter(&range), } @@ -734,10 +804,10 @@ impl SemanticModel { /// Returns a list with all write [Reference] of the specified declaration. /// Can also be called from "all_writes" extension method. - pub fn all_writes<'a>(&'a self, declaration: &impl IsDeclarationAstNode) -> ReferencesIter<'a> { + pub fn all_writes(&self, declaration: &impl IsDeclarationAstNode) -> WriteReferencesIter { let node = declaration.node(); let range = node.syntax().text_range(); - ReferencesIter { + WriteReferencesIter { data: self.data.clone(), iter: self.data.all_writes_iter(&range), } @@ -847,14 +917,14 @@ pub trait AllReferencesExtensions { model.all_references(self) } - fn all_reads<'a>(&self, model: &'a SemanticModel) -> ReferencesIter<'a> + fn all_reads<'a>(&self, model: &'a SemanticModel) -> ReadReferencesIter<'a> where Self: IsDeclarationAstNode, { model.all_reads(self) } - fn all_writes<'a>(&self, model: &'a SemanticModel) -> ReferencesIter<'a> + fn all_writes<'a>(&self, model: &'a SemanticModel) -> WriteReferencesIter<'a> where Self: IsDeclarationAstNode, { @@ -890,8 +960,6 @@ pub struct SemanticModelBuilder { scope_hoisted_to_by_range: FxHashMap, declarations_by_range: FxHashMap, declaration_all_references: FxHashMap>, - declaration_all_reads: FxHashMap>, - declaration_all_writes: FxHashMap>, exported: FxHashSet, unresolved_references: Vec<(ReferenceType, TextRange)>, global_references: Vec<(ReferenceType, TextRange)>, @@ -908,8 +976,7 @@ impl SemanticModelBuilder { scope_hoisted_to_by_range: FxHashMap::default(), declarations_by_range: FxHashMap::default(), declaration_all_references: FxHashMap::default(), - declaration_all_reads: FxHashMap::default(), - declaration_all_writes: FxHashMap::default(), + exported: FxHashSet::default(), unresolved_references: Vec::new(), global_references: Vec::new(), @@ -997,10 +1064,6 @@ impl SemanticModelBuilder { .entry(declaration_at) .or_default() .push((ReferenceType::Read { hoisted: false }, range)); - self.declaration_all_reads - .entry(declaration_at) - .or_default() - .push((ReferenceType::Read { hoisted: false }, range)); let scope = &mut self.scopes[scope_id]; scope.read_references.push(ScopeReference { range }); @@ -1015,10 +1078,6 @@ impl SemanticModelBuilder { .entry(declaration_at) .or_default() .push((ReferenceType::Read { hoisted: true }, range)); - self.declaration_all_reads - .entry(declaration_at) - .or_default() - .push((ReferenceType::Read { hoisted: true }, range)); let scope = &mut self.scopes[scope_id]; scope.read_references.push(ScopeReference { range }); @@ -1033,10 +1092,6 @@ impl SemanticModelBuilder { .entry(declaration_at) .or_default() .push((ReferenceType::Write { hoisted: false }, range)); - self.declaration_all_writes - .entry(declaration_at) - .or_default() - .push((ReferenceType::Write { hoisted: false }, range)); let scope = &mut self.scopes[scope_id]; scope.write_references.push(ScopeReference { range }); @@ -1051,11 +1106,6 @@ impl SemanticModelBuilder { .entry(declaration_at) .or_default() .push((ReferenceType::Write { hoisted: true }, range)); - self.declaration_all_writes - .entry(declaration_at) - .or_default() - .push((ReferenceType::Write { hoisted: true }, range)); - let scope = &mut self.scopes[scope_id]; scope.write_references.push(ScopeReference { range }); } @@ -1097,8 +1147,6 @@ impl SemanticModelBuilder { node_by_range: self.node_by_range, declared_at_by_range: self.declarations_by_range, declaration_all_references: self.declaration_all_references, - declaration_all_reads: self.declaration_all_reads, - declaration_all_writes: self.declaration_all_writes, exported: self.exported, unresolved_references: self.unresolved_references, global_references: self.global_references, @@ -1111,7 +1159,7 @@ impl SemanticModelBuilder { /// Extra options for the [SemanticModel] creation. pub struct SemanticModelOptions { /// All the allowed globals names - pub globals: HashSet, + pub globals: FxHashSet, } /// Build the complete [SemanticModel] of a parsed file.