class RuboCop::Cop::Performance::StringReplacement

This cop identifies places where `gsub` can be replaced by `tr` or `delete`.

@example

@bad
'abc'.gsub('b', 'd')
'abc'.gsub('a', '')
'abc'.gsub(/a/, 'd')
'abc'.gsub!('a', 'd')

@good
'abc'.gsub(/.*/, 'a')
'abc'.gsub(/a+/, 'd')
'abc'.tr('b', 'd')
'a b c'.delete(' ')

Constants

BANG
DELETE
DETERMINISTIC_REGEX
DETERMINISTIC_TYPES
GSUB_METHODS
MSG
REGEXP_CONSTRUCTOR_METHODS
SINGLE_QUOTE
TR

Public Instance Methods

autocorrect(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 58
def autocorrect(node)
  _string, method, first_param, second_param = *node
  first_source, = first_source(first_param)
  second_source, = *second_param

  if regex?(first_param)
    first_source = interpret_string_escapes(first_source)
  end

  replacement_method = replacement_method(method,
                                          first_source,
                                          second_source)

  lambda do |corrector|
    corrector.replace(node.loc.selector, replacement_method)
    unless first_param.str_type?
      corrector.replace(first_param.source_range,
                        to_string_literal(first_source))
    end

    if second_source.empty? && first_source.length == 1
      remove_second_param(corrector, node, first_param)
    end
  end
end
on_send(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 32
def on_send(node)
  _string, method, first_param, second_param = *node
  return unless GSUB_METHODS.include?(method)
  return unless string?(second_param)
  return unless DETERMINISTIC_TYPES.include?(first_param.type)

  first_source, options = first_source(first_param)
  second_source, = *second_param

  return if first_source.nil?

  if regex?(first_param)
    return unless first_source =~ DETERMINISTIC_REGEX
    return if options
    # This must be done after checking DETERMINISTIC_REGEX
    # Otherwise things like \s will trip us up
    first_source = interpret_string_escapes(first_source)
  end

  return if first_source.length != 1
  return unless second_source.length <= 1

  message = message(method, first_source, second_source)
  add_offense(node, range(node), message)
end

Private Instance Methods

bang_method?(method) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 162
def bang_method?(method)
  method.to_s.end_with?(BANG)
end
extract_source(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 102
def extract_source(node)
  case node.type
  when :regexp
    source_from_regex_literal(node)
  when :send
    source_from_regex_constructor(node)
  end
end
first_source(first_param) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 90
def first_source(first_param)
  case first_param.type
  when :regexp, :send
    return nil unless regex?(first_param)
    source, options = extract_source(first_param)
  when :str
    source, = *first_param
  end

  [source, options]
end
message(method, first_source, second_source) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 154
def message(method, first_source, second_source)
  replacement_method = replacement_method(method,
                                          first_source,
                                          second_source)

  format(MSG, replacement_method, method)
end
method_suffix(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 166
def method_suffix(node)
  node.loc.end ? node.loc.end.source : ''
end
range(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 138
def range(node)
  Parser::Source::Range.new(node.source_range.source_buffer,
                            node.loc.selector.begin_pos,
                            node.source_range.end_pos)
end
regex?(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 129
def regex?(node)
  return true if node.regexp_type?

  const, init, = *node
  _, klass = *const

  klass == :Regexp && REGEXP_CONSTRUCTOR_METHODS.include?(init)
end
remove_second_param(corrector, node, first_param) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 170
def remove_second_param(corrector, node, first_param)
  end_range =
    Parser::Source::Range.new(node.source_range.source_buffer,
                              first_param.source_range.end_pos,
                              node.source_range.end_pos)

  corrector.replace(end_range, method_suffix(node))
end
replacement_method(method, first_source, second_source) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 144
def replacement_method(method, first_source, second_source)
  replacement = if second_source.empty? && first_source.length == 1
                  DELETE
                else
                  TR
                end

  "#{replacement}#{BANG if bang_method?(method)}"
end
source_from_regex_constructor(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 118
def source_from_regex_constructor(node)
  _const, _init, regex = *node
  case regex.type
  when :regexp
    source_from_regex_literal(regex)
  when :str
    source, = *regex
    source
  end
end
source_from_regex_literal(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 111
def source_from_regex_literal(node)
  regex, options = *node
  source, = *regex
  options, = *options
  [source, options]
end
string?(node) click to toggle source
# File lib/rubocop/cop/performance/string_replacement.rb, line 86
def string?(node)
  node && node.str_type?
end