It shouldn't be such an error for the 2nd command since it's a positional Hash.
It should be a TypeError, like when passing e.g. nil instead of the positional Hash.
Also:
$ ruby -e 'p Struct.new(:a, {}).members'
[:a]
But it should be an error to pass a positional Hash.
I think this is worth fixing, because it basically breaks the separation of positional and keyword arguments for this method.
Also Struct.new does take a keyword argument, keyword_init: true.
A lot of C methods will still treat positional hashes as keywords. I think only those that use rb_scan_args/rb_scan_args_kw will error if a regular hash is passed instead of keywords. For example, you can call Kernel#raise with a positional hash and it will treat the hash as keywords. This explains the behavior of the second Struct issue with the empty hash, which is treated as empty keywords and is ignored.
In short, this is not an issue specific to Struct.new. If we consider this a bug, we have to modify all C functions that use option hashes/keywords and do no use rb_scan_args/rb_scan_args_kw, and make them raise ArgumentError if passing a positional hash and not keywords. Even that wouldn't change the behavior of C function methods defined in external gems.
For Kernel#raise I found that it's actually important to separate positional and kwargs, because raise does have cause: kwargs and optional args, and the 1st or 2nd argument can somtimes be a Hash: https://github.com/oracle/truffleruby/issues/2298
You mean rb_scan_args/rb_scan_args_kw correctly separate positional & kwargs, but the rest do not?
I think there is nothing we need to do for C functions not accepting kwargs, because then anyway there is no difference.
But for C functions taking kwargs they should not mix positional & kwargs and that is worth fixing.
You mean rb_scan_args/rb_scan_args_kw correctly separate positional & kwargs, but the rest do not?
Correct. I think most C functions that handle kwargs use rb_scan_args, but not all.
I recommend you add this as topic to the next developer meeting. Changes in this area will break backwards compatibility, so even if we decide to make them, we need to have an implementation plan, such as issuing deprecation warnings in 3.2 and breaking use with positional hashes in 3.3. I'm willing to do the work of auditing all core/ext methods and updating those that need changes if we decide to make this change.