diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index b29bdd7db..0beff659f 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -746,16 +746,22 @@ def handle_module_operation(node, operation) return unless arguments arguments.each do |node| - next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode) - - case operation - when :include - owner.mixin_operations << Entry::Include.new(node.full_name) - when :prepend - owner.mixin_operations << Entry::Prepend.new(node.full_name) - when :extend + next unless node.is_a?(Prism::ConstantReadNode) || node.is_a?(Prism::ConstantPathNode) || + (node.is_a?(Prism::SelfNode) && operation == :extend) + + if node.is_a?(Prism::SelfNode) singleton = @index.existing_or_new_singleton_class(owner.name) - singleton.mixin_operations << Entry::Include.new(node.full_name) + singleton.mixin_operations << Entry::Include.new(owner.name) + else + case operation + when :include + owner.mixin_operations << Entry::Include.new(node.full_name) + when :prepend + owner.mixin_operations << Entry::Prepend.new(node.full_name) + when :extend + singleton = @index.existing_or_new_singleton_class(owner.name) + singleton.mixin_operations << Entry::Include.new(node.full_name) + end end rescue Prism::ConstantPathNode::DynamicPartsInConstantPathError, Prism::ConstantPathNode::MissingNodesInConstantPathError diff --git a/lib/ruby_indexer/test/index_test.rb b/lib/ruby_indexer/test/index_test.rb index c83d3b0e6..603cea1d3 100644 --- a/lib/ruby_indexer/test/index_test.rb +++ b/lib/ruby_indexer/test/index_test.rb @@ -1672,6 +1672,38 @@ def test_linearizing_singleton_object ) end + def test_extend_self + @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) + module Foo + def bar + end + + extend self + + def baz + end + end + RUBY + + ["bar", "baz"].product(["Foo", "Foo::"]).each do |method, receiver| + entry = @index.resolve_method(method, receiver)&.first + refute_nil(entry) + assert_equal(method, T.must(entry).name) + end + + assert_equal( + [ + "Foo::", + "Foo", + "Module", + "Object", + "Kernel", + "BasicObject", + ], + @index.linearized_ancestors_of("Foo::"), + ) + end + def test_linearizing_singleton_ancestors @index.index_single(IndexablePath.new(nil, "/fake/path/foo.rb"), <<~RUBY) module First