class RuboCop::Cop::Style::MultipleComparison

Checks against comparing a variable with multiple items, where ‘Array#include?`, `Set#include?` or a `case` could be used instead to avoid code repetition. It accepts comparisons of multiple method calls to avoid unnecessary method calls by default. It can be configured by `AllowMethodComparison` option.

@example

# bad
a = 'a'
foo if a == 'a' || a == 'b' || a == 'c'

# good
a = 'a'
foo if ['a', 'b', 'c'].include?(a)

VALUES = Set['a', 'b', 'c'].freeze
# elsewhere...
foo if VALUES.include?(a)

case foo
when 'a', 'b', 'c' then foo
# ...
end

# accepted (but consider `case` as above)
foo if a == b.lightweight || a == b.heavyweight

@example AllowMethodComparison: true (default)

# good
foo if a == b.lightweight || a == b.heavyweight

@example AllowMethodComparison: false

# bad
foo if a == b.lightweight || a == b.heavyweight

# good
foo if [b.lightweight, b.heavyweight].include?(a)

@example ComparisonsThreshold: 2 (default)

# bad
foo if a == 'a' || a == 'b'

@example ComparisonsThreshold: 3

# good
foo if a == 'a' || a == 'b'

Constants

MSG

Public Instance Methods

on_or(node) click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 58
def on_or(node)
  root_of_or_node = root_of_or_node(node)
  return unless node == root_of_or_node
  return unless nested_comparison?(node)

  return unless (variable, values = find_offending_var(node))
  return if values.size < comparisons_threshold

  add_offense(node) do |corrector|
    elements = values.map(&:source).join(', ')
    prefer_method = "[#{elements}].include?(#{variable_name(variable)})"

    corrector.replace(node, prefer_method)
  end
end

Private Instance Methods

allow_method_comparison?() click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 142
def allow_method_comparison?
  cop_config.fetch('AllowMethodComparison', true)
end
comparison?(node) click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 121
def comparison?(node)
  simple_comparison?(node) || nested_comparison?(node)
end
comparisons_threshold() click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 146
def comparisons_threshold
  cop_config.fetch('ComparisonsThreshold', 2)
end
find_offending_var(node, variables = Set.new, values = []) click to toggle source

rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

# File lib/rubocop/cop/style/multiple_comparison.rb, line 90
def find_offending_var(node, variables = Set.new, values = [])
  if node.or_type?
    find_offending_var(node.lhs, variables, values)
    find_offending_var(node.rhs, variables, values)
  elsif simple_double_comparison?(node)
    return
  elsif (var, obj = simple_comparison?(node))
    return if allow_method_comparison? && obj.send_type?

    variables << var
    return if variables.size > 1

    values << obj
  end

  [variables.first, values] if variables.any?
end
nested_comparison?(node) click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 113
def nested_comparison?(node)
  if node.or_type?
    node.node_parts.all? { |node_part| comparison? node_part }
  else
    false
  end
end
root_of_or_node(or_node) click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 132
def root_of_or_node(or_node)
  return or_node unless or_node.parent

  if or_node.parent.or_type?
    root_of_or_node(or_node.parent)
  else
    or_node
  end
end
simple_comparison?(node) click to toggle source
# File lib/rubocop/cop/style/multiple_comparison.rb, line 125
def simple_comparison?(node)
  if (var, obj = simple_comparison_lhs?(node)) ||
     (obj, var = simple_comparison_rhs?(node))
    [var, obj]
  end
end
variable_name(node) click to toggle source

rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

# File lib/rubocop/cop/style/multiple_comparison.rb, line 109
def variable_name(node)
  node.children[0]
end