class RuboCop::Cop::Style::HashExcept


{foo: 1, bar: 2, baz: 3}.except(:bar)
# good
{foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
{foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
{foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
# bad
@example
And do not check ‘Hash#delete_if` and `Hash#keep_if` to change receiver object.
when used `==`.
For safe detection, it is limited to commonly used string and symbol comparisons
(`Hash#except` was added in Ruby 3.0.)
This cop should only be enabled on Ruby version 3.0 or higher.
that can be replaced with `Hash#except` method.
This cop checks for usages of `Hash#reject`, `Hash#select`, and `Hash#filter` methods

def except_key(node)

def except_key(node)
  key_argument = node.argument_list.first
  lhs, _method_name, rhs = *node.body
  [lhs, rhs].find { |operand| operand.source != key_argument.source }
end

def offense_range(node)

def offense_range(node)
  range_between(node.loc.selector.begin_pos, node.parent.loc.end.end_pos)
end

def on_send(node)

def on_send(node)
  block = node.parent
  return unless bad_method?(block) && semantically_except_method?(node, block)
  except_key = except_key(block)
  return unless safe_to_register_offense?(block, except_key)
  range = offense_range(node)
  preferred_method = "except(#{except_key.source})"
  add_offense(range, message: format(MSG, prefer: preferred_method)) do |corrector|
    corrector.replace(range, preferred_method)
  end
end

def safe_to_register_offense?(block, except_key)

def safe_to_register_offense?(block, except_key)
  return true if block.body.method?('eql?')
  except_key.sym_type? || except_key.str_type?
end

def semantically_except_method?(send, block)

def semantically_except_method?(send, block)
  body = block.body
  case send.method_name
  when :reject
    body.method?('==') || body.method?('eql?')
  when :select, :filter
    body.method?('!=')
  else
    false
  end
end