Skip to content

Commit

Permalink
[Fix #8335] Improve class detection in Style/RedundantRegexpEscape
Browse files Browse the repository at this point in the history
The previous code wouldn't detect character classes that had ":" as
their first element (due to a poor attempt at avoiding issues due to
POSIX bracket expressions). The handling of nested character classes was
also improved to avoid false-negatives.
  • Loading branch information
owst authored and marcandre committed Jul 14, 2020
1 parent 3f0aab3 commit 0adf3cd
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -7,6 +7,7 @@
* [#8324](https://github.com/rubocop-hq/rubocop/issues/8324): Fix crash for `Layout/SpaceAroundMethodCallOperator` when using `Proc#call` shorthand syntax. ([@fatkodima][])
* [#8323](https://github.com/rubocop-hq/rubocop/issues/8323): Fix a false positive for `Style/HashAsLastArrayItem` when hash is not a last array item. ([@fatkodima][])
* [#8299](https://github.com/rubocop-hq/rubocop/issues/8299): Fix an incorrect auto-correct for `Style/RedundantCondition` when using `raise`, `rescue`, or `and` without argument parentheses in `else`. ([@koic][])
* [#8335](https://github.com/rubocop-hq/rubocop/issues/8335): Fix incorrect character class detection for nested or POSIX bracket character classes in `Style/RedundantRegexpEscape`. ([@owst][])

## 0.88.0 (2020-07-13)

Expand Down
18 changes: 9 additions & 9 deletions lib/rubocop/cop/style/redundant_regexp_escape.rb
Expand Up @@ -92,18 +92,18 @@ def delimiter?(node, char)

def each_escape(node)
pattern_source(node).each_char.with_index.reduce(
[nil, false]
) do |(previous, within_character_class), (current, index)|
[nil, 0]
) do |(previous, char_class_depth), (current, index)|
if previous == '\\'
yield [current, index - 1, within_character_class]
yield [current, index - 1, !char_class_depth.zero?]

[nil, within_character_class]
elsif previous == '[' && current != ':'
[current, true]
elsif previous != ':' && current == ']'
[current, false]
[nil, char_class_depth]
elsif previous == '['
[current, char_class_depth + 1]
elsif current == ']'
[current, char_class_depth - 1]
else
[current, within_character_class]
[current, char_class_depth]
end
end
end
Expand Down
47 changes: 46 additions & 1 deletion spec/rubocop/cop/style/redundant_regexp_escape_spec.rb
Expand Up @@ -67,6 +67,19 @@
end
end

context 'with an escaped . inside a character class beginning with :' do
it 'registers an offense and corrects' do
expect_offense(<<~'RUBY')
foo = /[:\.]/
^^ Redundant escape inside regexp literal
RUBY

expect_correction(<<~'RUBY')
foo = /[:.]/
RUBY
end
end

context 'with an escaped character class and following escaped char' do
it 'does not register an offense' do
expect_no_offenses('foo = /\[\+/')
Expand All @@ -79,12 +92,44 @@
end
end

context 'with a POSIX character class inside a character class' do
context 'with a nested character class then allowed escape' do
it 'does not register an offense' do
expect_no_offenses('foo = /[a-w&&[^c-g]\-]/')
end
end

context 'with a nested character class containing redundant escape' do
it 'registers an offense and corrects' do
expect_offense(<<~'RUBY')
foo = /[[:punct:]&&[^\.]]/
^^ Redundant escape inside regexp literal
RUBY

expect_correction(<<~'RUBY')
foo = /[[:punct:]&&[^.]]/
RUBY
end
end

context 'with a POSIX character class then allowed escape inside a character class' do
it 'does not register an offense' do
expect_no_offenses('foo = /[[:alnum:]\-_]+/')
end
end

context 'with a POSIX character class then disallowed escape inside a character class' do
it 'registers an offense and corrects' do
expect_offense(<<~'RUBY')
foo = /[[:alnum:]\.]/
^^ Redundant escape inside regexp literal
RUBY

expect_correction(<<~'RUBY')
foo = /[[:alnum:].]/
RUBY
end
end

context 'with a backreference' do
it 'does not register an offense' do
expect_no_offenses('foo = /([a-z])\s*\1/')
Expand Down

0 comments on commit 0adf3cd

Please sign in to comment.