Fix String#[]= to allow assignment to zeroth element of an empty string in 1.9 mode #1604

Closed
wants to merge 2 commits into
from
View
100 kernel/common/string.rb
@@ -268,106 +268,6 @@ def [](index, other = undefined)
end
alias_method :slice, :[]
- # call-seq:
- # str[fixnum] = fixnum
- # str[fixnum] = new_str
- # str[fixnum, fixnum] = new_str
- # str[range] = aString
- # str[regexp] = new_str
- # str[regexp, fixnum] = new_str
- # str[other_str] = new_str
- #
- # Element Assignment --- Replaces some or all of the content of <i>self</i>. The
- # portion of the string affected is determined using the same criteria as
- # <code>String#[]</code>. If the replacement string is not the same length as
- # the text it is replacing, the string will be adjusted accordingly. If the
- # regular expression or string is used as the index doesn't match a position
- # in the string, <code>IndexError</code> is raised. If the regular expression
- # form is used, the optional second <code>Fixnum</code> allows you to specify
- # which portion of the match to replace (effectively using the
- # <code>MatchData</code> indexing rules. The forms that take a
- # <code>Fixnum</code> will raise an <code>IndexError</code> if the value is
- # out of range; the <code>Range</code> form will raise a
- # <code>RangeError</code>, and the <code>Regexp</code> and <code>String</code>
- # forms will silently ignore the assignment.
- def []=(index, replacement, three=undefined)
- unless three.equal?(undefined)
- if index.kind_of? Regexp
- subpattern_set index,
- Rubinius::Type.coerce_to(replacement, Integer, :to_int),
- three
- else
- start = Rubinius::Type.coerce_to(index, Integer, :to_int)
- fin = Rubinius::Type.coerce_to(replacement, Integer, :to_int)
-
- splice! start, fin, three
- end
-
- return three
- end
-
- case index
- when Fixnum
- # Handle this first because it's the most common.
- # This is duplicated from the else branch, but don't dry it up.
- if index < 0
- index += @num_bytes
- if index < 0 or index >= @num_bytes
- raise IndexError, "index #{index} out of string"
- end
- else
- raise IndexError, "index #{index} out of string" if index >= @num_bytes
- end
-
- if replacement.kind_of?(Fixnum)
- modify!
- @data[index] = replacement
- else
- splice! index, 1, replacement
- end
- when Regexp
- subpattern_set index, 0, replacement
- when String
- unless start = self.index(index)
- raise IndexError, "string not matched"
- end
-
- splice! start, index.length, replacement
- when Range
- start = Rubinius::Type.coerce_to(index.first, Integer, :to_int)
- length = Rubinius::Type.coerce_to(index.last, Integer, :to_int)
-
- start += @num_bytes if start < 0
-
- return nil if start < 0 || start > @num_bytes
-
- length = @num_bytes if length > @num_bytes
- length += @num_bytes if length < 0
- length += 1 unless index.exclude_end?
-
- length = length - start
- length = 0 if length < 0
-
- splice! start, length, replacement
- else
- index = Rubinius::Type.coerce_to(index, Integer, :to_int)
- raise IndexError, "index #{index} out of string" if @num_bytes <= index
-
- if index < 0
- raise IndexError, "index #{index} out of string" if -index > @num_bytes
- index += @num_bytes
- end
-
- if replacement.kind_of?(Fixnum)
- modify!
- @data[index] = replacement
- else
- splice! index, 1, replacement
- end
- end
- return replacement
- end
-
# Returns a copy of <i>self</i> with the first character converted to uppercase
# and the remainder to lowercase.
# Note: case conversion is effective only in ASCII region.
View
101 kernel/common/string18.rb
@@ -876,4 +876,105 @@ def gsub!(pattern, replacement=undefined)
return nil
end
end
+
+ # call-seq:
+ # str[fixnum] = fixnum
+ # str[fixnum] = new_str
+ # str[fixnum, fixnum] = new_str
+ # str[range] = aString
+ # str[regexp] = new_str
+ # str[regexp, fixnum] = new_str
+ # str[other_str] = new_str
+ #
+ # Element Assignment --- Replaces some or all of the content of <i>self</i>. The
+ # portion of the string affected is determined using the same criteria as
+ # <code>String#[]</code>. If the replacement string is not the same length as
+ # the text it is replacing, the string will be adjusted accordingly. If the
+ # regular expression or string is used as the index doesn't match a position
+ # in the string, <code>IndexError</code> is raised. If the regular expression
+ # form is used, the optional second <code>Fixnum</code> allows you to specify
+ # which portion of the match to replace (effectively using the
+ # <code>MatchData</code> indexing rules. The forms that take a
+ # <code>Fixnum</code> will raise an <code>IndexError</code> if the value is
+ # out of range; the <code>Range</code> form will raise a
+ # <code>RangeError</code>, and the <code>Regexp</code> and <code>String</code>
+ # forms will silently ignore the assignment.
+ def []=(index, replacement, three=undefined)
+ unless three.equal?(undefined)
+ if index.kind_of? Regexp
+ subpattern_set index,
+ Rubinius::Type.coerce_to(replacement, Integer, :to_int),
+ three
+ else
+ start = Rubinius::Type.coerce_to(index, Integer, :to_int)
+ fin = Rubinius::Type.coerce_to(replacement, Integer, :to_int)
+
+ splice! start, fin, three
+ end
+
+ return three
+ end
+
+ case index
+ when Fixnum
+ # Handle this first because it's the most common.
+ # This is duplicated from the else branch, but don't dry it up.
+ if index < 0
+ index += @num_bytes
+ if index < 0 or index >= @num_bytes
+ raise IndexError, "index #{index} out of string"
+ end
+ else
+ raise IndexError, "index #{index} out of string" if index >= @num_bytes
+ end
+
+ if replacement.kind_of?(Fixnum)
+ modify!
+ @data[index] = replacement
+ else
+ splice! index, 1, replacement
+ end
+ when Regexp
+ subpattern_set index, 0, replacement
+ when String
+ unless start = self.index(index)
+ raise IndexError, "string not matched"
+ end
+
+ splice! start, index.length, replacement
+ when Range
+ start = Rubinius::Type.coerce_to(index.first, Integer, :to_int)
+ length = Rubinius::Type.coerce_to(index.last, Integer, :to_int)
+
+ start += @num_bytes if start < 0
+
+ return nil if start < 0 || start > @num_bytes
+
+ length = @num_bytes if length > @num_bytes
+ length += @num_bytes if length < 0
+ length += 1 unless index.exclude_end?
+
+ length = length - start
+ length = 0 if length < 0
+
+ splice! start, length, replacement
+ else
+ index = Rubinius::Type.coerce_to(index, Integer, :to_int)
+ raise IndexError, "index #{index} out of string" if @num_bytes <= index
+
+ if index < 0
+ raise IndexError, "index #{index} out of string" if -index > @num_bytes
+ index += @num_bytes
+ end
+
+ if replacement.kind_of?(Fixnum)
+ modify!
+ @data[index] = replacement
+ else
+ splice! index, 1, replacement
+ end
+ end
+
+ return replacement
+ end
end
View
107 kernel/common/string19.rb
@@ -987,4 +987,111 @@ def gsub!(pattern, replacement=undefined)
replace(ret)
return self
end
+
+ # call-seq:
+ # str[fixnum] = fixnum
+ # str[fixnum] = new_str
+ # str[fixnum, fixnum] = new_str
+ # str[range] = aString
+ # str[regexp] = new_str
+ # str[regexp, fixnum] = new_str
+ # str[other_str] = new_str
+ #
+ # Element Assignment --- Replaces some or all of the content of <i>self</i>. The
+ # portion of the string affected is determined using the same criteria as
+ # <code>String#[]</code>. If the replacement string is not the same length as
+ # the text it is replacing, the string will be adjusted accordingly. If the
+ # regular expression or string is used as the index doesn't match a position
+ # in the string, <code>IndexError</code> is raised. If the regular expression
+ # form is used, the optional second <code>Fixnum</code> allows you to specify
+ # which portion of the match to replace (effectively using the
+ # <code>MatchData</code> indexing rules. The forms that take a
+ # <code>Fixnum</code> will raise an <code>IndexError</code> if the value is
+ # out of range; the <code>Range</code> form will raise a
+ # <code>RangeError</code>, and the <code>Regexp</code> and <code>String</code>
+ # forms will silently ignore the assignment.
+ def []=(index, replacement, three=undefined)
+ unless three.equal?(undefined)
+ if index.kind_of? Regexp
+ subpattern_set index,
+ Rubinius::Type.coerce_to(replacement, Integer, :to_int),
+ three
+ else
+ start = Rubinius::Type.coerce_to(index, Integer, :to_int)
+ fin = Rubinius::Type.coerce_to(replacement, Integer, :to_int)
+
+ splice! start, fin, three
+ end
+
+ return three
+ end
+
+ case index
+ when Fixnum
+ # Handle this first because it's the most common.
+ # This is duplicated from the else branch, but don't dry it up.
+
+ if index < 0
+ raise IndexError, "index #{index} out of string" if -index > @num_bytes
+ index += @num_bytes
+ else
+ # Ruby 1.9 allows assignment to the zeroth element of an empty string.
+ if @num_bytes > 0 && index >= @num_bytes
+ raise IndexError, "index #{index} out of string"
+ end
+ end
+
+ if replacement.kind_of?(Fixnum)
+ modify!
+ @data[index] = replacement
+ else
+ splice! index, 1, replacement
+ end
+ when Regexp
+ subpattern_set index, 0, replacement
+ when String
+ unless start = self.index(index)
+ raise IndexError, "string not matched"
+ end
+
+ splice! start, index.length, replacement
+ when Range
+ start = Rubinius::Type.coerce_to(index.first, Integer, :to_int)
+ length = Rubinius::Type.coerce_to(index.last, Integer, :to_int)
+
+ start += @num_bytes if start < 0
+
+ return nil if start < 0 || start > @num_bytes
+
+ length = @num_bytes if length > @num_bytes
+ length += @num_bytes if length < 0
+ length += 1 unless index.exclude_end?
+
+ length = length - start
+ length = 0 if length < 0
+
+ splice! start, length, replacement
+ else
+ index = Rubinius::Type.coerce_to(index, Integer, :to_int)
+
+ if index < 0
+ raise IndexError, "index #{index} out of string" if -index > @num_bytes
+ index += @num_bytes
+ else
+ # Ruby 1.9 allows assignment to the zeroth element of an empty string.
+ if @num_bytes > 0 && index >= @num_bytes
+ raise IndexError, "index #{index} out of string"
+ end
+ end
+
+ if replacement.kind_of?(Fixnum)
+ modify!
+ @data[index] = replacement
+ else
+ splice! index, 1, replacement
+ end
+ end
+
+ return replacement
+ end
end
View
4 spec/ruby/core/string/element_set_spec.rb
@@ -129,6 +129,10 @@
str = ""
str[0] = "bam"
str.should == "bam"
+
+ str = ""
+ str[0.0] = "bam"
+ str.should == "bam"
end
end
View
1 spec/tags/19/ruby/core/string/element_set_tags.txt
@@ -1 +0,0 @@
-fails:String#[]= with String allows assignment to the zero'th element of an empty String