diff --git a/CHANGELOG.md b/CHANGELOG.md index b1075917dc6..90ff270650c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * [#8801](https://github.com/rubocop-hq/rubocop/issues/8801): Fix `Layout/SpaceAroundEqualsInParameterDefault` only registered once in a line. ([@rdunlop][]) * [#8514](https://github.com/rubocop-hq/rubocop/issues/8514): Correct multiple `Style/MethodDefParentheses` per file. ([@rdunlop][]) * [#8825](https://github.com/rubocop-hq/rubocop/issues/8825): Fix crash in `Style/ExplicitBlockArgument` when code is called outside of a method. ([@ghiculescu][]) +* [#8354](https://github.com/rubocop-hq/rubocop/issues/8354): Detect regexp named captures in `Style/CaseLikeIf` cop. ([@dsavochkin][]) ### Changes diff --git a/lib/rubocop/cop/style/case_like_if.rb b/lib/rubocop/cop/style/case_like_if.rb index 2f9259763b2..094dca81d18 100644 --- a/lib/rubocop/cop/style/case_like_if.rb +++ b/lib/rubocop/cop/style/case_like_if.rb @@ -42,6 +42,8 @@ def on_if(node) convertible = true branch_conditions(node).each do |branch_condition| + return false if regexp_with_working_captures?(branch_condition) + conditions << [] convertible = collect_conditions(branch_condition, target, conditions.last) break unless convertible @@ -49,9 +51,7 @@ def on_if(node) return unless convertible - add_offense(node) do |corrector| - autocorrect(corrector, node) - end + add_offense(node) { |corrector| autocorrect(corrector, node) } end private @@ -107,7 +107,7 @@ def find_target_in_send_node(node) when :include?, :cover? receiver = deparenthesize(node.receiver) node.arguments.first if receiver.range_type? - when :match, :match? + when :match, :match?, :=~ find_target_in_match_node(node) end end @@ -230,6 +230,22 @@ def correction_range(node) def indent(node) ' ' * node.loc.column end + + # Named captures work with `=~` (if regexp is on lhs) and with `match` (both sides) + def regexp_with_working_captures?(node) + case node.type + when :match_with_lvasgn + lhs, _rhs = *node + node.loc.selector.source == '=~' && regexp_with_named_captures?(lhs) + when :send + lhs, method, rhs = *node + method == :match && [lhs, rhs].any? { |n| regexp_with_named_captures?(n) } + end + end + + def regexp_with_named_captures?(node) + node.regexp_type? && node.each_capture(named: true).count.positive? + end end end end diff --git a/spec/rubocop/cop/style/case_like_if_spec.rb b/spec/rubocop/cop/style/case_like_if_spec.rb index 00c2a23e0ee..8c0461f842a 100644 --- a/spec/rubocop/cop/style/case_like_if_spec.rb +++ b/spec/rubocop/cop/style/case_like_if_spec.rb @@ -344,4 +344,78 @@ end RUBY end + + context 'when using regexp with named captures' do + it 'does not register an offense with =~ and regexp on lhs' do + expect_no_offenses(<<~RUBY) + if /(?.*)/ =~ foo + elsif foo == 123 + end + RUBY + end + + it 'registers and corrects an offense with =~ and regexp on rhs' do + expect_offense(<<~RUBY) + if foo =~ /(?.*)/ + ^^^^^^^^^^^^^^^^^^^^^^^ Convert `if-elsif` to `case-when`. + elsif foo == 123 + end + RUBY + + expect_correction(<<~RUBY) + case foo + when /(?.*)/ + when 123 + end + RUBY + end + + it 'does not register an offense with match and regexp on lhs' do + expect_no_offenses(<<~RUBY) + if /(?.*)/.match(foo) + elsif foo == 123 + end + RUBY + end + + it 'does not register an offense with match and regexp on rhs' do + expect_no_offenses(<<~RUBY) + if foo.match(/(?.*)/) + elsif foo == 123 + end + RUBY + end + + it 'registers and corrects an offense with match? and regexp on lhs' do + expect_offense(<<~RUBY) + if /(?.*)/.match?(foo) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Convert `if-elsif` to `case-when`. + elsif foo == 123 + end + RUBY + + expect_correction(<<~RUBY) + case foo + when /(?.*)/ + when 123 + end + RUBY + end + + it 'registers and corrects an offense with match? and regexp on rhs' do + expect_offense(<<~RUBY) + if foo.match?(/(?.*)/) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Convert `if-elsif` to `case-when`. + elsif foo == 123 + end + RUBY + + expect_correction(<<~RUBY) + case foo + when /(?.*)/ + when 123 + end + RUBY + end + end end