Skip to content

Commit

Permalink
Support autocorrection for Lint/AmbiguousOperator
Browse files Browse the repository at this point in the history
This PR supports autocorrection for `Lint/AmbiguousOperator`.
  • Loading branch information
koic committed May 1, 2020
1 parent 1548f83 commit 1acc2ac
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 5 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@
* [#7916](https://github.com/rubocop-hq/rubocop/pull/7916): Support autocorrection for `Lint/AmbiguousRegexpLiteral`. ([@koic][])
* [#7917](https://github.com/rubocop-hq/rubocop/pull/7917): Support autocorrection for `Lint/UselessAccessModifier`. ([@koic][])
* [#595](https://github.com/rubocop-hq/rubocop/issues/595): Add ERB pre-processing for configuration files. ([@jonas054][])
* [#7918](https://github.com/rubocop-hq/rubocop/pull/7918): Support autocorrection for `Lint/AmbiguousOperator`. ([@koic][])

### Bug fixes

Expand Down
1 change: 1 addition & 0 deletions config/default.yml
Expand Up @@ -1317,6 +1317,7 @@ Lint/AmbiguousOperator:
StyleGuide: '#method-invocation-parens'
Enabled: true
VersionAdded: '0.17'
VersionChanged: '0.83'

Lint/AmbiguousRegexpLiteral:
Description: >-
Expand Down
37 changes: 35 additions & 2 deletions lib/rubocop/cop/lint/ambiguous_operator.rb
Expand Up @@ -38,22 +38,55 @@ class AmbiguousOperator < Cop
'a whitespace to the right of the `%<operator>s` if it ' \
'should be a %<possible>s.'

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

private

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.
ast = processed_source.ast
ast.each_node(:splat, :block_pass, :kwsplat) do |node|
next unless offense_position?(node, diagnostic)

offense_node = offense_node(node)
return offense_node if offense_node
end

ast.each_node(:send).find do |send_node|
offense_position?(send_node.first_argument, diagnostic) &&
unary_operator?(send_node.first_argument, diagnostic)
end
end

def alternative_message(diagnostic)
operator = diagnostic.location.source
hash = AMBIGUITIES[operator]
format(MSG_FORMAT, hash)
end

def offense_position?(node, diagnostic)
node.source_range.begin_pos == diagnostic.location.begin_pos
end

def offense_node(node)
case node.type
when :splat, :block_pass
node.parent
when :kwsplat
node.parent.parent
end
end

def unary_operator?(node, diagnostic)
node.source.start_with?(diagnostic.arguments[:prefix])
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion manual/cops_lint.md
Expand Up @@ -37,7 +37,7 @@ foo = ->(bar) { bar.baz }

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 operators in the first argument of a
method invocation without parentheses.
Expand Down
117 changes: 115 additions & 2 deletions spec/rubocop/cop/lint/ambiguous_operator_spec.rb
Expand Up @@ -3,15 +3,90 @@
RSpec.describe RuboCop::Cop::Lint::AmbiguousOperator do
subject(:cop) { described_class.new }

context 'with `+` unary operator in the first argument' do
context 'without parentheses' do
context 'without whitespaces on the right of the operator' do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY)
do_something(+24)
do_something +42
^ Ambiguous positive number operator. Parenthesize the method arguments if it's surely a positive number operator, or add a whitespace to the right of the `+` if it should be a addition.
RUBY

expect_correction(<<~RUBY)
do_something(+24)
do_something(+42)
RUBY
end
end

context 'with a whitespace on the right of the operator' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
do_something + 42
RUBY
end
end
end

context 'with parentheses around the operator' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
do_something(+42)
RUBY
end
end
end

context 'with `-` unary operator in the first argument' do
context 'without parentheses' do
context 'without whitespaces on the right of the operator' do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY)
do_something(-24)
do_something -42
^ Ambiguous negative number operator. Parenthesize the method arguments if it's surely a negative number operator, or add a whitespace to the right of the `-` if it should be a subtraction.
RUBY

expect_correction(<<~RUBY)
do_something(-24)
do_something(-42)
RUBY
end
end

context 'with a whitespace on the right of the operator' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
do_something - 42
RUBY
end
end
end

context 'with parentheses around the operator' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
do_something(-42)
RUBY
end
end
end

context 'with a splat operator in the first argument' do
context 'without parentheses' do
context 'without whitespaces on the right of the operator' do
it 'registers an offense' do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY)
array = [1, 2, 3]
puts *array
^ Ambiguous splat operator. Parenthesize the method arguments if it's surely a splat operator, or add a whitespace to the right of the `*` if it should be a multiplication.
RUBY

expect_correction(<<~RUBY)
array = [1, 2, 3]
puts(*array)
RUBY
end
end

Expand All @@ -38,12 +113,17 @@
context 'with a block ampersand in the first argument' do
context 'without parentheses' do
context 'without whitespaces on the right of the operator' do
it 'registers an offense' do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY)
process = proc { do_something }
2.times &process
^ Ambiguous block operator. Parenthesize the method arguments if it's surely a block operator, or add a whitespace to the right of the `&` if it should be a binary AND.
RUBY

expect_correction(<<~RUBY)
process = proc { do_something }
2.times(&process)
RUBY
end
end

Expand All @@ -66,4 +146,37 @@
end
end
end

context 'with a keyword splat operator in the first argument' do
context 'without parentheses' do
context 'without whitespaces on the right of the operator' do
it 'registers an offense and corrects' do
expect_offense(<<~RUBY)
do_something **kwargs
^^ Ambiguous keyword splat operator. Parenthesize the method arguments if it's surely a keyword splat operator, or add a whitespace to the right of the `**` if it should be a exponent.
RUBY

expect_correction(<<~RUBY)
do_something(**kwargs)
RUBY
end
end

context 'with a whitespace on the right of the operator' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
do_something ** kwargs
RUBY
end
end
end

context 'with parentheses around the keyword splat operator' do
it 'does not register an offense' do
expect_no_offenses(<<~RUBY)
do_something(**kwargs)
RUBY
end
end
end
end

0 comments on commit 1acc2ac

Please sign in to comment.