class RuboCop::Cop::Lint::DuplicateRegexpCharacterClassElement
Checks for duplicate elements in Regexp character classes.
@example
# bad r = /[xyx]/ # bad r = /[0-9x0-9]/ # good r = /[xy]/ # good r = /[0-9x]/
Constants
- MSG_REPEATED_ELEMENT
Public Instance Methods
each_repeated_character_class_element_loc(node) { |expression| ... }
click to toggle source
rubocop:disable Metrics/AbcSize, Metrics/MethodLength
# File lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb, line 36 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
on_regexp(node)
click to toggle source
# File lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb, line 27 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
Private Instance Methods
interpolation_locs(node)
click to toggle source
# File lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb, line 83 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 do |interpolation| interpolation.loc.expression end end
skip_expression?(expr)
click to toggle source
rubocop:enable Metrics/AbcSize, Metrics/MethodLength
# File lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb, line 65 def skip_expression?(expr) expr.type != :set || expr.token == :intersection end
start_with_escaped_zero_number?(current_child, next_child)
click to toggle source
# File lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb, line 78 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
within_interpolation?(node, child)
click to toggle source
Since we blank interpolations with a space for every char of the interpolation, we would mark every space (except the first) as duplicate if we do not skip regexp_parser nodes that are within an interpolation.
# File lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb, line 72 def within_interpolation?(node, child) parse_tree_child_loc = child.expression interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) } end