From e17ba51c037395136e4a2d88c3c01bb6164a2346 Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sat, 3 Apr 2021 11:35:13 +0200 Subject: [PATCH 1/3] Add return type restrictions to methods related to String --- src/char.cr | 66 +++++++-------- src/char/reader.cr | 8 +- src/int.cr | 2 +- src/levenshtein.cr | 2 +- src/regex.cr | 4 +- src/regex/match_data.cr | 38 ++++----- src/string.cr | 180 ++++++++++++++++++++-------------------- src/string/builder.cr | 8 +- src/string/formatter.cr | 6 +- src/string_pool.cr | 10 +-- src/string_scanner.cr | 26 +++--- src/symbol.cr | 2 +- src/unicode/unicode.cr | 20 ++--- 13 files changed, 186 insertions(+), 186 deletions(-) diff --git a/src/char.cr b/src/char.cr index 0e7414a31b64..cc1a47a4d380 100644 --- a/src/char.cr +++ b/src/char.cr @@ -59,7 +59,7 @@ struct Char # 'b' - 'a' # => 1 # 'c' - 'a' # => 2 # ``` - def -(other : Char) + def -(other : Char) : Int32 ord - other.ord end @@ -68,7 +68,7 @@ struct Char # ``` # 'f' + "oo" # => "foo" # ``` - def +(str : String) + def +(str : String) : String bytesize = str.bytesize + self.bytesize String.new(bytesize) do |buffer| count = 0 @@ -120,7 +120,7 @@ struct Char # Returns `true` if this char is an ASCII character # (codepoint is in (0..127)) - def ascii? + def ascii? : Bool ord < 128 end @@ -133,7 +133,7 @@ struct Char # 'z'.ascii_number? # => false # 'z'.ascii_number?(36) # => true # ``` - def ascii_number?(base : Int = 10) + def ascii_number?(base : Int = 10) : Bool !!to_i?(base) end @@ -143,7 +143,7 @@ struct Char # '1'.number? # => true # 'a'.number? # => false # ``` - def number? + def number? : Bool ascii? ? ascii_number? : Unicode.number?(self) end @@ -155,7 +155,7 @@ struct Char # 'G'.ascii_lowercase? # => false # '.'.ascii_lowercase? # => false # ``` - def ascii_lowercase? + def ascii_lowercase? : Bool 'a' <= self <= 'z' end @@ -167,7 +167,7 @@ struct Char # 'G'.lowercase? # => false # '.'.lowercase? # => false # ``` - def lowercase? + def lowercase? : Bool ascii? ? ascii_lowercase? : Unicode.lowercase?(self) end @@ -179,7 +179,7 @@ struct Char # 'c'.ascii_uppercase? # => false # '.'.ascii_uppercase? # => false # ``` - def ascii_uppercase? + def ascii_uppercase? : Bool 'A' <= self <= 'Z' end @@ -191,7 +191,7 @@ struct Char # 'c'.uppercase? # => false # '.'.uppercase? # => false # ``` - def uppercase? + def uppercase? : Bool ascii? ? ascii_uppercase? : Unicode.uppercase?(self) end @@ -202,7 +202,7 @@ struct Char # 'á'.ascii_letter? # => false # '8'.ascii_letter? # => false # ``` - def ascii_letter? + def ascii_letter? : Bool ascii_lowercase? || ascii_uppercase? end @@ -213,7 +213,7 @@ struct Char # 'á'.letter? # => true # '8'.letter? # => false # ``` - def letter? + def letter? : Bool ascii? ? ascii_letter? : Unicode.letter?(self) end @@ -224,7 +224,7 @@ struct Char # '8'.ascii_alphanumeric? # => true # '.'.ascii_alphanumeric? # => false # ``` - def ascii_alphanumeric? + def ascii_alphanumeric? : Bool ascii_letter? || ascii_number? end @@ -235,7 +235,7 @@ struct Char # '8'.alphanumeric? # => true # '.'.alphanumeric? # => false # ``` - def alphanumeric? + def alphanumeric? : Bool letter? || number? end @@ -246,7 +246,7 @@ struct Char # '\t'.ascii_whitespace? # => true # 'b'.ascii_whitespace? # => false # ``` - def ascii_whitespace? + def ascii_whitespace? : Bool self == ' ' || 9 <= ord <= 13 end @@ -257,7 +257,7 @@ struct Char # '\t'.whitespace? # => true # 'b'.whitespace? # => false # ``` - def whitespace? + def whitespace? : Bool ascii? ? ascii_whitespace? : Unicode.whitespace?(self) end @@ -269,7 +269,7 @@ struct Char # 'F'.hex? # => true # 'g'.hex? # => false # ``` - def hex? + def hex? : Bool ascii_number? 16 end @@ -300,7 +300,7 @@ struct Char # '\\'.in_set? "\\A" # => false # '\\'.in_set? "X-\\w" # => true # ``` - def in_set?(*sets : String) + def in_set?(*sets : String) : Bool if sets.size > 1 return sets.all? { |set| in_set?(set) } end @@ -366,7 +366,7 @@ struct Char # 'x'.downcase # => 'x' # '.'.downcase # => '.' # ``` - def downcase(options = Unicode::CaseOptions::None) + def downcase(options = Unicode::CaseOptions::None) : Char Unicode.downcase(self, options) end @@ -393,7 +393,7 @@ struct Char # 'X'.upcase # => 'X' # '.'.upcase # => '.' # ``` - def upcase(options = Unicode::CaseOptions::None) + def upcase(options = Unicode::CaseOptions::None) : Char Unicode.upcase(self, options) end @@ -424,7 +424,7 @@ struct Char # ``` # # This method allows creating a `Range` of chars. - def succ + def succ : Char (ord + 1).chr end @@ -434,7 +434,7 @@ struct Char # 'b'.pred # => 'a' # 'ぃ'.pred # => 'あ' # ``` - def pred + def pred : Char (ord - 1).chr end @@ -449,17 +449,17 @@ struct Char # char.control? # => true # end # ``` - def ascii_control? + def ascii_control? : Bool ord < 0x20 || (0x7F <= ord <= 0x9F) end # Returns `true` if this char is a control character according to unicode. - def control? + def control? : Bool ascii? ? ascii_control? : Unicode.control?(self) end # Returns `true` if this is char is a mark character according to unicode. - def mark? + def mark? : Bool Unicode.mark?(self) end @@ -499,7 +499,7 @@ struct Char # 'あ'.dump # => "'\\u{3042}'" # '\u0012'.dump # => "'\\u{12}'" # ``` - def dump + def dump : String dump_or_inspect do |io| if ascii_control? || ord >= 0x80 io << "\\u{" @@ -615,7 +615,7 @@ struct Char # '8'.to_f # => 8.0 # 'c'.to_f # raises ArgumentError # ``` - def to_f + def to_f : Float64 to_f64 end @@ -627,27 +627,27 @@ struct Char # '8'.to_f? # => 8.0 # 'c'.to_f? # => nil # ``` - def to_f? + def to_f? : Float64? to_f64? end # See also: `to_f`. - def to_f32 + def to_f32 : Float32 to_i.to_f32 end # See also: `to_f?`. - def to_f32? + def to_f32? : Float32? to_i?.try &.to_f32 end # Same as `to_f`. - def to_f64 + def to_f64 : Float64 to_i.to_f64 end # Same as `to_f?`. - def to_f64? + def to_f64? : Float64? to_i?.try &.to_f64 end @@ -710,7 +710,7 @@ struct Char # 'a'.bytesize # => 1 # '好'.bytesize # => 3 # ``` - def bytesize + def bytesize : Int32 # See http://en.wikipedia.org/wiki/UTF-8#Sample_code c = ord @@ -737,7 +737,7 @@ struct Char # 'a'.bytes # => [97] # 'あ'.bytes # => [227, 129, 130] # ``` - def bytes + def bytes : Array(UInt8) bytes = [] of UInt8 each_byte do |byte| bytes << byte diff --git a/src/char/reader.cr b/src/char/reader.cr index edc0450b2bf3..237ea89a57e2 100644 --- a/src/char/reader.cr +++ b/src/char/reader.cr @@ -82,7 +82,7 @@ struct Char # reader.has_next? # => true # reader.peek_next_char # => '\0' # ``` - def has_next? + def has_next? : Bool !@end end @@ -94,7 +94,7 @@ struct Char # reader = Char::Reader.new("ab") # reader.next_char # => 'b' # ``` - def next_char + def next_char : Char @pos += @current_char_width if @pos > @string.bytesize raise IndexError.new @@ -113,7 +113,7 @@ struct Char # reader.peek_next_char # => 'b' # reader.current_char # => 'a' # ``` - def peek_next_char + def peek_next_char : Char next_pos = @pos + @current_char_width if next_pos > @string.bytesize @@ -127,7 +127,7 @@ struct Char # Returns `true` if there are characters before # the current one. - def has_previous? + def has_previous? : Bool @pos > 0 end diff --git a/src/int.cr b/src/int.cr index 3e3781370fae..a34f1499b167 100644 --- a/src/int.cr +++ b/src/int.cr @@ -67,7 +67,7 @@ struct Int # ``` # 97.chr # => 'a' # ``` - def chr + def chr : Char unless 0 <= self <= Char::MAX_CODEPOINT raise ArgumentError.new("#{self} out of char range") end diff --git a/src/levenshtein.cr b/src/levenshtein.cr index 6137cbe49690..528af411f696 100644 --- a/src/levenshtein.cr +++ b/src/levenshtein.cr @@ -82,7 +82,7 @@ module Levenshtein end end - def best_match + def best_match : String? @best_entry.try &.value end diff --git a/src/regex.cr b/src/regex.cr index d02742adb961..3231ecebd2c0 100644 --- a/src/regex.cr +++ b/src/regex.cr @@ -368,7 +368,7 @@ class Regex # re.match("Skiing") # => Regex::MatchData("Skiing") # re.match("sledding") # => Regex::MatchData("sledding") # ``` - def +(other) + def +(other) : Regex Regex.union(self, other) end @@ -421,7 +421,7 @@ class Regex # /at/ =~ "input data" # => 7 # /ax/ =~ "input data" # => nil # ``` - def =~(other : String) + def =~(other : String) : Int32? match = self.match(other) $~ = match match.try &.begin(0) diff --git a/src/regex/match_data.cr b/src/regex/match_data.cr index c49cd9f33ddf..039674aec55c 100644 --- a/src/regex/match_data.cr +++ b/src/regex/match_data.cr @@ -50,7 +50,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil!.size # => 2 # "Crystal".match(/r(ys)(?ta)/).not_nil!.size # => 3 # ``` - def size + def size : Int32 group_size + 1 end @@ -64,7 +64,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil!.begin(1) # => 2 # "クリスタル".match(/リ(ス)/).not_nil!.begin(0) # => 1 # ``` - def begin(n = 0) + def begin(n = 0) : Int32? @string.byte_index_to_char_index byte_begin(n) end @@ -78,7 +78,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil!.end(1) # => 4 # "クリスタル".match(/リ(ス)/).not_nil!.end(0) # => 3 # ``` - def end(n = 0) + def end(n = 0) : Int32? @string.byte_index_to_char_index byte_end(n) end @@ -92,7 +92,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil!.byte_begin(1) # => 2 # "クリスタル".match(/リ(ス)/).not_nil!.byte_begin(0) # => 3 # ``` - def byte_begin(n = 0) + def byte_begin(n = 0) : Int32 check_index_out_of_bounds n n += size if n < 0 @ovector[n * 2] @@ -108,7 +108,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil!.byte_end(1) # => 4 # "クリスタル".match(/リ(ス)/).not_nil!.byte_end(0) # => 9 # ``` - def byte_end(n = 0) + def byte_end(n = 0) : Int32 check_index_out_of_bounds n n += size if n < 0 @ovector[n * 2 + 1] @@ -124,7 +124,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil![1]? # => "ys" # "Crystal".match(/r(ys)/).not_nil![2]? # => nil # ``` - def []?(n : Int) + def []?(n : Int) : String? return unless valid_group?(n) n += size if n < 0 @@ -141,7 +141,7 @@ class Regex # "Crystal".match(/r(ys)/).not_nil![1] # => "ys" # "Crystal".match(/r(ys)/).not_nil![2] # raises IndexError # ``` - def [](n : Int) + def [](n : Int) : String check_index_out_of_bounds n n += size if n < 0 @@ -164,7 +164,7 @@ class Regex # ``` # "Crystal".match(/(?Cr).*(?al)/).not_nil!["ok"]? # => "al" # ``` - def []?(group_name : String) + def []?(group_name : String) : String? max_start = -1 match = nil named_capture_number(group_name) do |n| @@ -191,7 +191,7 @@ class Regex # ``` # "Crystal".match(/(?Cr).*(?al)/).not_nil!["ok"] # => "al" # ``` - def [](group_name : String) + def [](group_name : String) : String match = self[group_name]? unless match named_capture_number(group_name) do @@ -203,23 +203,23 @@ class Regex end # Returns all matches that are within the given range. - def [](range : Range) + def [](range : Range) : Array(String) self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new] end # Like `#[Range]`, but returns `nil` if the range's start is out of range. - def []?(range : Range) + def []?(range : Range) : Array(String)? self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new]? end # Returns count or less (if there aren't enough) matches starting at the # given start index. - def [](start : Int, count : Int) + def [](start : Int, count : Int) : Array(String) self[start, count]? || raise IndexError.new end # Like `#[Int, Int]` but returns `nil` if the *start* index is out of range. - def []?(start : Int, count : Int) + def []?(start : Int, count : Int) : Array(String)? raise ArgumentError.new "Negative count: #{count}" if count < 0 return Array(String).new if start == size @@ -253,7 +253,7 @@ class Regex # ``` # "Crystal".match(/yst/).not_nil!.pre_match # => "Cr" # ``` - def pre_match + def pre_match : String @string.byte_slice(0, byte_begin(0)) end @@ -263,7 +263,7 @@ class Regex # ``` # "Crystal".match(/yst/).not_nil!.post_match # => "al" # ``` - def post_match + def post_match : String @string.byte_slice(byte_end(0)) end @@ -280,7 +280,7 @@ class Regex # match = "Crystal".match(/(Cr)(stal)?/).not_nil! # match.captures # => ["Cr", nil] # ``` - def captures + def captures : Array(String?) name_table = @regex.name_table caps = [] of String? @@ -302,7 +302,7 @@ class Regex # match = "Crystal".match(/(?Cr)(?stal)?/).not_nil! # match.named_captures # => {"name1" => "Cr", "name2" => nil} # ``` - def named_captures + def named_captures : Hash(String, String?) name_table = @regex.name_table caps = {} of String => String? @@ -326,7 +326,7 @@ class Regex # match = "Crystal".match(/(Cr)(?stal)?/).not_nil! # match.to_a # => ["Cr", "Cr", nil] # ``` - def to_a + def to_a : Array(String?) (0...size).map { |i| self[i]? } end @@ -341,7 +341,7 @@ class Regex # match = "Crystal".match(/(Cr)(?stal)?/).not_nil! # match.to_h # => {0 => "Cr", 1 => "Cr", "name1" => nil} # ``` - def to_h + def to_h : Hash(Int32 | String, String?) name_table = @regex.name_table hash = {} of (String | Int32) => String? diff --git a/src/string.cr b/src/string.cr index a8e30781cbd3..415473c88e2a 100644 --- a/src/string.cr +++ b/src/string.cr @@ -285,7 +285,7 @@ class String # "hello".bytesize # => 5 # "你好".bytesize # => 6 # ``` - def bytesize + def bytesize : Int32 @bytesize end @@ -657,7 +657,7 @@ class String end # :ditto: - def to_f64(whitespace : Bool = true, strict : Bool = true) + def to_f64(whitespace : Bool = true, strict : Bool = true) : Float64 to_f64?(whitespace: whitespace, strict: strict) || raise ArgumentError.new("Invalid Float64: #{self}") end @@ -682,7 +682,7 @@ class String end # :ditto: - def to_f64?(whitespace : Bool = true, strict : Bool = true) + def to_f64?(whitespace : Bool = true, strict : Bool = true) : Float64? to_f_impl(whitespace: whitespace, strict: strict) do v = LibC.strtod self, out endptr {v, endptr} @@ -763,7 +763,7 @@ class String # "hello"[-2] # => 'l' # "hello"[5] # raises IndexError # ``` - def [](index : Int) + def [](index : Int) : Char char_at(index) { raise IndexError.new } end @@ -780,7 +780,7 @@ class String # "hello"[1...-1] # => "ell" # "hello"[6..7] # raises IndexError # ``` - def [](range : Range) + def [](range : Range) : String self[*Indexable.range_to_index_and_count(range, size) || raise IndexError.new] end @@ -790,7 +790,7 @@ class String # "hello"[6..7]? # => nil # "hello"[6..]? # => nil # ``` - def []?(range : Range) + def []?(range : Range) : String? self[*Indexable.range_to_index_and_count(range, size) || return nil]? end @@ -802,12 +802,12 @@ class String # Raises `IndexError` if the *start* index is out of bounds. # # Raises `ArgumentError` if *count* is negative. - def [](start : Int, count : Int) + def [](start : Int, count : Int) : String self[start, count]? || raise IndexError.new end # Like `#[Int, Int]` but returns `nil` if the *start* index is out of bounds. - def []?(start : Int, count : Int) + def []?(start : Int, count : Int) : String? raise ArgumentError.new "Negative count: #{count}" if count < 0 return byte_slice?(start, count) if single_byte_optimizable? @@ -830,7 +830,7 @@ class String end end - def []?(index : Int) + def []?(index : Int) : Char? char_at(index) { nil } end @@ -838,11 +838,11 @@ class String includes?(str) ? str : nil end - def []?(regex : Regex) + def []?(regex : Regex) : String? self[regex, 0]? end - def []?(regex : Regex, group) + def []?(regex : Regex, group) : String? match = match(regex) match[group]? if match end @@ -851,11 +851,11 @@ class String self[str]?.not_nil! end - def [](regex : Regex) + def [](regex : Regex) : String self[regex]?.not_nil! end - def [](regex : Regex, group) + def [](regex : Regex, group) : String self[regex, group]?.not_nil! end @@ -922,7 +922,7 @@ class String # ``` # # Raises `IndexError` if any index is outside the bounds of this string. - def delete_at(range : Range) + def delete_at(range : Range) : String delete_at(*Indexable.range_to_index_and_count(range, size) || raise IndexError.new) end @@ -1400,7 +1400,7 @@ class String # "string".chomp # => "string" # "x".chomp.chomp # => "x" # ``` - def chomp + def chomp : String return self if empty? case to_unsafe[bytesize - 1] @@ -1424,7 +1424,7 @@ class String # "hello".chomp('o') # => "hell" # "hello".chomp('a') # => "hello" # ``` - def chomp(suffix : Char) + def chomp(suffix : Char) : String if suffix == '\n' chomp elsif ends_with?(suffix) @@ -1441,7 +1441,7 @@ class String # "hello".chomp("llo") # => "he" # "hello".chomp("ol") # => "hello" # ``` - def chomp(suffix : String) + def chomp(suffix : String) : String if suffix.bytesize == 1 chomp(suffix.to_unsafe[0].unsafe_chr) elsif ends_with?(suffix) @@ -1661,7 +1661,7 @@ class String # "abcd".insert(5, 'X') # raises IndexError # "abcd".insert(-6, 'X') # raises IndexError # ``` - def insert(index : Int, other : Char) + def insert(index : Int, other : Char) : String index = index.to_i index += size + 1 if index < 0 @@ -1692,7 +1692,7 @@ class String # "abcd".insert(5, "FOO") # raises IndexError # "abcd".insert(-6, "FOO") # raises IndexError # ``` - def insert(index : Int, other : String) + def insert(index : Int, other : String) : String index = index.to_i index += size + 1 if index < 0 @@ -1722,7 +1722,7 @@ class String # " hello ".strip # => "hello" # "\tgoodbye\r\n".strip # => "goodbye" # ``` - def strip + def strip : String excess_left = calc_excess_left if excess_left == bytesize return "" @@ -1737,7 +1737,7 @@ class String # ``` # "aaabcdaaa".strip('a') # => "bcd" # ``` - def strip(char : Char) + def strip(char : Char) : String return self if empty? excess_left = calc_excess_left(char) @@ -1756,7 +1756,7 @@ class String # ``` # "abcdefcba".strip("abc") # => "def" # ``` - def strip(chars : String) + def strip(chars : String) : String return self if empty? case chars.size @@ -1799,7 +1799,7 @@ class String # " hello ".rstrip # => " hello" # "\tgoodbye\r\n".rstrip # => "\tgoodbye" # ``` - def rstrip + def rstrip : String remove_excess_right(calc_excess_right) end @@ -1808,7 +1808,7 @@ class String # ``` # "aaabcdaaa".rstrip('a') # => "aaabcd" # ``` - def rstrip(char : Char) + def rstrip(char : Char) : String return self if empty? remove_excess_right(calc_excess_right(char)) @@ -1821,7 +1821,7 @@ class String # ``` # "abcdefcba".rstrip("abc") # => "abcdef" # ``` - def rstrip(chars : String) + def rstrip(chars : String) : String return self if empty? case chars.size @@ -1853,7 +1853,7 @@ class String # " hello ".lstrip # => "hello " # "\tgoodbye\r\n".lstrip # => "goodbye\r\n" # ``` - def lstrip + def lstrip : String remove_excess_left(calc_excess_left) end @@ -1862,7 +1862,7 @@ class String # ``` # "aaabcdaaa".lstrip('a') # => "bcdaaa" # ``` - def lstrip(char : Char) + def lstrip(char : Char) : String return self if empty? remove_excess_left(calc_excess_left(char)) @@ -1875,7 +1875,7 @@ class String # ``` # "bcadefcba".lstrip("abc") # => "defcba" # ``` - def lstrip(chars : String) + def lstrip(chars : String) : String return self if empty? case chars.size @@ -2013,7 +2013,7 @@ class String # "aabbcc".tr("abc", "x") # => "xxxxxx" # "aabbcc".tr("a", "xyz") # => "xxbbcc" # ``` - def tr(from : String, to : String) + def tr(from : String, to : String) : String return delete(from) if to.empty? if from.bytesize == 1 @@ -2083,7 +2083,7 @@ class String # "hello".sub('l', "lo") # => "helolo" # "hello world".sub('o', 'a') # => "hella world" # ``` - def sub(char : Char, replacement) + def sub(char : Char, replacement) : String if includes?(char) String.build(bytesize) do |buffer| reader = Char::Reader.new(self) @@ -2155,7 +2155,7 @@ class String # # Raises `IndexError` if a named group referenced in *replacement* is not present # in *pattern*. - def sub(pattern : Regex, replacement, backreferences = true) + def sub(pattern : Regex, replacement, backreferences = true) : String if backreferences && replacement.is_a?(String) && replacement.has_back_references? sub_append(pattern) { |_, match, buffer| scan_backreferences(replacement, match, buffer) } else @@ -2171,7 +2171,7 @@ class String # "hello".sub(/(he|l|o)/, {"he": "ha", "l": "la"}) # => "hallo" # "hello".sub(/(he|l|o)/, {"l": "la"}) # => "hello" # ``` - def sub(pattern : Regex, hash : Hash(String, _) | NamedTuple) + def sub(pattern : Regex, hash : Hash(String, _) | NamedTuple) : String sub(pattern) do |match| if hash.has_key?(match) hash[match] @@ -2187,7 +2187,7 @@ class String # ``` # "hello yellow".sub("ll", "dd") # => "heddo yellow" # ``` - def sub(string : String, replacement) + def sub(string : String, replacement) : String sub(string) { replacement } end @@ -2214,7 +2214,7 @@ class String # ``` # "hello".sub({'a' => 'b', 'l' => 'd'}) # => "hedlo" # ``` - def sub(hash : Hash(Char, _)) + def sub(hash : Hash(Char, _)) : String return self if empty? String.build(bytesize) do |buffer| @@ -2257,7 +2257,7 @@ class String # ``` # "hello".sub(1, 'a') # => "hallo" # ``` - def sub(index : Int, replacement : Char) + def sub(index : Int, replacement : Char) : String sub_index(index.to_i, replacement) do |buffer| replacement.each_byte do |byte| buffer.value = byte @@ -2273,7 +2273,7 @@ class String # ``` # "hello".sub(1, "eee") # => "heeello" # ``` - def sub(index : Int, replacement : String) + def sub(index : Int, replacement : String) : String sub_index(index.to_i, replacement) do |buffer| buffer.copy_from(replacement.to_unsafe, replacement.bytesize) buffer += replacement.bytesize @@ -2307,7 +2307,7 @@ class String # ``` # "hello".sub(1..2, 'a') # => "halo" # ``` - def sub(range : Range, replacement : Char) + def sub(range : Range, replacement : Char) : String sub_range(range, replacement) do |buffer, from_index, to_index| replacement.each_byte do |byte| buffer.value = byte @@ -2323,7 +2323,7 @@ class String # ``` # "hello".sub(1..2, "eee") # => "heeelo" # ``` - def sub(range : Range, replacement : String) + def sub(range : Range, replacement : String) : String sub_range(range, replacement) do |buffer| buffer.copy_from(replacement.to_unsafe, replacement.bytesize) buffer += replacement.bytesize @@ -2433,7 +2433,7 @@ class String # "hello".gsub('l', "lo") # => "heloloo" # "hello world".gsub('o', 'a') # => "hella warld" # ``` - def gsub(char : Char, replacement) + def gsub(char : Char, replacement) : String if replacement.is_a?(String) && replacement.bytesize == 1 return gsub(char, replacement.unsafe_byte_at(0).unsafe_chr) end @@ -2513,7 +2513,7 @@ class String # # Raises `IndexError` if a named group referenced in *replacement* is not present # in *pattern*. - def gsub(pattern : Regex, replacement, backreferences = true) + def gsub(pattern : Regex, replacement, backreferences = true) : String if backreferences && replacement.is_a?(String) && replacement.has_back_references? gsub_append(pattern) { |_, match, buffer| scan_backreferences(replacement, match, buffer) } else @@ -2531,7 +2531,7 @@ class String # # but "o" is not and so is not included # "hello".gsub(/(he|l|o)/, {"he": "ha", "l": "la"}) # => "halala" # ``` - def gsub(pattern : Regex, hash : Hash(String, _) | NamedTuple) + def gsub(pattern : Regex, hash : Hash(String, _) | NamedTuple) : String gsub(pattern) do |match| hash[match]? end @@ -2543,7 +2543,7 @@ class String # ``` # "hello yellow".gsub("ll", "dd") # => "heddo yeddow" # ``` - def gsub(string : String, replacement) + def gsub(string : String, replacement) : String if string.bytesize == 1 gsub(string.unsafe_byte_at(0).unsafe_chr, replacement) else @@ -2592,7 +2592,7 @@ class String # ``` # "hello".gsub({'e' => 'a', 'l' => 'd'}) # => "haddo" # ``` - def gsub(hash : Hash(Char, _)) + def gsub(hash : Hash(Char, _)) : String gsub do |char| hash[char]? || char end @@ -2604,7 +2604,7 @@ class String # ``` # "hello".gsub({e: 'a', l: 'd'}) # => "haddo" # ``` - def gsub(tuple : NamedTuple) + def gsub(tuple : NamedTuple) : String gsub do |char| tuple[char.to_s]? || char end @@ -2662,14 +2662,14 @@ class String # ``` # "aabbcc".count('a') # => 2 # ``` - def count(other : Char) + def count(other : Char) : Int32 count { |char| char == other } end # Sets should be a list of strings following the rules # described at `Char#in_set?`. Returns the number of characters # in this string that match the given set. - def count(*sets) + def count(*sets) : Int32 count { |char| char.in_set?(*sets) } end @@ -2693,7 +2693,7 @@ class String # ``` # "aabbcc".delete('b') # => "aacc" # ``` - def delete(char : Char) + def delete(char : Char) : String delete { |my_char| my_char == char } end @@ -2704,7 +2704,7 @@ class String # ``` # "aabbccdd".delete("a-c") # => "dd" # ``` - def delete(*sets) + def delete(*sets) : String delete { |char| char.in_set?(*sets) } end @@ -2732,7 +2732,7 @@ class String # ``` # "a bbb".squeeze(' ') # => "a bbb" # ``` - def squeeze(char : Char) + def squeeze(char : Char) : String squeeze { |my_char| char == my_char } end @@ -2747,7 +2747,7 @@ class String # "aaabbbcccddd".squeeze("b-d") # => "aaabcd" # "a bbb".squeeze # => "a b" # ``` - def squeeze(*sets : String) + def squeeze(*sets : String) : String squeeze { |char| char.in_set?(*sets) } end @@ -2757,12 +2757,12 @@ class String # ``` # "a bbb".squeeze # => "a b" # ``` - def squeeze + def squeeze : String squeeze { true } end # Returns `true` if this is the empty string, `""`. - def empty? + def empty? : Bool bytesize == 0 end @@ -2773,7 +2773,7 @@ class String # " ".blank? # => true # " a ".blank? # => false # ``` - def blank? + def blank? : Bool each_char do |char| return false unless char.whitespace? end @@ -2912,7 +2912,7 @@ class String # # "Haystack" =~ 45 # => nil # ``` - def =~(regex : Regex) + def =~(regex : Regex) : Int32? match = regex.match(self) $~ = match match.try &.begin(0) @@ -2929,7 +2929,7 @@ class String # "abc" + "def" # => "abcdef" # "abc" + 'd' # => "abcd" # ``` - def +(other : self) + def +(other : self) : String return self if other.empty? return other if self.empty? @@ -2947,7 +2947,7 @@ class String end # :ditto: - def +(char : Char) + def +(char : Char) : String bytes, count = String.char_bytes_and_bytesize(char) size = bytesize + count String.new(size) do |buffer| @@ -2968,7 +2968,7 @@ class String # "Developers! " * 4 # # => "Developers! Developers! Developers! Developers! " # ``` - def *(times : Int) + def *(times : Int) : String raise ArgumentError.new "Negative argument" if times < 0 if times == 0 || bytesize == 0 @@ -3021,7 +3021,7 @@ class String # "Hello, World".index(/[ ]+/) # => 6 # "Hello, World".index(/\d+/) # => nil # ``` - def index(search : Char, offset = 0) + def index(search : Char, offset = 0) : Int32? # If it's ASCII we can delegate to slice if search.ascii? && single_byte_optimizable? return to_slice.index(search.ord.to_u8, offset) @@ -3040,7 +3040,7 @@ class String end # :ditto: - def index(search : String, offset = 0) + def index(search : String, offset = 0) : Int32? offset += size if offset < 0 return if offset < 0 @@ -3099,7 +3099,7 @@ class String end # :ditto: - def index(search : Regex, offset = 0) + def index(search : Regex, offset = 0) : Int32? offset += size if offset < 0 return nil unless 0 <= offset <= size @@ -3116,7 +3116,7 @@ class String # "Hello, World".rindex("o", 5) # => 4 # "Hello, World".rindex("W", 2) # => nil # ``` - def rindex(search : Char, offset = size - 1) + def rindex(search : Char, offset = size - 1) : Int32? # If it's ASCII we can delegate to slice if search.ascii? && single_byte_optimizable? return to_slice.rindex(search.ord.to_u8, offset) @@ -3146,7 +3146,7 @@ class String end # :ditto: - def rindex(search : String, offset = size - search.size) + def rindex(search : String, offset = size - search.size) : Int32? offset += size if offset < 0 return if offset < 0 @@ -3199,7 +3199,7 @@ class String end # :ditto: - def rindex(search : Regex, offset = size) + def rindex(search : Regex, offset = size) : Int32? offset += size if offset < 0 return nil unless 0 <= offset <= size @@ -3421,7 +3421,7 @@ class String # # It is valid to pass `#bytesize` to *index*, and in this case the answer # will be the size of this string. - def byte_index_to_char_index(index) + def byte_index_to_char_index(index) : Int32? if single_byte_optimizable? return 0 <= index <= bytesize ? index : nil end @@ -3439,7 +3439,7 @@ class String # "Team".includes?('i') # => false # "Dysfunctional".includes?("fun") # => true # ``` - def includes?(search : Char | String) + def includes?(search : Char | String) : Bool !!index(search) end @@ -3458,7 +3458,7 @@ class String # old_pond.split # => ["Old", "pond", "a", "frog", "leaps", "in", "water's", "sound"] # old_pond.split(3) # => ["Old", "pond", "a frog leaps in\n water's sound\n"] # ``` - def split(limit : Int32? = nil) + def split(limit : Int32? = nil) : Array(String) ary = Array(String).new split(limit) do |string| ary << string @@ -3855,7 +3855,7 @@ class String end end - def lines(chomp = true) + def lines(chomp = true) : Array(String) lines = [] of String each_line(chomp: chomp) do |line| lines << line @@ -4042,7 +4042,7 @@ class String # "Argentina".reverse # => "anitnegrA" # "racecar".reverse # => "racecar" # ``` - def reverse + def reverse : String return self if bytesize <= 1 if single_byte_optimizable? @@ -4075,7 +4075,7 @@ class String # "Purple".ljust(8, '-') # => "Purple--" # "Aubergine".ljust(8) # => "Aubergine" # ``` - def ljust(len : Int, char : Char = ' ') + def ljust(len : Int, char : Char = ' ') : String just len, char, -1 end @@ -4099,7 +4099,7 @@ class String # "Purple".rjust(8, '-') # => "--Purple" # "Aubergine".rjust(8) # => "Aubergine" # ``` - def rjust(len : Int, char : Char = ' ') + def rjust(len : Int, char : Char = ' ') : String just len, char, 1 end @@ -4124,7 +4124,7 @@ class String # "Purple".center(9, '-') # => "-Purple--" # "Aubergine".center(8) # => "Aubergine" # ``` - def center(len : Int, char : Char = ' ') + def center(len : Int, char : Char = ' ') : String just len, char, 0 end @@ -4214,7 +4214,7 @@ class String # "ZZZ9999".succ # => "AAAA0000" # "***".succ # => "**+" # ``` - def succ + def succ : String return self if empty? chars = self.chars @@ -4310,7 +4310,7 @@ class String # Searches the string for instances of *pattern*, # returning an `Array` of `Regex::MatchData` for each match. - def scan(pattern : Regex) + def scan(pattern : Regex) : Array(Regex::MatchData) matches = [] of Regex::MatchData scan(pattern) do |match| matches << match @@ -4332,7 +4332,7 @@ class String # Searches the string for instances of *pattern*, # returning an array of the matched string for each match. - def scan(pattern : String) + def scan(pattern : String) : Array(String) matches = [] of String scan(pattern) do |match| matches << match @@ -4397,7 +4397,7 @@ class String # ``` # "ab☃".chars # => ['a', 'b', '☃'] # ``` - def chars + def chars : Array(Char) chars = Array(Char).new(@length > 0 ? @length : bytesize) each_char do |char| chars << char @@ -4443,7 +4443,7 @@ class String # ``` # # See also: `Char#ord`. - def codepoints + def codepoints : Array(Int32) codepoints = Array(Int32).new(@length > 0 ? @length : bytesize) each_codepoint do |codepoint| codepoints << codepoint @@ -4487,7 +4487,7 @@ class String # "hello".bytes # => [104, 101, 108, 108, 111] # "你好".bytes # => [228, 189, 160, 229, 165, 189] # ``` - def bytes + def bytes : Array(UInt8) Array.new(bytesize) { |i| to_unsafe[i] } end @@ -4767,7 +4767,7 @@ class String # "sum: %{one} + %{two} = %{three}" % {one: 1, two: 2, three: 1 + 2} # => "sum: 1 + 2 = 3" # "I have %s apples" % {apples: 4} # => "I have 4 apples" # ``` - def %(other) + def %(other) : String sprintf self, other end @@ -4782,7 +4782,7 @@ class String # "hello".size # => 5 # "你好".size # => 2 # ``` - def size + def size : Int32 if @length > 0 || @bytesize == 0 return @length end @@ -4797,7 +4797,7 @@ class String # "hello".ascii_only? # => true # "你好".ascii_only? # => false # ``` - def ascii_only? + def ascii_only? : Bool if @bytesize == size each_byte do |byte| return false unless byte < 0x80 @@ -4809,13 +4809,13 @@ class String end # :nodoc: - def single_byte_optimizable? + def single_byte_optimizable? : Bool @bytesize == size end # Returns `true` if this String is encoded correctly # according to the UTF-8 encoding. - def valid_encoding? + def valid_encoding? : Bool reader = Char::Reader.new(self) while reader.has_next? return false if reader.error @@ -4913,7 +4913,7 @@ class String end # :nodoc: - def size_known? + def size_known? : Bool @bytesize == 0 || @length > 0 end @@ -4999,7 +4999,7 @@ class String # Raises an `ArgumentError` if `self` has null bytes. Returns `self` otherwise. # # This method should sometimes be called before passing a `String` to a C function. - def check_no_null_byte(name = nil) + def check_no_null_byte(name = nil) : String if byte_index(0) name = "`#{name}` " if name raise ArgumentError.new("String #{name}contains null byte") @@ -5091,7 +5091,7 @@ class String # In this case the implementation just returns the same string. # # NOTE: there should never be a need to call this method instead of using string interpolation. - def self.interpolation(value : String) + def self.interpolation(value : String) : String value end @@ -5107,7 +5107,7 @@ class String # In this case the implementation just returns the result of calling `value.to_s`. # # NOTE: there should never be a need to call this method instead of using string interpolation. - def self.interpolation(value) + def self.interpolation(value) : String value.to_s end @@ -5123,7 +5123,7 @@ class String # In this case the implementation just does `value + char`. # # NOTE: there should never be a need to call this method instead of using string interpolation. - def self.interpolation(value : String, char : Char) + def self.interpolation(value : String, char : Char) : String value + char end @@ -5139,7 +5139,7 @@ class String # In this case the implementation just does `char + value`. # # NOTE: there should never be a need to call this method instead of using string interpolation. - def self.interpolation(char : Char, value : String) + def self.interpolation(char : Char, value : String) : String char + value end @@ -5157,7 +5157,7 @@ class String # it's a bit more performant than interpolating non-string values. # # NOTE: there should never be a need to call this method instead of using string interpolation. - def self.interpolation(*values : String) + def self.interpolation(*values : String) : String bytesize = values.sum(&.bytesize) size = if values.all?(&.size_known?) values.sum(&.size) @@ -5186,7 +5186,7 @@ class String # In this case the implementation will call `String.build` with the given values. # # NOTE: there should never be a need to call this method instead of using string interpolation. - def self.interpolation(*values : *T) forall T + def self.interpolation(*values : *T) : String forall T capacity = 0 {% for i in 0...T.size %} value{{i}} = values[{{i}}] diff --git a/src/string/builder.cr b/src/string/builder.cr index eb187535420a..15863632426e 100644 --- a/src/string/builder.cr +++ b/src/string/builder.cr @@ -64,17 +64,17 @@ class String::Builder < IO nil end - def buffer + def buffer : UInt8* @buffer + String::HEADER_SIZE end - def empty? + def empty? : Bool @bytesize == 0 end # Chomps the last byte from the string buffer. # If the byte is `'\n'` and there's a `'\r'` before it, it is also removed. - def chomp!(byte : UInt8) + def chomp!(byte : UInt8) : self if bytesize > 0 && buffer[bytesize - 1] == byte back(1) @@ -87,7 +87,7 @@ class String::Builder < IO # Moves the write pointer, and the resulting string bytesize, # by the given *amount*. - def back(amount : Int) + def back(amount : Int) : Int32 unless 0 <= amount <= @bytesize raise ArgumentError.new "Invalid back amount" end diff --git a/src/string/formatter.cr b/src/string/formatter.cr index 39cf6250f7ce..dbc00e828e9e 100644 --- a/src/string/formatter.cr +++ b/src/string/formatter.cr @@ -386,15 +386,15 @@ struct String::Formatter(A) @precision_size = 0 end - def left_padding? + def left_padding? : Bool @minus ? @width < 0 : @width > 0 end - def right_padding? + def right_padding? : Bool @minus ? @width > 0 : @width < 0 end - def padding_char + def padding_char : Char @zero ? '0' : ' ' end end diff --git a/src/string_pool.cr b/src/string_pool.cr index e02a707e59f0..16785d6272de 100644 --- a/src/string_pool.cr +++ b/src/string_pool.cr @@ -60,7 +60,7 @@ class StringPool # pool.get("crystal") # pool.empty? # => false # ``` - def empty? + def empty? : Bool @size == 0 end @@ -79,7 +79,7 @@ class StringPool # pool.get(slice) # pool.empty? # => false # ``` - def get(slice : Bytes) + def get(slice : Bytes) : String get slice.to_unsafe, slice.size end @@ -95,7 +95,7 @@ class StringPool # pool.get("hey".to_unsafe, 3) # pool.size # => 1 # ``` - def get(str : UInt8*, len) + def get(str : UInt8*, len) : String hash = hash(str, len) get(hash, str, len) end @@ -150,7 +150,7 @@ class StringPool # pool.get(io) # pool.empty? # => false # ``` - def get(str : IO::Memory) + def get(str : IO::Memory) : String get(str.buffer, str.bytesize) end @@ -168,7 +168,7 @@ class StringPool # pool.get(string) # pool.empty? # => false # ``` - def get(str : String) + def get(str : String) : String get(str.to_unsafe, str.bytesize) end diff --git a/src/string_scanner.cr b/src/string_scanner.cr index 5e9c42338ee7..b79725a9c354 100644 --- a/src/string_scanner.cr +++ b/src/string_scanner.cr @@ -72,7 +72,7 @@ class StringScanner end # Returns the current position of the scan offset. - def offset + def offset : Int32 @str.byte_index_to_char_index(@byte_offset).not_nil! end @@ -89,7 +89,7 @@ class StringScanner # s.scan(/\s\w+/) # => " string" # s.scan(/.*/) # => "" # ``` - def scan(pattern) + def scan(pattern) : String? match(pattern, advance: true, options: Regex::Options::ANCHORED) end @@ -105,7 +105,7 @@ class StringScanner # s.scan_until(/tr/) # => nil # s.scan_until(/g/) # => "ing" # ``` - def scan_until(pattern) + def scan_until(pattern) : String? match(pattern, advance: true, options: Regex::Options::None) end @@ -132,7 +132,7 @@ class StringScanner # # This method is the same as `#scan`, but without returning the matched # string. - def skip(pattern) + def skip(pattern) : Int32? match = scan(pattern) match.size if match end @@ -148,7 +148,7 @@ class StringScanner # # This method is the same as `#scan_until`, but without returning the matched # string. - def skip_until(pattern) + def skip_until(pattern) : Int32? match = scan_until(pattern) match.size if match end @@ -164,7 +164,7 @@ class StringScanner # s.check(/\w+/) # => "is" # s.check(/\w+/) # => "is" # ``` - def check(pattern) + def check(pattern) : String? match(pattern, advance: false, options: Regex::Options::ANCHORED) end @@ -178,7 +178,7 @@ class StringScanner # s.check_until(/tr/) # => "test str" # s.check_until(/g/) # => "test string" # ``` - def check_until(pattern) + def check_until(pattern) : String? match(pattern, advance: false, options: Regex::Options::None) end @@ -200,7 +200,7 @@ class StringScanner # s["month"] # => "Dec" # s["day"] # => "12" # ``` - def [](n) + def [](n) : String @last_match.not_nil![n] end @@ -226,7 +226,7 @@ class StringScanner # s.scan(/more/) # => nil # s[0]? # => nil # ``` - def []?(n) + def []?(n) : String? @last_match.try(&.[n]?) end @@ -240,7 +240,7 @@ class StringScanner # s.scan(/(\w+\s?){4}/) # => "this is a string" # s.eos? # => true # ``` - def eos? + def eos? : Bool @byte_offset >= @str.bytesize end @@ -257,13 +257,13 @@ class StringScanner end # Returns the string being scanned. - def string + def string : String @str end # Extracts a string corresponding to string[offset,*len*], without advancing # the scan offset. - def peek(len) + def peek(len) : String @str[offset, len] end @@ -276,7 +276,7 @@ class StringScanner # s.scan(/(\w+\s?){2}/) # => "this is " # s.rest # => "a string" # ``` - def rest + def rest : String @str.byte_slice(@byte_offset, @str.bytesize - @byte_offset) end diff --git a/src/symbol.cr b/src/symbol.cr index 4e5d5959bf84..34d2bf159358 100644 --- a/src/symbol.cr +++ b/src/symbol.cr @@ -76,7 +76,7 @@ struct Symbol # :nodoc: # Determines if a string needs to be quoted to be used for an external # parameter name or a named argument's key. - def self.needs_quotes_for_named_argument?(string) + def self.needs_quotes_for_named_argument?(string) : Bool case string when "", "_" true diff --git a/src/unicode/unicode.cr b/src/unicode/unicode.cr index aac38e0c4cf8..deb4377b0d2d 100644 --- a/src/unicode/unicode.cr +++ b/src/unicode/unicode.cr @@ -21,7 +21,7 @@ module Unicode end # :nodoc: - def self.upcase(char : Char, options : CaseOptions) + def self.upcase(char : Char, options : CaseOptions) : Char result = check_upcase_ascii(char, options) return result if result @@ -88,7 +88,7 @@ module Unicode end # :nodoc: - def self.downcase(char : Char, options : CaseOptions) + def self.downcase(char : Char, options : CaseOptions) : Char result = check_downcase_ascii(char, options) return result if result @@ -175,37 +175,37 @@ module Unicode end # :nodoc: - def self.lowercase?(char : Char) + def self.lowercase?(char : Char) : Bool in_category?(char.ord, category_Ll) end # :nodoc: - def self.uppercase?(char : Char) + def self.uppercase?(char : Char) : Bool in_category?(char.ord, category_Lu) end # :nodoc: - def self.letter?(char : Char) + def self.letter?(char : Char) : Bool in_any_category?(char.ord, category_Lu, category_Ll, category_Lt) end # :nodoc: - def self.number?(char : Char) + def self.number?(char : Char) : Bool in_any_category?(char.ord, category_Nd, category_Nl, category_No) end # :nodoc: - def self.control?(char : Char) + def self.control?(char : Char) : Bool in_any_category?(char.ord, category_Cs, category_Co, category_Cn, category_Cf, category_Cc) end # :nodoc: - def self.whitespace?(char : Char) + def self.whitespace?(char : Char) : Bool in_any_category?(char.ord, category_Zs, category_Zl, category_Zp) end # :nodoc: - def self.mark?(char : Char) + def self.mark?(char : Char) : Bool in_any_category?(char.ord, category_Mn, category_Me, category_Mc) end @@ -236,7 +236,7 @@ module Unicode end end - private def self.in_any_category?(needle, *haystacks) + private def self.in_any_category?(needle, *haystacks) : Bool haystacks.any? { |haystack| in_category?(needle, haystack) } end end From 1bc314b24b1cfb6222801a0dc178841ab52f4b7f Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Sun, 11 Apr 2021 02:29:15 +0200 Subject: [PATCH 2/3] More --- src/levenshtein.cr | 2 +- src/regex.cr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/levenshtein.cr b/src/levenshtein.cr index 528af411f696..deefe6b7d933 100644 --- a/src/levenshtein.cr +++ b/src/levenshtein.cr @@ -92,7 +92,7 @@ module Levenshtein sn.best_match end - def self.find(name, all_names, tolerance = nil) + def self.find(name, all_names, tolerance = nil) : String? find(name, tolerance) do |similar| all_names.each do |a_name| similar.test(a_name) diff --git a/src/regex.cr b/src/regex.cr index 3231ecebd2c0..cc4d13e5e852 100644 --- a/src/regex.cr +++ b/src/regex.cr @@ -267,7 +267,7 @@ class Regex # Regex.error?("(foo|bar)") # => nil # Regex.error?("(foo|bar") # => "missing ) at 8" # ``` - def self.error?(source) + def self.error?(source) : String? re = LibPCRE.compile(source, (Options::UTF_8 | Options::NO_UTF8_CHECK | Options::DUPNAMES), out errptr, out erroffset, nil) if re nil From 3fb210ca0a07ec9ffa8f1d2d8f2a9bbf9d690aae Mon Sep 17 00:00:00 2001 From: Oleh Prypin Date: Mon, 12 Apr 2021 20:05:59 +0200 Subject: [PATCH 3/3] Style --- src/string/builder.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/string/builder.cr b/src/string/builder.cr index 15863632426e..a07f7ae74473 100644 --- a/src/string/builder.cr +++ b/src/string/builder.cr @@ -64,7 +64,7 @@ class String::Builder < IO nil end - def buffer : UInt8* + def buffer : Pointer(UInt8) @buffer + String::HEADER_SIZE end