From 8a7d7bd224c8c0f3961932465fd24de4175c93ac Mon Sep 17 00:00:00 2001 From: Domingo2000 <40220174+domingo2000@users.noreply.github.com> Date: Thu, 28 Mar 2024 10:20:57 -0300 Subject: [PATCH] Indexed prepended modules on namespaces (#1820) --- .../lib/ruby_indexer/collector.rb | 15 +++++- lib/ruby_indexer/lib/ruby_indexer/entry.rb | 4 ++ .../test/classes_and_modules_test.rb | 46 +++++++++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) diff --git a/lib/ruby_indexer/lib/ruby_indexer/collector.rb b/lib/ruby_indexer/lib/ruby_indexer/collector.rb index aa0b6da34..dab6b359d 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/collector.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/collector.rb @@ -154,6 +154,8 @@ def handle_call_node(node) handle_attribute(node, reader: true, writer: true) when :include handle_include(node) + when :prepend + handle_prepend(node) end end @@ -355,6 +357,16 @@ def handle_attribute(node, reader:, writer:) sig { params(node: Prism::CallNode).void } def handle_include(node) + handle_module_operation(node, :included_modules) + end + + sig { params(node: Prism::CallNode).void } + def handle_prepend(node) + handle_module_operation(node, :prepended_modules) + end + + sig { params(node: Prism::CallNode, operation: Symbol).void } + def handle_module_operation(node, operation) return unless @current_owner arguments = node.arguments&.arguments @@ -369,7 +381,8 @@ def handle_include(node) # If a constant path reference is dynamic or missing parts, we can't # index it end - @current_owner.included_modules.concat(names) + collection = operation == :included_modules ? @current_owner.included_modules : @current_owner.prepended_modules + collection.concat(names) end end end diff --git a/lib/ruby_indexer/lib/ruby_indexer/entry.rb b/lib/ruby_indexer/lib/ruby_indexer/entry.rb index 85c269cdd..f1a3de424 100644 --- a/lib/ruby_indexer/lib/ruby_indexer/entry.rb +++ b/lib/ruby_indexer/lib/ruby_indexer/entry.rb @@ -43,6 +43,9 @@ class Namespace < Entry sig { returns(T::Array[String]) } attr_accessor :included_modules + sig { returns(T::Array[String]) } + attr_accessor :prepended_modules + sig do params( name: String, @@ -54,6 +57,7 @@ class Namespace < Entry def initialize(name, file_path, location, comments) super(name, file_path, location, comments) @included_modules = T.let([], T::Array[String]) + @prepended_modules = T.let([], T::Array[String]) end sig { returns(String) } diff --git a/lib/ruby_indexer/test/classes_and_modules_test.rb b/lib/ruby_indexer/test/classes_and_modules_test.rb index 204b7ca16..a33d18578 100644 --- a/lib/ruby_indexer/test/classes_and_modules_test.rb +++ b/lib/ruby_indexer/test/classes_and_modules_test.rb @@ -327,5 +327,51 @@ class ConstantPathReferences constant_path_references = T.must(@index["ConstantPathReferences"][0]) assert_equal(["Foo::Bar", "Foo::Bar2"], constant_path_references.included_modules) end + + def test_keeping_track_of_prepended_modules + index(<<~RUBY) + class Foo + # valid syntaxes that we can index + prepend A1 + self.prepend A2 + prepend A3, A4 + self.prepend A5, A6 + + # valid syntaxes that we cannot index because of their dynamic nature + prepend some_variable_or_method_call + self.prepend some_variable_or_method_call + + def something + prepend A7 # We should not index this because of this dynamic nature + end + + # Valid inner class syntax definition with its own modules prepended + class Qux + prepend Corge + self.prepend Corge + prepend Baz + + prepend some_variable_or_method_call + end + end + + class ConstantPathReferences + prepend Foo::Bar + self.prepend Foo::Bar2 + + prepend dynamic::Bar + prepend Foo:: + end + RUBY + + foo = T.must(@index["Foo"][0]) + assert_equal(["A1", "A2", "A3", "A4", "A5", "A6"], foo.prepended_modules) + + qux = T.must(@index["Foo::Qux"][0]) + assert_equal(["Corge", "Corge", "Baz"], qux.prepended_modules) + + constant_path_references = T.must(@index["ConstantPathReferences"][0]) + assert_equal(["Foo::Bar", "Foo::Bar2"], constant_path_references.prepended_modules) + end end end