class RuboCop::Cop::Lint::MixedCaseRange
Checks for mixed-case character ranges since they include likely unintended characters.
Offenses are registered for regexp character classes like ‘/[A-z]/` as well as range objects like `(’A’..‘z’)‘.
NOTE: Range objects cannot be autocorrected.
@safety
The cop autocorrects regexp character classes by replacing one character range with two: `A-z` becomes `A-Za-z`. In most cases this is probably what was originally intended but it changes the regexp to no longer match symbols it used to include. For this reason, this cop's autocorrect is unsafe (it will change the behavior of the code).
@example
# bad r = /[A-z]/ # good r = /[A-Za-z]/
Constants
- MSG
- RANGES
Public Instance Methods
each_unsafe_regexp_range(node) { |build_source_range(range_start, range_end)| ... }
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 58 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
on_irange(node)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 37 def on_irange(node) return unless node.children.compact.all?(&:str_type?) range_start, range_end = node.children return if range_start.nil? || range_end.nil? add_offense(node) if unsafe_range?(range_start.value, range_end.value) end
Also aliased as: on_erange
on_regexp(node)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 48 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
Private Instance Methods
build_source_range(range_start, range_end)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 74 def build_source_range(range_start, range_end) range_between(range_start.expression.begin_pos, range_end.expression.end_pos) end
range_for(char)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 78 def range_for(char) RANGES.detect do |range| range.include?(char) end end
range_pairs(expr)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 84 def range_pairs(expr) RuboCop::Cop::Utils::RegexpRanges.new(expr).pairs end
regexp_range(source)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 104 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
skip_expression?(expr)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 94 def skip_expression?(expr) !(expr.type == :set && expr.token == :character) end
skip_range?(range_start, range_end)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 98 def skip_range?(range_start, range_end) [range_start, range_end].any? do |bound| bound.type != :literal end end
unsafe_range?(range_start, range_end)
click to toggle source
# File lib/rubocop/cop/lint/mixed_case_range.rb, line 88 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