123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Style::ArgumentsForwarding::SendNodeClassifier

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, Macros
Inherits: Object
Defined in: lib/rubocop/cop/style/arguments_forwarding.rb

Overview

Classifies send nodes for possible rest/kwrest/all (including block) forwarding.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(def_node, send_node, referenced_lvars, forwardable_args, **config) ⇒ SendNodeClassifier

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 403

def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
  @def_node = def_node
  @send_node = send_node
  @referenced_lvars = referenced_lvars
  @rest_arg, @kwrest_arg, @block_arg = *forwardable_args
  @rest_arg_name, @kwrest_arg_name, @block_arg_name =
    *forwardable_args.map { |a| a&.name }
  @config = config
end

Instance Attribute Details

#additional_kwargs?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 513

def additional_kwargs?
  @def_node.arguments.any? { |a| a.kwarg_type? || a.kwoptarg_type? }
end

#additional_kwargs_or_forwarded_kwargs?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 509

def additional_kwargs_or_forwarded_kwargs?
  additional_kwargs? || forward_additional_kwargs?
end

#allow_offense_for_no_block?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 523

def allow_offense_for_no_block?
  !@config.fetch(:allow_only_rest_arguments)
end

#any_arg_referenced?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 494

def any_arg_referenced?
  referenced_rest_arg? || referenced_kwrest_arg? || referenced_block_arg?
end

#can_forward_all?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 446

def can_forward_all?
  return false if any_arg_referenced?
  return false if ruby_30_or_lower_optarg?
  return false if ruby_32_or_higher_missing_rest_or_kwest?
  return false unless offensive_block_forwarding?
  return false if additional_kwargs_or_forwarded_kwargs?

  no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
end

#forward_additional_kwargs?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 517

def forward_additional_kwargs?
  return false unless forwarded_kwrest_arg

  !forwarded_kwrest_arg.parent.children.one?
end

#missing_rest_arg_or_kwrest_arg?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 536

def missing_rest_arg_or_kwrest_arg?
  (@rest_arg_name && !forwarded_rest_arg) ||
    (@kwrest_arg_name && !forwarded_kwrest_arg)
end

#no_additional_args?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 527

def no_additional_args?
  forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size

  return false if missing_rest_arg_or_kwrest_arg?

  @def_node.arguments.size == forwardable_count &&
    @send_node.arguments.size == forwardable_count
end

#no_post_splat_args?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 502

def no_post_splat_args?
  return true unless (splat_index = arguments.index(forwarded_rest_arg))

  arg_after_splat = arguments[splat_index + 1]
  [nil, :hash, :block_pass].include?(arg_after_splat&.type)
end

#offensive_block_forwarding?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 470

def offensive_block_forwarding?
  @block_arg ? forwarded_block_arg : allow_offense_for_no_block?
end

#referenced_block_arg?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 490

def referenced_block_arg?
  @referenced_lvars.include?(@block_arg_name)
end

#referenced_kwrest_arg?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 486

def referenced_kwrest_arg?
  @referenced_lvars.include?(@kwrest_arg_name)
end

#referenced_rest_arg?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 482

def referenced_rest_arg?
  @referenced_lvars.include?(@rest_arg_name)
end

#ruby_30_or_lower_optarg?Boolean (readonly, private)

def foo(a = 41, …​) is a syntax error in 3.0.

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 458

def ruby_30_or_lower_optarg?
  target_ruby_version <= 3.0 && @def_node.arguments.any?(&:optarg_type?)
end

#ruby_32_only_anonymous_forwarding?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 462

def ruby_32_only_anonymous_forwarding?
  def_all_anonymous_args?(@def_node) && send_all_anonymous_args?(@send_node)
end

#ruby_32_or_higher_missing_rest_or_kwest?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 466

def ruby_32_or_higher_missing_rest_or_kwest?
  target_ruby_version >= 3.2 && !forwarded_rest_and_kwrest_args
end

Instance Method Details

#arguments (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 478

def arguments
  @send_node.arguments
end

#classification

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 431

def classification
  return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg

  if ruby_32_only_anonymous_forwarding?
    :all_anonymous
  elsif can_forward_all?
    :all
  else
    :rest_or_kwrest
  end
end

#def_all_anonymous_args?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 387

def_node_matcher :def_all_anonymous_args?, <<~PATTERN
  (
    def _
    (args ... (restarg) (kwrestarg) (blockarg nil?))
    _
  )
PATTERN

#extract_forwarded_kwrest_arg(node, kwrest_name)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 381

def_node_matcher :extract_forwarded_kwrest_arg, '(hash <$(kwsplat (lvar %1)) ...>)'

#forwarded_block_arg

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 425

def forwarded_block_arg
  return nil if referenced_block_arg?

  arguments.find { |arg| forwarded_block_arg?(arg, @block_arg_name) }
end

#forwarded_block_arg?(node, block_name)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 384

def_node_matcher :forwarded_block_arg?, '(block_pass {(lvar %1) nil?})'

#forwarded_kwrest_arg

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 419

def forwarded_kwrest_arg
  return nil if referenced_kwrest_arg?

  arguments.filter_map { |arg| extract_forwarded_kwrest_arg(arg, @kwrest_arg_name) }.first
end

#forwarded_rest_and_kwrest_args (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 474

def forwarded_rest_and_kwrest_args
  forwarded_rest_arg && forwarded_kwrest_arg
end

#forwarded_rest_arg

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 413

def forwarded_rest_arg
  return nil if referenced_rest_arg?

  arguments.find { |arg| forwarded_rest_arg?(arg, @rest_arg_name) }
end

#forwarded_rest_arg?(node, rest_name)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 378

def_node_matcher :forwarded_rest_arg?, '(splat (lvar %1))'

#send_all_anonymous_args?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 396

def_node_matcher :send_all_anonymous_args?, <<~PATTERN
  (
    send _ _
    ... (forwarded_restarg) (hash (forwarded_kwrestarg)) (block_pass nil?)
  )
PATTERN

#target_ruby_version (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/arguments_forwarding.rb', line 498

def target_ruby_version
  @config.fetch(:target_ruby_version)
end