123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Performance::RedundantEqualityComparisonBlock

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/redundant_equality_comparison_block.rb

Overview

Checks for uses Enumerable#all?, Enumerable#any?, Enumerable#one?, and Enumerable#none? are compared with === or similar methods in block.

By default, Object#=== behaves the same as Object#==, but this behavior is appropriately overridden in subclass. For example, Range#=== returns true when argument is within the range.

This cop has AllowRegexpMatch option and it is true by default because regexp.match?('string') often used in block changes to the opposite result:

[/pattern/].all? { |regexp| regexp.match?('pattern') } # => true
[/pattern/].all? { |regexp| regexp =~ 'pattern' }      # => true
[/pattern/].all?('pattern')                            # => false

Examples:

# bad
items.all? { |item| pattern === item }
items.all? { |item| item == other }
items.all? { |item| item.is_a?(Klass) }
items.all? { |item| item.kind_of?(Klass) }

# good
items.all?(pattern)
items.all?(Klass)

AllowRegexpMatch: true (default)

# good
items.all? { |item| item =~ pattern }
items.all? { |item| item.match?(pattern) }

AllowRegexpMatch: false

# bad
items.all? { |item| item =~ pattern }
items.all? { |item| item.match?(pattern) }

Cop Safety Information:

  • This cop is unsafe because === and == do not always behave the same.

Constant Summary

Instance Attribute Summary

Instance Method Summary

Instance Attribute Details

#allow_regexp_match?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 131

def allow_regexp_match?
  cop_config.fetch('AllowRegexpMatch', true)
end

Instance Method Details

#new_argument(block_argument, block_body) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 104

def new_argument(block_argument, block_body)
  if block_argument.source == block_body.receiver.source
    rhs = block_body.first_argument
    return if use_block_argument_in_method_argument_of_operand?(block_argument, rhs)

    rhs.source
  elsif block_argument.source == block_body.first_argument.source
    lhs = block_body.receiver
    return if use_block_argument_in_method_argument_of_operand?(block_argument, lhs)

    lhs.source
  end
end

#offense_range(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 127

def offense_range(node)
  node.send_node.loc.selector.join(node.source_range.end)
end

#on_block(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 62

def on_block(node)
  return unless TARGET_METHODS.include?(node.method_name)
  return unless one_block_argument?(node.arguments)

  block_argument = node.first_argument
  block_body = node.body
  return unless use_equality_comparison_block?(block_body)
  return if same_block_argument_and_is_a_argument?(block_body, block_argument)
  return unless (new_argument = new_argument(block_argument, block_body))

  range = offense_range(node)
  prefer = "#{node.method_name}(#{new_argument})"

  add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
    corrector.replace(range, prefer)
  end
end

#one_block_argument?(block_arguments) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 82

def one_block_argument?(block_arguments)
  block_arguments.one? && !block_arguments.source.include?(',')
end

#same_block_argument_and_is_a_argument?(block_body, block_argument) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 94

def same_block_argument_and_is_a_argument?(block_body, block_argument)
  if block_body.method?(:===)
    block_argument.source != block_body.children[2].source
  elsif IS_A_METHODS.include?(block_body.method_name)
    block_argument.source == block_body.first_argument.source
  else
    block_body.receiver.source == block_body.first_argument.receiver&.source
  end
end

#use_block_argument_in_method_argument_of_operand?(block_argument, operand) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 118

def use_block_argument_in_method_argument_of_operand?(block_argument, operand)
  return false unless operand.send_type?

  arguments = operand.arguments
  arguments.inject(arguments.map(&:source)) do |operand_sources, argument|
    operand_sources + argument.each_descendant(:lvar).map(&:source)
  end.any?(block_argument.source)
end

#use_equality_comparison_block?(block_body) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/performance/redundant_equality_comparison_block.rb', line 86

def use_equality_comparison_block?(block_body)
  return false unless block_body.send_type?

  method_name = block_body.method_name

  COMPARISON_METHODS.include?(method_name) || (!allow_regexp_match? && REGEXP_METHODS.include?(method_name))
end