Class: RuboCop::Cop::Rouge::NoBuildingAlternationPatternInRegexp

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/rouge/no_building_alternation_pattern_in_regexp.rb

Overview

Checks for the use of ‘.join(’|‘)` or `Regexp.union` inside interpolated regular expressions.

Building alternation patterns by joining arrays or using ‘Regexp.union` inside a regexp harms performance — the regex engine must backtrack through every alternative on each match attempt. It also risks quoting bugs when values contain regex metacharacters.

Prefer a ‘Set` lookup with a simple regex pattern instead.

Examples:

# bad — joins an array into a huge alternation regex
KEYWORDS = %w[if else while ...]
rule %r/\b(#{KEYWORDS.join('|')})\b/, Keyword

# bad — Regexp.union inside a regex has the same problem
rule %r/#{Regexp.union(keywords)}/

# good — simple regex + Set lookup
KEYWORDS = Set.new(%w[if else while ...])
rule %r/\w+/ do |m|
  if KEYWORDS.include?(m[0])
    token Keyword
  else
    token Name
  end
end

Constant Summary collapse

MSG =
'Avoid building alternation patterns inside a regexp. ' \
'Use a Set lookup with a simple regex pattern for performance.'

Instance Method Summary collapse

Instance Method Details

#join_pipe_call?(node) ⇒ Object

Checks if a node is a ‘.join(’|‘)` or `.join(“|”)` call.



41
42
43
# File 'lib/rubocop/cop/rouge/no_building_alternation_pattern_in_regexp.rb', line 41

def_node_matcher :join_pipe_call?, <<~PATTERN
  (send _ :join (str "|"))
PATTERN

#on_regexp(node) ⇒ Object



51
52
53
54
55
56
57
# File 'lib/rubocop/cop/rouge/no_building_alternation_pattern_in_regexp.rb', line 51

def on_regexp(node)
  node.children.each do |child|
    next unless child.begin_type?

    find_offending_calls(child)
  end
end

#regexp_union_call?(node) ⇒ Object

Checks if a node is a ‘Regexp.union(…)` call.



47
48
49
# File 'lib/rubocop/cop/rouge/no_building_alternation_pattern_in_regexp.rb', line 47

def_node_matcher :regexp_union_call?, <<~PATTERN
  (send (const nil? :Regexp) :union ...)
PATTERN