summaryrefslogtreecommitdiff
path: root/spec/ruby
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-12-07 08:35:55 -0800
committerGitHub <noreply@github.com>2023-12-07 08:35:55 -0800
commitca204a20231f1ecabf5a7343aec49c3ea1bac90a (patch)
tree95ceaa2753cfe0763b57c4fdf420bfa820f01118 /spec/ruby
parente6a6ea9dcf0741f415ccbdcdf7643315f032e4f9 (diff)
Fix keyword splat passing as regular argument
Since Ruby 3.0, Ruby has passed a keyword splat as a regular argument in the case of a call to a Ruby method where the method does not accept keyword arguments, if the method call does not contain an argument splat: ```ruby def self.f(obj) obj end def self.fs(*obj) obj[0] end h = {a: 1} f(**h).equal?(h) # Before: true; After: false fs(**h).equal?(h) # Before: true; After: false a = [] f(*a, **h).equal?(h) # Before and After: false fs(*a, **h).equal?(h) # Before and After: false ``` The fact that the behavior differs when passing an empty argument splat makes it obvious that something is not working the way it is intended. Ruby 2 always copied the keyword splat hash, and that is the expected behavior in Ruby 3. This bug is because of a missed check in setup_parameters_complex. If the keyword splat passed is not mutable, then it points to an existing object and not a new object, and therefore it must be copied. Now, there are 3 specs for the broken behavior of directly using the keyword splatted hash. Fix two specs and add a new version guard. Do not keep the specs for the broken behavior for earlier Ruby versions, in case this fix is backported. For the ruby2_keywords spec, just remove the related line, since that line is unrelated to what the spec is testing. Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
Diffstat (limited to 'spec/ruby')
-rw-r--r--spec/ruby/core/module/ruby2_keywords_spec.rb1
-rw-r--r--spec/ruby/language/hash_spec.rb18
-rw-r--r--spec/ruby/language/keyword_arguments_spec.rb15
3 files changed, 19 insertions, 15 deletions
diff --git a/spec/ruby/core/module/ruby2_keywords_spec.rb b/spec/ruby/core/module/ruby2_keywords_spec.rb
index dc16d712c7..a72612a670 100644
--- a/spec/ruby/core/module/ruby2_keywords_spec.rb
+++ b/spec/ruby/core/module/ruby2_keywords_spec.rb
@@ -22,7 +22,6 @@ describe "Module#ruby2_keywords" do
end
h = {a: 1}
- obj.regular(**h).should.equal?(h)
last = mark(**h).last
Hash.ruby2_keywords_hash?(last).should == true
diff --git a/spec/ruby/language/hash_spec.rb b/spec/ruby/language/hash_spec.rb
index 9e2b9bd4c5..6ac382c42c 100644
--- a/spec/ruby/language/hash_spec.rb
+++ b/spec/ruby/language/hash_spec.rb
@@ -220,15 +220,17 @@ describe "The ** operator" do
h.should == { one: 1, two: 2 }
end
- it "does not copy when calling a method taking a positional Hash" do
- def m(h)
- h.delete(:one); h
- end
+ ruby_bug "#20012", ""..."3.3" do
+ it "makes a copy when calling a method taking a positional Hash" do
+ def m(h)
+ h.delete(:one); h
+ end
- h = { one: 1, two: 2 }
- m(**h).should == { two: 2 }
- m(**h).should.equal?(h)
- h.should == { two: 2 }
+ h = { one: 1, two: 2 }
+ m(**h).should == { two: 2 }
+ m(**h).should_not.equal?(h)
+ h.should == { one: 1, two: 2 }
+ end
end
ruby_version_is "3.1" do
diff --git a/spec/ruby/language/keyword_arguments_spec.rb b/spec/ruby/language/keyword_arguments_spec.rb
index e2c816f622..ffb5b1fab0 100644
--- a/spec/ruby/language/keyword_arguments_spec.rb
+++ b/spec/ruby/language/keyword_arguments_spec.rb
@@ -87,13 +87,16 @@ describe "Keyword arguments" do
end
context "**" do
- it "does not copy a non-empty Hash for a method taking (*args)" do
- def m(*args)
- args[0]
- end
+ ruby_version_is "3.3" do
+ it "copies a non-empty Hash for a method taking (*args)" do
+ def m(*args)
+ args[0]
+ end
- h = {a: 1}
- m(**h).should.equal?(h)
+ h = {a: 1}
+ m(**h).should_not.equal?(h)
+ h.should == {a: 1}
+ end
end
it "copies the given Hash for a method taking (**kwargs)" do