123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Lint::RedundantSplatExpansion

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::RuboCop::Cop::AutoCorrector, ::RuboCop::Cop::Base, ::RuboCop::ExcludeLimit, NodePattern::Macros, RuboCop::AST::Sexp
Instance Chain:
Inherits: RuboCop::Cop::Base
Defined in: lib/rubocop/cop/lint/redundant_splat_expansion.rb

Overview

Checks for unneeded usages of splat expansion

Examples:

# bad
a = *[1, 2, 3]
a = *'a'
a = *1
['a', 'b', *%w(c d e), 'f', 'g']

# good
c = [1, 2, 3]
a = *c
a, b = *c
a, *b = *c
a = *1..10
a = ['a']
['a', 'b', 'c', 'd', 'e', 'f', 'g']

# bad
do_something(*['foo', 'bar', 'baz'])

# good
do_something('foo', 'bar', 'baz')

# bad
begin
  foo
rescue *[StandardError, ApplicationError]
  bar
end

# good
begin
  foo
rescue StandardError, ApplicationError
  bar
end

# bad
case foo
when *[1, 2, 3]
  bar
else
  baz
end

# good
case foo
when 1, 2, 3
  bar
else
  baz
end

AllowPercentLiteralArrayArgument: true (default)

# good
do_something(*%w[foo bar baz])

AllowPercentLiteralArrayArgument: false

# bad
do_something(*%w[foo bar baz])

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

Class Attribute Summary

::RuboCop::Cop::AutoCorrector - Extended

::RuboCop::Cop::Base - Inherited

.gem_requirements, .lint?,
.support_autocorrect?

Returns if class supports autocorrect.

.support_multiple_source?

Override if your cop should be called repeatedly for multiple investigations Between calls to on_new_investigation and on_investigation_end, the result of processed_source will remain constant.

.builtin?

Class Method Summary

::RuboCop::Cop::Base - Inherited

.autocorrect_incompatible_with

List of cops that should not try to autocorrect at the same time as this cop.

.badge

Naming.

.callbacks_needed, .cop_name, .department,
.documentation_url

Cops (other than builtin) are encouraged to implement this.

.exclude_from_registry

Call for abstract Cop classes.

.inherited,
.joining_forces

Override and return the Force class(es) you need to join.

.match?

Returns true if the cop name or the cop namespace matches any of the given names.

.new,
.requires_gem

Register a version requirement for the given gem name.

.restrict_on_send

::RuboCop::ExcludeLimit - Extended

exclude_limit

Sets up a configuration option to have an exclude limit tracked.

transform

Instance Attribute Summary

Instance Method Summary

::RuboCop::Cop::Base - Inherited

#add_global_offense

Adds an offense that has no particular location.

#add_offense

Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense.

#begin_investigation

Called before any investigation.

#callbacks_needed,
#cop_config

Configuration Helpers.

#cop_name, #excluded_file?,
#external_dependency_checksum

This method should be overridden when a cop’s behavior depends on state that lives outside of these locations:

#inspect,
#message

Gets called if no message is specified when calling add_offense or add_global_offense Cops are discouraged to override this; instead pass your message directly.

#name

Alias for Base#cop_name.

#offenses,
#on_investigation_end

Called after all on_…​

#on_new_investigation

Called before all on_…​

#on_other_file

Called instead of all on_…​

#parse

There should be very limited reasons for a Cop to do it’s own parsing.

#parser_engine,
#ready

Called between investigations.

#relevant_file?, #target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction,
#callback_argument

Reserved for Cop::Cop.

#complete_investigation

Called to complete an investigation.

#correct, #current_corrector,
#current_offense_locations

Reserved for Commissioner:

#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range, #reset_investigation, #use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Attribute Details

#allow_percent_literal_array_argument?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 203

def allow_percent_literal_array_argument?
  cop_config.fetch('AllowPercentLiteralArrayArgument', true)
end

Instance Method Details

#array_new?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 83

def_node_matcher :array_new?, <<~PATTERN
  {
    $(send (const {nil? cbase} :Array) :new ...)
    $(block (send (const {nil? cbase} :Array) :new ...) ...)
  }
PATTERN

#array_new_inside_array_literal?(array_new_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 131

def array_new_inside_array_literal?(array_new_node)
  return false unless array_new?(array_new_node)

  grandparent = array_new_node.parent.parent
  grandparent.array_type? && grandparent.children.size > 1
end

#array_splat?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 155

def array_splat?(node)
  node.children.first.array_type?
end

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 112

def autocorrect(corrector, node)
  range, content = replacement_range_and_content(node)

  corrector.replace(range, content)
end

#literal_expansion(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 91

def_node_matcher :literal_expansion, <<~PATTERN
  (splat {$({str dstr int float array} ...) (block $#array_new? ...) $#array_new?} ...)
PATTERN

#method_argument?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 159

def method_argument?(node)
  node.parent.send_type?
end

#on_splat(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 95

def on_splat(node)
  redundant_splat_expansion(node) do
    if array_splat?(node) && (method_argument?(node) || part_of_an_array?(node))
      return if allow_percent_literal_array_argument? &&
                use_percent_literal_array_argument?(node)

      add_offense(node, message: ARRAY_PARAM_MSG) do |corrector|
        autocorrect(corrector, node)
      end
    else
      add_offense(node) { |corrector| autocorrect(corrector, node) }
    end
  end
end

#part_of_an_array?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 163

def part_of_an_array?(node)
  # The parent of a splat expansion is an array that does not have
  # `begin` or `end`
  parent = node.parent
  parent.array_type? && parent.loc.begin && parent.loc.end
end

#redundant_brackets?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 170

def redundant_brackets?(node)
  parent = node.parent
  grandparent = node.parent.parent

  parent.when_type? || parent.send_type? || part_of_an_array?(node) ||
    grandparent&.resbody_type?
end

#redundant_splat_expansion(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 118

def redundant_splat_expansion(node)
  literal_expansion(node) do |expanded_item|
    if expanded_item.send_type?
      return if array_new_inside_array_literal?(expanded_item)

      grandparent = node.parent.parent
      return if grandparent && !ASSIGNMENT_TYPES.include?(grandparent.type)
    end

    yield
  end
end

#remove_brackets(array) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 178

def remove_brackets(array)
  array_start = array.loc.begin.source
  elements = *array
  elements = elements.map(&:source)

  if array_start.start_with?(PERCENT_W)
    "'#{elements.join("', '")}'"
  elsif array_start.start_with?(PERCENT_CAPITAL_W)
    %("#{elements.join('", "')}")
  elsif array_start.start_with?(PERCENT_I)
    ":#{elements.join(', :')}"
  elsif array_start.start_with?(PERCENT_CAPITAL_I)
    %(:"#{elements.join('", :"')}")
  else
    elements.join(', ')
  end
end

#replacement_range_and_content(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 138

def replacement_range_and_content(node)
  variable, = *node
  loc = node.loc
  expression = loc.expression

  if array_new?(variable)
    expression = node.parent.source_range if node.parent.array_type?
    [expression, variable.source]
  elsif !variable.array_type?
    [expression, "[#{variable.source}]"]
  elsif redundant_brackets?(node)
    [expression, remove_brackets(variable)]
  else
    [loc.operator, '']
  end
end

#use_percent_literal_array_argument?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/redundant_splat_expansion.rb', line 196

def use_percent_literal_array_argument?(node)
  argument = node.children.first

  node.parent.send_type? &&
    (argument.percent_literal?(:string) || argument.percent_literal?(:symbol))
end