Skip to content

Commit

Permalink
[Fix rubocop#7746] Add Lint/MixedRegexpCaptureTypes cop
Browse files Browse the repository at this point in the history
Close rubocop#7749 and Reopen rubocop#7749.
  • Loading branch information
pocke authored and koic committed Jun 1, 2020
1 parent c1e0f10 commit 3f66c24
Show file tree
Hide file tree
Showing 10 changed files with 148 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@
* [#7978](https://github.com/rubocop-hq/rubocop/pull/7978): Add new option `OnlyFor` to the `Bundler/GemComment` cop. ([@ric2b][])
* [#8063](https://github.com/rubocop-hq/rubocop/issues/8063): Add new `AllowedNames` option for `Naming/ClassAndModuleCamelCase`. ([@tejasbubane][])
* New option `--display-only-failed` that can be used with `--format junit`. Speeds up test report processing for large codebases and helps address the sorts of concerns raised at [mikian/rubocop-junit-formatter #18](https://github.com/mikian/rubocop-junit-formatter/issues/18). ([@burnettk][])
* [#7746](https://github.com/rubocop-hq/rubocop/issues/7746): Add new `Lint/MixedRegexpCaptureTypes` cop. ([@pocke][])

### Bug fixes

Expand Down
5 changes: 5 additions & 0 deletions config/default.yml
Expand Up @@ -1533,6 +1533,11 @@ Lint/MissingCopEnableDirective:
# .inf for any size
MaximumRangeSize: .inf

Lint/MixedRegexpCaptureTypes:
Description: 'Do not mix named captures and numbered captures in a Regexp literal.'
Enabled: pending
VersionAdded: '0.85'

Lint/MultipleComparison:
Description: "Use `&&` operator to compare multiple values."
Enabled: true
Expand Down
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Expand Up @@ -214,6 +214,7 @@ In the following section you find all available cops:
* xref:cops_lint.adoc#lintliteralininterpolation[Lint/LiteralInInterpolation]
* xref:cops_lint.adoc#lintloop[Lint/Loop]
* xref:cops_lint.adoc#lintmissingcopenabledirective[Lint/MissingCopEnableDirective]
* xref:cops_lint.adoc#lintmixedregexpcapturetypes[Lint/MixedRegexpCaptureTypes]
* xref:cops_lint.adoc#lintmultiplecomparison[Lint/MultipleComparison]
* xref:cops_lint.adoc#lintnestedmethoddefinition[Lint/NestedMethodDefinition]
* xref:cops_lint.adoc#lintnestedpercentliteral[Lint/NestedPercentLiteral]
Expand Down
29 changes: 29 additions & 0 deletions docs/modules/ROOT/pages/cops_lint.adoc
Expand Up @@ -1552,6 +1552,35 @@ x += 1
| Float
|===

== Lint/MixedRegexpCaptureTypes

|===
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged

| Pending
| Yes
| No
| 0.85
| -
|===

Do not mix named captures and numbered captures in a Regexp literal
because numbered capture is ignored if they're mixed.
Replace numbered captures with non-capturing groupings or
named captures.

# bad
/(?<foo>FOO)(BAR)/

# good
/(?<foo>FOO)(?<bar>BAR)/

# good
/(?<foo>FOO)(?:BAR)/

# good
/(FOO)(BAR)/

== Lint/MultipleComparison

|===
Expand Down
1 change: 1 addition & 0 deletions legacy-docs/cops.md
Expand Up @@ -212,6 +212,7 @@ In the following section you find all available cops:
* [Lint/LiteralInInterpolation](cops_lint.md#lintliteralininterpolation)
* [Lint/Loop](cops_lint.md#lintloop)
* [Lint/MissingCopEnableDirective](cops_lint.md#lintmissingcopenabledirective)
* [Lint/MixedRegexpCaptureTypes](cops_lint.md#lintmixedregexpcapturetypes)
* [Lint/MultipleComparison](cops_lint.md#lintmultiplecomparison)
* [Lint/NestedMethodDefinition](cops_lint.md#lintnestedmethoddefinition)
* [Lint/NestedPercentLiteral](cops_lint.md#lintnestedpercentliteral)
Expand Down
23 changes: 23 additions & 0 deletions legacy-docs/cops_lint.md
Expand Up @@ -1210,6 +1210,29 @@ Name | Default value | Configurable values
--- | --- | ---
MaximumRangeSize | `Infinity` | Float

## Lint/MixedRegexpCaptureTypes

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Pending | Yes | No | 0.85 | -

Do not mix named captures and numbered captures in a Regexp literal
because numbered capture is ignored if they're mixed.
Replace numbered captures with non-capturing groupings or
named captures.

# bad
/(?<foo>FOO)(BAR)/

# good
/(?<foo>FOO)(?<bar>BAR)/

# good
/(?<foo>FOO)(?:BAR)/

# good
/(FOO)(BAR)/

## Lint/MultipleComparison

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
Expand Down
3 changes: 3 additions & 0 deletions lib/rubocop.rb
Expand Up @@ -4,6 +4,8 @@

require 'English'
require 'set'
require 'forwardable'
require 'regexp_parser'
require 'unicode/display_width/no_string_ext'
require 'rubocop-ast'
require_relative 'rubocop/ast_aliases'
Expand Down Expand Up @@ -266,6 +268,7 @@
require_relative 'rubocop/cop/lint/literal_in_interpolation'
require_relative 'rubocop/cop/lint/loop'
require_relative 'rubocop/cop/lint/missing_cop_enable_directive'
require_relative 'rubocop/cop/lint/mixed_regexp_capture_types'
require_relative 'rubocop/cop/lint/multiple_comparison'
require_relative 'rubocop/cop/lint/nested_method_definition'
require_relative 'rubocop/cop/lint/nested_percent_literal'
Expand Down
51 changes: 51 additions & 0 deletions lib/rubocop/cop/lint/mixed_regexp_capture_types.rb
@@ -0,0 +1,51 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Lint
# Do not mix named captures and numbered captures in a Regexp literal
# because numbered capture is ignored if they're mixed.
# Replace numbered captures with non-capturing groupings or
# named captures.
#
# # bad
# /(?<foo>FOO)(BAR)/
#
# # good
# /(?<foo>FOO)(?<bar>BAR)/
#
# # good
# /(?<foo>FOO)(?:BAR)/
#
# # good
# /(FOO)(BAR)/
#
class MixedRegexpCaptureTypes < Cop
MSG = 'Do not mix named captures and numbered captures ' \
'in a Regexp literal.'

def on_regexp(node)
tree = Regexp::Parser.parse(node.content)
return unless named_capture?(tree)
return unless numbered_capture?(tree)

add_offense(node)
end

private

def named_capture?(tree)
tree.each_expression.any? do |e|
e.instance_of?(Regexp::Expression::Group::Capture)
end
end

def numbered_capture?(tree)
tree.each_expression.any? do |e|
e.instance_of?(Regexp::Expression::Group::Named)
end
end
end
end
end
end
1 change: 1 addition & 0 deletions rubocop.gemspec
Expand Up @@ -36,6 +36,7 @@ Gem::Specification.new do |s|
s.add_runtime_dependency('parallel', '~> 1.10')
s.add_runtime_dependency('parser', '>= 2.7.0.1')
s.add_runtime_dependency('rainbow', '>= 2.2.2', '< 4.0')
s.add_runtime_dependency('regexp_parser', '>= 1.7')
s.add_runtime_dependency('rexml')
s.add_runtime_dependency('rubocop-ast', '>= 0.0.3')
s.add_runtime_dependency('ruby-progressbar', '~> 1.7')
Expand Down
33 changes: 33 additions & 0 deletions spec/rubocop/cop/lint/mixed_regexp_capture_types_spec.rb
@@ -0,0 +1,33 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Lint::MixedRegexpCaptureTypes do
subject(:cop) { described_class.new(config) }

let(:config) { RuboCop::Config.new }

it 'registers an offense when both of named and numbered captures are used' do
expect_offense(<<~RUBY)
/(?<foo>bar)(baz)/
^^^^^^^^^^^^^^^^^^ Do not mix named captures and numbered captures in a Regexp literal.
RUBY
end

it 'does not register offense to a regexp with named capture only' do
expect_no_offenses(<<~RUBY)
/(?<foo>foo?<bar>bar)/
RUBY
end

it 'does not register offense to a regexp with numbered capture only' do
expect_no_offenses(<<~RUBY)
/(foo)(bar)/
RUBY
end

it 'does not register offense to a regexp with named capture and ' \
'non-capturing group' do
expect_no_offenses(<<~RUBY)
/(?<foo>bar)(?:bar)/
RUBY
end
end

0 comments on commit 3f66c24

Please sign in to comment.