Issue #14415 has been updated by jeremyevans0 (Jeremy Evans).


With recent changes to the master branch, you now the get the following results:

```ruby
kws = {}
->{}.call **kws      # => nil
->{}.call **{}       # => nil
->{}.call **({})     # => nil
->{}.call **({};)    # => nil
->{}.call **(;{})    # => nil
->{}.call **{**{}}   # => nil
->{}.call **({};{})  # => nil
->{}.call **{**kws}  # => nil

->a{a}.call **{}     rescue $!  # => #<ArgumentError: wrong number of arguments (given 0, expected 1)>
->a{a}.call **kws    rescue $!  # => #<ArgumentError: wrong number of arguments (given 0, expected 1)>
->a{a}.call **(;{})  rescue $!  # => #<ArgumentError: wrong number of arguments (given 0, expected 1)>

->a,b:{}.call **{b:1} rescue $!
# warning: The keyword argument for `call' (defined at (irb):13) is passed as the last hash parameter
# => #<ArgumentError: missing keyword: :b>

->a,b:,**c{[a,b,c]}.call 1, b:2                     # => [1, 2, {}]
->a,b:,**c{[a,b,c]}.call 1, b:2, 3=>4               # => [1, 2, {3=>4}]
->a,b:,**c{[a,b,c]}.call({1=>2}, b: 3)              # => [{1=>2}, 3, {}]

->a,b:,**c{[a,b,c]}.call 1=>2, b:3       rescue $!
# warning: The keyword argument for `call' (defined at (irb):16) is passed as the last hash parameter
# => ArgumentError (missing keyword: :b)

->a,b:,**c{[a,b,c]}.call 1=>2, **{b:3}   rescue $!
# warning: The keyword argument for `call' (defined at (irb):17) is passed as the last hash parameter
# => #<ArgumentError: missing keyword: :b>

->a,b:,**c{[a,b,c]}.call({1=>2}, {b: 3})
# warning: The last argument for `call' (defined at (irb):19) is used as the keyword parameter
# => [{1=>2}, 3, {}]

->*a       {a      }.call 1, b:2, c:3, 4=>5         # => [1, {:b=>2, :c=>3, 4=>5}]
->*a,b:,**c{[a,b,c]}.call 1, b:2, c:3, 4=>5         # => [[1], 2, {:c=>3, 4=>5}]

[*[1,2], *[:c, :d]]              # => [1, 2, :c, :d]
{**{1=>2}, **{c: :d}}            # => {1=>2, :c=>:d}
[1,2,**{a:3}]                    # => [1, 2, {:a=>3}]
[1,2,**{}]                       # => [1, 2]
[1,2,**kws]                      # => [1, 2, {}]

-> a='a', b:'b' { [a, b] }.call a: 2, b: 3 rescue $!  # => #<ArgumentError: unknown keyword: :a>

-> a='a', b:'b' { [a, b] }.call 1 => 2, b: 3
# warning: The last argument for `call' (defined at (irb):2) is split into positional and keyword parameters
# => [{1=>2}, 3]

-> a='a', b:'b' { [a, b] }.call b: 3 # => ["a", 3]

-> a { a }.call b: 3
# => {:b=>3}

-> a, **b { [a, b] }.call b: 3 rescue $!
# warning: The keyword argument for `call' (defined at (irb):5) is passed as the last hash parameter
# => [{:b=>3}, {}]
```

For the calls that warn, you will get the following behavior in Ruby 3:

```ruby
->a,b:{}.call **{b:1} rescue $!
# => ArgumentError (wrong number of arguments (given 0, expected 1))

->a,b:,**c{[a,b,c]}.call 1=>2, b:3 rescue $!
# => ArgumentError (wrong number of arguments (given 0, expected 1))

->a,b:,**c{[a,b,c]}.call 1=>2, **{b:3}
# => ArgumentError (wrong number of arguments (given 0, expected 1))

->a,b:,**c{[a,b,c]}.call({1=>2}, {b: 3})
# => ArgumentError (wrong number of arguments (given 2, expected 1))

-> a='a', b:'b' { [a, b] }.call 1 => 2, b: 3
# ['a', {1 => 2, :b => 3}]

-> a, **b { [a, b] }.call b: 3 rescue $!
# => ArgumentError (wrong number of arguments (given 0, expected 1))
```

I think the only case that is questionable still is:

```ruby
[**({};)]
# => []
[**(;{})] # and h = {}; [**h]
# => [{}]
``` 

The keyword argument separation changes just made to the master branch did not affect this code, since it isn't a method call.  This behavior has been present since Ruby 2.2.  I think it would be a good idea to make both `[**({};)]` and `[**(;{})]` return `[]`.

----------------------------------------
Bug #14415: Empty keyword hashes get assigned to ordinal args.
https://bugs.ruby-lang.org/issues/14415#change-81308

* Author: josh.cheek (Josh Cheek)
* Status: Open
* Priority: Normal
* Assignee: 
* Target version: 
* ruby -v: ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin17]
* Backport: 2.3: UNKNOWN, 2.4: UNKNOWN, 2.5: UNKNOWN
----------------------------------------
Spreading empty arrays works, even when they go through a variable, or are disguised:

~~~ruby
args = []           # => []
->{}.call *[]       # => nil
->{}.call *args     # => nil
->{}.call *([])     # => nil
->{}.call *([];)    # => nil
->{}.call *(;[])    # => nil
->{}.call *[*[]]    # => nil
->{}.call *([];[])  # => nil
->{}.call *[*args]  # => nil
~~~

Spreading empty keywords does not, when going through a variable, or sufficiently disguised:

~~~ruby
kws = {}                       # => {}
->{}.call **{}                 # => nil
->{}.call **kws     rescue $!  # => #<ArgumentError: wrong number of arguments (given 1, expected 0)>
->{}.call **({})               # => nil
->{}.call **({};)              # => nil
->{}.call **(;{})   rescue $!  # => #<ArgumentError: wrong number of arguments (given 1, expected 0)>
->{}.call **{**{}}             # => nil
->{}.call **({};{}) rescue $!  # => #<ArgumentError: wrong number of arguments (given 1, expected 0)>
->{}.call **{**kws} rescue $!  # => #<ArgumentError: wrong number of arguments (given 1, expected 0)>
~~~

It seems that `**{}` gets optimized out of the code, as expected. Likely due to https://bugs.ruby-lang.org/issues/10719
But `**empty_kws` still gets incorrectly passed as a hash, despite an attempt to fix it in https://bugs.ruby-lang.org/issues/13717

~~~ruby
->a{a}.call **{}  rescue $!  # => #<ArgumentError: wrong number of arguments (given 0, expected 1)>
->a{a}.call **kws            # => {}
->a{a}.call **(;{})          # => {}
(;{})                        # => {}
~~~

Further confusion, it's missing `a`, not `b`:

~~~ruby
->a,b:{}.call **{b:1} rescue $!  # => #<ArgumentError: missing keyword: b>
~~~

Treating keywords as a special form of hash makes them very difficult to reason about.
Arrays manage to pull off destructuring and spreading with no issue, as we saw above.
I just want hashes to work like arrays with named matching instead of ordinal matching.

For each example below, try looking at the LHS and predicting what the result will be.

~~~ruby
->a,b:,**c{[a,b,c]}.call 1, b:2                     # => [1, 2, {}]
->a,b:,**c{[a,b,c]}.call 1, b:2, 3=>4    rescue $!  # => #<ArgumentError: wrong number of arguments (given 2, expected 1; required keyword: b)>
->a,b:,**c{[a,b,c]}.call 1=>2, b:3       rescue $!  # => #<ArgumentError: missing keyword: b>
->a,b:,**c{[a,b,c]}.call 1=>2, **{b:3}   rescue $!  # => #<ArgumentError: missing keyword: b>
->a,b:,**c{[a,b,c]}.call({1=>2}, b: 3)              # => [{1=>2}, 3, {}]
->a,b:,**c{[a,b,c]}.call({1=>2}, {b: 3})            # => [{1=>2}, 3, {}]
->*a       {a      }.call 1, b:2, c:3, 4=>5         # => [1, {:b=>2, :c=>3, 4=>5}]
->*a,b:,**c{[a,b,c]}.call 1, b:2, c:3, 4=>5         # => [[1, {4=>5}], 2, {:c=>3}]
~~~

Keywords are getting in the way of beautiful hash spreading!

~~~ruby
[*[1,2], *[:c, :d]]              # => [1, 2, :c, :d]
{**{1=>2}, **{c: :d}} rescue $!  # => #<TypeError: wrong argument type Integer (expected Symbol)>
[1,2,**{a:3}]                    # => [1, 2, {:a=>3}]
[1,2,**{}]                       # => [1, 2]
[1,2,**kws]                      # => [1, 2, {}]
~~~


Note that the latest JS's behaviour is congruent with my expected outputs:

~~~sh
$ node -v
  # >> v8.9.4

$ node -p '
  (({a, c, ...rest}) => [a, c, rest])
    ({a: 1, b: 2, c: 3, d: 4})
  '
  # >> [ 1, 3, { b: 2, d: 4 } ]
$ node -p '
   const a=1, b=2, e={f: 5, g: 6}
   ;({...{a, b}, ...{c: 3, d: 4}, ...e})
   '
  # >> { a: 1, b: 2, c: 3, d: 4, f: 5, g: 6 }
~~~



-- 
https://bugs.ruby-lang.org/

Unsubscribe: <mailto:ruby-core-request / ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>