class RuboCop::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
autocorrect(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 90 def autocorrect(node) _check, body, = node.node_parts _checked_variable, matching_receiver, = extract_parts(node) method_call, = matching_receiver.parent 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/rubocop/cop/style/safe_navigation.rb, line 81 def check_node(node) return if target_ruby_version < 2.3 checked_variable, receiver, method = extract_parts(node) return unless receiver == checked_variable return if unsafe_method?(method) add_offense(node) end
on_and(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 77 def on_and(node) check_node(node) end
on_if(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 72 def on_if(node) return if allowed_if_condition?(node) check_node(node) end
Private Instance Methods
allowed_if_condition?(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 104 def allowed_if_condition?(node) node.else? || node.elsif? || node.ternary? end
begin_range(node, method_call)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 166 def begin_range(node, method_call) range_between(node.loc.expression.begin_pos, method_call.loc.expression.begin_pos) end
end_range(node, method_call)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 171 def end_range(node, method_call) range_between(method_call.loc.expression.end_pos, node.loc.expression.end_pos) end
extract_common_parts(continuation, checked_variable)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 134 def extract_common_parts(continuation, checked_variable) matching_receiver = find_matching_receiver_invocation(continuation, checked_variable) method = matching_receiver.parent if matching_receiver [checked_variable, matching_receiver, method] end
extract_parts(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 108 def extract_parts(node) case node.type when :if extract_parts_from_if(node) when :and extract_parts_from_and(node) end end
extract_parts_from_and(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 124 def extract_parts_from_and(node) checked_variable, rhs = *node if cop_config['ConvertCodeThatCanStartToReturnNil'] checked_variable = not_nil_check?(checked_variable) || checked_variable end extract_common_parts(rhs, checked_variable) end
extract_parts_from_if(node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 117 def extract_parts_from_if(node) checked_variable, receiver = modifier_if_safe_navigation_candidate?(node) extract_common_parts(receiver, checked_variable) end
find_matching_receiver_invocation(node, checked_variable)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 143 def find_matching_receiver_invocation(node, checked_variable) return nil unless node receiver = if node.block_type? node.send_node.receiver else node.receiver end return receiver if receiver == checked_variable find_matching_receiver_invocation(receiver, checked_variable) end
negated?(send_node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 162 def negated?(send_node) send_node.parent.send_type? && send_node.parent.method?(:!) end
unsafe_method?(send_node)
click to toggle source
# File lib/rubocop/cop/style/safe_navigation.rb, line 157 def unsafe_method?(send_node) NIL_METHODS.include?(send_node.method_name) || negated?(send_node) || !send_node.dot? end