Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support autocorrection for Lint/AmbiguousRegexpLiteral #7916

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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