Skip to content

Commit

Permalink
[Fix rubocop#8131]: Ignore escapes of delimiters in Style/RedundantRe…
Browse files Browse the repository at this point in the history
…gexpEscape
  • Loading branch information
owst committed Jun 10, 2020
1 parent 3b0552b commit e223f77
Show file tree
Hide file tree
Showing 4 changed files with 70 additions and 8 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -10,6 +10,7 @@
### Bug fixes

* [#8115](https://github.com/rubocop-hq/rubocop/issues/8115): Fix false negative for `Lint::FormatParameterMismatch` when argument contains formatting. ([@andrykonchin][])
* [#8131](https://github.com/rubocop-hq/rubocop/pull/8131): Fix false positive for Style/RedundantRegexpEscape with escaped delimiters. ([@owst][])
* [#8124](https://github.com/rubocop-hq/rubocop/issues/8124): Fix a false positive for `Lint/FormatParameterMismatch` when using named parameters with escaped `%`. ([@koic][])

## 0.85.1 (2020-06-07)
Expand Down
3 changes: 3 additions & 0 deletions docs/modules/ROOT/pages/cops_style.adoc
Expand Up @@ -7180,6 +7180,9 @@ This cop checks for redundant escapes inside Regexp literals.
# good
%r/foo\/bar/
# good
%r!foo\!bar!
# bad
/a\-b/
Expand Down
22 changes: 14 additions & 8 deletions lib/rubocop/cop/style/redundant_regexp_escape.rb
Expand Up @@ -18,6 +18,9 @@ module Style
# # good
# %r/foo\/bar/
#
# # good
# %r!foo\!bar!
#
# # bad
# /a\-b/
#
Expand Down Expand Up @@ -63,27 +66,30 @@ def autocorrect(node)

private

def slash_literal?(node)
['/', '%r/'].include?(node.loc.begin.source)
end

def allowed_escape?(node, char, within_character_class)
# Strictly speaking a few single-letter metachars are currently
# unnecessary to "escape", e.g. g, i, E, F, but enumerating them is
# rather difficult, and their behaviour could change over time with
# different versions of Ruby so that e.g. /\g/ != /g/
return true if /[[:alnum:]]/.match?(char)
return true if ALLOWED_ALWAYS_ESCAPES.include?(char)
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)

if char == '/'
slash_literal?(node)
elsif within_character_class
if within_character_class
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
else
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES.include?(char)
end
end

def delimiter?(node, char)
delimiters = [
node.loc.begin.source.chars.last,
node.loc.end.source.chars.first
]

delimiters.include?(char)
end

def each_escape(node)
pattern_source(node).each_char.with_index.reduce(
[nil, false]
Expand Down
52 changes: 52 additions & 0 deletions spec/rubocop/cop/style/redundant_regexp_escape_spec.rb
Expand Up @@ -249,6 +249,18 @@
end
end

context 'with an escaped { or } outside a character class' do
it 'does not register an offense' do
expect_no_offenses('foo = %r{\{\}}')
end
end

context 'with an escaped { or } inside a character class' do
it 'does not register an offense' do
expect_no_offenses('foo = %r{[\{\}]}')
end
end

context 'with redundantly-escaped slashes' do
it 'registers an offense and corrects' do
expect_offense(<<~'RUBY')
Expand All @@ -264,6 +276,46 @@
end
end

[
'!',
'~',
'@',
'_',
'^',
'<>',
'()'
].each do |delims|
l, r = delims.chars
r = l if r.nil?
escaped_delims = "\\#{l}\\#{r}"

context "with a single-line %r#{l}#{r} regexp" do
context 'without escapes' do
it 'does not register an offense' do
expect_no_offenses("foo = %r#{l}a#{r}")
end
end

context 'with escaped delimiters and regexp options' do
it 'does not register an offense' do
expect_no_offenses("foo = %r#{l}#{escaped_delims}#{r}i")
end
end

context 'with escaped delimiters outside a character-class' do
it 'does not register an offense' do
expect_no_offenses("foo = %r#{l}#{escaped_delims}#{r}")
end
end

context 'with escaped delimiters inside a character-class' do
it 'does not register an offense' do
expect_no_offenses("foo = %r#{l}a[#{escaped_delims}]b#{r}")
end
end
end
end

context 'with a single-line %r// regexp' do
context 'without escapes' do
it 'does not register an offense' do
Expand Down

0 comments on commit e223f77

Please sign in to comment.