diff --git a/changelog/fix_fix_8708_fix_bad_regexp_recognition_in.md b/changelog/fix_fix_8708_fix_bad_regexp_recognition_in.md new file mode 100644 index 00000000000..be293fe4c06 --- /dev/null +++ b/changelog/fix_fix_8708_fix_bad_regexp_recognition_in.md @@ -0,0 +1 @@ +* [#8708](https://github.com/rubocop-hq/rubocop/pull/8708): Fix bad regexp recognition in `Lint/OutOfRangeRegexpRef` when there are multiple regexps. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb b/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb index bdd251fae45..66abbc8a58d 100644 --- a/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +++ b/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb @@ -35,14 +35,13 @@ def on_match_with_lvasgn(node) check_regexp(node.children.first) end - def on_send(node) + def after_send(node) @valid_ref = nil - if node.receiver&.regexp_type? - check_regexp(node.receiver) - elsif node.first_argument&.regexp_type? \ - && REGEXP_ARGUMENT_METHODS.include?(node.method_name) + if regexp_first_argument?(node) check_regexp(node.first_argument) + elsif regexp_receiver?(node) + check_regexp(node.receiver) end end @@ -80,6 +79,19 @@ def check_regexp(node) node.each_capture(named: false).count end end + + def regexp_first_argument?(send_node) + send_node.first_argument&.regexp_type? \ + && REGEXP_ARGUMENT_METHODS.include?(send_node.method_name) + end + + def regexp_receiver?(send_node) + send_node.receiver&.regexp_type? + end + + def nth_ref_receiver?(send_node) + send_node.receiver&.nth_ref_type? + end end end end diff --git a/spec/rubocop/cop/lint/out_of_range_regexp_ref_spec.rb b/spec/rubocop/cop/lint/out_of_range_regexp_ref_spec.rb index df3baec9a4e..01eb8fd6567 100644 --- a/spec/rubocop/cop/lint/out_of_range_regexp_ref_spec.rb +++ b/spec/rubocop/cop/lint/out_of_range_regexp_ref_spec.rb @@ -234,6 +234,51 @@ end end + context 'when both the LHS and RHS use regexp' do + it 'only considers the RHS regexp' do + expect_no_offenses(<<~RUBY) + if "foo bar".gsub(/\s+/, "") =~ /foo(bar)/ + p $1 + end + RUBY + + expect_offense(<<~RUBY) + if "foo bar".gsub(/(\s+)/, "") =~ /foobar/ + p $1 + ^^ $1 is out of range (no regexp capture groups detected). + end + RUBY + end + end + + context 'when calling a regexp method on a nth-ref node' do + it 'does not register an offense when calling gsub on a valid nth-ref' do + expect_no_offenses(<<~RUBY) + if "some : line " =~ / : (.+)/ + $1.gsub(/\s{2}/, " ") + end + RUBY + end + + it 'registers an offense when calling gsub on an invalid nth-ref' do + expect_offense(<<~RUBY) + if "some : line " =~ / : (.+)/ + $2.gsub(/\s{2}/, " ") + ^^ $2 is out of range (1 regexp capture group detected). + end + RUBY + end + + it 'registers an offense if the capturing groups have changed' do + expect_offense(<<~RUBY) + "some : line " =~ / : (.+)/ + $1.gsub(/\s{2}/, " ") + puts $1 + ^^ $1 is out of range (no regexp capture groups detected). + RUBY + end + end + %i[gsub gsub! sub sub! scan].each do |method| context "matching with #{method}" do it 'does not register an offense when in range references are used' do