class RuboCop::Cop::Lint::RedundantSafeNavigation


do_something if attrs&.not_nil_safe_method(:[])
do_something if attrs.nil_safe_method(:[])
# good
do_something if attrs&.nil_safe_method(:[])
# bad
@example AllowedMethods: [nil_safe_method]
self.foo
# good
self&.foo
# bad
foo.to_s
foo.to_f
foo.to_i
foo.to_a
foo.to_h { |k, v| [k, v] }
foo.to_h
# good
foo&.to_s || ”
foo&.to_f || 0.0
foo&.to_i || 0
foo&.to_a || []
foo&.to_h { |k, v| [k, v] } || {}
foo&.to_h || {}
# bad - for ‘nil`s conversion methods return default values for the type
foo&.respond_to?(:to_a)
# good - without `&.` this will always return `true`
end
node = node.parent
while node.is_a?(BeginNode)
# good
end
node = node.parent
while node&.is_a?(BeginNode)
# bad
do_something if attrs.respond_to?(:[])
# good
do_something if attrs&.respond_to?(:[])
# bad
CamelCaseConst.do_something
# good
CamelCaseConst&.do_something
# bad
@example
will be autocorrected to never return `nil`.
the expression. An offending expression that previously could return `nil`
This cop is unsafe, because autocorrection can change the return type of
@safety
because `NilClass` has methods like `respond_to?` and `is_a?`.
In the example below, the safe navigation operator (`&.`) is unnecessary
for which to suppress (allow) this cop’s check.
Note that the ‘AllowedMethod` option is not an option that specifies methods
in other words, it is a method that is allowed to skip safe navigation.
The `AllowedMethods` option specifies nil-safe methods,
These are customizable with `AllowedMethods` option.
and `equal?` methods are checked by default.
For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
to `self`, and to literal receivers, except for `nil`.
and an offense is not detected when the receiver is a constant. The detection also applies
Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
Checks for redundant safe navigation calls.

def assume_receiver_instance_exists?(receiver)

def assume_receiver_instance_exists?(receiver)
  return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
  receiver.self_type? || (receiver.literal? && !receiver.nil_type?)
end

def check?(node)

def check?(node)
  parent = node.parent
  return false unless parent
  condition?(parent, node) ||
    parent.operator_keyword? ||
    (parent.send_type? && parent.negation_method?)
end

def condition?(parent, node)

def condition?(parent, node)
  (parent.conditional? || parent.post_condition_loop?) && parent.condition == node
end

def on_csend(node)

rubocop:disable Metrics/AbcSize
def on_csend(node)
  unless assume_receiver_instance_exists?(node.receiver)
    return unless check?(node) && allowed_method?(node.method_name)
    return if respond_to_nil_specific_method?(node)
  end
  range = node.loc.dot
  add_offense(range) { |corrector| corrector.replace(range, '.') }
end

def on_or(node)

def on_or(node)
  conversion_with_default?(node) do |send_node|
    range = send_node.loc.dot.begin.join(node.source_range.end)
    add_offense(range, message: MSG_LITERAL) do |corrector|
      corrector.replace(send_node.loc.dot, '.')
      range_with_default = node.lhs.source_range.end.begin.join(node.source_range.end)
      corrector.remove(range_with_default)
    end
  end
end