class RuboCop::Cop::Style::LineEndConcatenation

Checks for string literal concatenation at the end of a line.

@safety

This cop is unsafe because it cannot be guaranteed that the
receiver is a string, in which case replacing `<<` with `\`
would result in a syntax error.

For example, this would be a false positive:
[source,ruby]
----
array << 'foo' <<
         'bar' <<
         'baz'
----

@example

# bad
some_str = 'ala' +
           'bala'

some_str = 'ala' <<
           'bala'

# good
some_str = 'ala' \
           'bala'

Constants

COMPLEX_STRING_BEGIN_TOKEN
COMPLEX_STRING_END_TOKEN
CONCAT_TOKEN_TYPES
HIGH_PRECEDENCE_OP_TOKEN_TYPES
MSG
QUOTE_DELIMITERS
SIMPLE_STRING_TOKEN_TYPE

Public Class Methods

autocorrect_incompatible_with() click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 47
def self.autocorrect_incompatible_with
  [Style::RedundantInterpolation]
end

Public Instance Methods

on_new_investigation() click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 51
def on_new_investigation
  processed_source.tokens.each_index { |index| check_token_set(index) }
end

Private Instance Methods

autocorrect(corrector, operator_range) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 71
def autocorrect(corrector, operator_range)
  # Include any trailing whitespace so we don't create a syntax error.
  operator_range = range_with_surrounding_space(operator_range,
                                                side: :right,
                                                newlines: false)
  one_more_char = operator_range.resize(operator_range.size + 1)
  # Don't create a double backslash at the end of the line, in case
  # there already was a backslash after the concatenation operator.
  operator_range = one_more_char if one_more_char.source.end_with?('\\')

  corrector.replace(operator_range, '\\')
end
check_token_set(index) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 57
def check_token_set(index)
  predecessor, operator, successor = processed_source.tokens[index, 3]

  return unless eligible_token_set?(predecessor, operator, successor)

  return if same_line?(operator, successor)

  next_successor = token_after_last_string(successor, index)

  return unless eligible_next_successor?(next_successor)

  add_offense(operator.pos) { |corrector| autocorrect(corrector, operator.pos) }
end
eligible_next_successor?(next_successor) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 98
def eligible_next_successor?(next_successor)
  !(next_successor && HIGH_PRECEDENCE_OP_TOKEN_TYPES.include?(next_successor.type))
end
eligible_operator?(operator) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 94
def eligible_operator?(operator)
  CONCAT_TOKEN_TYPES.include?(operator.type)
end
eligible_predecessor?(predecessor) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 102
def eligible_predecessor?(predecessor)
  standard_string_literal?(predecessor)
end
eligible_successor?(successor) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 90
def eligible_successor?(successor)
  successor && standard_string_literal?(successor)
end
eligible_token_set?(predecessor, operator, successor) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 84
def eligible_token_set?(predecessor, operator, successor)
  eligible_successor?(successor) &&
    eligible_operator?(operator) &&
    eligible_predecessor?(predecessor)
end
standard_string_literal?(token) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 121
def standard_string_literal?(token)
  case token.type
  when SIMPLE_STRING_TOKEN_TYPE
    true
  when COMPLEX_STRING_BEGIN_TOKEN, COMPLEX_STRING_END_TOKEN
    QUOTE_DELIMITERS.include?(token.text)
  else
    false
  end
end
token_after_last_string(successor, base_index) click to toggle source
# File lib/rubocop/cop/style/line_end_concatenation.rb, line 106
def token_after_last_string(successor, base_index)
  index = base_index + 3
  if successor.type == COMPLEX_STRING_BEGIN_TOKEN
    ends_to_find = 1
    while ends_to_find.positive?
      case processed_source.tokens[index].type
      when COMPLEX_STRING_BEGIN_TOKEN then ends_to_find += 1
      when COMPLEX_STRING_END_TOKEN then ends_to_find -= 1
      end
      index += 1
    end
  end
  processed_source.tokens[index]
end