class RuboCop::Cop::Style::BlockDelimiters

Check for uses of braces or do/end around single line or multi-line blocks.

Methods that can be either procedural or functional and cannot be categorised from their usage alone is ignored. `lambda`, `proc`, and `it` are their defaults. Additional methods can be added to the `AllowedMethods`.

@example EnforcedStyle: line_count_based (default)

# bad - single line block
items.each do |item| item / 5 end

# good - single line block
items.each { |item| item / 5 }

# bad - multi-line block
things.map { |thing|
  something = thing.some_method
  process(something)
}

# good - multi-line block
things.map do |thing|
  something = thing.some_method
  process(something)
end

@example EnforcedStyle: semantic

# Prefer `do...end` over `{...}` for procedural blocks.

# return value is used/assigned
# bad
foo = map do |x|
  x
end
puts (map do |x|
  x
end)

# return value is not used out of scope
# good
map do |x|
  x
end

# Prefer `{...}` over `do...end` for functional blocks.

# return value is not used out of scope
# bad
each { |x|
  x
}

# return value is used/assigned
# good
foo = map { |x|
  x
}
map { |x|
  x
}.inspect

# The AllowBracesOnProceduralOneLiners option is allowed unless the
# EnforcedStyle is set to `semantic`. If so:

# If the AllowBracesOnProceduralOneLiners option is unspecified, or
# set to `false` or any other falsey value, then semantic purity is
# maintained, so one-line procedural blocks must use do-end, not
# braces.

# bad
collection.each { |element| puts element }

# good
collection.each do |element| puts element end

# If the AllowBracesOnProceduralOneLiners option is set to `true`, or
# any other truthy value, then one-line procedural blocks may use
# either style. (There is no setting for requiring braces on them.)

# good
collection.each { |element| puts element }

# also good
collection.each do |element| puts element end

@example EnforcedStyle: braces_for_chaining

# bad
words.each do |word|
  word.flip.flop
end.join("-")

# good
words.each { |word|
  word.flip.flop
}.join("-")

@example EnforcedStyle: always_braces

# bad
words.each do |word|
  word.flip.flop
end

# good
words.each { |word|
  word.flip.flop
}

@example BracesRequiredMethods: ['sig']

# Methods listed in the BracesRequiredMethods list, such as 'sig'
# in this example, will require `{...}` braces. This option takes
# precedence over all other configurations except AllowedMethods.

# bad
sig do
  params(
    foo: string,
  ).void
end
def bar(foo)
  puts foo
end

# good
sig {
  params(
    foo: string,
  ).void
}
def bar(foo)
  puts foo
end

@example AllowedMethods: ['lambda', 'proc', 'it' ] (default)

# good
foo = lambda do |x|
  puts "Hello, #{x}"
end

foo = lambda do |x|
  x * 100
end

@example AllowedPatterns: [] (default)

# bad
things.map { |thing|
  something = thing.some_method
  process(something)
}

@example AllowedPatterns: ['map']

# good
things.map { |thing|
  something = thing.some_method
  process(something)
}

Constants

ALWAYS_BRACES_MESSAGE
BRACES_REQUIRED_MESSAGE

Public Instance Methods

on_block(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 194
def on_block(node)
  return if ignored_node?(node)
  return if proper_block_style?(node)

  message = message(node)
  add_offense(node.loc.begin, message: message) do |corrector|
    autocorrect(corrector, node)
  end
end
Also aliased as: on_numblock
on_numblock(node)
Alias for: on_block
on_send(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 179
def on_send(node)
  return unless node.arguments?
  return if node.parenthesized?
  return if node.operator_method? || node.assignment_method?

  node.arguments.each do |arg|
    get_blocks(arg) do |block|
      # If there are no parentheses around the arguments, then braces
      # and do-end have different meaning due to how they bind, so we
      # allow either.
      ignore_node(block)
    end
  end
end

Private Instance Methods

array_or_range?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 443
def array_or_range?(node)
  node.array_type? || node.range_type?
end
autocorrect(corrector, node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 208
def autocorrect(corrector, node)
  return if correction_would_break_code?(node)

  if node.braces?
    replace_braces_with_do_end(corrector, node.loc)
  else
    replace_do_end_with_braces(corrector, node)
  end
end
begin_required?(block_node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 447
def begin_required?(block_node)
  # If the block contains `rescue` or `ensure`, it needs to be wrapped in
  # `begin`...`end` when changing `do-end` to `{}`.
  block_node.each_child_node(:rescue, :ensure).any? && !block_node.single_line?
end
braces_for_chaining_message(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 236
def braces_for_chaining_message(node)
  if node.multiline?
    if node.chained?
      'Prefer `{...}` over `do...end` for multi-line chained blocks.'
    else
      'Prefer `do...end` for multi-line blocks without chaining.'
    end
  else
    'Prefer `{...}` over `do...end` for single-line blocks.'
  end
end
braces_for_chaining_style?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 384
def braces_for_chaining_style?(node)
  block_begin = node.loc.begin.source

  block_begin == if node.multiline?
                   (node.chained? ? '{' : 'do')
                 else
                   '{'
                 end
end
braces_required_message(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 248
def braces_required_message(node)
  format(BRACES_REQUIRED_MESSAGE, method_name: node.method_name.to_s)
end
braces_required_method?(method_name) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 361
def braces_required_method?(method_name)
  braces_required_methods.include?(method_name.to_s)
end
braces_required_methods() click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 365
def braces_required_methods
  cop_config.fetch('BracesRequiredMethods', [])
end
braces_style?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 394
def braces_style?(node)
  node.loc.begin.source == '{'
end
conditional?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 439
def conditional?(node)
  node.if_type? || node.or_type? || node.and_type?
end
correction_would_break_code?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 398
def correction_would_break_code?(node)
  return unless node.keywords?

  node.send_node.arguments? && !node.send_node.parenthesized?
end
end_of_chain(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 309
def end_of_chain(node)
  return end_of_chain(node.block_node) if with_block?(node)
  return node unless node.chained?

  end_of_chain(node.parent)
end
functional_block?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 408
def functional_block?(node)
  return_value_used?(node) || return_value_of_scope?(node)
end
functional_method?(method_name) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 404
def functional_method?(method_name)
  cop_config['FunctionalMethods'].map(&:to_sym).include?(method_name)
end
get_blocks(node) { |node| ... } click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 320
def get_blocks(node, &block)
  case node.type
  when :block, :numblock
    yield node
  when :send
    get_blocks(node.receiver, &block) if node.receiver
  when :hash
    # A hash which is passed as method argument may have no braces
    # In that case, one of the K/V pairs could contain a block node
    # which could change in meaning if do...end replaced {...}
    return if node.braces?

    node.each_child_node { |child| get_blocks(child, &block) }
  when :pair
    node.each_child_node { |child| get_blocks(child, &block) }
  end
end
line_count_based_block_style?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 369
def line_count_based_block_style?(node)
  node.multiline? ^ node.braces?
end
line_count_based_message(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 218
def line_count_based_message(node)
  if node.multiline?
    'Avoid using `{...}` for multi-line blocks.'
  else
    'Prefer `{...}` over `do...end` for single-line blocks.'
  end
end
message(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 252
def message(node)
  return braces_required_message(node) if braces_required_method?(node.method_name)

  case style
  when :line_count_based    then line_count_based_message(node)
  when :semantic            then semantic_message(node)
  when :braces_for_chaining then braces_for_chaining_message(node)
  when :always_braces       then ALWAYS_BRACES_MESSAGE
  end
end
move_comment_before_block(corrector, comment, block_node, closing_brace) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 300
def move_comment_before_block(corrector, comment, block_node, closing_brace)
  range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
  comment_range = range_between(range.end_pos, comment.loc.expression.end_pos)
  corrector.remove(range_with_surrounding_space(comment_range, side: :right))
  corrector.insert_after(range, "\n")

  corrector.insert_before(block_node, "#{comment.text}\n")
end
procedural_method?(method_name) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 416
def procedural_method?(method_name)
  cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name)
end
procedural_oneliners_may_have_braces?() click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 412
def procedural_oneliners_may_have_braces?
  cop_config['AllowBracesOnProceduralOneLiners']
end
proper_block_style?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 338
def proper_block_style?(node)
  return special_method_proper_block_style?(node) if special_method?(node.method_name)

  case style
  when :line_count_based    then line_count_based_block_style?(node)
  when :semantic            then semantic_block_style?(node)
  when :braces_for_chaining then braces_for_chaining_style?(node)
  when :always_braces       then braces_style?(node)
  end
end
replace_braces_with_do_end(corrector, loc) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 263
def replace_braces_with_do_end(corrector, loc)
  b = loc.begin
  e = loc.end

  corrector.insert_before(b, ' ') unless whitespace_before?(b)
  corrector.insert_before(e, ' ') unless whitespace_before?(e)
  corrector.insert_after(b, ' ') unless whitespace_after?(b)
  corrector.replace(b, 'do')

  if (comment = processed_source.comment_at_line(e.line))
    move_comment_before_block(corrector, comment, loc.node, e)
  end

  corrector.replace(e, 'end')
end
replace_do_end_with_braces(corrector, node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 279
def replace_do_end_with_braces(corrector, node)
  loc = node.loc
  b = loc.begin
  e = loc.end

  corrector.insert_after(b, ' ') unless whitespace_after?(b, 2)

  corrector.replace(b, '{')
  corrector.replace(e, '}')

  corrector.wrap(node.body, "begin\n", "\nend") if begin_required?(node)
end
return_value_of_scope?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 432
def return_value_of_scope?(node)
  return unless node.parent

  conditional?(node.parent) || array_or_range?(node.parent) ||
    node.parent.children.last == node
end
return_value_used?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 420
def return_value_used?(node)
  return unless node.parent

  # If there are parentheses around the block, check if that
  # is being used.
  if node.parent.begin_type?
    return_value_used?(node.parent)
  else
    node.parent.assignment? || node.parent.send_type?
  end
end
semantic_block_style?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 373
def semantic_block_style?(node)
  method_name = node.method_name

  if node.braces?
    functional_method?(method_name) || functional_block?(node) ||
      (procedural_oneliners_may_have_braces? && !node.multiline?)
  else
    procedural_method?(method_name) || !return_value_used?(node)
  end
end
semantic_message(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 226
def semantic_message(node)
  block_begin = node.loc.begin.source

  if block_begin == '{'
    'Prefer `do...end` over `{...}` for procedural blocks.'
  else
    'Prefer `{...}` over `do...end` for functional blocks.'
  end
end
special_method?(method_name) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 349
def special_method?(method_name)
  allowed_method?(method_name) ||
    matches_allowed_pattern?(method_name) ||
    braces_required_method?(method_name)
end
special_method_proper_block_style?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 355
def special_method_proper_block_style?(node)
  method_name = node.method_name
  return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
  return node.braces? if braces_required_method?(method_name)
end
whitespace_after?(range, length = 1) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 296
def whitespace_after?(range, length = 1)
  /\s/.match?(range.source_buffer.source[range.begin_pos + length, 1])
end
whitespace_before?(range) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 292
def whitespace_before?(range)
  /\s/.match?(range.source_buffer.source[range.begin_pos - 1, 1])
end
with_block?(node) click to toggle source
# File lib/rubocop/cop/style/block_delimiters.rb, line 316
def with_block?(node)
  node.respond_to?(:block_node) && node.block_node
end