class RuboCop::Cop::Style::RedundantRegexpCharacterClass

Checks for unnecessary single-element Regexp character classes.

@example

# bad
r = /[x]/

# good
r = /x/

# bad
r = /[\s]/

# good
r = /\s/

# bad
r = %r{/[b]}

# good
r = %r{/b}

# good
r = /[ab]/

Constants

MSG_REDUNDANT_CHARACTER_CLASS
REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS

Public Instance Methods

on_regexp(node) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 37
def on_regexp(node)
  each_redundant_character_class(node) do |loc|
    add_offense(
      loc, message: format(
        MSG_REDUNDANT_CHARACTER_CLASS,
        char_class: loc.source,
        element: without_character_class(loc)
      )
    ) do |corrector|
      corrector.replace(loc, without_character_class(loc))
    end
  end
end

Private Instance Methods

backslash_b?(elem) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 101
def backslash_b?(elem)
  # \b's behavior is different inside and outside of a character class, matching word
  # boundaries outside but backspace (0x08) when inside.
  elem == '\b'
end
backslash_zero?(elem) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 107
def backslash_zero?(elem)
  # See https://github.com/rubocop/rubocop/issues/11067 for details - in short "\0" != "0" -
  # the former means an Unicode code point `"\u0000"`, the latter a number character `"0"`.
  # Similarly "\032" means "\u001A". Other numbers starting with "\0" can also be mentioned.
  elem == '\0'
end
each_redundant_character_class(node) { |loc.body| ... } click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 53
def each_redundant_character_class(node)
  each_single_element_character_class(node) do |char_class|
    next unless redundant_single_element_character_class?(node, char_class)

    yield char_class.loc.body
  end
end
each_single_element_character_class(node) { |expr| ... } click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 61
def each_single_element_character_class(node)
  node.parsed_tree&.each_expression do |expr|
    next if expr.type != :set || expr.expressions.size != 1
    next if expr.negative?
    next if %i[set posixclass nonposixclass].include?(expr.expressions.first.type)
    next if multiple_codepoins?(expr.expressions.first)

    yield expr
  end
end
multiple_codepoins?(expression) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 83
def multiple_codepoins?(expression)
  expression.respond_to?(:codepoints) && expression.codepoints.count >= 2
end
redundant_single_element_character_class?(node, char_class) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 72
def redundant_single_element_character_class?(node, char_class)
  class_elem = char_class.expressions.first.text

  non_redundant =
    whitespace_in_free_space_mode?(node, class_elem) ||
    backslash_b?(class_elem) || backslash_zero?(class_elem) ||
    requires_escape_outside_char_class?(class_elem)

  !non_redundant
end
requires_escape_outside_char_class?(elem) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 114
def requires_escape_outside_char_class?(elem)
  REQUIRES_ESCAPE_OUTSIDE_CHAR_CLASS_CHARS.include?(elem)
end
whitespace_in_free_space_mode?(node, elem) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 95
def whitespace_in_free_space_mode?(node, elem)
  return false unless node.extended?

  /\s/.match?(elem)
end
without_character_class(loc) click to toggle source
# File lib/rubocop/cop/style/redundant_regexp_character_class.rb, line 87
def without_character_class(loc)
  without_character_class = loc.source[1..-2]

  # Adds `\` to prevent autocorrection that changes to an interpolated string when `[#]`.
  # e.g. From `/[#]{0}/` to `/#{0}/`
  loc.source == '[#]' ? "\\#{without_character_class}" : without_character_class
end