123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::ParenthesesCorrector

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

Overview

This autocorrects parentheses

Constant Summary

Class Method Summary

RangeHelp - Extended

add_range,
arguments_range

A range containing the first to the last argument of a method call or method definition.

column_offset_between,
contents_range

A range containing only the contents of a literal with delimiters (e.g.

directions,
effective_column

Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value.

final_pos

rubocop:disable-next Metrics/ParameterLists.

move_pos, move_pos_str, range_between, range_by_whole_lines, range_with_comments, range_with_comments_and_lines, range_with_surrounding_comma, range_with_surrounding_space, source_range

Class Method Details

.add_heredoc_comma(corrector, node) (private)

Add a comma back after the heredoc identifier

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 145

def add_heredoc_comma(corrector, node)
  return unless heredoc?(node)

  corrector.insert_after(node.child_nodes.last, ',')
end

.chained_after_close_paren?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 89

def chained_after_close_paren?(node)
  trimmed = text_after_close_paren(node)

  !trimmed.empty? && !trimmed.start_with?('#')
end

.comment_above_close_paren_swallows_chain?(node, buffer) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 75

def comment_above_close_paren_swallows_chain?(node, buffer)
  last_child = node.children.last
  return false unless last_child

  body_end = last_child.source_range.end_pos
  close_paren_begin = node.loc.end.begin_pos
  return false if body_end >= close_paren_begin

  source_between = buffer.source[body_end...close_paren_begin]
  return false unless source_between.match?(/#[^\n]*\n/)

  chained_after_close_paren?(node)
end

.correct(corrector, node)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 12

def correct(corrector, node)
  buffer = node.source_range.source_buffer
  corrector.remove(range_with_surrounding_space(range: node.loc.begin, buffer: buffer,
                                                side: :right, whitespace: true))
  remove_close_paren(corrector, node, buffer)
  handle_orphaned_comma(corrector, node)

  return unless ternary_condition?(node) && next_char_is_question_mark?(node)

  corrector.insert_after(node.loc.end, ' ')
end

.extend_range_for_heredoc(node, range) (private)

If the node contains a heredoc, remove the comma too It’ll be added back in the right place later

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 135

def extend_range_for_heredoc(node, range)
  return range unless heredoc?(node)

  comma_line = range_by_whole_lines(node.loc.end, buffer: node.source_range.source_buffer)
  offset = comma_line.source.match(COMMA_REGEXP)[0]&.size || 0

  range.adjust(end_pos: offset)
end

.handle_orphaned_comma(corrector, node) (private)

If removing parentheses leaves a comma on its own line, remove all the whitespace preceding it to prevent a syntax error.

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 112

def handle_orphaned_comma(corrector, node)
  return unless only_closing_paren_before_comma?(node)

  range = extend_range_for_heredoc(node, parens_range(node))
  corrector.remove(range)

  add_heredoc_comma(corrector, node)
end

.heredoc?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 151

def heredoc?(node)
  node.child_nodes.last.loc.is_a?(Parser::Source::Map::Heredoc)
end

.heredoc_above_close_paren_swallows_chain?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 53

def heredoc_above_close_paren_swallows_chain?(node)
  return false unless heredoc_body_before_close_paren?(node)

  text_after_close_paren(node).start_with?('.', '&.')
end

.heredoc_body_before_close_paren?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 59

def heredoc_body_before_close_paren?(node)
  return false unless (last_child = node.children.last)

  close_paren_pos = node.loc.end.begin_pos

  last_child.each_node(:any_str).any? do |str_node|
    str_node.heredoc? && str_node.loc.heredoc_end.end_pos <= close_paren_pos
  end
end

.heredoc_strands_code_after_close_paren?(node) ⇒ Boolean

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 24

def heredoc_strands_code_after_close_paren?(node)
  return false unless heredoc_body_before_close_paren?(node)

  after_paren = text_after_close_paren(node)

  !after_paren.empty? && !after_paren.start_with?('.', '&.', ',')
end

.next_char_is_question_mark?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 99

def next_char_is_question_mark?(node)
  node.loc.last_column == node.parent.loc.question.column
end

.only_closing_paren_before_comma?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 103

def only_closing_paren_before_comma?(node)
  source_buffer = node.source_range.source_buffer
  line_range = source_buffer.line_range(node.loc.end.line)

  line_range.source.start_with?(/\s*\)\s*,/)
end

.parens_range(node) (private)

Get a range for the closing parenthesis and all whitespace to the left of it

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 122

def parens_range(node)
  range_with_surrounding_space(
    range: node.loc.end,
    buffer: node.source_range.source_buffer,
    side: :left,
    newlines: true,
    whitespace: true,
    continuations: true
  )
end

.remove_close_paren(corrector, node, buffer) (private)

When the line above ) ends with a comment and a chained call follows ), crossing the newline would pull the chain into the comment. Preserve the newline. When a heredoc body comes before ), a chained call has to start on the line after the terminator instead, since the terminator must stay alone on its line.

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 38

def remove_close_paren(corrector, node, buffer)
  if heredoc_above_close_paren_swallows_chain?(node)
    range = range_with_surrounding_space(range: node.loc.end, buffer: buffer, side: :left)

    corrector.replace(range, "\n")
  else
    newlines = !comment_above_close_paren_swallows_chain?(node, buffer)
    range = range_with_surrounding_space(
      range: node.loc.end, buffer: buffer, side: :left, newlines: newlines
    )

    corrector.remove(range)
  end
end

.ternary_condition?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 95

def ternary_condition?(node)
  node.parent&.if_type? && node.parent.ternary?
end

.text_after_close_paren(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/correctors/parentheses_corrector.rb', line 69

def text_after_close_paren(node)
  close_paren = node.loc.end

  close_paren.source_line[(close_paren.column + 1)..].to_s.lstrip
end