From b72ac025515fd4b05e4ef3dbef3ceef5a9b11d8c Mon Sep 17 00:00:00 2001 From: Niki Jiandani Date: Thu, 18 Apr 2024 10:45:11 -0400 Subject: [PATCH] Add support for method aliases Co-authored-by: Vinicius Stock --- .../lib/ruby_indexer/declaration_listener.rb | 54 +++++++++++++++++++ lib/ruby_indexer/lib/ruby_indexer/entry.rb | 28 ++++++++++ lib/ruby_indexer/test/method_test.rb | 20 +++++++ lib/ruby_indexer/test/test_case.rb | 1 + 4 files changed, 103 insertions(+) diff --git a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb index fa503cdde..459e5ab7b 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/declaration_listener.rb @@ -52,6 +52,7 @@ def initialize(index, dispatcher, parse_result, file_path) :on_instance_variable_operator_write_node_enter, :on_instance_variable_or_write_node_enter, :on_instance_variable_target_node_enter, + :on_alias_method_node_enter, ) end @@ -209,6 +210,8 @@ def on_call_node_enter(node) handle_attribute(node, reader: false, writer: true) when :attr_accessor handle_attribute(node, reader: true, writer: true) + when :alias_method + handle_alias_method(node) when :include, :prepend, :extend handle_module_operation(node, message) when :public @@ -338,6 +341,20 @@ def on_instance_variable_target_node_enter(node) ) end + sig { params(node: Prism::AliasMethodNode).void } + def on_alias_method_node_enter(node) + method_name = node.new_name.slice + comments = collect_comments(node) + @index << Entry::UnresolvedMethodAlias.new( + method_name, + node.old_name.slice, + @owner_stack.last, + @file_path, + node.new_name.location, + comments, + ) + end + private sig { params(node: Prism::CallNode).void } @@ -365,6 +382,43 @@ def handle_private_constant(node) entries&.each { |entry| entry.visibility = Entry::Visibility::PRIVATE } end + sig { params(node: Prism::CallNode).void } + def handle_alias_method(node) + arguments = node.arguments&.arguments + return unless arguments + + new_name, old_name = arguments + return unless new_name && old_name + + new_name_value = case new_name + when Prism::StringNode + new_name.content + when Prism::SymbolNode + new_name.value + end + + return unless new_name_value + + old_name_value = case old_name + when Prism::StringNode + old_name.content + when Prism::SymbolNode + old_name.value + end + + return unless old_name_value + + comments = collect_comments(node) + @index << Entry::UnresolvedMethodAlias.new( + new_name_value, + old_name_value, + @owner_stack.last, + @file_path, + new_name.location, + comments, + ) + end + sig do params( node: T.any( diff --git a/lib/ruby_indexer/lib/ruby_indexer/entry.rb b/lib/ruby_indexer/lib/ruby_indexer/entry.rb index 5a3ace7f3..8ae271712 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/entry.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/entry.rb @@ -469,5 +469,33 @@ def initialize(name, file_path, location, comments, owner) @owner = owner end end + + class UnresolvedMethodAlias < Entry + extend T::Sig + + sig { returns(String) } + attr_reader :new_name, :old_name + + sig { returns(T.nilable(Entry::Namespace)) } + attr_reader :owner + + sig do + params( + new_name: String, + old_name: String, + owner: T.nilable(Entry::Namespace), + file_path: String, + location: Prism::Location, + comments: T::Array[String], + ).void + end + def initialize(new_name, old_name, owner, file_path, location, comments) # rubocop:disable Metrics/ParameterLists + super(new_name, file_path, location, comments) + + @new_name = new_name + @old_name = old_name + @owner = owner + end + end end end diff --git a/lib/ruby_indexer/test/method_test.rb b/lib/ruby_indexer/test/method_test.rb index 4565bcb2a..852924cc9 100644 --- a/lib/ruby_indexer/test/method_test.rb +++ b/lib/ruby_indexer/test/method_test.rb @@ -378,5 +378,25 @@ def third; end entry = T.must(@index["third"]&.first) assert_equal("Foo", T.must(entry.owner).name) end + + def test_keeps_track_of_aliases + index(<<~RUBY) + class Foo + alias whatever to_s + alias_method :foo, :to_a + alias_method "bar", "to_a" + + # These two are not indexed because they are dynamic or incomplete + alias_method baz, :to_a + alias_method :baz + end + RUBY + + assert_entry("whatever", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:1-8:1-16") + assert_entry("foo", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:2-15:2-19") + assert_entry("bar", Entry::UnresolvedMethodAlias, "/fake/path/foo.rb:3-15:3-20") + # Foo plus 3 valid aliases + assert_equal(4, @index.instance_variable_get(:@entries).length) + end end end diff --git a/lib/ruby_indexer/test/test_case.rb b/lib/ruby_indexer/test/test_case.rb index 61708495d..aa4ee4ba2 100644 --- a/lib/ruby_indexer/test/test_case.rb +++ b/lib/ruby_indexer/test/test_case.rb @@ -17,6 +17,7 @@ def index(source) def assert_entry(expected_name, type, expected_location, visibility: nil) entries = @index[expected_name] + refute_nil(entries, "Expected #{expected_name} to be indexed") refute_empty(entries, "Expected #{expected_name} to be indexed") entry = entries.first