Skip to content

Commit

Permalink
Enable basic autocorrection for Style/Semicolon.
Browse files Browse the repository at this point in the history
When a semicolon is found between expressions, it is replaced with a newline. In order to avoid adding multiple newlines, when the semicolons are making a single-line method, autocorrection is disabled in favour of the autocorrection from `Style/SingleLineMethods`.
  • Loading branch information
dvandersluis authored and bbatsov committed Aug 4, 2021
1 parent 5708d66 commit b989ed7
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 20 deletions.
1 change: 1 addition & 0 deletions changelog/change_enable_basic_autocorrection_for.md
@@ -0,0 +1 @@
* [#9979](https://github.com/rubocop/rubocop/pull/9979): Enable basic autocorrection for `Style/Semicolon`. ([@dvandersluis][])
19 changes: 13 additions & 6 deletions lib/rubocop/cop/style/semicolon.rb
Expand Up @@ -32,6 +32,10 @@ class Semicolon < Base

MSG = 'Do not use semicolons to terminate expressions.'

def self.autocorrect_incompatible_with
[Style::SingleLineMethods]
end

def on_new_investigation
return if processed_source.blank?

Expand All @@ -50,7 +54,7 @@ def on_begin(node)
# potential offense
next unless expr_on_line.size > 1

find_semicolon_positions(line) { |pos| register_semicolon(line, pos, false) }
find_semicolon_positions(line) { |pos| register_semicolon(line, pos, true) }
end
end

Expand All @@ -60,7 +64,7 @@ def check_for_line_terminator_or_opener
# Make the obvious check first
return unless processed_source.raw_source.include?(';')

each_semicolon { |line, column| register_semicolon(line, column, true) }
each_semicolon { |line, column| register_semicolon(line, column, false) }
end

def each_semicolon
Expand All @@ -74,12 +78,15 @@ def tokens_for_lines
processed_source.tokens.group_by(&:line)
end

def register_semicolon(line, column, autocorrect)
def register_semicolon(line, column, after_expression)
range = source_range(processed_source.buffer, line, column)
# Don't attempt to autocorrect if semicolon is separating statements
# on the same line

add_offense(range) do |corrector|
corrector.remove(range) if autocorrect
if after_expression
corrector.replace(range, "\n")
else
corrector.remove(range)
end
end
end

Expand Down
54 changes: 42 additions & 12 deletions spec/rubocop/cli/autocorrect_spec.rb
Expand Up @@ -902,7 +902,10 @@ def func
end
def func
x; y; rescue StandardError; z
x
y
rescue StandardError
z
end
def method
Expand Down Expand Up @@ -1285,7 +1288,8 @@ def func2() do_1; do_2; end
exit_status = cli.run(
%w[--auto-correct-all --format offenses --only] << %w[
SingleLineMethods Semicolon EmptyLineBetweenDefs
DefWithParentheses TrailingWhitespace
DefWithParentheses TrailingWhitespace TrailingBodyOnMethodDefinition
DefEndAlignment IndentationConsistency
].join(',')
)
expect(exit_status).to eq(0)
Expand All @@ -1302,13 +1306,16 @@ def func2
RUBY
expect($stdout.string).to eq(<<~RESULT)
6 Layout/TrailingWhitespace
4 Layout/TrailingWhitespace
3 Style/Semicolon
2 Layout/IndentationConsistency
2 Style/SingleLineMethods
1 Layout/DefEndAlignment
1 Layout/EmptyLineBetweenDefs
1 Style/DefWithParentheses
1 Style/TrailingBodyOnMethodDefinition
--
13 Total
15 Total
RESULT
end
Expand Down Expand Up @@ -1529,16 +1536,11 @@ def self.some_method(foo, bar: 1)
log.debug(foo)
end
RUBY
corrected = <<~RUBY
func a do b end
Signal.trap('TERM') { system(cmd); exit }
def self.some_method(foo, bar: 1)
log.debug(foo)
end
RUBY
create_file('.rubocop.yml', <<~YAML)
AllCops:
TargetRubyVersion: 2.5
Style/Semicolon:
AutoCorrect: false
YAML
create_file('example.rb', src)
exit_status = cli.run(
Expand All @@ -1547,7 +1549,7 @@ def self.some_method(foo, bar: 1)
)
expect(exit_status).to eq(1)
expect($stderr.string).to eq('')
expect(File.read('example.rb')).to eq(corrected)
expect(File.read('example.rb')).to eq(src)
expect($stdout.string).to eq(<<~RESULT)
== example.rb ==
C: 1: 8: Style/BlockDelimiters: Prefer {...} over do...end for single-line blocks.
Expand Down Expand Up @@ -2162,4 +2164,32 @@ def my_func
}
RUBY
end

it 'avoids adding extra spaces when both `Style/Semicolon` and `Style/SingleLineMethods`' \
'both apply' do
source_file = Pathname('example.rb')
create_file(source_file, <<~RUBY)
def foo(a) x(1); y(2); z(3); end
RUBY

create_file('.rubocop.yml', <<~YAML)
Style/Semicolon:
AllowAsExpressionSeparator: false
YAML

status = cli.run(
%w[--auto-correct --only] << %w[
Semicolon SingleLineMethods TrailingBodyOnMethodDefinition
DefEndAlignment TrailingWhitespace IndentationConsistency
].join(',')
)
expect(status).to eq(0)
expect(source_file.read).to eq(<<~RUBY)
def foo(a)
x(1)
y(2)
z(3)
end
RUBY
end
end
12 changes: 10 additions & 2 deletions spec/rubocop/cop/style/semicolon_spec.rb
Expand Up @@ -20,7 +20,10 @@
^ Do not use semicolons to terminate expressions.
RUBY

expect_no_corrections
expect_correction(<<~RUBY)
puts "this is a test"
puts "So is this"
RUBY
end

it 'registers an offense for one line method with two statements' do
Expand All @@ -31,7 +34,12 @@ def foo(a) x(1); y(2); z(3); end
^ Do not use semicolons to terminate expressions.
RUBY

expect_no_corrections
expect_correction(<<~RUBY)
def foo(a) x(1)
y(2)
z(3)
end
RUBY
end

it 'accepts semicolon before end if so configured' do
Expand Down

0 comments on commit b989ed7

Please sign in to comment.