class RubbyCop::Cop::Style::SafeNavigation

This cop transforms usages of a method call safeguarded by a non `nil` check for the variable whose method is being called to safe navigation (`&.`).

Configuration option: ConvertCodeThatCanStartToReturnNil The default for this is `false`. When configured to `true`, this will check for code in the format `!foo.nil? && foo.bar`. As it is written, the return of this code is limited to `false` and whatever the return of the method is. If this is converted to safe navigation, `foo&.bar` can start returning `nil` as well as what the method returns.

@example

# bad
foo.bar if foo
foo.bar(param1, param2) if foo
foo.bar { |e| e.something } if foo
foo.bar(param) { |e| e.something } if foo

foo.bar if !foo.nil?
foo.bar unless !foo
foo.bar unless foo.nil?

foo && foo.bar
foo && foo.bar(param1, param2)
foo && foo.bar { |e| e.something }
foo && foo.bar(param) { |e| e.something }

# good
foo&.bar
foo&.bar(param1, param2)
foo&.bar { |e| e.something }
foo&.bar(param) { |e| e.something }

foo.nil? || foo.bar
!foo || foo.bar

# Methods that `nil` will `respond_to?` should not be converted to
# use safe navigation
foo.to_i if foo

Constants

MSG
NIL_METHODS

Public Instance Methods

allowed_if_condition?(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 100
def allowed_if_condition?(node)
  node.if_type? && (node.else? || node.elsif?)
end
autocorrect(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 113
def autocorrect(node)
  if node.if_type?
    _check, body, = *node.node_parts
  else
    _check, body = *node
  end

  method_call, = *body if body.block_type?

  lambda do |corrector|
    corrector.remove(begin_range(node, body))
    corrector.remove(end_range(node, body))
    corrector.insert_before((method_call || body).loc.dot, '&')
  end
end
check_node(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 89
def check_node(node)
  return if target_ruby_version < 2.3
  return if allowed_if_condition?(node)
  checked_variable, receiver, method = extract_parts(node)
  return unless receiver == checked_variable
  return if NIL_METHODS.include?(method)
  return unless method =~ /\w+[=!?]?/

  add_offense(node, :expression)
end
extract_parts(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 104
def extract_parts(node)
  if cop_config['ConvertCodeThatCanStartToReturnNil']
    safe_navigation_candidate(node) ||
      candidate_that_may_introduce_nil(node)
  else
    safe_navigation_candidate(node)
  end
end
on_and(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 81
def on_and(node)
  check_node(node)
end
on_if(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 75
def on_if(node)
  return if node.ternary?

  check_node(node)
end
on_or(node) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 85
def on_or(node)
  check_node(node)
end

Private Instance Methods

begin_range(node, method_call) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 131
def begin_range(node, method_call)
  Parser::Source::Range.new(node.loc.expression.source_buffer,
                            node.loc.expression.begin_pos,
                            method_call.loc.expression.begin_pos)
end
end_range(node, method_call) click to toggle source
# File lib/rubbycop/cop/style/safe_navigation.rb, line 137
def end_range(node, method_call)
  Parser::Source::Range.new(node.loc.expression,
                            method_call.loc.expression.end_pos,
                            node.loc.expression.end_pos)
end