diff --git a/ext/rubydex/definition.c b/ext/rubydex/definition.c index 53d4b544c..631e5449c 100644 --- a/ext/rubydex/definition.c +++ b/ext/rubydex/definition.c @@ -308,8 +308,12 @@ static VALUE rdxr_definition_mixins(VALUE self) { CMixin entry; while (rdx_mixins_iter_next(iter, &entry)) { VALUE constant_ref = rdxi_build_constant_reference(data->graph_obj, &entry.constant_reference); + VALUE location = rdxi_build_location_value(entry.location); + rdx_location_free(entry.location); + VALUE mixin_class = rdxi_mixin_class_for_kind(entry.kind); - VALUE mixin = rb_class_new_instance(1, &constant_ref, mixin_class); + VALUE argv[] = {constant_ref, location}; + VALUE mixin = rb_class_new_instance(2, argv, mixin_class); rb_ary_push(ary, mixin); } diff --git a/lib/rubydex/mixin.rb b/lib/rubydex/mixin.rb index 86feefc65..704e95c6a 100644 --- a/lib/rubydex/mixin.rb +++ b/lib/rubydex/mixin.rb @@ -5,9 +5,13 @@ class Mixin #: ConstantReference attr_reader :constant_reference - #: (ConstantReference) -> void - def initialize(constant_reference) + #: Location + attr_reader :location + + #: (ConstantReference, Location) -> void + def initialize(constant_reference, location) @constant_reference = constant_reference + @location = location end end diff --git a/rbi/rubydex.rbi b/rbi/rubydex.rbi index 28db342a4..593d598c3 100644 --- a/rbi/rubydex.rbi +++ b/rbi/rubydex.rbi @@ -201,8 +201,11 @@ class Rubydex::Mixin sig { returns(Rubydex::ConstantReference) } attr_reader :constant_reference - sig { params(constant_reference: Rubydex::ConstantReference).void } - def initialize(constant_reference); end + sig { returns(Rubydex::Location) } + attr_reader :location + + sig { params(constant_reference: Rubydex::ConstantReference, location: Rubydex::Location).void } + def initialize(constant_reference, location); end end class Rubydex::Include < Rubydex::Mixin; end diff --git a/rust/rubydex-sys/src/definition_api.rs b/rust/rubydex-sys/src/definition_api.rs index b6f470595..3a6242bef 100644 --- a/rust/rubydex-sys/src/definition_api.rs +++ b/rust/rubydex-sys/src/definition_api.rs @@ -417,6 +417,7 @@ pub enum MixinKind { pub struct CMixin { pub kind: MixinKind, pub constant_reference: CConstantReference, + pub location: *mut Location, } #[derive(Debug)] @@ -482,9 +483,21 @@ pub unsafe extern "C" fn rdx_definition_mixins(pointer: GraphPointer, definition let entries: Vec = mixins .iter() - .map(|mixin| CMixin { - kind: map_mixin_kind(mixin), - constant_reference: CConstantReference::from_id(graph, *mixin.constant_reference_id()), + .map(|mixin| { + let constant_reference = graph + .constant_references() + .get(mixin.constant_reference_id()) + .expect("Constant reference not found"); + let document = graph + .documents() + .get(&constant_reference.uri_id()) + .expect("document should exist"); + + CMixin { + kind: map_mixin_kind(mixin), + constant_reference: CConstantReference::from_id(graph, *mixin.constant_reference_id()), + location: create_location_for_uri_and_offset(graph, document, mixin.offset()), + } }) .collect(); diff --git a/rust/rubydex/src/indexing/rbs_indexer.rs b/rust/rubydex/src/indexing/rbs_indexer.rs index b27bb76d2..9582a3d14 100644 --- a/rust/rubydex/src/indexing/rbs_indexer.rs +++ b/rust/rubydex/src/indexing/rbs_indexer.rs @@ -125,7 +125,7 @@ impl<'a> RBSIndexer<'a> { } } - fn index_mixin(&mut self, type_name: &TypeNameNode, mixin_fn: fn(ConstantReferenceId) -> Mixin) { + fn index_mixin(&mut self, type_name: &TypeNameNode, mixin_fn: fn(ConstantReferenceId, Offset) -> Mixin) { let Some(lexical_nesting_id) = self.parent_lexical_scope_id() else { return; }; @@ -136,9 +136,9 @@ impl<'a> RBSIndexer<'a> { let constant_ref_id = self.local_graph - .add_constant_reference(ConstantReference::new(name_id, self.uri_id, offset)); + .add_constant_reference(ConstantReference::new(name_id, self.uri_id, offset.clone())); - self.add_mixin_to_current_lexical_scope(lexical_nesting_id, mixin_fn(constant_ref_id)); + self.add_mixin_to_current_lexical_scope(lexical_nesting_id, mixin_fn(constant_ref_id, offset)); } fn add_member_to_current_lexical_scope(&mut self, owner_id: DefinitionId, member_id: DefinitionId) { @@ -511,20 +511,20 @@ impl Visit for RBSIndexer<'_> { } fn visit_include_node(&mut self, include_node: &IncludeNode) { - self.index_mixin(&include_node.name(), |ref_id| { - Mixin::Include(IncludeDefinition::new(ref_id)) + self.index_mixin(&include_node.name(), |ref_id, offset| { + Mixin::Include(IncludeDefinition::new(ref_id, offset)) }); } fn visit_prepend_node(&mut self, prepend_node: &PrependNode) { - self.index_mixin(&prepend_node.name(), |ref_id| { - Mixin::Prepend(PrependDefinition::new(ref_id)) + self.index_mixin(&prepend_node.name(), |ref_id, offset| { + Mixin::Prepend(PrependDefinition::new(ref_id, offset)) }); } fn visit_extend_node(&mut self, extend_node: &ExtendNode) { - self.index_mixin(&extend_node.name(), |ref_id| { - Mixin::Extend(ExtendDefinition::new(ref_id)) + self.index_mixin(&extend_node.name(), |ref_id, offset| { + Mixin::Extend(ExtendDefinition::new(ref_id, offset)) }); } diff --git a/rust/rubydex/src/indexing/ruby_indexer.rs b/rust/rubydex/src/indexing/ruby_indexer.rs index 9562c1b8f..1e485562b 100644 --- a/rust/rubydex/src/indexing/ruby_indexer.rs +++ b/rust/rubydex/src/indexing/ruby_indexer.rs @@ -964,6 +964,7 @@ impl<'a> RubyIndexer<'a> { }; let parent_nesting_id = self.current_nesting_definition_id(); + let mixin_offset = Offset::from_prism_location(&node.location()); // Collect all arguments as constant references. Ignore anything that isn't a constant let mixin_arguments = arguments @@ -1015,9 +1016,9 @@ impl<'a> RubyIndexer<'a> { .add_constant_reference(ConstantReference::new(id, self.uri_id, offset)); let mixin = match mixin_type { - MixinType::Include => Mixin::Include(IncludeDefinition::new(constant_ref_id)), - MixinType::Prepend => Mixin::Prepend(PrependDefinition::new(constant_ref_id)), - MixinType::Extend => Mixin::Extend(ExtendDefinition::new(constant_ref_id)), + MixinType::Include => Mixin::Include(IncludeDefinition::new(constant_ref_id, mixin_offset.clone())), + MixinType::Prepend => Mixin::Prepend(PrependDefinition::new(constant_ref_id, mixin_offset.clone())), + MixinType::Extend => Mixin::Extend(ExtendDefinition::new(constant_ref_id, mixin_offset.clone())), }; match self.local_graph.get_definition_mut(lexical_nesting_id).unwrap() { diff --git a/rust/rubydex/src/model/definitions.rs b/rust/rubydex/src/model/definitions.rs index d4cb5d7ca..508c4df7f 100644 --- a/rust/rubydex/src/model/definitions.rs +++ b/rust/rubydex/src/model/definitions.rs @@ -209,6 +209,15 @@ impl Mixin { Mixin::Extend(def) => def.constant_reference_id(), } } + + #[must_use] + pub fn offset(&self) -> &Offset { + match self { + Mixin::Include(def) => def.offset(), + Mixin::Prepend(def) => def.offset(), + Mixin::Extend(def) => def.offset(), + } + } } macro_rules! mixin_definition { @@ -216,13 +225,15 @@ macro_rules! mixin_definition { #[derive(Debug, Clone)] pub struct $name { constant_reference_id: ConstantReferenceId, + offset: Offset, } impl $name { #[must_use] - pub const fn new(constant_reference_id: ConstantReferenceId) -> Self { + pub const fn new(constant_reference_id: ConstantReferenceId, offset: Offset) -> Self { Self { constant_reference_id, + offset, } } @@ -230,6 +241,11 @@ macro_rules! mixin_definition { pub fn constant_reference_id(&self) -> &ConstantReferenceId { &self.constant_reference_id } + + #[must_use] + pub fn offset(&self) -> &Offset { + &self.offset + } } }; } diff --git a/rust/rubydex/src/operation/applier.rs b/rust/rubydex/src/operation/applier.rs index bf2997722..39fcb63ed 100644 --- a/rust/rubydex/src/operation/applier.rs +++ b/rust/rubydex/src/operation/applier.rs @@ -333,9 +333,9 @@ impl OperationApplier { }; let mixin = match op.kind { - MixinKind::Include => Mixin::Include(IncludeDefinition::new(constant_reference_id)), - MixinKind::Prepend => Mixin::Prepend(PrependDefinition::new(constant_reference_id)), - MixinKind::Extend => Mixin::Extend(ExtendDefinition::new(constant_reference_id)), + MixinKind::Include => Mixin::Include(IncludeDefinition::new(constant_reference_id, op.offset.clone())), + MixinKind::Prepend => Mixin::Prepend(PrependDefinition::new(constant_reference_id, op.offset.clone())), + MixinKind::Extend => Mixin::Extend(ExtendDefinition::new(constant_reference_id, op.offset.clone())), }; if let Some(owner) = self.local_graph.get_definition_mut(owner_id) { diff --git a/rust/rubydex/src/operation/mod.rs b/rust/rubydex/src/operation/mod.rs index 797a5fef0..8cfccfa03 100644 --- a/rust/rubydex/src/operation/mod.rs +++ b/rust/rubydex/src/operation/mod.rs @@ -219,6 +219,7 @@ pub struct SetConstantVisibility { pub struct Mixin { pub kind: MixinKind, pub target: Target, + pub offset: Offset, } #[derive(Debug)] diff --git a/rust/rubydex/src/operation/ruby_builder.rs b/rust/rubydex/src/operation/ruby_builder.rs index 681060f93..8e9c9f9c7 100644 --- a/rust/rubydex/src/operation/ruby_builder.rs +++ b/rust/rubydex/src/operation/ruby_builder.rs @@ -845,6 +845,7 @@ impl<'a> RubyOperationBuilder<'a> { }; let has_owner = self.current_owner_name_id().is_some(); + let mixin_offset = Offset::from_prism_location(&node.location()); let mixin_arguments = arguments .arguments() @@ -893,6 +894,7 @@ impl<'a> RubyOperationBuilder<'a> { self.operations.push(Operation::Mixin(op::Mixin { kind, target: Target::Constant(name_id), + offset: mixin_offset.clone(), })); } } diff --git a/test/definition_test.rb b/test/definition_test.rb index cc3f84315..da5e5d15c 100644 --- a/test/definition_test.rb +++ b/test/definition_test.rb @@ -272,12 +272,42 @@ class NoMixins; end assert_instance_of(Rubydex::Include, mixins[0]) assert_instance_of(Rubydex::UnresolvedConstantReference, mixins[0].constant_reference) + assert_equal( + Rubydex::DisplayLocation.new( + uri: context.uri_to("file1.rb"), + start_line: 6, + start_column: 3, + end_line: 6, + end_column: 13, + ), + mixins[0].location.to_display, + ) assert_instance_of(Rubydex::Prepend, mixins[1]) assert_instance_of(Rubydex::UnresolvedConstantReference, mixins[1].constant_reference) + assert_equal( + Rubydex::DisplayLocation.new( + uri: context.uri_to("file1.rb"), + start_line: 7, + start_column: 3, + end_line: 7, + end_column: 13, + ), + mixins[1].location.to_display, + ) assert_instance_of(Rubydex::Extend, mixins[2]) assert_instance_of(Rubydex::UnresolvedConstantReference, mixins[2].constant_reference) + assert_equal( + Rubydex::DisplayLocation.new( + uri: context.uri_to("file1.rb"), + start_line: 8, + start_column: 3, + end_line: 8, + end_column: 12, + ), + mixins[2].location.to_display, + ) # After resolution, mixins have resolved constant references graph.resolve