Skip to content

Parameter type includes values assigned via Hash#[]= on local variable #362

@sinsoku

Description

@sinsoku

Steps to reproduce

Analyze the following file with TypeProf.

def foo(options)
  options[:name] = "str"
  nil
end

foo(Hash.new)

Expected behavior

class Object
  def foo: (Hash[untyped, untyped]) -> nil
end

Parameter types should only reflect the types passed from call sites, not values assigned inside the method.

Actual behavior

$ bin/typeprof reproduce.rb
# TypeProf 0.31.1

# reproduce.rb
class Object
  def foo: (Hash[:name, String]) -> nil
end

System configuration

Ruby version: ruby 4.0.0 (2025-12-25 revision 553f1675f3) +PRISM [arm64-darwin25]
TypeProf version: 753de9b

Real-world use cases

I noticed this issue when analyzing Action Pack with TypeProf.

The minimum steps required to actually analyze Action Pack are as follows:

$ bin/typeprof tmp/rails/actionpack/test/controller/routing_test.rb \
  tmp/rails/actionpack/lib/action_dispatch/routing/route_set.rb \
  tmp/rails/actionpack/lib/action_dispatch/journey/formatter.rb > ap.rbs

$ cat ap.rbs | grep url_for | wc -c
   13936

$ cat ap.rbs | grep url_for
      def url_for: (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], (Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], String] | Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], untyped] | String)?] | Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | :path | :path_params | :person | :port | :protocol | :script_name | :subdomain | :tld_length | :trailing_slash | :use_route | :user | Range[Integer], String] | Hash[:_recall | :action | :anchor | :controller | :domain | :foo | :format | :host | :id | :name | :only_path | :original_script_name | :params | :password | ...(omitted

The parameter type contains a recursive structure such as Hash[:_recall | ..., Hash[:_recall | ..., Hash[:_recall, ...].
I have identified the cause of this as Hash#[]=.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions