class RuboCop::Cop::Lint::DuplicateRegexpCharacterClassElement

r = /[0-9x]/
# good
r = /[xy]/
# good
r = /[0-9x0-9]/
# bad
r = /[xyx]/
# bad
@example
Checks for duplicate elements in Regexp character classes.

def each_repeated_character_class_element_loc(node)

rubocop:disable Metrics/AbcSize, Metrics/MethodLength
def each_repeated_character_class_element_loc(node)
  node.parsed_tree&.each_expression do |expr|
    next if skip_expression?(expr)
    seen = Set.new
    enum = expr.expressions.to_enum
    expression_count = expr.expressions.count
    expression_count.times do |current_number|
      current_child = enum.next
      next if within_interpolation?(node, current_child)
      current_child_source = current_child.to_s
      next_child = enum.peek if current_number + 1 < expression_count
      if seen.include?(current_child_source)
        next if start_with_escaped_zero_number?(current_child_source, next_child.to_s)
        yield current_child.expression
      end
      seen << current_child_source
    end
  end
end

def interpolation_locs(node)

def interpolation_locs(node)
  @interpolation_locs ||= {}
  # Cache by loc, not by regexp content, as content can be repeated in multiple patterns
  key = node.loc
  @interpolation_locs[key] ||= node.children.select(&:begin_type?).map(&:source_range)
end

def on_regexp(node)

def on_regexp(node)
  each_repeated_character_class_element_loc(node) do |loc|
    add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
      corrector.remove(loc)
    end
  end
end

def skip_expression?(expr)

def skip_expression?(expr)
  expr.type != :set || expr.token == :intersection
end

def start_with_escaped_zero_number?(current_child, next_child)

def start_with_escaped_zero_number?(current_child, next_child)
  # Represents escaped code from `"\00"` (`"\u0000"`) to `"\07"` (`"\a"`).
  current_child == '\\0' && next_child.match?(/[0-7]/)
end

def within_interpolation?(node, child)

that are within an interpolation.
mark every space (except the first) as duplicate if we do not skip regexp_parser nodes
Since we blank interpolations with a space for every char of the interpolation, we would
def within_interpolation?(node, child)
  parse_tree_child_loc = child.expression
  interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
end