From 045ec928ceb71897c38a8b0ef4ca1b5319eee640 Mon Sep 17 00:00:00 2001 From: Weilong Date: Thu, 17 Oct 2024 10:27:00 -0400 Subject: [PATCH 1/5] add module_function to on_call_node_enter --- .../lib/ruby_indexer/declaration_listener.rb | 23 +++++++++++++++ lib/ruby_indexer/test/method_test.rb | 28 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index d96adc04e..d2d03b6bf 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -312,6 +312,29 @@ def on_call_node_enter(node) @visibility_stack.push(Entry::Visibility::PROTECTED) when :private @visibility_stack.push(Entry::Visibility::PRIVATE) + when :module_function + name_argument = node.arguments.arguments.first + return unless name_argument + + entries = @index.resolve_method(name_argument.value, @owner_stack.last.name) + return unless entries + + entries.each do |e| + e.visibility = Entry::Visibility::PRIVATE + + singleton = @index.existing_or_new_singleton_class(e.owner.name) + + @index.add(Entry::Method.new( + name_argument.value, + @file_path, + Location.from_prism_location(node.location, @code_units_cache), + Location.from_prism_location(node.message_loc, @code_units_cache), + collect_comments(node), + [], + Entry::Visibility::PUBLIC, + singleton, + )) + end end @enhancements.each do |enhancement| diff --git a/lib/ruby_indexer/test/method_test.rb b/lib/ruby_indexer/test/method_test.rb index ca9bb1282..86bf99db6 100644 --- a/lib/ruby_indexer/test/method_test.rb +++ b/lib/ruby_indexer/test/method_test.rb @@ -123,6 +123,34 @@ def baz; end assert_entry("baz", Entry::Method, "/fake/path/foo.rb:9-2:9-14", visibility: Entry::Visibility::PRIVATE) end + def test_visibility_tracking_with_module_function + index(<<~RUBY) + module Foo + def foo; end + module_function :foo + end + + class Bar + include Foo + end + RUBY + + entries = T.must(@index["foo"]) + # receive two entries because module_function creates a singleton method + # for the Foo module and a private method for classes include Foo module + assert_equal(entries.size, 2) + # The first entry points to the location of the module_function call + first_entry = entries.first + assert_equal("Foo", first_entry.owner.name) + assert_instance_of(Entry::Module, first_entry.owner) + assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility) + # The second entry points to the public singleton method + second_entry = entries.last + assert_equal("Foo::", second_entry.owner.name) + assert_instance_of(Entry::SingletonClass, second_entry.owner) + assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility) + end + def test_method_with_parameters index(<<~RUBY) class Foo From 16d87f15a5eaab037fc3bac9b5db0b33be1da6c8 Mon Sep 17 00:00:00 2001 From: Weilong Date: Thu, 17 Oct 2024 23:08:47 -0400 Subject: [PATCH 2/5] add handle_module_function and fix typecheck failures --- .../lib/ruby_indexer/declaration_listener.rb | 58 ++++++++++++------- lib/ruby_indexer/test/method_test.rb | 3 +- 2 files changed, 37 insertions(+), 24 deletions(-) diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index d2d03b6bf..39b6dc53a 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -313,28 +313,7 @@ def on_call_node_enter(node) when :private @visibility_stack.push(Entry::Visibility::PRIVATE) when :module_function - name_argument = node.arguments.arguments.first - return unless name_argument - - entries = @index.resolve_method(name_argument.value, @owner_stack.last.name) - return unless entries - - entries.each do |e| - e.visibility = Entry::Visibility::PRIVATE - - singleton = @index.existing_or_new_singleton_class(e.owner.name) - - @index.add(Entry::Method.new( - name_argument.value, - @file_path, - Location.from_prism_location(node.location, @code_units_cache), - Location.from_prism_location(node.message_loc, @code_units_cache), - collect_comments(node), - [], - Entry::Visibility::PUBLIC, - singleton, - )) - end + handle_module_function(node) end @enhancements.each do |enhancement| @@ -774,6 +753,41 @@ def handle_module_operation(node, operation) end end + sig { params(node: Prism::CallNode).void } + def handle_module_function(node) + arguments_node = node.arguments + return unless arguments_node + + name_argument = arguments_node.arguments.first + return unless name_argument.is_a?(Prism::SymbolNode) + + method_name = name_argument.value.to_s + owner_name = @owner_stack.last&.name + return unless owner_name + + entries = @index.resolve_method(method_name, owner_name) + return unless entries + + entries.each do |entry| + entry_owner_name = entry.owner&.name + next unless entry_owner_name + + entry.visibility = Entry::Visibility::PRIVATE + + singleton = @index.existing_or_new_singleton_class(entry_owner_name) + @index.add(Entry::Method.new( + method_name, + @file_path, + Location.from_prism_location(node.location, @code_units_cache), + Location.from_prism_location(T.must(node.message_loc), @code_units_cache), + collect_comments(node), + [], + Entry::Visibility::PUBLIC, + singleton, + )) + end + end + sig { returns(Entry::Visibility) } def current_visibility T.must(@visibility_stack.last) diff --git a/lib/ruby_indexer/test/method_test.rb b/lib/ruby_indexer/test/method_test.rb index 86bf99db6..4841af705 100644 --- a/lib/ruby_indexer/test/method_test.rb +++ b/lib/ruby_indexer/test/method_test.rb @@ -139,13 +139,12 @@ class Bar # receive two entries because module_function creates a singleton method # for the Foo module and a private method for classes include Foo module assert_equal(entries.size, 2) + first_entry, second_entry = *entries # The first entry points to the location of the module_function call - first_entry = entries.first assert_equal("Foo", first_entry.owner.name) assert_instance_of(Entry::Module, first_entry.owner) assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility) # The second entry points to the public singleton method - second_entry = entries.last assert_equal("Foo::", second_entry.owner.name) assert_instance_of(Entry::SingletonClass, second_entry.owner) assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility) From 31b8c9093ca4a3e0d98fad20cebdf1e97b288684 Mon Sep 17 00:00:00 2001 From: Weilong Date: Tue, 22 Oct 2024 22:35:29 -0400 Subject: [PATCH 3/5] handle setting multiple methods and support StringNode --- .../lib/ruby_indexer/declaration_listener.rb | 48 +++++++++++-------- lib/ruby_indexer/test/method_test.rb | 43 ++++++++--------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index 39b6dc53a..8668dceb2 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -758,33 +758,39 @@ def handle_module_function(node) arguments_node = node.arguments return unless arguments_node - name_argument = arguments_node.arguments.first - return unless name_argument.is_a?(Prism::SymbolNode) - - method_name = name_argument.value.to_s owner_name = @owner_stack.last&.name return unless owner_name - entries = @index.resolve_method(method_name, owner_name) - return unless entries + arguments_node.arguments.each do |argument| + method_name = case argument + when Prism::StringNode + argument.content + when Prism::SymbolNode + argument.value + end + next unless method_name - entries.each do |entry| - entry_owner_name = entry.owner&.name - next unless entry_owner_name + entries = @index.resolve_method(method_name, owner_name) + next unless entries - entry.visibility = Entry::Visibility::PRIVATE + entries.each do |entry| + entry_owner_name = entry.owner&.name + next unless entry_owner_name - singleton = @index.existing_or_new_singleton_class(entry_owner_name) - @index.add(Entry::Method.new( - method_name, - @file_path, - Location.from_prism_location(node.location, @code_units_cache), - Location.from_prism_location(T.must(node.message_loc), @code_units_cache), - collect_comments(node), - [], - Entry::Visibility::PUBLIC, - singleton, - )) + entry.visibility = Entry::Visibility::PRIVATE + + singleton = @index.existing_or_new_singleton_class(entry_owner_name) + @index.add(Entry::Method.new( + method_name, + @file_path, + Location.from_prism_location(node.location, @code_units_cache), + Location.from_prism_location(T.must(node.message_loc), @code_units_cache), + collect_comments(node), + entry.signatures, + Entry::Visibility::PUBLIC, + singleton, + )) + end end end diff --git a/lib/ruby_indexer/test/method_test.rb b/lib/ruby_indexer/test/method_test.rb index 4841af705..809980e18 100644 --- a/lib/ruby_indexer/test/method_test.rb +++ b/lib/ruby_indexer/test/method_test.rb @@ -125,29 +125,28 @@ def baz; end def test_visibility_tracking_with_module_function index(<<~RUBY) - module Foo + module Test def foo; end - module_function :foo - end - - class Bar - include Foo - end - RUBY - - entries = T.must(@index["foo"]) - # receive two entries because module_function creates a singleton method - # for the Foo module and a private method for classes include Foo module - assert_equal(entries.size, 2) - first_entry, second_entry = *entries - # The first entry points to the location of the module_function call - assert_equal("Foo", first_entry.owner.name) - assert_instance_of(Entry::Module, first_entry.owner) - assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility) - # The second entry points to the public singleton method - assert_equal("Foo::", second_entry.owner.name) - assert_instance_of(Entry::SingletonClass, second_entry.owner) - assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility) + def bar; end + module_function :foo, "bar" + end + RUBY + + ["foo", "bar"].each do |keyword| + entries = T.must(@index[keyword]) + # should receive two entries because module_function creates a singleton method + # for the Test module and a private method for classes include the Test module + assert_equal(entries.size, 2) + first_entry, second_entry = *entries + # The first entry points to the location of the module_function call + assert_equal("Test", first_entry.owner.name) + assert_instance_of(Entry::Module, first_entry.owner) + assert_equal(Entry::Visibility::PRIVATE, first_entry.visibility) + # The second entry points to the public singleton method + assert_equal("Test::", second_entry.owner.name) + assert_instance_of(Entry::SingletonClass, second_entry.owner) + assert_equal(Entry::Visibility::PUBLIC, second_entry.visibility) + end end def test_method_with_parameters From c67066efe898cf66205d713bf3e303590548a8cf Mon Sep 17 00:00:00 2001 From: Weilong Date: Wed, 23 Oct 2024 16:17:57 -0400 Subject: [PATCH 4/5] update singleton method location --- lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index 8668dceb2..b4a704c5e 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -780,11 +780,12 @@ def handle_module_function(node) entry.visibility = Entry::Visibility::PRIVATE singleton = @index.existing_or_new_singleton_class(entry_owner_name) + location = Location.from_prism_location(argument.location, @code_units_cache) @index.add(Entry::Method.new( method_name, @file_path, - Location.from_prism_location(node.location, @code_units_cache), - Location.from_prism_location(T.must(node.message_loc), @code_units_cache), + location, + location, collect_comments(node), entry.signatures, Entry::Visibility::PUBLIC, From d9da0e4ed68f0acdd470d7beb018dc3578561ea6 Mon Sep 17 00:00:00 2001 From: Weilong Date: Thu, 24 Oct 2024 11:49:13 -0400 Subject: [PATCH 5/5] concate entry comments --- lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index b4a704c5e..9d150022d 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -786,7 +786,7 @@ def handle_module_function(node) @file_path, location, location, - collect_comments(node), + collect_comments(node)&.concat(entry.comments), entry.signatures, Entry::Visibility::PUBLIC, singleton,