class RuboCop::Cop::Style::DoubleNegation

Checks for uses of double negation (‘!!`) to convert something to a boolean value.

When using ‘EnforcedStyle: allowed_in_returns`, allow double negation in contexts that use boolean as a return value. When using `EnforcedStyle: forbidden`, double negation should be forbidden always.

NOTE: when ‘something` is a boolean value `!!something` and `!something.nil?` are not the same thing. As you’re unlikely to write code that can accept values of any type this is rarely a problem in practice.

@safety

Autocorrection is unsafe when the value is `false`, because the result
of the expression will change.

[source,ruby]
----
!!false     #=> false
!false.nil? #=> true
----

@example

# bad
!!something

# good
!something.nil?

@example EnforcedStyle: allowed_in_returns (default)

# good
def foo?
  !!return_value
end

define_method :foo? do
  !!return_value
end

define_singleton_method :foo? do
  !!return_value
end

@example EnforcedStyle: forbidden

# bad
def foo?
  !!return_value
end

define_method :foo? do
  !!return_value
end

define_singleton_method :foo? do
  !!return_value
end

Constants

MSG
RESTRICT_ON_SEND

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 71
def on_send(node)
  return unless double_negative?(node) && node.prefix_bang?
  return if style == :allowed_in_returns && allowed_in_returns?(node)

  location = node.loc.selector
  add_offense(location) do |corrector|
    corrector.remove(location)
    corrector.insert_after(node, '.nil?')
  end
end

Private Instance Methods

allowed_in_returns?(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 84
def allowed_in_returns?(node)
  node.parent&.return_type? || end_of_method_definition?(node)
end
define_mehod?(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 111
def define_mehod?(node)
  return false unless node.block_type?

  child = node.child_nodes.first
  return false unless child.send_type?

  child.method?(:define_method) || child.method?(:define_singleton_method)
end
double_negative_condition_return_value?(node, last_child, conditional_node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 138
def double_negative_condition_return_value?(node, last_child, conditional_node)
  parent = find_parent_not_enumerable(node)
  if parent.begin_type?
    node.loc.line == parent.loc.last_line
  else
    last_child.last_line <= conditional_node.last_line
  end
end
end_of_method_definition?(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 88
def end_of_method_definition?(node)
  return false unless (def_node = find_def_node_from_ascendant(node))

  conditional_node = find_conditional_node_from_ascendant(node)
  last_child = find_last_child(def_node.send_type? ? def_node : def_node.body)

  if conditional_node
    double_negative_condition_return_value?(node, last_child, conditional_node)
  elsif last_child.pair_type? || last_child.hash_type? || last_child.parent.array_type?
    false
  else
    last_child.last_line <= node.last_line
  end
end
find_conditional_node_from_ascendant(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 120
def find_conditional_node_from_ascendant(node)
  return unless (parent = node.parent)
  return parent if parent.conditional?

  find_conditional_node_from_ascendant(parent)
end
find_def_node_from_ascendant(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 103
def find_def_node_from_ascendant(node)
  return unless (parent = node.parent)
  return parent if parent.def_type? || parent.defs_type?
  return node.parent.child_nodes.first if define_mehod?(parent)

  find_def_node_from_ascendant(node.parent)
end
find_last_child(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 127
def find_last_child(node)
  case node.type
  when :rescue
    find_last_child(node.body)
  when :ensure
    find_last_child(node.child_nodes.first)
  else
    node.child_nodes.last
  end
end
find_parent_not_enumerable(node) click to toggle source
# File lib/rubocop/cop/style/double_negation.rb, line 147
def find_parent_not_enumerable(node)
  return unless (parent = node.parent)

  if parent.pair_type? || parent.hash_type? || parent.array_type?
    find_parent_not_enumerable(parent)
  else
    parent
  end
end