diff options
author | Jeremy Evans <code@jeremyevans.net> | 2020-10-24 11:52:30 -0700 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2020-11-20 16:30:23 -0800 |
commit | 58325daae3beefda13ed100782cd19a89cc68771 (patch) | |
tree | fd1857904c1c86d9c7c21f6038535f3beeafc4bb | |
parent | 4f5d14eb8cb7fba1c583d516d597c5e28d7a5540 (diff) |
Make String methods return String instances when called on a subclass instance
This modifies the following String methods to return String instances
instead of subclass instances:
* String#*
* String#capitalize
* String#center
* String#chomp
* String#chop
* String#delete
* String#delete_prefix
* String#delete_suffix
* String#downcase
* String#dump
* String#each/#each_line
* String#gsub
* String#ljust
* String#lstrip
* String#partition
* String#reverse
* String#rjust
* String#rpartition
* String#rstrip
* String#scrub
* String#slice!
* String#slice/#[]
* String#split
* String#squeeze
* String#strip
* String#sub
* String#succ/#next
* String#swapcase
* String#tr
* String#tr_s
* String#upcase
This also fixes a bug in String#swapcase where it would return the
receiver instead of a copy of the receiver if the receiver was the
empty string.
Some string methods were left to return subclass instances:
* String#+@
* String#-@
Both of these methods will return the receiver (subclass instance)
in some cases, so it is best to keep the returned class consistent.
Fixes [#10845]
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/3701
28 files changed, 591 insertions, 211 deletions
diff --git a/spec/ruby/core/string/capitalize_spec.rb b/spec/ruby/core/string/capitalize_spec.rb index e6cf365ac3..21df18a5ae 100644 --- a/spec/ruby/core/string/capitalize_spec.rb +++ b/spec/ruby/core/string/capitalize_spec.rb @@ -80,9 +80,18 @@ describe "String#capitalize" do -> { "abc".capitalize(:invalid_option) }.should raise_error(ArgumentError) end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("hello").capitalize.should be_an_instance_of(String) + StringSpecs::MyString.new("Hello").capitalize.should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/center_spec.rb b/spec/ruby/core/string/center_spec.rb index 8a4be0a81e..b66015172c 100644 --- a/spec/ruby/core/string/center_spec.rb +++ b/spec/ruby/core/string/center_spec.rb @@ -91,13 +91,26 @@ describe "String#center with length, padding" do -> { "hello".center(0, "") }.should raise_error(ArgumentError) end - it "returns subclass instances when called on subclasses" do - StringSpecs::MyString.new("").center(10).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on subclasses" do + StringSpecs::MyString.new("").center(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + + "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end + end - "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) - "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + ruby_version_is '3.0' do + it "returns String instances when called on subclasses" do + StringSpecs::MyString.new("").center(10).should be_an_instance_of(String) + StringSpecs::MyString.new("foo").center(10).should be_an_instance_of(String) + StringSpecs::MyString.new("foo").center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + + "".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".center(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do diff --git a/spec/ruby/core/string/chomp_spec.rb b/spec/ruby/core/string/chomp_spec.rb index 7a1118d534..3d6207f876 100644 --- a/spec/ruby/core/string/chomp_spec.rb +++ b/spec/ruby/core/string/chomp_spec.rb @@ -46,9 +46,18 @@ describe "String#chomp" do end end - it "returns subclass instances when called on a subclass" do - str = StringSpecs::MyString.new("hello\n").chomp - str.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + str = StringSpecs::MyString.new("hello\n").chomp + str.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + str = StringSpecs::MyString.new("hello\n").chomp + str.should be_an_instance_of(String) + end end it "removes trailing characters that match $/ when it has been assigned a value" do diff --git a/spec/ruby/core/string/chop_spec.rb b/spec/ruby/core/string/chop_spec.rb index 582d3c58c5..9b4e7363c6 100644 --- a/spec/ruby/core/string/chop_spec.rb +++ b/spec/ruby/core/string/chop_spec.rb @@ -61,8 +61,16 @@ describe "String#chop" do end end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("hello\n").chop.should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/delete_prefix_spec.rb b/spec/ruby/core/string/delete_prefix_spec.rb index bd5b56a312..8469791f74 100644 --- a/spec/ruby/core/string/delete_prefix_spec.rb +++ b/spec/ruby/core/string/delete_prefix_spec.rb @@ -41,9 +41,18 @@ describe "String#delete_prefix" do 'hello'.delete_prefix(o).should == 'o' end - it "returns a subclass instance when called on a subclass instance" do - s = StringSpecs::MyString.new('hello') - s.delete_prefix('hell').should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance when called on a subclass instance" do + s = StringSpecs::MyString.new('hello') + s.delete_prefix('hell').should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a String instance when called on a subclass instance" do + s = StringSpecs::MyString.new('hello') + s.delete_prefix('hell').should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/delete_spec.rb b/spec/ruby/core/string/delete_spec.rb index b6e54f8386..ebca0b7dae 100644 --- a/spec/ruby/core/string/delete_spec.rb +++ b/spec/ruby/core/string/delete_spec.rb @@ -93,8 +93,16 @@ describe "String#delete" do -> { "hello world".delete(mock('x')) }.should raise_error(TypeError) end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("oh no!!!").delete("!").should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/delete_suffix_spec.rb b/spec/ruby/core/string/delete_suffix_spec.rb index dbb5f9df9e..12d0ee175e 100644 --- a/spec/ruby/core/string/delete_suffix_spec.rb +++ b/spec/ruby/core/string/delete_suffix_spec.rb @@ -41,9 +41,18 @@ describe "String#delete_suffix" do 'hello'.delete_suffix(o).should == 'h' end - it "returns a subclass instance when called on a subclass instance" do - s = StringSpecs::MyString.new('hello') - s.delete_suffix('ello').should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance when called on a subclass instance" do + s = StringSpecs::MyString.new('hello') + s.delete_suffix('ello').should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a String instance when called on a subclass instance" do + s = StringSpecs::MyString.new('hello') + s.delete_suffix('ello').should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/downcase_spec.rb b/spec/ruby/core/string/downcase_spec.rb index 8c840bdcc4..4427c9df10 100644 --- a/spec/ruby/core/string/downcase_spec.rb +++ b/spec/ruby/core/string/downcase_spec.rb @@ -76,8 +76,16 @@ describe "String#downcase" do end end - it "returns a subclass instance for subclasses" do - StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance for subclasses" do + StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a String instance for subclasses" do + StringSpecs::MyString.new("FOObar").downcase.should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/dump_spec.rb b/spec/ruby/core/string/dump_spec.rb index ebdd0167aa..817dec6c4d 100644 --- a/spec/ruby/core/string/dump_spec.rb +++ b/spec/ruby/core/string/dump_spec.rb @@ -19,8 +19,16 @@ describe "String#dump" do "foo".freeze.dump.should_not.frozen? end - it "returns a subclass instance" do - StringSpecs::MyString.new.dump.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance" do + StringSpecs::MyString.new.dump.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a String instance" do + StringSpecs::MyString.new.dump.should be_an_instance_of(String) + end end it "wraps string with \"" do diff --git a/spec/ruby/core/string/gsub_spec.rb b/spec/ruby/core/string/gsub_spec.rb index 23e0ad2260..54a2be0fed 100644 --- a/spec/ruby/core/string/gsub_spec.rb +++ b/spec/ruby/core/string/gsub_spec.rb @@ -236,11 +236,22 @@ describe "String#gsub with pattern and replacement" do -> { "hello".gsub(/[aeiou]/, nil) }.should raise_error(TypeError) end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("").gsub(//, "").should be_an_instance_of(String) + StringSpecs::MyString.new("").gsub(/foo/, "").should be_an_instance_of(String) + StringSpecs::MyString.new("foo").gsub(/foo/, "").should be_an_instance_of(String) + StringSpecs::MyString.new("foo").gsub("foo", "").should be_an_instance_of(String) + end end # Note: $~ cannot be tested because mspec messes with it diff --git a/spec/ruby/core/string/ljust_spec.rb b/spec/ruby/core/string/ljust_spec.rb index 481ef77ca8..0c3b2a2f44 100644 --- a/spec/ruby/core/string/ljust_spec.rb +++ b/spec/ruby/core/string/ljust_spec.rb @@ -74,13 +74,26 @@ describe "String#ljust with length, padding" do -> { "hello".ljust(10, '') }.should raise_error(ArgumentError) end - it "returns subclass instances when called on subclasses" do - StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on subclasses" do + StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + + "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end + end - "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) - "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + ruby_version_is '3.0' do + it "returns String instances when called on subclasses" do + StringSpecs::MyString.new("").ljust(10).should be_an_instance_of(String) + StringSpecs::MyString.new("foo").ljust(10).should be_an_instance_of(String) + StringSpecs::MyString.new("foo").ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + + "".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".ljust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do diff --git a/spec/ruby/core/string/rjust_spec.rb b/spec/ruby/core/string/rjust_spec.rb index bf39f4fdb9..f51aacff44 100644 --- a/spec/ruby/core/string/rjust_spec.rb +++ b/spec/ruby/core/string/rjust_spec.rb @@ -74,13 +74,26 @@ describe "String#rjust with length, padding" do -> { "hello".rjust(10, '') }.should raise_error(ArgumentError) end - it "returns subclass instances when called on subclasses" do - StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on subclasses" do + StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(StringSpecs::MyString) + + "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end + end - "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) - "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + ruby_version_is '3.0' do + it "returns String instances when called on subclasses" do + StringSpecs::MyString.new("").rjust(10).should be_an_instance_of(String) + StringSpecs::MyString.new("foo").rjust(10).should be_an_instance_of(String) + StringSpecs::MyString.new("foo").rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + + "".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + "foo".rjust(10, StringSpecs::MyString.new("x")).should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do diff --git a/spec/ruby/core/string/shared/each_line.rb b/spec/ruby/core/string/shared/each_line.rb index 156565ff13..f9c910596a 100644 --- a/spec/ruby/core/string/shared/each_line.rb +++ b/spec/ruby/core/string/shared/each_line.rb @@ -93,10 +93,20 @@ describe :string_each_line, shared: true do end end - it "yields subclass instances for subclasses" do - a = [] - StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class } - a.should == [StringSpecs::MyString, StringSpecs::MyString] + ruby_version_is ''...'3.0' do + it "yields subclass instances for subclasses" do + a = [] + StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class } + a.should == [StringSpecs::MyString, StringSpecs::MyString] + end + end + + ruby_version_is '3.0' do + it "yields String instances for subclasses" do + a = [] + StringSpecs::MyString.new("hello\nworld").send(@method) { |s| a << s.class } + a.should == [String, String] + end end it "returns self" do diff --git a/spec/ruby/core/string/shared/slice.rb b/spec/ruby/core/string/shared/slice.rb index 6b6f37217a..69997b7c1d 100644 --- a/spec/ruby/core/string/shared/slice.rb +++ b/spec/ruby/core/string/shared/slice.rb @@ -163,11 +163,22 @@ describe :string_slice_index_length, shared: true do -> { "hello".send(@method, 0, bignum_value) }.should raise_error(RangeError) end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.send(@method, 0,0).should be_an_instance_of(StringSpecs::MyString) - s.send(@method, 0,4).should be_an_instance_of(StringSpecs::MyString) - s.send(@method, 1,4).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, 0,0).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 0,4).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 1,4).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, 0,0).should be_an_instance_of(String) + s.send(@method, 0,4).should be_an_instance_of(String) + s.send(@method, 1,4).should be_an_instance_of(String) + end end it "handles repeated application" do @@ -252,11 +263,22 @@ describe :string_slice_range, shared: true do end end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.send(@method, 0...0).should be_an_instance_of(StringSpecs::MyString) - s.send(@method, 0..4).should be_an_instance_of(StringSpecs::MyString) - s.send(@method, 1..4).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, 0...0).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 0..4).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, 1..4).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, 0...0).should be_an_instance_of(String) + s.send(@method, 0..4).should be_an_instance_of(String) + s.send(@method, 1..4).should be_an_instance_of(String) + end end it "calls to_int on range arguments" do @@ -348,10 +370,20 @@ describe :string_slice_regexp, shared: true do end end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.send(@method, //).should be_an_instance_of(StringSpecs::MyString) - s.send(@method, /../).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, //).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, /../).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, //).should be_an_instance_of(String) + s.send(@method, /../).should be_an_instance_of(String) + end end it "sets $~ to MatchData when there is a match and nil when there's none" do @@ -436,10 +468,20 @@ describe :string_slice_regexp_index, shared: true do -> { "hello".send(@method, /(.)(.)(.)/, nil) }.should raise_error(TypeError) end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.send(@method, /(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString) - s.send(@method, /(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, /(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString) + s.send(@method, /(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, /(.)(.)/, 0).should be_an_instance_of(String) + s.send(@method, /(.)(.)/, 1).should be_an_instance_of(String) + end end it "sets $~ to MatchData when there is a match and nil when there's none" do @@ -493,11 +535,22 @@ describe :string_slice_string, shared: true do -> { "hello".send(@method, o) }.should raise_error(TypeError) end - it "returns a subclass instance when given a subclass instance" do - s = StringSpecs::MyString.new("el") - r = "hello".send(@method, s) - r.should == "el" - r.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance when given a subclass instance" do + s = StringSpecs::MyString.new("el") + r = "hello".send(@method, s) + r.should == "el" + r.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a String instance when given a subclass instance" do + s = StringSpecs::MyString.new("el") + r = "hello".send(@method, s) + r.should == "el" + r.should be_an_instance_of(String) + end end end @@ -567,9 +620,18 @@ describe :string_slice_regexp_group, shared: true do -> { "hello".send(@method, /(?<q>)/, '') }.should raise_error(IndexError) end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.send(@method, /(?<q>.)/, 'q').should be_an_instance_of(String) + end end it "sets $~ to MatchData when there is a match and nil when there's none" do diff --git a/spec/ruby/core/string/shared/succ.rb b/spec/ruby/core/string/shared/succ.rb index 7ca488dd88..25602103b6 100644 --- a/spec/ruby/core/string/shared/succ.rb +++ b/spec/ruby/core/string/shared/succ.rb @@ -59,10 +59,20 @@ describe :string_succ, shared: true do "\xFF\xFF".send(@method).should == "\x01\x00\x00" end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").send(@method).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("").send(@method).should be_an_instance_of(String) + StringSpecs::MyString.new("a").send(@method).should be_an_instance_of(String) + StringSpecs::MyString.new("z").send(@method).should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do diff --git a/spec/ruby/core/string/slice_spec.rb b/spec/ruby/core/string/slice_spec.rb index bd0a63971b..83b475c8b5 100644 --- a/spec/ruby/core/string/slice_spec.rb +++ b/spec/ruby/core/string/slice_spec.rb @@ -142,12 +142,21 @@ describe "String#slice! with index, length" do "hello".slice!(obj, obj).should == "ll" end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.slice!(0, 0).should be_an_instance_of(StringSpecs::MyString) - s.slice!(0, 4).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(0, 0).should be_an_instance_of(StringSpecs::MyString) + s.slice!(0, 4).should be_an_instance_of(StringSpecs::MyString) + end end + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(0, 0).should be_an_instance_of(String) + s.slice!(0, 4).should be_an_instance_of(String) + end + end it "returns the substring given by the character offsets" do "hellö there".slice!(1,0).should == "" @@ -196,10 +205,20 @@ describe "String#slice! Range" do end end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.slice!(0...0).should be_an_instance_of(StringSpecs::MyString) - s.slice!(0..4).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(0...0).should be_an_instance_of(StringSpecs::MyString) + s.slice!(0..4).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(0...0).should be_an_instance_of(String) + s.slice!(0..4).should be_an_instance_of(String) + end end it "calls to_int on range arguments" do @@ -299,10 +318,20 @@ describe "String#slice! with Regexp" do end end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.slice!(//).should be_an_instance_of(StringSpecs::MyString) - s.slice!(/../).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(//).should be_an_instance_of(StringSpecs::MyString) + s.slice!(/../).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(//).should be_an_instance_of(String) + s.slice!(/../).should be_an_instance_of(String) + end end it "returns the matching portion of self with a multi byte character" do @@ -383,10 +412,20 @@ describe "String#slice! with Regexp, index" do "har".slice!(/(.)(.)(.)/, obj).should == "a" end - it "returns subclass instances" do - s = StringSpecs::MyString.new("hello") - s.slice!(/(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString) - s.slice!(/(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(/(.)(.)/, 0).should be_an_instance_of(StringSpecs::MyString) + s.slice!(/(.)(.)/, 1).should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + s = StringSpecs::MyString.new("hello") + s.slice!(/(.)(.)/, 0).should be_an_instance_of(String) + s.slice!(/(.)(.)/, 1).should be_an_instance_of(String) + end end it "returns the encoding aware capture for the given index" do @@ -461,11 +500,22 @@ describe "String#slice! with String" do -> { "hello".slice!(o) }.should raise_error(TypeError) end - it "returns a subclass instance when given a subclass instance" do - s = StringSpecs::MyString.new("el") - r = "hello".slice!(s) - r.should == "el" - r.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance when given a subclass instance" do + s = StringSpecs::MyString.new("el") + r = "hello".slice!(s) + r.should == "el" + r.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a subclass instance when given a subclass instance" do + s = StringSpecs::MyString.new("el") + r = "hello".slice!(s) + r.should == "el" + r.should be_an_instance_of(String) + end end it "raises a FrozenError if self is frozen" do diff --git a/spec/ruby/core/string/split_spec.rb b/spec/ruby/core/string/split_spec.rb index 7ac8d7815c..c5441e3a49 100644 --- a/spec/ruby/core/string/split_spec.rb +++ b/spec/ruby/core/string/split_spec.rb @@ -159,28 +159,48 @@ describe "String#split with String" do "foo".split("bar", 3).should == ["foo"] end - it "returns subclass instances based on self" do - ["", "x.y.z.", " x y "].each do |str| - ["", ".", " "].each do |pat| - [-1, 0, 1, 2].each do |limit| - StringSpecs::MyString.new(str).split(pat, limit).each do |x| - x.should be_an_instance_of(StringSpecs::MyString) - end + ruby_version_is ''...'3.0' do + it "returns subclass instances based on self" do + ["", "x.y.z.", " x y "].each do |str| + ["", ".", " "].each do |pat| + [-1, 0, 1, 2].each do |limit| + StringSpecs::MyString.new(str).split(pat, limit).each do |x| + x.should be_an_instance_of(StringSpecs::MyString) + end - str.split(StringSpecs::MyString.new(pat), limit).each do |x| - x.should be_an_instance_of(String) + str.split(StringSpecs::MyString.new(pat), limit).each do |x| + x.should be_an_instance_of(String) + end end end end end + + it "does not call constructor on created subclass instances" do + # can't call should_not_receive on an object that doesn't yet exist + # so failure here is signalled by exception, not expectation failure + + s = StringSpecs::StringWithRaisingConstructor.new('silly:string') + s.split(':').first.should == 'silly' + end end - it "does not call constructor on created subclass instances" do - # can't call should_not_receive on an object that doesn't yet exist - # so failure here is signalled by exception, not expectation failure + ruby_version_is '3.0' do + it "returns String instances based on self" do + ["", "x.y.z.", " x y "].each do |str| + ["", ".", " "].each do |pat| + [-1, 0, 1, 2].each do |limit| + StringSpecs::MyString.new(str).split(pat, limit).each do |x| + x.should be_an_instance_of(String) + end - s = StringSpecs::StringWithRaisingConstructor.new('silly:string') - s.split(':').first.should == 'silly' + str.split(StringSpecs::MyString.new(pat), limit).each do |x| + x.should be_an_instance_of(String) + end + end + end + end + end end ruby_version_is ''...'2.7' do @@ -355,24 +375,40 @@ describe "String#split with Regexp" do "foo".split(/bar/, 3).should == ["foo"] end - it "returns subclass instances based on self" do - ["", "x:y:z:", " x y "].each do |str| - [//, /:/, /\s+/].each do |pat| - [-1, 0, 1, 2].each do |limit| - StringSpecs::MyString.new(str).split(pat, limit).each do |x| - x.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances based on self" do + ["", "x:y:z:", " x y "].each do |str| + [//, /:/, /\s+/].each do |pat| + [-1, 0, 1, 2].each do |limit| + StringSpecs::MyString.new(str).split(pat, limit).each do |x| + x.should be_an_instance_of(StringSpecs::MyString) + end end end end end - end - it "does not call constructor on created subclass instances" do - # can't call should_not_receive on an object that doesn't yet exist - # so failure here is signalled by exception, not expectation failure + it "does not call constructor on created subclass instances" do + # can't call should_not_receive on an object that doesn't yet exist + # so failure here is signalled by exception, not expectation failure + + s = StringSpecs::StringWithRaisingConstructor.new('silly:string') + s.split(/:/).first.should == 'silly' + end + end - s = StringSpecs::StringWithRaisingConstructor.new('silly:string') - s.split(/:/).first.should == 'silly' + ruby_version_is '3.0' do + it "returns String instances based on self" do + ["", "x:y:z:", " x y "].each do |str| + [//, /:/, /\s+/].each do |pat| + [-1, 0, 1, 2].each do |limit| + StringSpecs::MyString.new(str).split(pat, limit).each do |x| + x.should be_an_instance_of(String) + end + end + end + end + end end ruby_version_is ''...'2.7' do @@ -493,16 +529,32 @@ describe "String#split with Regexp" do end describe "for a String subclass" do - it "yields instances of the same subclass" do - a = [] - StringSpecs::MyString.new("a|b").split("|") { |str| a << str } - first, last = a + ruby_version_is ''...'3.0' do + it "yields instances of the same subclass" do + a = [] + StringSpecs::MyString.new("a|b").split("|") { |str| a << str } + first, last = a + + first.should be_an_instance_of(StringSpecs::MyString) + first.should == "a" - first.should be_an_instance_of(StringSpecs::MyString) - first.should == "a" + last.should be_an_instance_of(StringSpecs::MyString) + last.should == "b" + end + end + + ruby_version_is '3.0' do + it "yields instances of String" do + a = [] + StringSpecs::MyString.new("a|b").split("|") { |str| a << str } + first, last = a - last.should be_an_instance_of(StringSpecs::MyString) - last.should == "b" + first.should be_an_instance_of(String) + first.should == "a" + + last.should be_an_instance_of(String) + last.should == "b" + end end end end diff --git a/spec/ruby/core/string/squeeze_spec.rb b/spec/ruby/core/string/squeeze_spec.rb index 5a7fbb59de..6f75402c9c 100644 --- a/spec/ruby/core/string/squeeze_spec.rb +++ b/spec/ruby/core/string/squeeze_spec.rb @@ -80,8 +80,16 @@ describe "String#squeeze" do -> { "hello world".squeeze(mock('x')) }.should raise_error(TypeError) end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("oh no!!!").squeeze("!").should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/sub_spec.rb b/spec/ruby/core/string/sub_spec.rb index 80cf8622d5..5ae9a4bbf3 100644 --- a/spec/ruby/core/string/sub_spec.rb +++ b/spec/ruby/core/string/sub_spec.rb @@ -192,11 +192,22 @@ describe "String#sub with pattern, replacement" do -> { "hello".sub(/[aeiou]/, 99) }.should raise_error(TypeError) end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("").sub(//, "").should be_an_instance_of(String) + StringSpecs::MyString.new("").sub(/foo/, "").should be_an_instance_of(String) + StringSpecs::MyString.new("foo").sub(/foo/, "").should be_an_instance_of(String) + StringSpecs::MyString.new("foo").sub("foo", "").should be_an_instance_of(String) + end end it "sets $~ to MatchData of match and nil when there's none" do diff --git a/spec/ruby/core/string/swapcase_spec.rb b/spec/ruby/core/string/swapcase_spec.rb index 0b50cc35c4..32b358607e 100644 --- a/spec/ruby/core/string/swapcase_spec.rb +++ b/spec/ruby/core/string/swapcase_spec.rb @@ -73,9 +73,18 @@ describe "String#swapcase" do -> { "abc".swapcase(:invalid_option) }.should raise_error(ArgumentError) end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("").swapcase.should be_an_instance_of(StringSpecs::MyString) - StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("").swapcase.should be_an_instance_of(StringSpecs::MyString) + StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("").swapcase.should be_an_instance_of(String) + StringSpecs::MyString.new("hello").swapcase.should be_an_instance_of(String) + end end end diff --git a/spec/ruby/core/string/tr_s_spec.rb b/spec/ruby/core/string/tr_s_spec.rb index cdf5667ea3..c6ad12139e 100644 --- a/spec/ruby/core/string/tr_s_spec.rb +++ b/spec/ruby/core/string/tr_s_spec.rb @@ -45,8 +45,16 @@ describe "String#tr_s" do "bla".tr_s(from_str, to_str).should == "BlA" end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances when called on a subclass" do + StringSpecs::MyString.new("hello").tr_s("e", "a").should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do diff --git a/spec/ruby/core/string/tr_spec.rb b/spec/ruby/core/string/tr_spec.rb index 212d06f679..06be1e6641 100644 --- a/spec/ruby/core/string/tr_spec.rb +++ b/spec/ruby/core/string/tr_spec.rb @@ -57,8 +57,16 @@ describe "String#tr" do "bla".tr(from_str, to_str).should == "BlA" end - it "returns subclass instances when called on a subclass" do - StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances when called on a subclass" do + StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns Stringinstances when called on a subclass" do + StringSpecs::MyString.new("hello").tr("e", "a").should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do diff --git a/spec/ruby/core/string/upcase_spec.rb b/spec/ruby/core/string/upcase_spec.rb index 13ebd09a95..6cf44b4703 100644 --- a/spec/ruby/core/string/upcase_spec.rb +++ b/spec/ruby/core/string/upcase_spec.rb @@ -73,8 +73,16 @@ describe "String#upcase" do end end - it "returns a subclass instance for subclasses" do - StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(StringSpecs::MyString) + ruby_version_is ''...'3.0' do + it "returns a subclass instance for subclasses" do + StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(StringSpecs::MyString) + end + end + + ruby_version_is '3.0' do + it "returns a String instance for subclasses" do + StringSpecs::MyString.new("fooBAR").upcase.should be_an_instance_of(String) + end end end diff --git a/spec/ruby/shared/string/times.rb b/spec/ruby/shared/string/times.rb index 4dd59884c8..cd4edf5340 100644 --- a/spec/ruby/shared/string/times.rb +++ b/spec/ruby/shared/string/times.rb @@ -32,10 +32,20 @@ describe :string_times, shared: true do @object.call("", max_long).should == "" end - it "returns subclass instances" do - @object.call(MyString.new("cool"), 0).should be_an_instance_of(MyString) - @object.call(MyString.new("cool"), 1).should be_an_instance_of(MyString) - @object.call(MyString.new("cool"), 2).should be_an_instance_of(MyString) + ruby_version_is ''...'3.0' do + it "returns subclass instances" do + @object.call(MyString.new("cool"), 0).should be_an_instance_of(MyString) + @object.call(MyString.new("cool"), 1).should be_an_instance_of(MyString) + @object.call(MyString.new("cool"), 2).should be_an_instance_of(MyString) + end + end + + ruby_version_is '3.0' do + it "returns String instances" do + @object.call(MyString.new("cool"), 0).should be_an_instance_of(String) + @object.call(MyString.new("cool"), 1).should be_an_instance_of(String) + @object.call(MyString.new("cool"), 2).should be_an_instance_of(String) + end end ruby_version_is ''...'2.7' do @@ -1228,6 +1228,13 @@ rb_str_new_frozen(VALUE orig) return str_new_frozen(rb_obj_class(orig), orig); } +static VALUE +rb_str_new_frozen_String(VALUE orig) +{ + if (OBJ_FROZEN(orig) && rb_obj_class(orig) == rb_cString) return orig; + return str_new_frozen(rb_cString, orig); +} + VALUE rb_str_tmp_frozen_acquire(VALUE orig) { @@ -1336,6 +1343,14 @@ str_new_empty(VALUE str) return v; } +static VALUE +str_new_empty_String(VALUE str) +{ + VALUE v = rb_str_new(0, 0); + rb_enc_copy(v, str); + return v; +} + #define STR_BUF_MIN_SIZE 63 STATIC_ASSERT(STR_BUF_MIN_SIZE, STR_BUF_MIN_SIZE > RSTRING_EMBED_LEN_MAX); @@ -2036,10 +2051,10 @@ rb_str_times(VALUE str, VALUE times) int termlen; if (times == INT2FIX(1)) { - return rb_str_dup(str); + return str_duplicate(rb_cString, str); } if (times == INT2FIX(0)) { - str2 = str_alloc(rb_obj_class(str)); + str2 = str_alloc(rb_cString); rb_enc_copy(str2, str); return str2; } @@ -2048,7 +2063,7 @@ rb_str_times(VALUE str, VALUE times) rb_raise(rb_eArgError, "negative argument"); } if (RSTRING_LEN(str) == 1 && RSTRING_PTR(str)[0] == 0) { - str2 = str_alloc(rb_obj_class(str)); + str2 = str_alloc(rb_cString); if (!STR_EMBEDDABLE_P(len, 1)) { RSTRING(str2)->as.heap.aux.capa = len; RSTRING(str2)->as.heap.ptr = ZALLOC_N(char, (size_t)len + 1); @@ -2064,7 +2079,7 @@ rb_str_times(VALUE str, VALUE times) len *= RSTRING_LEN(str); termlen = TERM_LEN(str); - str2 = str_new0(rb_obj_class(str), 0, len, termlen); + str2 = str_new0(rb_cString, 0, len, termlen); ptr2 = RSTRING_PTR(str2); if (len) { n = RSTRING_LEN(str); @@ -2545,13 +2560,13 @@ rb_str_subseq(VALUE str, long beg, long len) if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(beg, len, RSTRING_LEN(str))) { long olen; - str2 = rb_str_new_shared(rb_str_new_frozen(str)); + str2 = rb_str_new_shared(rb_str_new_frozen_String(str)); RSTRING(str2)->as.heap.ptr += beg; olen = RSTRING(str2)->as.heap.len; if (olen > len) RSTRING(str2)->as.heap.len = len; } else { - str2 = rb_str_new_with_class(str, RSTRING_PTR(str)+beg, len); + str2 = rb_str_new(RSTRING_PTR(str)+beg, len); RB_GC_GUARD(str); } @@ -2664,14 +2679,14 @@ str_substr(VALUE str, long beg, long len, int empty) SHARABLE_SUBSTRING_P(p, len, RSTRING_END(str))) { long ofs = p - RSTRING_PTR(str); str2 = rb_str_new_frozen(str); - str2 = str_new_shared(rb_obj_class(str2), str2); + str2 = str_new_shared(rb_cString, str2); RSTRING(str2)->as.heap.ptr += ofs; RSTRING(str2)->as.heap.len = len; ENC_CODERANGE_CLEAR(str2); } else { if (!len && !empty) return Qnil; - str2 = rb_str_new_with_class(str, p, len); + str2 = rb_str_new(p, len); RB_GC_GUARD(str); } rb_enc_cr_str_copy_for_substr(str2, str); @@ -4215,7 +4230,7 @@ VALUE rb_str_succ(VALUE orig) { VALUE str; - str = rb_str_new_with_class(orig, RSTRING_PTR(orig), RSTRING_LEN(orig)); + str = rb_str_new(RSTRING_PTR(orig), RSTRING_LEN(orig)); rb_enc_cr_str_copy_for_substr(str, orig); return str_succ(str); } @@ -4444,7 +4459,7 @@ rb_str_upto_each(VALUE beg, VALUE end, int excl, int (*each)(VALUE, VALUE), VALU if (n > 0 || (excl && n == 0)) return beg; after_end = rb_funcallv(end, succ, 0, 0); - current = rb_str_dup(beg); + current = str_duplicate(rb_cString, beg); while (!rb_str_equal(current, after_end)) { VALUE next = Qnil; if (excl || !rb_str_equal(current, end)) @@ -4492,7 +4507,7 @@ rb_str_upto_endless_each(VALUE beg, int (*each)(VALUE, VALUE), VALUE arg) } } /* normal case */ - current = rb_str_dup(beg); + current = str_duplicate(rb_cString, beg); while (1) { VALUE next = rb_funcallv(current, succ, 0, 0); if ((*each)(current, arg)) break; @@ -4582,7 +4597,7 @@ rb_str_aref(VALUE str, VALUE indx) } else if (RB_TYPE_P(indx, T_STRING)) { if (rb_str_index(str, indx, 0) != -1) - return rb_str_dup(indx); + return str_duplicate(rb_cString, indx); return Qnil; } else { @@ -5005,7 +5020,7 @@ rb_str_slice_bang(int argc, VALUE *argv, VALUE str) beg = rb_str_index(str, indx, 0); if (beg == -1) return Qnil; len = RSTRING_LEN(indx); - result = rb_str_dup(indx); + result = str_duplicate(rb_cString, indx); goto squash; } else { @@ -5028,7 +5043,7 @@ rb_str_slice_bang(int argc, VALUE *argv, VALUE str) beg = p - RSTRING_PTR(str); subseq: - result = rb_str_new_with_class(str, RSTRING_PTR(str)+beg, len); + result = rb_str_new(RSTRING_PTR(str)+beg, len); rb_enc_cr_str_copy_for_substr(result, str); squash: @@ -5107,7 +5122,7 @@ rb_pat_search(VALUE pat, VALUE str, long pos, int set_backref_str) pos = rb_strseq_index(str, pat, pos, 1); if (set_backref_str) { if (pos >= 0) { - str = rb_str_new_frozen(str); + str = rb_str_new_frozen_String(str); rb_backref_set_string(str, pos, RSTRING_LEN(pat)); } else { @@ -5301,7 +5316,7 @@ rb_str_sub_bang(int argc, VALUE *argv, VALUE str) static VALUE rb_str_sub(int argc, VALUE *argv, VALUE str) { - str = rb_str_dup(str); + str = str_duplicate(rb_cString, str); rb_str_sub_bang(argc, argv, str); return str; } @@ -5341,7 +5356,7 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) beg = rb_pat_search(pat, str, 0, need_backref); if (beg < 0) { if (bang) return Qnil; /* no match, no substitution */ - return rb_str_dup(str); + return str_duplicate(rb_cString, str); } offset = 0; @@ -5422,7 +5437,6 @@ str_gsub(int argc, VALUE *argv, VALUE str, int bang) str_shared_replace(str, dest); } else { - RBASIC_SET_CLASS(dest, rb_obj_class(str)); str = dest; } @@ -5685,12 +5699,12 @@ str_byte_substr(VALUE str, long beg, long len, int empty) if (!STR_EMBEDDABLE_P(len, TERM_LEN(str)) && SHARABLE_SUBSTRING_P(beg, len, n)) { str2 = rb_str_new_frozen(str); - str2 = str_new_shared(rb_obj_class(str2), str2); + str2 = str_new_shared(rb_cString, str2); RSTRING(str2)->as.heap.ptr += beg; RSTRING(str2)->as.heap.len = len; } else { - str2 = rb_str_new_with_class(str, p, len); + str2 = rb_str_new(p, len); } str_enc_copy(str2, str); @@ -5792,9 +5806,9 @@ rb_str_reverse(VALUE str) char *s, *e, *p; int cr; - if (RSTRING_LEN(str) <= 1) return rb_str_dup(str); + if (RSTRING_LEN(str) <= 1) return str_duplicate(rb_cString, str); enc = STR_ENC_GET(str); - rev = rb_str_new_with_class(str, 0, RSTRING_LEN(str)); + rev = rb_str_new(0, RSTRING_LEN(str)); s = RSTRING_PTR(str); e = RSTRING_END(str); p = RSTRING_END(rev); cr = ENC_CODERANGE(str); @@ -6273,7 +6287,7 @@ rb_str_dump(VALUE str) len += clen; } - result = rb_str_new_with_class(str, 0, len); + result = rb_str_new(0, len); p = RSTRING_PTR(str); pend = p + RSTRING_LEN(str); q = RSTRING_PTR(result); qend = q + len + 1; @@ -6696,7 +6710,7 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc) size_t buffer_count = 0; int buffer_length_or_invalid; - if (RSTRING_LEN(source) == 0) return rb_str_dup(source); + if (RSTRING_LEN(source) == 0) return str_duplicate(rb_cString, source); source_current = (OnigUChar*)RSTRING_PTR(source); source_end = (OnigUChar*)RSTRING_END(source); @@ -6732,12 +6746,12 @@ rb_str_casemap(VALUE source, OnigCaseFoldType *flags, rb_encoding *enc) } if (buffer_count==1) { - target = rb_str_new_with_class(source, (const char*)current_buffer->space, target_length); + target = rb_str_new((const char*)current_buffer->space, target_length); } else { char *target_current; - target = rb_str_new_with_class(source, 0, target_length); + target = rb_str_new(0, target_length); target_current = RSTRING_PTR(target); current_buffer = DATA_PTR(buffer_anchor); while (current_buffer) { @@ -6870,12 +6884,12 @@ rb_str_upcase(int argc, VALUE *argv, VALUE str) flags = check_case_options(argc, argv, flags); enc = str_true_enc(str); if (case_option_single_p(flags, enc, str)) { - ret = rb_str_new_with_class(str, RSTRING_PTR(str), RSTRING_LEN(str)); + ret = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str)); str_enc_copy(ret, str); upcase_single(ret); } else if (flags&ONIGENC_CASE_ASCII_ONLY) { - ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str)); + ret = rb_str_new(0, RSTRING_LEN(str)); rb_str_ascii_casemap(str, ret, &flags, enc); } else { @@ -6998,12 +7012,12 @@ rb_str_downcase(int argc, VALUE *argv, VALUE str) flags = check_case_options(argc, argv, flags); enc = str_true_enc(str); if (case_option_single_p(flags, enc, str)) { - ret = rb_str_new_with_class(str, RSTRING_PTR(str), RSTRING_LEN(str)); + ret = rb_str_new(RSTRING_PTR(str), RSTRING_LEN(str)); str_enc_copy(ret, str); downcase_single(ret); } else if (flags&ONIGENC_CASE_ASCII_ONLY) { - ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str)); + ret = rb_str_new(0, RSTRING_LEN(str)); rb_str_ascii_casemap(str, ret, &flags, enc); } else { @@ -7078,7 +7092,7 @@ rb_str_capitalize(int argc, VALUE *argv, VALUE str) enc = str_true_enc(str); if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return str; if (flags&ONIGENC_CASE_ASCII_ONLY) { - ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str)); + ret = rb_str_new(0, RSTRING_LEN(str)); rb_str_ascii_casemap(str, ret, &flags, enc); } else { @@ -7142,9 +7156,9 @@ rb_str_swapcase(int argc, VALUE *argv, VALUE str) flags = check_case_options(argc, argv, flags); enc = str_true_enc(str); - if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return str; + if (RSTRING_LEN(str) == 0 || !RSTRING_PTR(str)) return str_duplicate(rb_cString, str); if (flags&ONIGENC_CASE_ASCII_ONLY) { - ret = rb_str_new_with_class(str, 0, RSTRING_LEN(str)); + ret = rb_str_new(0, RSTRING_LEN(str)); rb_str_ascii_casemap(str, ret, &flags, enc); } else { @@ -7518,7 +7532,7 @@ rb_str_tr_bang(VALUE str, VALUE src, VALUE repl) static VALUE rb_str_tr(VALUE str, VALUE src, VALUE repl) { - str = rb_str_dup(str); + str = str_duplicate(rb_cString, str); tr_trans(str, src, repl, 0); return str; } @@ -7697,7 +7711,7 @@ rb_str_delete_bang(int argc, VALUE *argv, VALUE str) static VALUE rb_str_delete(int argc, VALUE *argv, VALUE str) { - str = rb_str_dup(str); + str = str_duplicate(rb_cString, str); rb_str_delete_bang(argc, argv, str); return str; } @@ -7805,7 +7819,7 @@ rb_str_squeeze_bang(int argc, VALUE *argv, VALUE str) static VALUE rb_str_squeeze(int argc, VALUE *argv, VALUE str) { - str = rb_str_dup(str); + str = str_duplicate(rb_cString, str); rb_str_squeeze_bang(argc, argv, str); return str; } @@ -7842,7 +7856,7 @@ rb_str_tr_s_bang(VALUE str, VALUE src, VALUE repl) static VALUE rb_str_tr_s(VALUE str, VALUE src, VALUE repl) { - str = rb_str_dup(str); + str = str_duplicate(rb_cString, str); tr_trans(str, src, repl, 1); return str; } @@ -7986,12 +8000,12 @@ split_string(VALUE result, VALUE str, long beg, long len, long empty_count) /* make different substrings */ if (result) { do { - rb_ary_push(result, str_new_empty(str)); + rb_ary_push(result, str_new_empty_String(str)); } while (--empty_count > 0); } else { do { - rb_yield(str_new_empty(str)); + rb_yield(str_new_empty_String(str)); } while (--empty_count > 0); } } @@ -8106,11 +8120,11 @@ rb_str_split_m(int argc, VALUE *argv, VALUE str) if (lim <= 0) limit = Qnil; else if (lim == 1) { if (RSTRING_LEN(str) == 0) - return result ? rb_ary_new2(0) : str; - tmp = rb_str_dup(str); + return result ? rb_ary_new2(0) : str; + tmp = str_duplicate(rb_cString, str); if (!result) { rb_yield(tmp); - return str; + return str; } return rb_ary_new3(1, tmp); } @@ -9194,7 +9208,7 @@ static VALUE rb_str_chomp(int argc, VALUE *argv, VALUE str) { VALUE rs = chomp_rs(argc, argv); - if (NIL_P(rs)) return rb_str_dup(str); + if (NIL_P(rs)) return str_duplicate(rb_cString, str); return rb_str_subseq(str, 0, chompped_length(str, rs)); } @@ -9281,7 +9295,7 @@ rb_str_lstrip(VALUE str) long len, loffset; RSTRING_GETMEM(str, start, len); loffset = lstrip_offset(str, start, start+len, STR_ENC_GET(str)); - if (loffset <= 0) return rb_str_dup(str); + if (loffset <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, loffset, len - loffset); } @@ -9374,7 +9388,7 @@ rb_str_rstrip(VALUE str) RSTRING_GETMEM(str, start, olen); roffset = rstrip_offset(str, start, start+olen, enc); - if (roffset <= 0) return rb_str_dup(str); + if (roffset <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, 0, olen-roffset); } @@ -9447,7 +9461,7 @@ rb_str_strip(VALUE str) loffset = lstrip_offset(str, start, start+olen, enc); roffset = rstrip_offset(str, start+loffset, start+olen, enc); - if (loffset <= 0 && roffset <= 0) return rb_str_dup(str); + if (loffset <= 0 && roffset <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, loffset, olen-loffset-roffset); } @@ -9843,7 +9857,7 @@ rb_str_justify(int argc, VALUE *argv, VALUE str, char jflag) } } len = str_strlen(str, enc); /* rb_enc_check */ - if (width < 0 || len >= width) return rb_str_dup(str); + if (width < 0 || len >= width) return str_duplicate(rb_cString, str); n = width - len; llen = (jflag == 'l') ? 0 : ((jflag == 'r') ? n : n/2); rlen = n - llen; @@ -9859,7 +9873,7 @@ rb_str_justify(int argc, VALUE *argv, VALUE str, char jflag) rb_raise(rb_eArgError, "argument too big"); } len += size; - res = str_new0(rb_obj_class(str), 0, len, termlen); + res = str_new0(rb_cString, 0, len, termlen); p = RSTRING_PTR(res); if (flen <= 1) { memset(p, *f, llen); @@ -10006,7 +10020,7 @@ rb_str_partition(VALUE str, VALUE sep) RSTRING_LEN(str)-pos-RSTRING_LEN(sep))); failed: - return rb_ary_new3(3, rb_str_dup(str), str_new_empty(str), str_new_empty(str)); + return rb_ary_new3(3, str_duplicate(rb_cString, str), str_new_empty_String(str), str_new_empty_String(str)); } /* @@ -10054,7 +10068,7 @@ rb_str_rpartition(VALUE str, VALUE sep) rb_str_subseq(str, pos+RSTRING_LEN(sep), RSTRING_LEN(str)-pos-RSTRING_LEN(sep))); failed: - return rb_ary_new3(3, str_new_empty(str), str_new_empty(str), rb_str_dup(str)); + return rb_ary_new3(3, str_new_empty_String(str), str_new_empty_String(str), str_duplicate(rb_cString, str)); } /* @@ -10200,7 +10214,7 @@ rb_str_delete_prefix(VALUE str, VALUE prefix) long prefixlen; prefixlen = deleted_prefix_length(str, prefix); - if (prefixlen <= 0) return rb_str_dup(str); + if (prefixlen <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, prefixlen, RSTRING_LEN(str) - prefixlen); } @@ -10286,7 +10300,7 @@ rb_str_delete_suffix(VALUE str, VALUE suffix) long suffixlen; suffixlen = deleted_suffix_length(str, suffix); - if (suffixlen <= 0) return rb_str_dup(str); + if (suffixlen <= 0) return str_duplicate(rb_cString, str); return rb_str_subseq(str, 0, RSTRING_LEN(str) - suffixlen); } @@ -10417,7 +10431,7 @@ rb_str_ellipsize(VALUE str, long len) else if (len <= ellipsislen || !(e = rb_enc_step_back(p, e, e, len = ellipsislen, enc))) { if (rb_enc_asciicompat(enc)) { - ret = rb_str_new_with_class(str, ellipsis, len); + ret = rb_str_new(ellipsis, len); rb_enc_associate(ret, enc); } else { @@ -10742,7 +10756,7 @@ str_scrub(int argc, VALUE *argv, VALUE str) { VALUE repl = argc ? (rb_check_arity(argc, 0, 1), argv[0]) : Qnil; VALUE new = rb_str_scrub(str, repl); - return NIL_P(new) ? rb_str_dup(str): new; + return NIL_P(new) ? str_duplicate(rb_cString, str): new; } /* diff --git a/test/-ext-/string/test_cstr.rb b/test/-ext-/string/test_cstr.rb index 2083016db2..d909781700 100644 --- a/test/-ext-/string/test_cstr.rb +++ b/test/-ext-/string/test_cstr.rb @@ -13,13 +13,13 @@ class Test_StringCStr < Test::Unit::TestCase end def test_long - s = Bug::String.new("abcdef")*100000 + s = Bug::String.new(Bug::String.new("abcdef")*100000) s.cstr_unterm('x') assert_equal(0, s.cstr_term, Bug4319) end def test_shared - s = Bug::String.new("abcdef")*5 + s = Bug::String.new(Bug::String.new("abcdef")*5) s = s.unterminated_substring(0, 29) assert_equal(0, s.cstr_term, Bug4319) end @@ -28,7 +28,7 @@ class Test_StringCStr < Test::Unit::TestCase s0 = Bug::String.new("abcdefgh"*8) [4, 4*3-1, 8*3-1, 64].each do |n| - s = s0[0, n] + s = Bug::String.new(s0[0, n]) s.cstr_unterm('x') s.freeze assert_equal(0, s.cstr_term) @@ -67,7 +67,7 @@ class Test_StringCStr < Test::Unit::TestCase n = 100 len = str.size * n WCHARS.each do |enc| - s = Bug::String.new(str.encode(enc))*n + s = Bug::String.new(Bug::String.new(str.encode(enc))*n) s.cstr_unterm('x') assert_nothing_raised(ArgumentError, enc.name) {s.cstr_term} s.set_len(s.bytesize / 2) diff --git a/test/-ext-/string/test_ellipsize.rb b/test/-ext-/string/test_ellipsize.rb index d7947041d5..d340abd58a 100644 --- a/test/-ext-/string/test_ellipsize.rb +++ b/test/-ext-/string/test_ellipsize.rb @@ -10,7 +10,7 @@ class Test_StringEllipsize < Test::Unit::TestCase def assert_equal_with_class(expected, result, *rest) assert_equal(expected.encoding, result.encoding, *rest) assert_equal(expected, result, result.encoding.name) - assert_instance_of(Bug::String, result, *rest) + assert_instance_of(String, result, *rest) end def test_longer diff --git a/test/ruby/test_string.rb b/test/ruby/test_string.rb index 810c700c8c..4814872789 100644 --- a/test/ruby/test_string.rb +++ b/test/ruby/test_string.rb @@ -2089,6 +2089,8 @@ CODE def test_swapcase assert_equal(S("hi&LOW"), S("HI&low").swapcase) + s = S("") + assert_not_same(s, s.swapcase) end def test_swapcase! |