class RuboCop::Cop::Lint::MixedCaseRange

r = /[A-Za-z]/
# good
r = /[A-z]/
# bad
@example
change the behavior of the code).
For this reason, this cop’s autocorrect is unsafe (it will
but it changes the regexp to no longer match symbols it used to include.
In most cases this is probably what was originally intended
by replacing one character range with two: ‘A-z` becomes `A-Za-z`.
The cop autocorrects regexp character classes
@safety
NOTE: Range objects cannot be autocorrected.
as well as range objects like `(’A’..‘z’)‘.
Offenses are registered for regexp character classes like `/[A-z]/`
Checks for mixed-case character ranges since they include likely unintended characters.

def build_source_range(range_start, range_end)

def build_source_range(range_start, range_end)
  range_between(range_start.expression.begin_pos, range_end.expression.end_pos)
end

def each_unsafe_regexp_range(node)

def each_unsafe_regexp_range(node)
  node.parsed_tree&.each_expression do |expr|
    next if skip_expression?(expr)
    range_pairs(expr).reject do |range_start, range_end|
      next if skip_range?(range_start, range_end)
      next unless unsafe_range?(range_start.text, range_end.text)
      yield(build_source_range(range_start, range_end))
    end
  end
end

def on_irange(node)

def on_irange(node)
  return unless node.children.compact.all?(&:str_type?)
  return if node.begin.nil? || node.end.nil?
  add_offense(node) if unsafe_range?(node.begin.value, node.end.value)
end

def on_regexp(node)

def on_regexp(node)
  each_unsafe_regexp_range(node) do |loc|
    next unless (replacement = regexp_range(loc.source))
    add_offense(loc) do |corrector|
      corrector.replace(loc, replacement)
    end
  end
end

def range_for(char)

def range_for(char)
  RANGES.detect do |range|
    range.include?(char)
  end
end

def range_pairs(expr)

def range_pairs(expr)
  RuboCop::Cop::Utils::RegexpRanges.new(expr).pairs
end

def regexp_range(source)

def regexp_range(source)
  open, close = source.split('-')
  return unless (open_range = range_for(open))
  return unless (close_range = range_for(close))
  first = [open, open_range.end]
  second = [close_range.begin, close]
  "#{first.uniq.join('-')}#{second.uniq.join('-')}"
end

def skip_expression?(expr)

def skip_expression?(expr)
  !(expr.type == :set && expr.token == :character)
end

def skip_range?(range_start, range_end)

def skip_range?(range_start, range_end)
  [range_start, range_end].any? do |bound|
    bound.type != :literal
  end
end

def unsafe_range?(range_start, range_end)

def unsafe_range?(range_start, range_end)
  return false if range_start.length != 1 || range_end.length != 1
  range_for(range_start) != range_for(range_end)
end