Skip to content

Fix wrong arity diagnostics for .new and .[] of a Struct class - #491

Merged
sinsoku merged 2 commits into
ruby:masterfrom
sinsoku:fix-struct-aref-initialize
Sep 27, 2026
Merged

sinsoku merged 2 commits into
ruby:masterfrom
sinsoku:fix-struct-aref-initialize

Conversation

@sinsoku

@sinsoku sinsoku commented Sep 27, 2026 •

Copy link
Copy Markdown
Collaborator

Motivation / Background

This Pull Request has been created because TypeProf reports valid calls to a struct class as wrong number of arguments.

Pt = Struct.new(:x, :y) do
  def initialize(x = 0, y = 0)
    super
  end
end
Pt[]     # wrong number of arguments (0 for 2)

Size = Struct.new(:width, :height)
Size.new # wrong number of arguments (0 for 2)

Struct.[] is an alias for Struct.new, but TypeProf generated it from the member list, so it did not follow an overridden initialize. This is the part of #458 that #466 left as a limitation. The generated initialize also required every member, although Ruby sets omitted members to nil.

Detail

Struct.[] now uses the same builtin as Class#new, so it calls the initialize of the receiver, including a user-defined one. Since [] no longer has its own method definition, the RBS output builds self.[] from that initialize, e.g. def self.[]: (?Integer, ?Integer) -> Pt. For class Sub < Pt; end, Sub[1, 2] now returns Sub instead of Pt.

The generated initialize and self.[] now show every member as optional, e.g. (?Integer, ?String). Omitted members are not typed as nil, because that would make every reader nilable even when all calls pass every member. So the readers keep the types of the given values, e.g. def x: -> Integer.

The IDE features also treat Pt[...] like Pt.new: its hover shows the signature of initialize, e.g. Pt#initialize : (?Integer, ?Integer) -> void, and self.[] no longer appears in the completion for Pt..

Data.define is out of scope: D[...] is still undefined, and D.new(1, 2) is still reported.

Verification

bundle exec rake test passes.

🤖 Generated with Claude Code

Struct.[] is an alias for Struct.new, but TypeProf generated it from the
member list independently of initialize. When a struct overrode
initialize, `self.[]` did not follow it: the RBS showed a stale
signature and valid calls were reported as wrong number of arguments.
Struct.new(:x, :y).new and Struct.new(:x, :y)[] are valid, but TypeProf
required every member as an argument and reported these calls as wrong
number of arguments. The omitted members are not typed as nil, so the
readers keep the types of the given values.
@sinsoku
sinsoku force-pushed the fix-struct-aref-initialize branch from 2b6f083 to b65ce97 Compare September 27, 2026 09:23
@sinsoku sinsoku changed the title Fix wrong arity diagnostics for Struct.new and Struct.[] Fix wrong arity diagnostics for .new and .[] of a Struct class Sep 27, 2026
@sinsoku
sinsoku merged commit 371f90a into ruby:master Sep 27, 2026
6 checks passed
@sinsoku
sinsoku deleted the fix-struct-aref-initialize branch September 27, 2026 09:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant