class RuboCop::Cop::Lint::OutOfRangeRegexpRef
puts $1 # => foo
# good
puts $2 # => nil
# bad - always returns nil
/(foo)bar/ =~ ‘foobar’
@example
however.
This might be a good indication of code that should be refactored,
—-
# the cop will mark this as an offense.
# $2 is defined for the first condition but not the second, however
do_something if $2
foo ? /©(b)/ =~ str : /(b)/ =~ str
—-
[source,ruby]
leads to false positives, such as:
it cannot handle some cases, such as conditional regexp matches, which
references are available based on the last encountered regexp, but
This cop is unsafe because it is naive in how it determines what
@safety
and thus always returns nil.
Looks for references of Regexp captures that are out of range
def after_send(node)
def after_send(node) @valid_ref = nil if regexp_first_argument?(node) check_regexp(node.first_argument) elsif regexp_receiver?(node) check_regexp(node.receiver) end end
def check_regexp(node)
def check_regexp(node) return if node.interpolation? named_capture = node.each_capture(named: true).count @valid_ref = if named_capture.positive? named_capture else node.each_capture(named: false).count end end
def nth_ref_receiver?(send_node)
def nth_ref_receiver?(send_node) send_node.receiver&.nth_ref_type? end
def on_in_pattern(node)
def on_in_pattern(node) regexp_patterns = regexp_patterns(node) @valid_ref = regexp_patterns.filter_map { |pattern| check_regexp(pattern) }.max end
def on_match_with_lvasgn(node)
def on_match_with_lvasgn(node) check_regexp(node.children.first) end
def on_new_investigation
def on_new_investigation @valid_ref = 0 end
def on_nth_ref(node)
def on_nth_ref(node) backref, = *node return if @valid_ref.nil? || backref <= @valid_ref message = format( MSG, backref: backref, count: @valid_ref.zero? ? 'no' : @valid_ref, group: @valid_ref == 1 ? 'group' : 'groups' ) add_offense(node, message: message) end
def on_when(node)
def on_when(node) regexp_conditions = node.conditions.select(&:regexp_type?) @valid_ref = regexp_conditions.filter_map { |condition| check_regexp(condition) }.max end
def regexp_first_argument?(send_node)
def regexp_first_argument?(send_node) send_node.first_argument&.regexp_type? \ && REGEXP_ARGUMENT_METHODS.include?(send_node.method_name) end
def regexp_patterns(in_node)
def regexp_patterns(in_node) pattern = in_node.pattern if pattern.regexp_type? [pattern] else pattern.each_descendant(:regexp).to_a end end
def regexp_receiver?(send_node)
def regexp_receiver?(send_node) send_node.receiver&.regexp_type? end