Skip to content

Commit

Permalink
Support autocorrection for Lint/AmbiguousRegexpLiteral
Browse files Browse the repository at this point in the history
This PR supports autocorrection for `Lint/AmbiguousRegexpLiteral`.
  • Loading branch information
koic committed Apr 28, 2020
1 parent 89671c5 commit cf02f40
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 23 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -5,6 +5,7 @@
### New features

* [#7895](https://github.com/rubocop-hq/rubocop/pull/7895): Include `.simplecov` file by default. ([@robotdana][])
* [#7916](https://github.com/rubocop-hq/rubocop/pull/7916): Support autocorrection for `Lint/AmbiguousRegexpLiteral`. ([@koic][])

### Bug fixes

Expand Down
1 change: 1 addition & 0 deletions config/default.yml
Expand Up @@ -1324,6 +1324,7 @@ Lint/AmbiguousRegexpLiteral:
a method invocation without parentheses.
Enabled: true
VersionAdded: '0.17'
VersionChanged: '0.83'

Lint/AssignmentInCondition:
Description: "Don't use assignment in conditions."
Expand Down
5 changes: 5 additions & 0 deletions lib/rubocop/cop/lint/ambiguous_operator.rb
Expand Up @@ -44,6 +44,11 @@ def relevant_diagnostic?(diagnostic)
diagnostic.reason == :ambiguous_prefix
end

def find_offense_node_by(diagnostic)
# TODO: When implementing auto-correction, this method should return
# an offense node passed as first argument of `add_offense` method.
end

def alternative_message(diagnostic)
operator = diagnostic.location.source
hash = AMBIGUITIES[operator]
Expand Down
14 changes: 14 additions & 0 deletions lib/rubocop/cop/lint/ambiguous_regexp_literal.rb
Expand Up @@ -28,12 +28,26 @@ class AmbiguousRegexpLiteral < Cop
"if it's surely a regexp literal, or add a whitespace to the " \
'right of the `/` if it should be a division.'

def autocorrect(node)
lambda do |corrector|
add_parentheses(node, corrector)
end
end

private

def relevant_diagnostic?(diagnostic)
diagnostic.reason == :ambiguous_literal
end

def find_offense_node_by(diagnostic)
node = processed_source.ast.each_node(:regexp).find do |regexp_node|
regexp_node.source_range.begin_pos == diagnostic.location.begin_pos
end

node.parent
end

def alternative_message(_diagnostic)
MSG
end
Expand Down
5 changes: 5 additions & 0 deletions lib/rubocop/cop/lint/useless_else_without_rescue.rb
Expand Up @@ -40,6 +40,11 @@ def relevant_diagnostic?(diagnostic)
diagnostic.reason == :useless_else
end

def find_offense_node_by(diagnostic)
# TODO: When implementing auto-correction, this method should return
# an offense node passed as first argument of `add_offense` method.
end

def alternative_message(_diagnostic)
MSG
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rubocop/cop/mixin/parser_diagnostic.rb
Expand Up @@ -26,7 +26,7 @@ def investigate(processed_source)
d.message.capitalize
end

add_offense(nil,
add_offense(find_offense_node_by(d),
location: d.location,
message: message,
severity: d.level)
Expand Down
20 changes: 0 additions & 20 deletions lib/rubocop/cop/style/lambda_call.rb
Expand Up @@ -52,26 +52,6 @@ def offense?(node)
implicit_style? && !node.implicit_call?
end

def add_parentheses(node, corrector)
if node.arguments.empty?
corrector.insert_after(node, '()')
else
corrector.replace(args_begin(node), '(')
corrector.insert_after(args_end(node), ')')
end
end

def args_begin(node)
loc = node.loc
selector =
node.super_type? || node.yield_type? ? loc.keyword : loc.selector
selector.end.resize(1)
end

def args_end(node)
node.loc.expression.end
end

def message(_node)
if explicit_style?
'Prefer the use of `lambda.call(...)` over `lambda.(...)`.'
Expand Down
20 changes: 20 additions & 0 deletions lib/rubocop/cop/util.rb
Expand Up @@ -26,6 +26,26 @@ def parentheses?(node)
node.loc.end.is?(')')
end

def add_parentheses(node, corrector)
if node.arguments.empty?
corrector.insert_after(node, '()')
else
corrector.replace(args_begin(node), '(')
corrector.insert_after(args_end(node), ')')
end
end

def args_begin(node)
loc = node.loc
selector =
node.super_type? || node.yield_type? ? loc.keyword : loc.selector
selector.end.resize(1)
end

def args_end(node)
node.loc.expression.end
end

def on_node(syms, sexp, excludes = [], &block)
return to_enum(:on_node, syms, sexp, excludes) unless block_given?

Expand Down
2 changes: 1 addition & 1 deletion manual/cops_lint.md
Expand Up @@ -66,7 +66,7 @@ do_something(*some_array)

Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
--- | --- | --- | --- | ---
Enabled | Yes | No | 0.17 | -
Enabled | Yes | Yes | 0.17 | 0.83

This cop checks for ambiguous regexp literals in the first argument of
a method invocation without parentheses.
Expand Down
46 changes: 45 additions & 1 deletion spec/rubocop/cop/lint/ambiguous_regexp_literal_spec.rb
Expand Up @@ -5,11 +5,55 @@

context 'with a regexp literal in the first argument' do
context 'without parentheses' do
it 'registers an offense' do
it 'registers an offense and corrects when single argument' do
expect_offense(<<~RUBY)
p /pattern/
^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division.
RUBY

expect_correction(<<~RUBY)
p(/pattern/)
RUBY
end

it 'registers an offense and corrects when multiple arguments' do
expect_offense(<<~RUBY)
p /pattern/, foo
^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division.
RUBY

expect_correction(<<~RUBY)
p(/pattern/, foo)
RUBY
end

it 'registers an offense and corrects when using block argument' do
expect_offense(<<~RUBY)
p /pattern/, foo do |arg|
^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division.
end
RUBY

expect_correction(<<~RUBY)
p(/pattern/, foo) do |arg|
end
RUBY
end

it 'registers an offense and corrects when nesting' do
expect_offense(<<~RUBY)
p /pattern/ do
^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division.
p /pattern/
^ Ambiguous regexp literal. Parenthesize the method arguments if it's surely a regexp literal, or add a whitespace to the right of the `/` if it should be a division.
end
RUBY

expect_correction(<<~RUBY)
p(/pattern/) do
p(/pattern/)
end
RUBY
end
end

Expand Down

0 comments on commit cf02f40

Please sign in to comment.