class RuboCop::Cop::Style::Lambda

This cop (by default) checks for uses of the lambda literal syntax for single line lambdas, and the method call syntax for multiline lambdas. It is configurable to enforce one of the styles for both single line and multiline lambdas as well.

@example

# EnforcedStyle: line_count_dependent (default)

# bad
f = lambda { |x| x }
f = ->(x) do
      x
    end

# good
f = ->(x) { x }
f = lambda do |x|
      x
    end

@example

# EnforcedStyle: lambda

# bad
f = ->(x) { x }
f = ->(x) do
      x
    end

# good
f = lambda { |x| x }
f = lambda do |x|
      x
    end

@example

# EnforcedStyle: literal

# bad
f = lambda { |x| x }
f = lambda do |x|
      x
    end

# good
f = ->(x) { x }
f = ->(x) do
      x
    end

Constants

LITERAL_MESSAGE
METHOD_MESSAGE
OFFENDING_SELECTORS

Public Instance Methods

on_block(node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 75
def on_block(node)
  return unless node.lambda?

  selector = node.send_node.source

  return unless offending_selector?(node, selector)

  add_offense(node,
              location: node.send_node.source_range,
              message: message(node, selector))
end

Private Instance Methods

arg_to_unparenthesized_call?(arg_node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 175
def arg_to_unparenthesized_call?(arg_node)
  parent = arg_node.parent

  if parent && parent.pair_type?
    arg_node = parent.parent
    parent = arg_node.parent
  end

  return false unless parent && parent.send_type?
  return false if parenthesized_call?(parent)

  arg_node.sibling_index > 1
end
autocorrect(node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 110
def autocorrect(node)
  block_method, _args = *node
  selector = block_method.source

  # Don't autocorrect if this would change the meaning of the code
  return if selector == '->' && arg_to_unparenthesized_call?(node)

  lambda do |corrector|
    if selector == 'lambda'
      autocorrect_method_to_literal(corrector, node)
    else
      autocorrect_literal_to_method(corrector, node)
    end
  end
end
autocorrect_literal_to_method(corrector, node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 126
def autocorrect_literal_to_method(corrector, node)
  block_method, args = *node

  # Check for unparenthesized args' preceding and trailing whitespaces.
  remove_unparenthesized_whitespace(corrector, node)

  # Avoid correcting to `lambdado` by inserting whitespace
  # if none exists before or after the lambda arguments.
  if needs_whitespace?(block_method, args, node)
    corrector.insert_before(node.loc.begin, ' ')
  end
  corrector.replace(block_method.source_range, 'lambda')
  corrector.remove(args.source_range) if args.source_range
  return if args.children.empty?
  arg_str = " |#{lambda_arg_string(args)}|"
  corrector.insert_after(node.loc.begin, arg_str)
end
autocorrect_method_to_literal(corrector, node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 144
def autocorrect_method_to_literal(corrector, node)
  block_method, args = *node
  corrector.replace(block_method.source_range, '->')
  return if args.children.empty?

  arg_str = "(#{lambda_arg_string(args)})"
  whitespace_and_old_args = node.loc.begin.end.join(args.loc.end)
  corrector.insert_after(block_method.source_range, arg_str)
  corrector.remove(whitespace_and_old_args)
end
begin_pos(node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 163
def begin_pos(node)
  node.loc.begin && node.loc.begin.begin_pos
end
end_pos(node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 167
def end_pos(node)
  node.loc.end && node.loc.end.end_pos
end
lambda_arg_string(args) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 171
def lambda_arg_string(args)
  args.children.map(&:source).join(', ')
end
message(node, selector) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 95
def message(node, selector)
  message = selector == '->' ? METHOD_MESSAGE : LITERAL_MESSAGE

  format(message, message_line_modifier(node))
end
message_line_modifier(node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 101
def message_line_modifier(node)
  case style
  when :line_count_dependent
    node.multiline? ? 'multiline' : 'single line'
  else
    'all'
  end
end
needs_whitespace?(block_method, args, node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 155
def needs_whitespace?(block_method, args, node)
  selector_end = block_method.loc.selector.end.end_pos
  block_begin  = node.loc.begin.begin_pos

  (block_begin == end_pos(args) && selector_end == begin_pos(args)) ||
    (block_begin == selector_end)
end
offending_selector?(node, selector) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 89
def offending_selector?(node, selector)
  lines = node.multiline? ? :multiline : :single_line

  selector == OFFENDING_SELECTORS[:style][style][lines]
end
remove_leading_whitespace(node, corrector) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 198
def remove_leading_whitespace(node, corrector)
  corrector.remove_preceding(
    node.arguments.source_range,
    node.arguments.source_range.begin_pos -
      node.send_node.source_range.end_pos
  )
end
remove_trailing_whitespace(node, corrector) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 206
def remove_trailing_whitespace(node, corrector)
  corrector.remove_preceding(
    node.loc.begin,
    node.loc.begin.begin_pos - node.arguments.source_range.end_pos - 1
  )
end
remove_unparenthesized_whitespace(corrector, node) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 189
def remove_unparenthesized_whitespace(corrector, node)
  args = node.arguments

  return unless unparenthesized_literal_args?(args)

  remove_leading_whitespace(node, corrector)
  remove_trailing_whitespace(node, corrector)
end
unparenthesized_literal_args?(args) click to toggle source
# File lib/rubocop/cop/style/lambda.rb, line 213
def unparenthesized_literal_args?(args)
  args.source_range && args.source_range.begin && !parentheses?(args)
end