123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Performance::RegexpMatch

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, TargetRubyVersion, AutoCorrector, Base
Instance Chain:
self, Base
Inherits: Base
  • Object
Defined in: lib/rubocop/cop/performance/regexp_match.rb

Overview

In Ruby 2.4, String#match?, Regexp#match?, and Symbol#match? have been added. The methods are faster than match. Because the methods avoid creating a MatchData object or saving backref. So, when MatchData is not used, use match? instead of match.

Examples:

# bad
def foo
  if x =~ /re/
    do_something
  end
end

# bad
def foo
  if x !~ /re/
    do_something
  end
end

# bad
def foo
  if x.match(/re/)
    do_something
  end
end

# bad
def foo
  if /re/ === x
    do_something
  end
end

# good
def foo
  if x.match?(/re/)
    do_something
  end
end

# good
def foo
  if !x.match?(/re/)
    do_something
  end
end

# good
def foo
  if x =~ /re/
    do_something(Regexp.last_match)
  end
end

# good
def foo
  if x.match(/re/)
    do_something($~)
  end
end

# good
def foo
  if /re/ === x
    do_something($~)
  end
end

Constant Summary

Class Method Summary

Instance Method Summary

Class Method Details

.autocorrect_incompatible_with

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 134

def self.autocorrect_incompatible_with
  [ConstantRegexp]
end

Instance Method Details

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 165

def autocorrect(corrector, node)
  if match_method?(node) || match_with_int_arg_method?(node)
    corrector.replace(node.loc.selector, 'match?')
  elsif match_operator?(node) || match_threequals?(node)
    recv, oper, arg = *node
    correct_operator(corrector, recv, arg, oper)
  elsif match_with_lvasgn?(node)
    recv, arg = *node
    correct_operator(corrector, recv, arg)
  end
end

#check_condition(cond) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 154

def check_condition(cond)
  match_node?(cond) do
    return if last_match_used?(cond)

    message = message(cond)
    add_offense(cond, message: message) do |corrector|
      autocorrect(corrector, cond)
    end
  end
end

#correct_operator(corrector, recv, arg, oper = nil) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 250

def correct_operator(corrector, recv, arg, oper = nil)
  op_range = recv.source_range.end.join(arg.source_range.begin)

  replace_with_match_predicate_method(corrector, recv, arg, op_range)

  corrector.insert_after(arg, ')') unless op_range.source.end_with?('(')
  corrector.insert_before(recv, '!') if oper == :!~
end

#find_last_match(body, range, scope_root) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 221

def find_last_match(body, range, scope_root)
  last_matches(body).find do |ref|
    ref_pos = ref.source_range.begin_pos
    range.cover?(ref_pos) && scope_root(ref) == scope_root
  end
end

#last_match_used?(match_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 181

def last_match_used?(match_node)
  scope_root = scope_root(match_node)
  body = scope_root ? scope_body(scope_root) : match_node.ancestors.last

  range = range_to_search_for_last_matches(match_node, body, scope_root)

  find_last_match(body, range, scope_root)
end

#match_gvar?(sym) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 246

def match_gvar?(sym)
  %i[$~ $MATCH $PREMATCH $POSTMATCH $LAST_PAREN_MATCH $LAST_MATCH_INFO].include?(sym)
end

#match_with_lvasgn?(node) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 105

def match_with_lvasgn?(node)
  return false unless node.match_with_lvasgn_type?

  regexp, _rhs = *node
  regexp.to_regexp.named_captures.empty?
end

#message(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 177

def message(node)
  format(MSG, current: node.loc.selector.source)
end

#modifier_form?(match_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 217

def modifier_form?(match_node)
  match_node.parent.if_type? && match_node.parent.modifier_form?
end

#next_match_pos(body, match_node_pos, scope_root) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 203

def next_match_pos(body, match_node_pos, scope_root)
  node = search_match_nodes(body).find do |match|
    begin_pos = if modifier_form?(match)
                  match.parent.if_branch.source_range.begin_pos
                else
                  match.source_range.begin_pos
                end

    begin_pos > match_node_pos && scope_root(match) == scope_root
  end

  node ? node.source_range.begin_pos : Float::INFINITY
end

#on_case(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 142

def on_case(node)
  return if node.condition

  node.each_when do |when_node|
    when_node.each_condition do |condition|
      check_condition(condition)
    end
  end
end

#on_if(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 138

def on_if(node)
  check_condition(node.condition)
end

#range_to_search_for_last_matches(match_node, body, scope_root) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 190

def range_to_search_for_last_matches(match_node, body, scope_root)
  expression = if modifier_form?(match_node)
                 match_node.parent.if_branch.source_range
               else
                 match_node.source_range
               end

  match_node_pos = expression.begin_pos
  next_match_pos = next_match_pos(body, match_node_pos, scope_root)

  match_node_pos..next_match_pos
end

#replace_with_match_predicate_method(corrector, recv, arg, op_range) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 259

def replace_with_match_predicate_method(corrector, recv, arg, op_range)
  if TYPES_IMPLEMENTING_MATCH.include?(recv.type)
    corrector.replace(op_range, '.match?(')
  elsif TYPES_IMPLEMENTING_MATCH.include?(arg.type)
    corrector.replace(op_range, '.match?(')
    swap_receiver_and_arg(corrector, recv, arg)
  else
    corrector.replace(op_range, '&.match?(')
  end
end

#scope_body(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 228

def scope_body(node)
  children = node.children
  case node.type
  when :module
    children[1]
  when :defs
    children[3]
  else
    children[2]
  end
end

#scope_root(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 240

def scope_root(node)
  node.each_ancestor.find do |ancestor|
    ancestor.def_type? || ancestor.defs_type? || ancestor.class_type? || ancestor.module_type?
  end
end

#swap_receiver_and_arg(corrector, recv, arg) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/regexp_match.rb', line 270

def swap_receiver_and_arg(corrector, recv, arg)
  corrector.replace(recv, arg.source)
  corrector.replace(arg, recv.source)
end