class RuboCop::Cop::Style::StringConcatenation


email_with_name = format(‘%s <%s>’, user.name, user.email)
email_with_name = “#{user.name} <#{user.email}>”
# good
email_with_name = user.name + ‘ <’ + user.email + ‘>’
# bad
@example
variables or methods which you can then interpolate in a string.
In those cases, it might be useful to extract statements to local
more complex cases where the resulting code would be harder to read.
The cop can autocorrect simple cases but will skip autocorrecting
can be replaced with string interpolation.
This cop checks for places where string concatenation

def collect_parts(node, parts)

def collect_parts(node, parts)
  return unless node
  if plus_node?(node)
    collect_parts(node.receiver, parts)
    collect_parts(node.first_argument, parts)
  else
    parts << node
  end
end

def corrected_ancestor?(node)

def corrected_ancestor?(node)
  node.each_ancestor(:send).any? { |ancestor| @corrected_nodes&.include?(ancestor) }
end

def find_topmost_plus_node(node)

def find_topmost_plus_node(node)
  current = node
  while (parent = current.parent) && plus_node?(parent)
    current = parent
  end
  current
end

def on_new_investigation

def on_new_investigation
  @corrected_nodes = nil
end

def on_send(node)

def on_send(node)
  return unless string_concatenation?(node)
  topmost_plus_node = find_topmost_plus_node(node)
  parts = []
  collect_parts(topmost_plus_node, parts)
  add_offense(topmost_plus_node) do |corrector|
    correctable_parts = parts.none? { |part| uncorrectable?(part) }
    if correctable_parts && !corrected_ancestor?(topmost_plus_node)
      corrector.replace(topmost_plus_node, replacement(parts))
      @corrected_nodes ||= Set.new.compare_by_identity
      @corrected_nodes.add(topmost_plus_node)
    end
  end
end

def plus_node?(node)

def plus_node?(node)
  node.send_type? && node.method?(:+)
end

def replacement(parts)

def replacement(parts)
  interpolated_parts =
    parts.map do |part|
      if part.str_type?
        if single_quoted?(part)
          part.value.gsub('\\') { '\\\\' }
        else
          part.value.inspect[1..-2]
        end
      else
        "\#{#{part.source}}"
      end
    end
  "\"#{interpolated_parts.join}\""
end

def single_quoted?(str_node)

def single_quoted?(str_node)
  str_node.source.start_with?("'")
end

def uncorrectable?(part)

def uncorrectable?(part)
  part.multiline? ||
    part.dstr_type? ||
    (part.str_type? && part.heredoc?) ||
    part.each_descendant(:block).any?
end