From 6cf8b13713722055e72b35026257f3e8ed82552e Mon Sep 17 00:00:00 2001 From: Yusuke Endoh Date: Mon, 28 Sep 2026 21:40:56 +0900 Subject: [PATCH] Do not forward the optional parameters before `...` `...` forwards only the arguments it takes, but it also forwarded the optional parameters before it, a leftover from when a bare `super` shared this code (#492 gave it its own). So in def foo(a = 1, ...) = bar(...) foo(:sym, "str") bar received :sym as well as "str". A parameter cannot follow `...`, so the loop over the post parameters goes away with it. Co-Authored-By: Claude Opus 5.5 --- lib/typeprof/core/env/method.rb | 16 +++------------- scenario/args/forwarding_arguments.rb | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/lib/typeprof/core/env/method.rb b/lib/typeprof/core/env/method.rb index 6fdef35dc..fc7f2f8ea 100644 --- a/lib/typeprof/core/env/method.rb +++ b/lib/typeprof/core/env/method.rb @@ -397,30 +397,20 @@ def initialize(req_positionals, opt_positionals, rest_positionals, post_position @param_names = param_names end - # The arguments that `...` forwards: only the ones the caller passed. + # The arguments that `...` forwards: only the ones the caller passed to it. + # The parameters before `...` (`def foo(a, b = 1, ...)`) are not forwarded, + # and none can follow it. def to_actual_arguments(genv, changes, node) positionals = [] splat_flags = [] positionals_omittable = [] - @opt_positionals.each do |elem_vtx| - positionals << Source.new(genv.gen_ary_type(elem_vtx)) - splat_flags << true - positionals_omittable << true - end - if @rest_positionals positionals << Source.new(genv.gen_ary_type(@rest_positionals)) splat_flags << true positionals_omittable << true end - @post_positionals.each do |arg| - positionals << arg - splat_flags << false - positionals_omittable << false - end - # `...` cannot follow keyword parameters, so only the rest keywords remain ForwardingActualArguments.new(positionals, splat_flags, @rest_keywords, @block, positionals_omittable, !!@rest_keywords, true, @activation, true) end diff --git a/scenario/args/forwarding_arguments.rb b/scenario/args/forwarding_arguments.rb index 7eb580e0d..c766b52b3 100644 --- a/scenario/args/forwarding_arguments.rb +++ b/scenario/args/forwarding_arguments.rb @@ -132,3 +132,21 @@ class Object def foo: (*Integer, **Integer) -> Integer def bar: (*Integer, **Integer) -> [Array[Integer], { x: Integer, y: Integer }] end + +## update +# `...` forwards only what it takes, not the optional parameters before it +def foo(a = 1, ...) + bar(...) +end + +def bar(*r) + r +end + +foo(:sym, "str") + +## assert +class Object + def foo: (?:sym | Integer, *String, **untyped) -> Array[String] + def bar: (*String) -> Array[String] +end