class RubbyCop::Cop::Style::BlockDelimiters

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

Public Instance Methods

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

  add_offense(node, :begin) unless proper_block_style?(node)
end
on_send(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 11
def on_send(node)
  return unless node.arguments?
  return if node.parenthesized? || node.operator_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/rubbycop/cop/style/block_delimiters.rb, line 222
def array_or_range?(node)
  node.array_type? || node.irange_type? || node.erange_type?
end
autocorrect(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 71
def autocorrect(node)
  return if correction_would_break_code?(node)

  if node.loc.begin.is?('{')
    replace_braces_with_do_end(node.loc)
  else
    replace_do_end_with_braces(node.loc)
  end
end
braces_for_chaining_message(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 51
def braces_for_chaining_message(node)
  if block_length(node) > 0
    if return_value_chaining?(node)
      '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/rubbycop/cop/style/block_delimiters.rb, line 158
def braces_for_chaining_style?(node)
  block_length = block_length(node)
  block_begin = node.loc.begin.source

  block_begin == if block_length > 0
                   (return_value_chaining?(node) ? '{' : 'do')
                 else
                   '{'
                 end
end
conditional?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 218
def conditional?(node)
  node.if_type? || node.or_type? || node.and_type?
end
correction_would_break_code?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 173
def correction_would_break_code?(node)
  return unless node.loc.begin.is?('do')

  # Converting `obj.method arg do |x| end` to use `{}` would cause
  # a syntax error.
  send = node.children.first

  send.arguments? && !send.parenthesized?
end
functional_block?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 191
def functional_block?(node)
  return_value_used?(node) || return_value_of_scope?(node)
end
functional_method?(method_name) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 187
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/rubbycop/cop/style/block_delimiters.rb, line 113
def get_blocks(node, &block)
  case node.type
  when :block
    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.loc.begin
    node.each_child_node { |child| get_blocks(child, &block) }
  when :pair
    node.each_child_node { |child| get_blocks(child, &block) }
  end
  nil
end
ignored_method?(method_name) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 183
def ignored_method?(method_name)
  cop_config['IgnoredMethods'].map(&:to_sym).include?(method_name)
end
line_count_based_block_style?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 141
def line_count_based_block_style?(node)
  block_begin = node.loc.begin.source

  (block_length(node) > 0) ^ (block_begin == '{')
end
line_count_based_message(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 33
def line_count_based_message(node)
  if block_length(node) > 0
    '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/rubbycop/cop/style/block_delimiters.rb, line 63
def message(node)
  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)
  end
end
procedural_method?(method_name) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 195
def procedural_method?(method_name)
  cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name)
end
proper_block_style?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 131
def proper_block_style?(node)
  return true if ignored_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)
  end
end
replace_braces_with_do_end(loc) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 81
def replace_braces_with_do_end(loc)
  b = loc.begin
  e = loc.end

  lambda do |corrector|
    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')
    corrector.replace(e, 'end')
  end
end
replace_do_end_with_braces(loc) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 94
def replace_do_end_with_braces(loc)
  b = loc.begin
  e = loc.end

  lambda do |corrector|
    corrector.insert_after(b, ' ') unless whitespace_after?(b, 2)
    corrector.replace(b, '{')
    corrector.replace(e, '}')
  end
end
return_value_chaining?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 169
def return_value_chaining?(node)
  node.parent && node.parent.send_type? && node.parent.dot?
end
return_value_of_scope?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 211
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/rubbycop/cop/style/block_delimiters.rb, line 199
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/rubbycop/cop/style/block_delimiters.rb, line 147
def semantic_block_style?(node)
  method_name = node.method_name
  block_begin = node.loc.begin.source

  if block_begin == '{'
    functional_method?(method_name) || functional_block?(node)
  else
    procedural_method?(method_name) || !return_value_used?(node)
  end
end
semantic_message(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 41
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
whitespace_after?(node, length = 1) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 109
def whitespace_after?(node, length = 1)
  node.source_buffer.source[node.begin_pos + length, 1] =~ /\s/
end
whitespace_before?(node) click to toggle source
# File lib/rubbycop/cop/style/block_delimiters.rb, line 105
def whitespace_before?(node)
  node.source_buffer.source[node.begin_pos - 1, 1] =~ /\s/
end