From cd6ee9ca2ea66b20a814607ab2f9994920a6b2be Mon Sep 17 00:00:00 2001 From: Mark Nuzzolilo Date: Thu, 17 Oct 2024 21:41:10 -0700 Subject: [PATCH 1/2] Add test for method aliases in gems --- spec/tapioca/gem/pipeline_spec.rb | 32 +++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/spec/tapioca/gem/pipeline_spec.rb b/spec/tapioca/gem/pipeline_spec.rb index 2d2d4a17d..41b3c7d4c 100644 --- a/spec/tapioca/gem/pipeline_spec.rb +++ b/spec/tapioca/gem/pipeline_spec.rb @@ -3181,6 +3181,23 @@ module ClassMethodsWithVariance end RUBY + add_ruby_file("method_aliases.rb", <<~RUBY) + class MethodAliases + extend T::Helpers + extend T::Sig + + # Foo + sig { params(a: Integer, b: String).returns(Object) } + def foo(a, b); end + + alias :bar :foo + alias_method :baz, :bar + + def fuzz(x); end + alias_method :buzz, :fuzz + end + RUBY + add_ruby_file("generic.rb", <<~RUBY) module Generics class ComplexGenericType @@ -3389,6 +3406,21 @@ def something(foo); end Generics::SimpleGenericType::NullGenericType = T.let(T.unsafe(nil), Generics::SimpleGenericType[::Integer]) + class MethodAliases + sig { params(a: ::Integer, b: ::String).returns(::Object) } + def bar(a, b); end + + sig { params(a: ::Integer, b: ::String).returns(::Object) } + def baz(a, b); end + + def buzz(x); end + + sig { params(a: ::Integer, b: ::String).returns(::Object) } + def foo(a, b); end + + def fuzz(x); end + end + module Quux interface! From e2593391994c937726188af9f9ac24da95e9d444 Mon Sep 17 00:00:00 2001 From: Mark Nuzzolilo Date: Thu, 17 Oct 2024 21:41:16 -0700 Subject: [PATCH 2/2] Fix method aliases in gems --- lib/tapioca/gem/listeners/methods.rb | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/tapioca/gem/listeners/methods.rb b/lib/tapioca/gem/listeners/methods.rb index 1b3714d6f..dfd52c430 100644 --- a/lib/tapioca/gem/listeners/methods.rb +++ b/lib/tapioca/gem/listeners/methods.rb @@ -73,15 +73,29 @@ def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public return unless method_owned_by_constant?(method, constant) return if @pipeline.symbol_in_payload?(symbol_name) && !@pipeline.method_in_gem?(method) + is_alias = method.name != method.original_name signature = lookup_signature_of(method) - method = T.let(signature.method, UnboundMethod) if signature + parameters = T.let(nil, T.nilable(T::Array[[Symbol, T.nilable(Symbol)]])) + + if signature + method = T.let(signature.method, UnboundMethod) + elsif is_alias && signature.nil? && constant.method_defined?(method.original_name) + alias_source_method = constant.instance_method(method.original_name) + signature = lookup_signature_of(alias_source_method) + + # Skip abstract methods if they are defined this way. + # Aliasing abstract methods is likely to yield unwanted results + signature = nil if signature&.mode == "abstract" + parameters = T.let(signature&.method&.parameters, T.nilable(T::Array[[Symbol, T.nilable(Symbol)]])) + end method_name = method.name.to_s return unless valid_method_name?(method_name) return if struct_method?(constant, method_name) return if method_name.start_with?("__t_props_generated_") - parameters = T.let(method.parameters, T::Array[[Symbol, T.nilable(Symbol)]]) + method = T.let(signature.method, UnboundMethod) if signature + parameters ||= T.let(method.parameters, T::Array[[Symbol, T.nilable(Symbol)]]) sanitized_parameters = parameters.each_with_index.map do |(type, name), index| fallback_arg_name = "_arg#{index}"