[ruby-core:125661] [Ruby Bug#22099] Keyword-only method silently accepts a positional argument
Issue #22099 has been reported by rumoto (Hiroki Motoyoshi). ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099 * Author: rumoto (Hiroki Motoyoshi) * Status: Open * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
Issue #22099 has been updated by nobu (Nobuyoshi Nakada). Backport changed from 3.3: UNKNOWN, 3.4: UNKNOWN, 4.0: UNKNOWN to 3.3: DONTNEED, 3.4: REQUIRED, 4.0: REQUIRED It appears to be since 3.4. ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099#change-117525 * Author: rumoto (Hiroki Motoyoshi) * Status: Open * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: DONTNEED, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
Issue #22099 has been updated by jeremyevans0 (Jeremy Evans). I bisected the failure to commit:147ca9585ede559fd68e162cbbbaba84f009c9a1 CC @tenderlovemaking @jhawthorn ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099#change-117528 * Author: rumoto (Hiroki Motoyoshi) * Status: Open * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: DONTNEED, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
Issue #22099 has been updated by luke-gru (Luke Gruber). I created a [PR](https://github.com/ruby/ruby/pull/17581). It's currently being reviewed. ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099#change-117885 * Author: rumoto (Hiroki Motoyoshi) * Status: Open * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: DONTNEED, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
Issue #22099 has been updated by k0kubun (Takashi Kokubun). ruby_4_0 backport draft PR: https://github.com/ruby/ruby/pull/17763 ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099#change-117983 * Author: rumoto (Hiroki Motoyoshi) * Status: Closed * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: DONTNEED, 3.4: REQUIRED, 4.0: REQUIRED ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
Issue #22099 has been updated by k0kubun (Takashi Kokubun). Backport changed from 3.3: DONTNEED, 3.4: REQUIRED, 4.0: REQUIRED to 3.3: DONTNEED, 3.4: REQUIRED, 4.0: DONE ruby_4_0 commit:aad9ef5ce42d07004cebe4dab1d8d661a195d550 merged revision(s) commit:034fa35ff042933ddb722cadf438e120dbe05881. ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099#change-117991 * Author: rumoto (Hiroki Motoyoshi) * Status: Closed * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: DONTNEED, 3.4: REQUIRED, 4.0: DONE ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
Issue #22099 has been updated by nagachika (Tomoyuki Chikanaga). Backport changed from 3.3: DONTNEED, 3.4: REQUIRED, 4.0: DONE to 3.3: DONTNEED, 3.4: DONE, 4.0: DONE ruby_3_4 commit:6162535e5d0a72b315141d18cf20463ec6da3143 merged revision(s) commit:034fa35ff042933ddb722cadf438e120dbe05881. ---------------------------------------- Bug #22099: Keyword-only method silently accepts a positional argument https://bugs.ruby-lang.org/issues/22099#change-118455 * Author: rumoto (Hiroki Motoyoshi) * Status: Closed * ruby -v: ruby 3.4.9 (2026-03-11 revision 76cca827ab) +PRISM [arm64-darwin24] * Backport: 3.3: DONTNEED, 3.4: DONE, 4.0: DONE ---------------------------------------- A method that declares only keyword parameters accepts a positional argument without raising, but only from the second call onward; a prior valid keyword call to the same method is required to trigger it. Once triggered, the positional value is bound to the first keyword and an explicitly passed keyword of the same name is dropped. ```ruby class Foo def bar(a: nil, b: nil) p [:body, a: a, b: b] end end foo = Foo.new foo.bar(a: 1, b: 2) #=> [:body, {a: 1, b: 2}] foo.bar(99, a: 0) #=> [:body, {a: 99, b: nil}] foo.bar(99, b: 0) #=> [:body, {a: nil, b: 99}] ``` Although one positional argument is passed, no exception is raised. The positional value (`99`) is bound to the first keyword `a`, and the explicitly passed keyword (`a: 0` / `b: 0`) is not reflected in the result. Since `bar` accepts no positional arguments, an `ArgumentError (wrong number of arguments (given 1, expected 0))` should be raised. -- https://bugs.ruby-lang.org/
participants (6)
-
jeremyevans0 (Jeremy Evans) -
k0kubun (Takashi Kokubun) -
luke-gru (Luke Gruber) -
nagachika (Tomoyuki Chikanaga) -
nobu (Nobuyoshi Nakada) -
rumoto (Hiroki Motoyoshi)