123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Style::SuperArguments

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/style/super_arguments.rb

Overview

Checks for redundant argument forwarding when calling super with arguments identical to the method definition.

Using zero arity super within a define_method block results in RuntimeError:

def m
  define_method(:foo) { super() } # => OK
end

def m
  define_method(:foo) { super }   # => RuntimeError
end

Furthermore, any arguments accompanied by a block may potentially be delegating to define_method, therefore, super used within these blocks will be allowed. This approach might result in false negatives, yet ensuring safe detection takes precedence.

Examples:

# bad
def method(*args, **kwargs)
  super(*args, **kwargs)
end

# good - implicitly passing all arguments
def method(*args, **kwargs)
  super
end

# good - forwarding a subset of the arguments
def method(*args, **kwargs)
  super(*args)
end

# good - forwarding no arguments
def method(*args, **kwargs)
  super()
end

# good - assigning to the block variable before calling super
def method(&block)
  # Assigning to the block variable would pass the old value to super,
  # under this circumstance the block must be referenced explicitly.
  block ||= proc { 'fallback behavior' }
  super(&block)
end

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.

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

Returns an url to view this cops documentation online.

.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

Actually private methods.

#use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

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

Instance Method Details

#arguments_identical?(def_node, def_args, super_args) ⇒ Boolean (private)

Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 81

def arguments_identical?(def_node, def_args, super_args)
  super_args = preprocess_super_args(super_args)
  return false if def_args.size != super_args.size

  def_args.zip(super_args).each do |def_arg, super_arg|
    next if positional_arg_same?(def_arg, super_arg)
    next if positional_rest_arg_same(def_arg, super_arg)
    next if keyword_arg_same?(def_arg, super_arg)
    next if keyword_rest_arg_same?(def_arg, super_arg)
    next if block_arg_same?(def_node, def_arg, super_arg)
    next if forward_arg_same?(def_arg, super_arg)

    return false
  end
  true
end

#block_arg_same?(def_node, def_arg, super_arg) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 136

def block_arg_same?(def_node, def_arg, super_arg)
  return false unless def_arg.blockarg_type? && super_arg.block_pass_type?
  # anonymous forwarding
  return true if (block_pass_child = super_arg.children.first).nil? && def_arg.name.nil?

  block_arg_name = block_pass_child.children.first
  def_arg.name == block_arg_name && !block_reassigned?(def_node, block_arg_name)
end

#block_reassigned?(def_node, block_arg_name) ⇒ Boolean (private)

Reassigning the block argument will still pass along the original block to super https://bugs.ruby-lang.org/issues/20505

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 147

def block_reassigned?(def_node, block_arg_name)
  def_node.each_node(*ASSIGN_TYPES).any? do |assign_node|
    # TODO: Since `Symbol#name` is supported from Ruby 3.0, the inheritance check for
    # `AST::Node` can be removed when requiring Ruby 3.0+.
    lhs = assign_node.node_parts[0]
    next if lhs.is_a?(AST::Node) && !lhs.respond_to?(:name)

    assign_node.name == block_arg_name
  end
end

#forward_arg_same?(def_arg, super_arg) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 158

def forward_arg_same?(def_arg, super_arg)
  def_arg.forward_arg_type? && super_arg.forwarded_args_type?
end

#keyword_arg_same?(def_arg, super_arg) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 116

def keyword_arg_same?(def_arg, super_arg)
  return false unless def_arg.kwarg_type? || def_arg.kwoptarg_type?
  return false unless (pair_node = super_arg).pair_type?
  return false unless (sym_node = pair_node.key).sym_type?
  return false unless (lvar_node = pair_node.value).lvar_type?
  return false unless sym_node.source == lvar_node.source

  def_arg.name == sym_node.value
end

#keyword_rest_arg_same?(def_arg, super_arg) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 126

def keyword_rest_arg_same?(def_arg, super_arg)
  return false unless def_arg.kwrestarg_type?
  # anonymous forwarding
  return true if def_arg.name.nil? && super_arg.forwarded_kwrestarg_type?
  return false unless super_arg.kwsplat_type?
  return false unless (lvar_node = super_arg.children.first).lvar_type?

  def_arg.name == lvar_node.children.first
end

#on_super(super_node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 62

def on_super(super_node)
  def_node = super_node.ancestors.find do |node|
    # When defining dynamic methods, implicitly calling `super` is not possible.
    # Since there is a possibility of delegation to `define_method`,
    # `super` used within the block is always allowed.
    break if node.block_type?

    break node if DEF_TYPES.include?(node.type)
  end
  return unless def_node
  return unless arguments_identical?(def_node, def_node.arguments.argument_list,
                                     super_node.arguments)

  add_offense(super_node) { |corrector| corrector.replace(super_node, 'super') }
end

#positional_arg_same?(def_arg, super_arg) ⇒ Boolean (private)

Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 99

def positional_arg_same?(def_arg, super_arg)
  return false unless def_arg.arg_type? || def_arg.optarg_type?
  return false unless super_arg.lvar_type?

  def_arg.name == super_arg.children.first
end

#positional_rest_arg_same(def_arg, super_arg) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 106

def positional_rest_arg_same(def_arg, super_arg)
  return false unless def_arg.restarg_type?
  # anonymous forwarding
  return true if def_arg.name.nil? && super_arg.forwarded_restarg_type?
  return false unless super_arg.splat_type?
  return false unless (lvar_node = super_arg.children.first).lvar_type?

  def_arg.name == lvar_node.children.first
end

#preprocess_super_args(super_args) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/super_arguments.rb', line 162

def preprocess_super_args(super_args)
  super_args.flat_map do |node|
    if node.hash_type? && !node.braces?
      node.children
    else
      node
    end
  end
end