class RuboCop::Cop::Style::SafeNavigation
foo.to_i if foo
# use safe navigation
# Methods that ‘nil` will `respond_to?` should not be converted to
!foo || foo.bar
foo.nil? || foo.bar
foo&.bar(param) { |e| e.something }
foo&.bar { |e| e.something }
foo&.bar(param1, param2)
foo&.bar
# good
foo && foo.bar(param) { |e| e.something }
foo && foo.bar { |e| e.something }
foo && foo.bar(param1, param2)
foo && foo.bar
foo.bar unless foo.nil?
foo.bar unless !foo
foo.bar if !foo.nil?
foo.bar(param) { |e| e.something } if foo
foo.bar { |e| e.something } if foo
foo.bar(param1, param2) if foo
foo.bar if foo
# bad
@example
returns.
`foo&.bar` can start returning `nil` as well as what the method
of the method is. If this is converted to safe navigation,
the return of this code is limited to `false` and whatever the return
check for code in the format `!foo.nil? && foo.bar`. As it is written,
The default for this is `false`. When configured to `true`, this will
Configuration option: ConvertCodeThatCanStartToReturnNil
safe navigation (`&.`).
check for the variable whose method is being called to
This cop transforms usages of a method call safeguarded by a non `nil`
def allowed_if_condition?(node)
def allowed_if_condition?(node) node.if_type? && (node.else? || node.elsif?) end
def autocorrect(node)
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
def begin_range(node, method_call)
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
def check_node(node)
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
def end_range(node, method_call)
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
def extract_parts(node)
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
def on_and(node)
def on_and(node) check_node(node) end
def on_if(node)
def on_if(node) return if node.ternary? check_node(node) end
def on_or(node)
def on_or(node) check_node(node) end