From 3de0e343e27c4bbb76c3c9fda0d8be4d83807a2d Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Mon, 24 Jun 2024 15:18:14 +0200 Subject: [PATCH] Do not warn about extra `format` argument when passed as keywords [Bug #20593] It's fairly common to use `format` to interpolate a number of values into a user provided strings. The arguments not matching are a problem when they are positional, but when they are named, it's absolutely fine and we shouldn't emit a warning. --- spec/ruby/core/kernel/format_spec.rb | 32 ++++++++++++++++++++++++++++ sprintf.c | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/spec/ruby/core/kernel/format_spec.rb b/spec/ruby/core/kernel/format_spec.rb index e8b031e480..1d0c000c15 100644 --- a/spec/ruby/core/kernel/format_spec.rb +++ b/spec/ruby/core/kernel/format_spec.rb @@ -12,4 +12,36 @@ describe "Kernel.format" do it "is accessible as a module function" do Kernel.format("%s", "hello").should == "hello" end + + describe "when $VERBOSE is true" do + it "warns if too many arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test", 1) + RUBY + + ruby_exe(code, args: "2>&1").should include("warning: too many arguments for format string") + end + + it "does not warns if too many keyword arguments are passed" do + code = <<~RUBY + $VERBOSE = true + format("test %{test}", test: 1, unused: 2) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + + ruby_bug "#20593", ""..."3.4" do + it "doesn't warns if keyword arguments are passed and none are used" do + code = <<~RUBY + $VERBOSE = true + format("test", test: 1) + format("test", {}) + RUBY + + ruby_exe(code, args: "2>&1").should_not include("warning") + end + end + end end diff --git a/sprintf.c b/sprintf.c index 98877d25d2..4fa531d53e 100644 --- a/sprintf.c +++ b/sprintf.c @@ -937,7 +937,7 @@ rb_str_format(int argc, const VALUE *argv, VALUE fmt) rb_str_tmp_frozen_release(orig, fmt); /* XXX - We cannot validate the number of arguments if (digit)$ style used. */ - if (posarg >= 0 && nextarg < argc) { + if (posarg >= 0 && nextarg < argc && !(argc == 2 && RB_TYPE_P(argv[1], T_HASH))) { const char *mesg = "too many arguments for format string"; if (RTEST(ruby_debug)) rb_raise(rb_eArgError, "%s", mesg); if (RTEST(ruby_verbose)) rb_warn("%s", mesg);