class RuboCop::Cop::Style::TrailingUnderscoreVariable

Checks for extra underscores in variable assignment.

@example

# bad
a, b, _ = foo()
a, b, _, = foo()
a, _, _ = foo()
a, _, _, = foo()

# good
a, b, = foo()
a, = foo()
*a, b, _ = foo()
# => We need to know to not include 2 variables in a
a, *b, _ = foo()
# => The correction `a, *b, = foo()` is a syntax error

@example AllowNamedUnderscoreVariables: true (default)

# good
a, b, _something = foo()

@example AllowNamedUnderscoreVariables: false

# bad
a, b, _something = foo()

Constants

DISALLOW
MSG
UNDERSCORE

Public Instance Methods

on_masgn(node) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 41
def on_masgn(node)
  ranges = unneeded_ranges(node)

  ranges.each do |range|
    good_code = node.source
    offset = range.begin_pos - node.source_range.begin_pos
    good_code[offset, range.size] = ''

    add_offense(range, message: format(MSG, code: good_code)) do |corrector|
      corrector.remove(range)
    end
  end
end

Private Instance Methods

allow_named_underscore_variables() click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 92
def allow_named_underscore_variables
  @allow_named_underscore_variables ||= cop_config['AllowNamedUnderscoreVariables']
end
children_offenses(variables) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 125
def children_offenses(variables)
  variables.select(&:mlhs_type?).flat_map { |v| unneeded_ranges(v) }
end
find_first_offense(variables) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 57
def find_first_offense(variables)
  first_offense = find_first_possible_offense(variables.reverse)

  return unless first_offense
  return if splat_variable_before?(first_offense, variables)

  first_offense
end
find_first_possible_offense(variables) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 66
def find_first_possible_offense(variables)
  variables.reduce(nil) do |offense, variable|
    break offense unless DISALLOW.include?(variable.type)

    var, = *variable
    var, = *var

    break offense if (allow_named_underscore_variables && var != :_) ||
                     !var.to_s.start_with?(UNDERSCORE)

    variable
  end
end
main_node_offense(node) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 108
def main_node_offense(node)
  node.masgn_type? ? (mlhs_node, right = *node) : mlhs_node = node

  variables = *mlhs_node
  first_offense = find_first_offense(variables)

  return unless first_offense

  if unused_variables_only?(first_offense, variables)
    return unused_range(node.type, mlhs_node, right)
  end

  return range_for_parentheses(first_offense, mlhs_node) if Util.parentheses?(mlhs_node)

  range_between(first_offense.source_range.begin_pos, node.loc.operator.begin_pos)
end
range_for_parentheses(offense, left) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 146
def range_for_parentheses(offense, left)
  range_between(offense.source_range.begin_pos - 1, left.source_range.end_pos - 1)
end
reverse_index(collection, item) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 88
def reverse_index(collection, item)
  collection.size - 1 - collection.reverse.index(item)
end
splat_variable_before?(first_offense, variables) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 80
def splat_variable_before?(first_offense, variables)
  # Account for cases like `_, *rest, _`, where we would otherwise get
  # the index of the first underscore.
  first_offense_index = reverse_index(variables, first_offense)

  variables[0...first_offense_index].any?(&:splat_type?)
end
unneeded_ranges(node) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 96
def unneeded_ranges(node)
  node.masgn_type? ? (mlhs_node, = *node) : mlhs_node = node
  variables = *mlhs_node

  main_offense = main_node_offense(node)
  if main_offense.nil?
    children_offenses(variables)
  else
    children_offenses(variables) << main_offense
  end
end
unused_range(node_type, mlhs_node, right) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 133
def unused_range(node_type, mlhs_node, right)
  start_range = mlhs_node.source_range.begin_pos

  end_range = case node_type
              when :masgn
                right.source_range.begin_pos
              when :mlhs
                mlhs_node.source_range.end_pos
              end

  range_between(start_range, end_range)
end
unused_variables_only?(offense, variables) click to toggle source
# File lib/rubocop/cop/style/trailing_underscore_variable.rb, line 129
def unused_variables_only?(offense, variables)
  offense.source_range == variables.first.source_range
end