From 46d6d26ddd98ad72e4cff7b55545ae7a0a8a6852 Mon Sep 17 00:00:00 2001 From: Vinicius Stock Date: Thu, 26 Mar 2026 10:12:22 -0400 Subject: [PATCH] Specialize reference IDs --- rust/rubydex-mcp/src/server.rs | 10 +- rust/rubydex-sys/src/declaration_api.rs | 32 ++++-- rust/rubydex-sys/src/reference_api.rs | 10 +- rust/rubydex/src/indexing/local_graph.rs | 18 ++-- rust/rubydex/src/indexing/rbs_indexer.rs | 4 +- rust/rubydex/src/model/declaration.rs | 104 ++++++++++++------ rust/rubydex/src/model/definitions.rs | 16 +-- rust/rubydex/src/model/document.rs | 18 ++-- rust/rubydex/src/model/graph.rs | 125 ++++++++++++++++++---- rust/rubydex/src/model/ids.rs | 36 +++++-- rust/rubydex/src/model/references.rs | 10 +- rust/rubydex/src/resolution.rs | 4 +- rust/rubydex/src/test_utils/graph_test.rs | 17 ++- 13 files changed, 287 insertions(+), 117 deletions(-) diff --git a/rust/rubydex-mcp/src/server.rs b/rust/rubydex-mcp/src/server.rs index 3d376439..571a2f82 100644 --- a/rust/rubydex-mcp/src/server.rs +++ b/rust/rubydex-mcp/src/server.rs @@ -419,8 +419,16 @@ impl RubydexServer { let limit = params.limit.filter(|&l| l > 0).unwrap_or(50).min(200); // default 50, max 200 let offset = params.offset.unwrap_or(0); + let empty = rubydex::model::identity_maps::IdentityHashSet::default(); + let constant_refs = match decl { + rubydex::model::declaration::Declaration::Namespace(ns) => ns.references(), + rubydex::model::declaration::Declaration::Constant(c) => c.references(), + rubydex::model::declaration::Declaration::ConstantAlias(ca) => ca.references(), + _ => &empty, + }; + let (references, total) = paginate!( - decl.references().iter(), + constant_refs.iter(), offset, limit, |ref_id| { diff --git a/rust/rubydex-sys/src/declaration_api.rs b/rust/rubydex-sys/src/declaration_api.rs index be613501..33bced25 100644 --- a/rust/rubydex-sys/src/declaration_api.rs +++ b/rust/rubydex-sys/src/declaration_api.rs @@ -449,21 +449,31 @@ pub unsafe extern "C" fn rdx_declaration_references_iter_new( return ReferencesIter::new(Vec::new().into_boxed_slice()); }; - let kind = match decl { - Declaration::Namespace(_) | Declaration::Constant(_) | Declaration::ConstantAlias(_) => { - ReferenceKind::Constant - } - Declaration::Method(_) => ReferenceKind::Method, + let entries: Vec<_> = match decl { + Declaration::Namespace(ns) => ns + .references() + .iter() + .map(|ref_id| CReference::new(**ref_id, ReferenceKind::Constant)) + .collect(), + Declaration::Constant(c) => c + .references() + .iter() + .map(|ref_id| CReference::new(**ref_id, ReferenceKind::Constant)) + .collect(), + Declaration::ConstantAlias(ca) => ca + .references() + .iter() + .map(|ref_id| CReference::new(**ref_id, ReferenceKind::Constant)) + .collect(), + Declaration::Method(m) => m + .references() + .iter() + .map(|ref_id| CReference::new(**ref_id, ReferenceKind::Method)) + .collect(), Declaration::GlobalVariable(_) | Declaration::InstanceVariable(_) | Declaration::ClassVariable(_) => { return ReferencesIter::new(Vec::new().into_boxed_slice()); } }; - - let entries: Vec<_> = decl - .references() - .iter() - .map(|ref_id| CReference::new(**ref_id, kind)) - .collect(); ReferencesIter::new(entries.into_boxed_slice()) }) } diff --git a/rust/rubydex-sys/src/reference_api.rs b/rust/rubydex-sys/src/reference_api.rs index 61792019..cb4f9203 100644 --- a/rust/rubydex-sys/src/reference_api.rs +++ b/rust/rubydex-sys/src/reference_api.rs @@ -5,7 +5,7 @@ use std::ffi::CString; use crate::graph_api::{GraphPointer, with_graph}; use crate::location_api::{Location, create_location_for_uri_and_offset}; use libc::c_char; -use rubydex::model::ids::ReferenceId; +use rubydex::model::ids::{ConstantReferenceId, MethodReferenceId}; /// Kind of reference for FFI dispatch #[repr(C)] @@ -107,7 +107,7 @@ pub unsafe extern "C" fn rdx_references_iter_free(iter: *mut ReferencesIter) { #[unsafe(no_mangle)] pub unsafe extern "C" fn rdx_constant_reference_name(pointer: GraphPointer, reference_id: u64) -> *const c_char { with_graph(pointer, |graph| { - let ref_id = ReferenceId::new(reference_id); + let ref_id = ConstantReferenceId::new(reference_id); let reference = graph.constant_references().get(&ref_id).expect("Reference not found"); let name = graph.names().get(reference.name_id()).expect("Name ID should exist"); @@ -133,7 +133,7 @@ pub unsafe extern "C" fn rdx_constant_reference_name(pointer: GraphPointer, refe #[unsafe(no_mangle)] pub unsafe extern "C" fn rdx_method_reference_name(pointer: GraphPointer, reference_id: u64) -> *const c_char { with_graph(pointer, |graph| { - let ref_id = ReferenceId::new(reference_id); + let ref_id = MethodReferenceId::new(reference_id); let reference = graph.method_references().get(&ref_id).expect("Reference not found"); let name = graph .strings() @@ -158,7 +158,7 @@ pub unsafe extern "C" fn rdx_method_reference_name(pointer: GraphPointer, refere #[unsafe(no_mangle)] pub unsafe extern "C" fn rdx_constant_reference_location(pointer: GraphPointer, reference_id: u64) -> *mut Location { with_graph(pointer, |graph| { - let ref_id = ReferenceId::new(reference_id); + let ref_id = ConstantReferenceId::new(reference_id); let reference = graph.constant_references().get(&ref_id).expect("Reference not found"); let document = graph .documents() @@ -183,7 +183,7 @@ pub unsafe extern "C" fn rdx_constant_reference_location(pointer: GraphPointer, #[unsafe(no_mangle)] pub unsafe extern "C" fn rdx_method_reference_location(pointer: GraphPointer, reference_id: u64) -> *mut Location { with_graph(pointer, |graph| { - let ref_id = ReferenceId::new(reference_id); + let ref_id = MethodReferenceId::new(reference_id); let reference = graph.method_references().get(&ref_id).expect("Reference not found"); let document = graph .documents() diff --git a/rust/rubydex/src/indexing/local_graph.rs b/rust/rubydex/src/indexing/local_graph.rs index 6f5bbcf9..df29e6cc 100644 --- a/rust/rubydex/src/indexing/local_graph.rs +++ b/rust/rubydex/src/indexing/local_graph.rs @@ -5,7 +5,7 @@ use crate::model::definitions::Definition; use crate::model::document::Document; use crate::model::graph::NameDependent; use crate::model::identity_maps::IdentityHashMap; -use crate::model::ids::{DefinitionId, NameId, ReferenceId, StringId, UriId}; +use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId, NameId, StringId, UriId}; use crate::model::name::{Name, NameRef}; use crate::model::references::{ConstantReference, MethodRef}; use crate::model::string_ref::StringRef; @@ -17,8 +17,8 @@ type LocalGraphParts = ( IdentityHashMap, IdentityHashMap, IdentityHashMap, - IdentityHashMap, - IdentityHashMap, + IdentityHashMap, + IdentityHashMap, IdentityHashMap>, ); @@ -29,8 +29,8 @@ pub struct LocalGraph { definitions: IdentityHashMap, strings: IdentityHashMap, names: IdentityHashMap, - constant_references: IdentityHashMap, - method_references: IdentityHashMap, + constant_references: IdentityHashMap, + method_references: IdentityHashMap, name_dependents: IdentityHashMap>, } @@ -150,11 +150,11 @@ impl LocalGraph { // Constant references #[must_use] - pub fn constant_references(&self) -> &IdentityHashMap { + pub fn constant_references(&self) -> &IdentityHashMap { &self.constant_references } - pub fn add_constant_reference(&mut self, reference: ConstantReference) -> ReferenceId { + pub fn add_constant_reference(&mut self, reference: ConstantReference) -> ConstantReferenceId { let reference_id = reference.id(); self.name_dependents .entry(*reference.name_id()) @@ -172,11 +172,11 @@ impl LocalGraph { // Method references #[must_use] - pub fn method_references(&self) -> &IdentityHashMap { + pub fn method_references(&self) -> &IdentityHashMap { &self.method_references } - pub fn add_method_reference(&mut self, reference: MethodRef) -> ReferenceId { + pub fn add_method_reference(&mut self, reference: MethodRef) -> MethodReferenceId { let reference_id = reference.id(); if self.method_references.insert(reference_id, reference).is_some() { diff --git a/rust/rubydex/src/indexing/rbs_indexer.rs b/rust/rubydex/src/indexing/rbs_indexer.rs index 239934af..3662f76a 100644 --- a/rust/rubydex/src/indexing/rbs_indexer.rs +++ b/rust/rubydex/src/indexing/rbs_indexer.rs @@ -16,7 +16,7 @@ use crate::model::definitions::{ PrependDefinition, Receiver, Signature, Signatures, }; use crate::model::document::Document; -use crate::model::ids::{DefinitionId, NameId, ReferenceId, StringId, UriId}; +use crate::model::ids::{ConstantReferenceId, DefinitionId, NameId, StringId, UriId}; use crate::model::name::{Name, ParentScope}; use crate::model::references::ConstantReference; use crate::model::visibility::Visibility; @@ -124,7 +124,7 @@ impl<'a> RBSIndexer<'a> { } } - fn index_mixin(&mut self, type_name: &TypeNameNode, mixin_fn: fn(ReferenceId) -> Mixin) { + fn index_mixin(&mut self, type_name: &TypeNameNode, mixin_fn: fn(ConstantReferenceId) -> Mixin) { let Some(lexical_nesting_id) = self.parent_lexical_scope_id() else { return; }; diff --git a/rust/rubydex/src/model/declaration.rs b/rust/rubydex/src/model/declaration.rs index d61b6520..f8b99fe8 100644 --- a/rust/rubydex/src/model/declaration.rs +++ b/rust/rubydex/src/model/declaration.rs @@ -1,8 +1,11 @@ use crate::assert_mem_size; use crate::diagnostic::Diagnostic; +use crate::model::ids::{ + ClassVariableReferenceId, GlobalVariableReferenceId, InstanceVariableReferenceId, MethodReferenceId, +}; use crate::model::{ identity_maps::{IdentityHashMap, IdentityHashSet}, - ids::{DeclarationId, DefinitionId, NameId, ReferenceId, StringId}, + ids::{ConstantReferenceId, DeclarationId, DefinitionId, NameId, StringId}, }; /// A single ancestor in the linearized ancestor chain @@ -92,7 +95,7 @@ macro_rules! namespace_declaration { /// The list of definition IDs that compose this declaration definition_ids: Vec, /// The set of references that are made to this declaration - references: IdentityHashSet, + references: IdentityHashSet, /// The ID of the owner of this declaration. For singleton classes, this is the ID of the attached object owner_id: DeclarationId, /// The entities that are owned by this declaration. For example, constants and methods that are defined inside of @@ -129,14 +132,32 @@ macro_rules! namespace_declaration { pub fn extend(&mut self, mut other: Declaration) { self.definition_ids.extend(other.definitions()); - self.references.extend(other.references()); self.diagnostics.extend(other.take_diagnostics()); - if let Declaration::Namespace(namespace) = other { - self.members.extend(namespace.members()); + match other { + Declaration::Namespace(namespace) => { + self.members.extend(namespace.members()); + self.references.extend(namespace.references()); + } + Declaration::Constant(constant) => { + self.references.extend(constant.references()); + } + Declaration::ConstantAlias(constant_alias) => { + self.references.extend(constant_alias.references()); + } + Declaration::Method(_) + | Declaration::GlobalVariable(_) + | Declaration::InstanceVariable(_) + | Declaration::ClassVariable(_) => { + panic!("Cannot extend a namespace declaration with a method declaration"); + } } } + pub fn add_reference(&mut self, reference_id: ConstantReferenceId) { + self.references.insert(reference_id); + } + pub fn set_singleton_class_id(&mut self, declaration_id: DeclarationId) { self.singleton_class_id = Some(declaration_id); } @@ -206,7 +227,7 @@ macro_rules! namespace_declaration { /// Macro to generate a new struct for simple declarations like variables and methods macro_rules! simple_declaration { - ($name:ident) => { + ($name:ident, $reference_type:ty) => { #[derive(Debug)] pub struct $name { /// The fully qualified name of this declaration @@ -214,7 +235,7 @@ macro_rules! simple_declaration { /// The list of definition IDs that compose this declaration definition_ids: Vec, /// The set of references that are made to this declaration - references: IdentityHashSet, + references: IdentityHashSet<$reference_type>, /// The ID of the owner of this declaration owner_id: DeclarationId, /// Diagnostics associated with this declaration @@ -233,10 +254,32 @@ macro_rules! simple_declaration { } } - pub fn extend(&mut self, mut other: Declaration) { + pub fn extend(&mut self, mut other: $name) { self.definition_ids.extend(other.definitions()); - self.references.extend(other.references()); self.diagnostics.extend(other.take_diagnostics()); + self.references.extend(other.references()); + } + + #[must_use] + pub fn references(&self) -> &IdentityHashSet<$reference_type> { + &self.references + } + + pub fn add_reference(&mut self, reference_id: $reference_type) { + self.references.insert(reference_id); + } + + pub fn remove_reference(&mut self, reference_id: &$reference_type) { + self.references.remove(reference_id); + } + + pub fn take_diagnostics(&mut self) -> Vec { + std::mem::take(&mut self.diagnostics) + } + + #[must_use] + pub fn definitions(&self) -> &[DefinitionId] { + &self.definition_ids } } }; @@ -292,11 +335,6 @@ impl Declaration { } } - #[must_use] - pub fn references(&self) -> &IdentityHashSet { - all_declarations!(self, it => &it.references) - } - #[must_use] pub fn definitions(&self) -> &[DefinitionId] { all_declarations!(self, it => &it.definition_ids) @@ -318,18 +356,6 @@ impl Declaration { }); } - pub fn add_reference(&mut self, id: ReferenceId) { - all_declarations!(self, it => { - it.references.insert(id); - }); - } - - pub fn remove_reference(&mut self, reference_id: &ReferenceId) { - all_declarations!(self, it => { - it.references.remove(reference_id); - }); - } - // Deletes a definition from this declaration pub fn remove_definition(&mut self, definition_id: &DefinitionId) -> bool { all_declarations!(self, it => { @@ -396,10 +422,22 @@ impl Namespace { } #[must_use] - pub fn references(&self) -> &IdentityHashSet { + pub fn references(&self) -> &IdentityHashSet { all_namespaces!(self, it => &it.references) } + pub fn remove_reference(&mut self, reference_id: &ConstantReferenceId) { + all_namespaces!(self, it => { + it.references.remove(reference_id); + }); + } + + pub fn add_reference(&mut self, reference_id: ConstantReferenceId) { + all_namespaces!(self, it => { + it.references.insert(reference_id); + }); + } + #[must_use] pub fn definitions(&self) -> &[DefinitionId] { all_namespaces!(self, it => &it.definition_ids) @@ -514,17 +552,17 @@ namespace_declaration!(SingletonClass, SingletonClassDeclaration); assert_mem_size!(SingletonClassDeclaration, 216); namespace_declaration!(Todo, TodoDeclaration); assert_mem_size!(TodoDeclaration, 216); -simple_declaration!(ConstantDeclaration); +simple_declaration!(ConstantDeclaration, ConstantReferenceId); assert_mem_size!(ConstantDeclaration, 112); -simple_declaration!(MethodDeclaration); +simple_declaration!(MethodDeclaration, MethodReferenceId); assert_mem_size!(MethodDeclaration, 112); -simple_declaration!(GlobalVariableDeclaration); +simple_declaration!(GlobalVariableDeclaration, GlobalVariableReferenceId); assert_mem_size!(GlobalVariableDeclaration, 112); -simple_declaration!(InstanceVariableDeclaration); +simple_declaration!(InstanceVariableDeclaration, InstanceVariableReferenceId); assert_mem_size!(InstanceVariableDeclaration, 112); -simple_declaration!(ClassVariableDeclaration); +simple_declaration!(ClassVariableDeclaration, ClassVariableReferenceId); assert_mem_size!(ClassVariableDeclaration, 112); -simple_declaration!(ConstantAliasDeclaration); +simple_declaration!(ConstantAliasDeclaration, ConstantReferenceId); assert_mem_size!(ConstantAliasDeclaration, 112); #[cfg(test)] diff --git a/rust/rubydex/src/model/definitions.rs b/rust/rubydex/src/model/definitions.rs index 55876ebb..598ce515 100644 --- a/rust/rubydex/src/model/definitions.rs +++ b/rust/rubydex/src/model/definitions.rs @@ -29,7 +29,7 @@ use crate::{ assert_mem_size, model::{ comment::Comment, - ids::{DefinitionId, NameId, ReferenceId, StringId, UriId}, + ids::{ConstantReferenceId, DefinitionId, NameId, StringId, UriId}, visibility::Visibility, }, offset::Offset, @@ -192,7 +192,7 @@ pub enum Mixin { impl Mixin { #[must_use] - pub fn constant_reference_id(&self) -> &ReferenceId { + pub fn constant_reference_id(&self) -> &ConstantReferenceId { match self { Mixin::Include(def) => def.constant_reference_id(), Mixin::Prepend(def) => def.constant_reference_id(), @@ -205,19 +205,19 @@ macro_rules! mixin_definition { ($variant:ident, $name:ident) => { #[derive(Debug, Clone)] pub struct $name { - constant_reference_id: ReferenceId, + constant_reference_id: ConstantReferenceId, } impl $name { #[must_use] - pub const fn new(constant_reference_id: ReferenceId) -> Self { + pub const fn new(constant_reference_id: ConstantReferenceId) -> Self { Self { constant_reference_id, } } #[must_use] - pub fn constant_reference_id(&self) -> &ReferenceId { + pub fn constant_reference_id(&self) -> &ConstantReferenceId { &self.constant_reference_id } } @@ -245,7 +245,7 @@ pub struct ClassDefinition { comments: Box<[Comment]>, lexical_nesting_id: Option, members: Vec, - superclass_ref: Option, + superclass_ref: Option, mixins: Vec, } assert_mem_size!(ClassDefinition, 120); @@ -261,7 +261,7 @@ impl ClassDefinition { comments: Box<[Comment]>, flags: DefinitionFlags, lexical_nesting_id: Option, - superclass_ref: Option, + superclass_ref: Option, ) -> Self { Self { name_id, @@ -313,7 +313,7 @@ impl ClassDefinition { } #[must_use] - pub fn superclass_ref(&self) -> Option<&ReferenceId> { + pub fn superclass_ref(&self) -> Option<&ConstantReferenceId> { self.superclass_ref.as_ref() } diff --git a/rust/rubydex/src/model/document.rs b/rust/rubydex/src/model/document.rs index b8c84a4f..339a5ae5 100644 --- a/rust/rubydex/src/model/document.rs +++ b/rust/rubydex/src/model/document.rs @@ -4,7 +4,7 @@ use line_index::LineIndex; use url::Url; use crate::diagnostic::Diagnostic; -use crate::model::ids::{DefinitionId, ReferenceId}; +use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId}; // Represents a document currently loaded into memory. Identified by its unique URI, it holds the edges to all // definitions and references discovered in it @@ -13,8 +13,8 @@ pub struct Document { uri: String, line_index: LineIndex, definition_ids: Vec, - method_reference_ids: Vec, - constant_reference_ids: Vec, + method_reference_ids: Vec, + constant_reference_ids: Vec, diagnostics: Vec, } @@ -56,20 +56,20 @@ impl Document { } #[must_use] - pub fn method_references(&self) -> &[ReferenceId] { + pub fn method_references(&self) -> &[MethodReferenceId] { &self.method_reference_ids } - pub fn add_method_reference(&mut self, reference_id: ReferenceId) { + pub fn add_method_reference(&mut self, reference_id: MethodReferenceId) { self.method_reference_ids.push(reference_id); } #[must_use] - pub fn constant_references(&self) -> &[ReferenceId] { + pub fn constant_references(&self) -> &[ConstantReferenceId] { &self.constant_reference_ids } - pub fn add_constant_reference(&mut self, reference_id: ReferenceId) { + pub fn add_constant_reference(&mut self, reference_id: ConstantReferenceId) { self.constant_reference_ids.push(reference_id); } @@ -150,8 +150,8 @@ mod tests { #[test] fn tracking_references() { let mut document = Document::new("file:///foo.rb".to_string(), "class Foo; end"); - let method_ref = ReferenceId::new(1); - let constant_ref = ReferenceId::new(2); + let method_ref = MethodReferenceId::new(1); + let constant_ref = ConstantReferenceId::new(2); document.add_method_reference(method_ref); document.add_constant_reference(constant_ref); diff --git a/rust/rubydex/src/model/graph.rs b/rust/rubydex/src/model/graph.rs index bbf40a5b..e8f28037 100644 --- a/rust/rubydex/src/model/graph.rs +++ b/rust/rubydex/src/model/graph.rs @@ -8,7 +8,7 @@ use crate::model::definitions::Definition; use crate::model::document::Document; use crate::model::encoding::Encoding; use crate::model::identity_maps::{IdentityHashMap, IdentityHashSet}; -use crate::model::ids::{DeclarationId, DefinitionId, NameId, ReferenceId, StringId, UriId}; +use crate::model::ids::{ConstantReferenceId, DeclarationId, DefinitionId, MethodReferenceId, NameId, StringId, UriId}; use crate::model::name::{Name, NameRef, ParentScope, ResolvedName}; use crate::model::references::{ConstantReference, MethodRef}; use crate::model::string_ref::StringRef; @@ -19,7 +19,7 @@ use crate::stats; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum NameDependent { Definition(DefinitionId), - Reference(ReferenceId), + Reference(ConstantReferenceId), /// This name's `parent_scope` is the key name — structural dependency. ChildName(NameId), /// This name's `nesting` is the key name — reference-only dependency. @@ -47,7 +47,7 @@ pub enum Unit { /// A definition that defines a constant and might require resolution Definition(DefinitionId), /// A constant reference that needs to be resolved - ConstantRef(ReferenceId), + ConstantRef(ConstantReferenceId), /// A declaration whose ancestors need re-linearization Ancestors(DeclarationId), } @@ -68,9 +68,9 @@ pub struct Graph { // Map of names names: IdentityHashMap, // Map of constant references - constant_references: IdentityHashMap, + constant_references: IdentityHashMap, // Map of method references that still need to be resolved - method_references: IdentityHashMap, + method_references: IdentityHashMap, /// The position encoding used for LSP line/column locations. Not related to the actual encoding of the file position_encoding: Encoding, @@ -395,13 +395,13 @@ impl Graph { // Returns an immutable reference to the constant references map #[must_use] - pub fn constant_references(&self) -> &IdentityHashMap { + pub fn constant_references(&self) -> &IdentityHashMap { &self.constant_references } // Returns an immutable reference to the method references map #[must_use] - pub fn method_references(&self) -> &IdentityHashMap { + pub fn method_references(&self) -> &IdentityHashMap { &self.method_references } @@ -591,14 +591,34 @@ impl Graph { /// Unresolves a constant reference: removes it from the target declaration's reference set /// and unresolves its underlying name. - fn unresolve_reference(&mut self, reference_id: ReferenceId) -> Option { + fn unresolve_reference(&mut self, reference_id: ConstantReferenceId) -> Option { let constant_ref = self.constant_references.get(&reference_id)?; let name_id = *constant_ref.name_id(); if let Some(old_decl_id) = self.unresolve_name(name_id) { - if let Some(declaration) = self.declarations.get_mut(&old_decl_id) { - declaration.remove_reference(&reference_id); + match self.declarations.get_mut(&old_decl_id) { + Some(Declaration::Namespace(it)) => { + it.remove_reference(&reference_id); + } + Some(Declaration::Constant(it)) => { + it.remove_reference(&reference_id); + } + Some(Declaration::ConstantAlias(it)) => { + it.remove_reference(&reference_id); + } + Some( + Declaration::Method(_) + | Declaration::GlobalVariable(_) + | Declaration::InstanceVariable(_) + | Declaration::ClassVariable(_), + ) => { + unreachable!("Cannot unresolve reference for something that isn't a constant") + } + None => { + panic!("Tried to unresolve reference for declaration that doesn't exist in the graph") + } } + Some(old_decl_id) } else { None @@ -765,9 +785,31 @@ impl Graph { } } - pub fn record_resolved_reference(&mut self, reference_id: ReferenceId, declaration_id: DeclarationId) { - if let Some(declaration) = self.declarations.get_mut(&declaration_id) { - declaration.add_reference(reference_id); + /// # Panics + /// + /// Will panic if invoked for a non existing declaration + pub fn record_resolved_reference(&mut self, reference_id: ConstantReferenceId, declaration_id: DeclarationId) { + match self.declarations.get_mut(&declaration_id) { + Some(Declaration::Namespace(it)) => { + it.add_reference(reference_id); + } + Some(Declaration::Constant(it)) => { + it.add_reference(reference_id); + } + Some(Declaration::ConstantAlias(it)) => { + it.add_reference(reference_id); + } + Some( + Declaration::Method(_) + | Declaration::GlobalVariable(_) + | Declaration::InstanceVariable(_) + | Declaration::ClassVariable(_), + ) => { + unreachable!("Cannot record constant reference for something that isn't a constant") + } + None => { + panic!("Tried to record a constant reference for a declaration that doesn't exist") + } } } @@ -945,7 +987,23 @@ impl Graph { if let NameRef::Resolved(resolved) = self.names.get(constant_ref.name_id()).unwrap() && let Some(declaration) = self.declarations.get_mut(resolved.declaration_id()) { - declaration.remove_reference(ref_id); + match declaration { + Declaration::Namespace(it) => { + it.remove_reference(ref_id); + } + Declaration::Constant(it) => { + it.remove_reference(ref_id); + } + Declaration::ConstantAlias(it) => { + it.remove_reference(ref_id); + } + Declaration::Method(_) + | Declaration::GlobalVariable(_) + | Declaration::InstanceVariable(_) + | Declaration::ClassVariable(_) => { + unreachable!("Cannot remove constant reference ID from non constant declaration") + } + } } self.remove_name_dependent(*constant_ref.name_id(), NameDependent::Reference(*ref_id)); @@ -1123,7 +1181,23 @@ impl Graph { match dep { NameDependent::Reference(ref_id) => { if let Some(decl) = self.declarations.get_mut(&old_decl_id) { - decl.remove_reference(ref_id); + match decl { + Declaration::Namespace(it) => { + it.remove_reference(ref_id); + } + Declaration::Constant(it) => { + it.remove_reference(ref_id); + } + Declaration::ConstantAlias(it) => { + it.remove_reference(ref_id); + } + Declaration::Method(_) + | Declaration::GlobalVariable(_) + | Declaration::InstanceVariable(_) + | Declaration::ClassVariable(_) => { + unreachable!("Cannot unresolve reference for something that isn't a constant") + } + } } self.push_work(Unit::ConstantRef(*ref_id)); } @@ -1216,7 +1290,18 @@ impl Graph { } } - for ref_id in decl.references() { + let empty = IdentityHashSet::default(); + let references = match decl { + Declaration::Namespace(it) => it.references(), + Declaration::Constant(it) => it.references(), + Declaration::ConstantAlias(it) => it.references(), + Declaration::Method(_) + | Declaration::GlobalVariable(_) + | Declaration::InstanceVariable(_) + | Declaration::ClassVariable(_) => &empty, + }; + + for ref_id in references { if let Some(constant_ref) = self.constant_references.get(ref_id) { let name_id = *constant_ref.name_id(); if matches!(self.names.get(&name_id), Some(NameRef::Resolved(_))) { @@ -1498,7 +1583,7 @@ mod tests { assert_eq!(context.graph().constant_references.len(), 2); { let declaration = context.graph().declarations().get(&DeclarationId::from("Foo")).unwrap(); - assert_eq!(declaration.references().len(), 1); + assert_eq!(declaration.as_namespace().unwrap().references().len(), 1); } // Update with empty content to remove definitions but keep the URI @@ -1510,7 +1595,7 @@ mod tests { assert!(context.graph().constant_references.is_empty()); { let declaration = context.graph().declarations().get(&DeclarationId::from("Foo")).unwrap(); - assert!(declaration.references().is_empty()); + assert!(declaration.as_namespace().unwrap().references().is_empty()); } } @@ -2200,7 +2285,7 @@ mod tests { // Bar declaration should have 1 reference (from foo.rb) let bar_decl = context.graph().declarations().get(&DeclarationId::from("Bar")).unwrap(); - assert_eq!(bar_decl.references().len(), 1); + assert_eq!(bar_decl.as_namespace().unwrap().references().len(), 1); // Update foo.rb to remove the Bar reference context.index_uri("file:///foo.rb", "module Foo; end"); @@ -2208,7 +2293,7 @@ mod tests { let bar_decl = context.graph().declarations().get(&DeclarationId::from("Bar")).unwrap(); assert!( - bar_decl.references().is_empty(), + bar_decl.as_namespace().unwrap().references().is_empty(), "Reference to Bar should be detached from declaration" ); diff --git a/rust/rubydex/src/model/ids.rs b/rust/rubydex/src/model/ids.rs index 11baa64a..98b0c15b 100644 --- a/rust/rubydex/src/model/ids.rs +++ b/rust/rubydex/src/model/ids.rs @@ -24,15 +24,37 @@ pub struct StringMarker; pub type StringId = Id; assert_mem_size!(StringId, 8); -#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] -pub struct ReferenceMarker; -/// `ReferenceId` represents the ID of a reference occurrence in a file. -/// It is built from the reference kind, `uri_id` and the reference `offset`. -pub type ReferenceId = Id; -assert_mem_size!(ReferenceId, 8); - #[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] pub struct NameMarker; /// `NameId` represents an ID for any constant name that we find as part of a reference or definition pub type NameId = Id; assert_mem_size!(NameId, 8); + +// Reference IDs +// +// This section is for specialized IDs for each type of declaration reference + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +pub struct ConstantMarker; +pub type ConstantReferenceId = Id; +assert_mem_size!(ConstantReferenceId, 8); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +pub struct MethodMarker; +pub type MethodReferenceId = Id; +assert_mem_size!(MethodReferenceId, 8); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +pub struct GlobalVariableMarker; +pub type GlobalVariableReferenceId = Id; +assert_mem_size!(GlobalVariableReferenceId, 8); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +pub struct ClassVariableMarker; +pub type ClassVariableReferenceId = Id; +assert_mem_size!(ClassVariableReferenceId, 8); + +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] +pub struct InstanceVariableMarker; +pub type InstanceVariableReferenceId = Id; +assert_mem_size!(InstanceVariableReferenceId, 8); diff --git a/rust/rubydex/src/model/references.rs b/rust/rubydex/src/model/references.rs index 5a2f9a92..bb1b5053 100644 --- a/rust/rubydex/src/model/references.rs +++ b/rust/rubydex/src/model/references.rs @@ -1,6 +1,6 @@ use crate::{ assert_mem_size, - model::ids::{NameId, ReferenceId, StringId, UriId}, + model::ids::{ConstantReferenceId, MethodReferenceId, NameId, StringId, UriId}, offset::Offset, }; @@ -42,8 +42,8 @@ impl ConstantReference { } #[must_use] - pub fn id(&self) -> ReferenceId { - ReferenceId::from(&format!( + pub fn id(&self) -> ConstantReferenceId { + ConstantReferenceId::from(&format!( "{}:{}:{}-{}", self.name_id, self.uri_id, @@ -99,8 +99,8 @@ impl MethodRef { } #[must_use] - pub fn id(&self) -> ReferenceId { - ReferenceId::from(&format!( + pub fn id(&self) -> MethodReferenceId { + MethodReferenceId::from(&format!( "{}:{}:{}-{}", self.str, self.uri_id, diff --git a/rust/rubydex/src/resolution.rs b/rust/rubydex/src/resolution.rs index d76d68ff..10ca1a32 100644 --- a/rust/rubydex/src/resolution.rs +++ b/rust/rubydex/src/resolution.rs @@ -12,7 +12,7 @@ use crate::model::{ definitions::{Definition, Mixin, Receiver}, graph::{CLASS_ID, Graph, MODULE_ID, OBJECT_ID, Unit}, identity_maps::{IdentityHashBuilder, IdentityHashMap, IdentityHashSet}, - ids::{DeclarationId, DefinitionId, NameId, ReferenceId, StringId}, + ids::{ConstantReferenceId, DeclarationId, DefinitionId, NameId, StringId}, name::{Name, NameRef, ParentScope}, }; @@ -221,7 +221,7 @@ impl<'a> Resolver<'a> { } /// Handles a unit of work for resolving a constant reference - fn handle_reference_unit(&mut self, unit_id: Unit, id: ReferenceId) { + fn handle_reference_unit(&mut self, unit_id: Unit, id: ConstantReferenceId) { let constant_ref = self.graph.constant_references().get(&id).unwrap(); match self.resolve_constant_internal(*constant_ref.name_id()) { diff --git a/rust/rubydex/src/test_utils/graph_test.rs b/rust/rubydex/src/test_utils/graph_test.rs index 1572eff4..9f6c2c8b 100644 --- a/rust/rubydex/src/test_utils/graph_test.rs +++ b/rust/rubydex/src/test_utils/graph_test.rs @@ -351,13 +351,20 @@ macro_rules! assert_declaration_references_count_eq { .get(&$crate::model::ids::DeclarationId::from($declaration_name)) .unwrap(); + let count = match declaration { + $crate::model::declaration::Declaration::Namespace(it) => it.references().len(), + $crate::model::declaration::Declaration::Constant(it) => it.references().len(), + $crate::model::declaration::Declaration::ConstantAlias(it) => it.references().len(), + $crate::model::declaration::Declaration::Method(it) => it.references().len(), + $crate::model::declaration::Declaration::GlobalVariable(it) => it.references().len(), + $crate::model::declaration::Declaration::InstanceVariable(it) => it.references().len(), + $crate::model::declaration::Declaration::ClassVariable(it) => it.references().len(), + }; + assert_eq!( - declaration.references().len(), - $expected_references, + count, $expected_references, "Expected exactly {} references for `{}`, but got {}", - $expected_references, - $declaration_name, - declaration.references().len() + $expected_references, $declaration_name, count, ); }; }