Skip to content

Commit

Permalink
merge revision(s) 67217: [Backport #15658]
Browse files Browse the repository at this point in the history
	The combination of non-Symbol keys and Symbol keys is now allowed again

	Revert r64358.  [Bug #15658]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/branches/ruby_2_6@67220 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
  • Loading branch information
nurse committed Mar 11, 2019
1 parent 50a334d commit dca6958
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 139 deletions.
13 changes: 11 additions & 2 deletions NEWS
Expand Up @@ -35,8 +35,6 @@ sufficient information, see the ChangeLog file or Redmine
(1...).each {|index| block } # infinite loop from index 1 (1...).each {|index| block } # infinite loop from index 1
ary.zip(1..) {|elem, index| block } # ary.each.with_index(1) { } ary.zip(1..) {|elem, index| block } # ary.each.with_index(1) { }


* Non-Symbol keys in a keyword arguments hash cause an exception.

* The "shadowing outer local variable" warning is removed. [Feature #12490] * The "shadowing outer local variable" warning is removed. [Feature #12490]


You can now write the following without warning: You can now write the following without warning:
Expand All @@ -48,6 +46,9 @@ sufficient information, see the ChangeLog file or Redmine


* The flip-flop syntax is deprecated. [Feature #5400] * The flip-flop syntax is deprecated. [Feature #5400]


* Non-Symbol keys in a keyword arguments hash was prohibited at 2.6.0, but
now allowed again. [Bug #15658]

=== Core classes updates (outstanding ones only) === Core classes updates (outstanding ones only)


[Array] [Array]
Expand Down Expand Up @@ -571,6 +572,7 @@ sufficient information, see the ChangeLog file or Redmine


=== Stdlib compatibility issues (excluding feature bug fixes) === Stdlib compatibility issues (excluding feature bug fixes)


<<<<<<< .working

This comment has been minimized.

Copy link
@krzysiek1507

krzysiek1507 Mar 11, 2019

merge conflict @nurse

This comment has been minimized.

Copy link
@nurse

nurse Mar 11, 2019

Author Member

Thanks! I fixed it at c96e0f6!

* These standard libraries have been promoted to default gems. * These standard libraries have been promoted to default gems.


* e2mmap * e2mmap
Expand All @@ -588,6 +590,13 @@ sufficient information, see the ChangeLog file or Redmine
* thwait * thwait
* tracer * tracer


||||||| .merge-left.r67216
profile.rb, Profiler__::

* Removed from standard library. No one maintains it from Ruby 2.0.0.

=======
>>>>>>> .merge-right.r67217
[BigDecimal] [BigDecimal]


* The following methods are removed. * The following methods are removed.
Expand Down
44 changes: 10 additions & 34 deletions class.c
Expand Up @@ -1801,57 +1801,33 @@ unknown_keyword_error(VALUE hash, const ID *table, int keywords)
rb_keyword_error("unknown", rb_hash_keys(hash)); rb_keyword_error("unknown", rb_hash_keys(hash));
} }


struct extract_keywords {
VALUE kwdhash, nonsymkey;
};


static int static int
separate_symbol(st_data_t key, st_data_t value, st_data_t arg) separate_symbol(st_data_t key, st_data_t value, st_data_t arg)
{ {
struct extract_keywords *argp = (struct extract_keywords *)arg; VALUE *kwdhash = (VALUE *)arg;
VALUE k = (VALUE)key, v = (VALUE)value; if (!SYMBOL_P(key)) kwdhash++;

if (!*kwdhash) *kwdhash = rb_hash_new();
if (argp->kwdhash) { rb_hash_aset(*kwdhash, (VALUE)key, (VALUE)value);
if (UNLIKELY(!SYMBOL_P(k))) {
argp->nonsymkey = k;
return ST_STOP;
}
}
else if (SYMBOL_P(k)) {
if (UNLIKELY(argp->nonsymkey != Qundef)) {
argp->kwdhash = Qnil;
return ST_STOP;
}
argp->kwdhash = rb_hash_new();
}
else {
if (argp->nonsymkey == Qundef)
argp->nonsymkey = k;
return ST_CONTINUE;
}
rb_hash_aset(argp->kwdhash, k, v);
return ST_CONTINUE; return ST_CONTINUE;
} }


VALUE VALUE
rb_extract_keywords(VALUE *orighash) rb_extract_keywords(VALUE *orighash)
{ {
struct extract_keywords arg = {0, Qundef}; VALUE parthash[2] = {0, 0};
VALUE hash = *orighash; VALUE hash = *orighash;


if (RHASH_EMPTY_P(hash)) { if (RHASH_EMPTY_P(hash)) {
*orighash = 0; *orighash = 0;
return hash; return hash;
} }
rb_hash_foreach(hash, separate_symbol, (st_data_t)&arg); rb_hash_foreach(hash, separate_symbol, (st_data_t)&parthash);
if (arg.kwdhash) { *orighash = parthash[1];
if (arg.nonsymkey != Qundef) { if (parthash[1] && RBASIC_CLASS(hash) != rb_cHash) {
rb_raise(rb_eArgError, "non-symbol key in keyword arguments: %+"PRIsVALUE, RBASIC_SET_CLASS(parthash[1], RBASIC_CLASS(hash));
arg.nonsymkey);
}
*orighash = 0;
} }
return arg.kwdhash; return parthash[0];
} }


int int
Expand Down
29 changes: 17 additions & 12 deletions spec/ruby/language/block_spec.rb
Expand Up @@ -49,7 +49,20 @@ def m(a) yield a end
result.should == [1, 2, [], 3, 2, {x: 9}] result.should == [1, 2, [], 3, 2, {x: 9}]
end end


it "calls #to_hash on the argument" do it "assigns symbol keys from a Hash to keyword arguments" do
result = m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }
result.should == [{"a" => 1}, a: 10]
end

it "assigns symbol keys from a Hash returned by #to_hash to keyword arguments" do
obj = mock("coerce block keyword arguments")
obj.should_receive(:to_hash).and_return({"a" => 1, b: 2})

result = m([obj]) { |a=nil, **b| [a, b] }
result.should == [{"a" => 1}, b: 2]
end

it "calls #to_hash on the argument but does not use the result when no keywords are present" do
obj = mock("coerce block keyword arguments") obj = mock("coerce block keyword arguments")
obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2}) obj.should_receive(:to_hash).and_return({"a" => 1, "b" => 2})


Expand All @@ -58,17 +71,9 @@ def m(a) yield a end
end end


describe "when non-symbol keys are in a keyword arguments Hash" do describe "when non-symbol keys are in a keyword arguments Hash" do
ruby_version_is ""..."2.6" do it "separates non-symbol keys and symbol keys" do
it "separates non-symbol keys and symbol keys" do result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] }
result = m(["a" => 10, b: 2]) { |a=nil, **b| [a, b] } result.should == [{"a" => 10}, {b: 2}]
result.should == [{"a" => 10}, {b: 2}]
end
end

ruby_version_is "2.6" do
it "raises an ArgumentError" do
lambda {m(["a" => 1, a: 10]) { |a=nil, **b| [a, b] }}.should raise_error(ArgumentError)
end
end end
end end


Expand Down
91 changes: 13 additions & 78 deletions spec/ruby/language/method_spec.rb
Expand Up @@ -849,12 +849,7 @@ def m(a=1, b:) [a, b] end


m(b: 2).should == [1, 2] m(b: 2).should == [1, 2]
m(2, b: 1).should == [2, 1] m(2, b: 1).should == [2, 1]
ruby_version_is ""..."2.6" do m("a" => 1, b: 2).should == [{"a" => 1}, 2]
m("a" => 1, b: 2).should == [{"a" => 1}, 2]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, b: 2)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand All @@ -864,12 +859,7 @@ def m(a=1, b: 2) [a, b] end
m().should == [1, 2] m().should == [1, 2]
m(2).should == [2, 2] m(2).should == [2, 2]
m(b: 3).should == [1, 3] m(b: 3).should == [1, 3]
ruby_version_is ""..."2.6" do m("a" => 1, b: 2).should == [{"a" => 1}, 2]
m("a" => 1, b: 2).should == [{"a" => 1}, 2]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, b: 2)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand All @@ -878,12 +868,7 @@ def m(a=1, **) a end


m().should == 1 m().should == 1
m(2, a: 1, b: 0).should == 2 m(2, a: 1, b: 0).should == 2
ruby_version_is ""..."2.6" do m("a" => 1, a: 2).should == {"a" => 1}
m("a" => 1, a: 2).should == {"a" => 1}
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 2)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand Down Expand Up @@ -929,12 +914,7 @@ def m(*, a:) a end


m(a: 1).should == 1 m(a: 1).should == 1
m(1, 2, a: 3).should == 3 m(1, 2, a: 3).should == 3
ruby_version_is ""..."2.6" do m("a" => 1, a: 2).should == 2
m("a" => 1, a: 2).should == 2
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 2)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand All @@ -943,12 +923,7 @@ def m(*a, b:) [a, b] end


m(b: 1).should == [[], 1] m(b: 1).should == [[], 1]
m(1, 2, b: 3).should == [[1, 2], 3] m(1, 2, b: 3).should == [[1, 2], 3]
ruby_version_is ""..."2.6" do m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, b: 2)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand All @@ -959,12 +934,7 @@ def m(*, a: 1) a end
m(1, 2).should == 1 m(1, 2).should == 1
m(a: 2).should == 2 m(a: 2).should == 2
m(1, a: 2).should == 2 m(1, a: 2).should == 2
ruby_version_is ""..."2.6" do m("a" => 1, a: 2).should == 2
m("a" => 1, a: 2).should == 2
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 2)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand All @@ -973,12 +943,7 @@ def m(*a, b: 1) [a, b] end


m().should == [[], 1] m().should == [[], 1]
m(1, 2, 3, b: 4).should == [[1, 2, 3], 4] m(1, 2, 3, b: 4).should == [[1, 2, 3], 4]
ruby_version_is ""..."2.6" do m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
m("a" => 1, b: 2).should == [[{"a" => 1}], 2]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, b: 2)}.should raise_error(ArgumentError)
end


a = mock("splat") a = mock("splat")
a.should_not_receive(:to_ary) a.should_not_receive(:to_ary)
Expand Down Expand Up @@ -1009,12 +974,7 @@ def m(*a, **) a end


m().should == [] m().should == []
m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3] m(1, 2, 3, a: 4, b: 5).should == [1, 2, 3]
ruby_version_is ""..."2.6" do m("a" => 1, a: 1).should == [{"a" => 1}]
m("a" => 1, a: 1).should == [{"a" => 1}]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 1)}.should raise_error(ArgumentError)
end
m(1, **{a: 2}).should == [1] m(1, **{a: 2}).should == [1]


h = mock("keyword splat") h = mock("keyword splat")
Expand All @@ -1028,12 +988,7 @@ def m(*, **k) k end


m().should == {} m().should == {}
m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5} m(1, 2, 3, a: 4, b: 5).should == {a: 4, b: 5}
ruby_version_is ""..."2.6" do m("a" => 1, a: 1).should == {a: 1}
m("a" => 1, a: 1).should == {a: 1}
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 1)}.should raise_error(ArgumentError)
end


h = mock("keyword splat") h = mock("keyword splat")
h.should_receive(:to_hash).and_return({a: 1}) h.should_receive(:to_hash).and_return({a: 1})
Expand All @@ -1047,22 +1002,12 @@ def m(a = nil, **k) [a, k] end
m().should == [nil, {}] m().should == [nil, {}]
m("a" => 1).should == [{"a" => 1}, {}] m("a" => 1).should == [{"a" => 1}, {}]
m(a: 1).should == [nil, {a: 1}] m(a: 1).should == [nil, {a: 1}]
ruby_version_is ""..."2.6" do m("a" => 1, a: 1).should == [{"a" => 1}, {a: 1}]
m("a" => 1, a: 1).should == [{"a" => 1}, {a: 1}]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 1)}.should raise_error(ArgumentError)
end
m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}] m({ "a" => 1 }, a: 1).should == [{"a" => 1}, {a: 1}]
m({a: 1}, {}).should == [{a: 1}, {}] m({a: 1}, {}).should == [{a: 1}, {}]


h = {"a" => 1, b: 2} h = {"a" => 1, b: 2}
ruby_version_is ""..."2.6" do m(h).should == [{"a" => 1}, {b: 2}]
m(h).should == [{"a" => 1}, {b: 2}]
end
ruby_version_is "2.6" do
lambda {m(h)}.should raise_error(ArgumentError)
end
h.should == {"a" => 1, b: 2} h.should == {"a" => 1, b: 2}


h = {"a" => 1} h = {"a" => 1}
Expand All @@ -1082,12 +1027,7 @@ def m(a = nil, **k) [a, k] end


h = mock("keyword splat") h = mock("keyword splat")
h.should_receive(:to_hash).and_return({"a" => 1, a: 2}) h.should_receive(:to_hash).and_return({"a" => 1, a: 2})
ruby_version_is ""..."2.6" do m(h).should == [{"a" => 1}, {a: 2}]
m(h).should == [{"a" => 1}, {a: 2}]
end
ruby_version_is "2.6" do
lambda {m(h)}.should raise_error(ArgumentError)
end
end end


evaluate <<-ruby do evaluate <<-ruby do
Expand All @@ -1101,12 +1041,7 @@ def m(*a, **k) [a, k] end


m("a" => 1).should == [[{"a" => 1}], {}] m("a" => 1).should == [[{"a" => 1}], {}]
m(a: 1).should == [[], {a: 1}] m(a: 1).should == [[], {a: 1}]
ruby_version_is ""..."2.6" do m("a" => 1, a: 1).should == [[{"a" => 1}], {a: 1}]
m("a" => 1, a: 1).should == [[{"a" => 1}], {a: 1}]
end
ruby_version_is "2.6" do
lambda {m("a" => 1, a: 1)}.should raise_error(ArgumentError)
end
m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}] m({ "a" => 1 }, a: 1).should == [[{"a" => 1}], {a: 1}]
m({a: 1}, {}).should == [[{a: 1}], {}] m({a: 1}, {}).should == [[{a: 1}], {}]
m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}] m({a: 1}, {"a" => 1}).should == [[{a: 1}, {"a" => 1}], {}]
Expand Down

0 comments on commit dca6958

Please sign in to comment.