diff --git a/changelog/new_layout_multiline_method_parameter_line_breaks_cop.md b/changelog/new_layout_multiline_method_parameter_line_breaks_cop.md new file mode 100644 index 00000000000..68e065ae4a8 --- /dev/null +++ b/changelog/new_layout_multiline_method_parameter_line_breaks_cop.md @@ -0,0 +1 @@ +* [#10691](https://github.com/rubocop/rubocop/pull/10691): Add new `Layout/MultilineMethodParameterLineBreaks` cop. ([@Korri][]) diff --git a/config/default.yml b/config/default.yml index 8f16f84e6d5..931d9a89731 100644 --- a/config/default.yml +++ b/config/default.yml @@ -1158,6 +1158,13 @@ Layout/MultilineMethodDefinitionBraceLayout: - new_line - same_line +Layout/MultilineMethodParameterLineBreaks: + Description: >- + Checks that each parameter in a multi-line method definition + starts on a separate line. + Enabled: false + VersionAdded: '<>' + Layout/MultilineOperationIndentation: Description: >- Checks indentation of binary operations that span more than diff --git a/lib/rubocop.rb b/lib/rubocop.rb index 7a7539001c1..126274caeb2 100644 --- a/lib/rubocop.rb +++ b/lib/rubocop.rb @@ -233,6 +233,7 @@ require_relative 'rubocop/cop/layout/multiline_method_call_brace_layout' require_relative 'rubocop/cop/layout/multiline_method_call_indentation' require_relative 'rubocop/cop/layout/multiline_method_definition_brace_layout' +require_relative 'rubocop/cop/layout/multiline_method_parameter_line_breaks' require_relative 'rubocop/cop/layout/multiline_operation_indentation' require_relative 'rubocop/cop/layout/parameter_alignment' require_relative 'rubocop/cop/layout/redundant_line_break' diff --git a/lib/rubocop/cop/layout/line_length.rb b/lib/rubocop/cop/layout/line_length.rb index f43445b19fc..51912d1906f 100644 --- a/lib/rubocop/cop/layout/line_length.rb +++ b/lib/rubocop/cop/layout/line_length.rb @@ -37,6 +37,7 @@ module Layout # * MultilineHashBraceLayout # * MultilineHashKeyLineBreaks # * MultilineMethodArgumentLineBreaks + # * MultilineMethodParameterLineBreaks # * ParameterAlignment # # Together, these cops will pretty print hashes, arrays, diff --git a/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb b/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb index 0bf2c183aae..702217b8f3b 100644 --- a/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +++ b/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb @@ -6,7 +6,7 @@ module Layout # Ensures that each argument in a multi-line method call # starts on a separate line. # - # NOTE: this cop does not move the first argument, if you want that to + # NOTE: This cop does not move the first argument, if you want that to # be on a separate line, see `Layout/FirstMethodArgumentLineBreak`. # # @example @@ -22,6 +22,9 @@ module Layout # b, # c # ) + # + # # good + # foo(a, b, c) class MultilineMethodArgumentLineBreaks < Base include MultilineElementLineBreaks extend AutoCorrector diff --git a/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb b/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb new file mode 100644 index 00000000000..c9388287bf7 --- /dev/null +++ b/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Layout + # Ensures that each parameter in a multi-line method definition + # starts on a separate line. + # + # NOTE: This cop does not move the first argument, if you want that to + # be on a separate line, see `Layout/FirstMethodParameterLineBreak`. + # + # @example + # + # # bad + # def foo(a, b, + # c + # ) + # end + # + # # good + # def foo( + # a, + # b, + # c + # ) + # end + # + # # good + # def foo(a, b, c) + # end + class MultilineMethodParameterLineBreaks < Base + include MultilineElementLineBreaks + extend AutoCorrector + + MSG = 'Each parameter in a multi-line method definition must start on a separate line.' + + def on_def(node) + return if node.arguments.empty? + + check_line_breaks(node, node.arguments) + end + end + end + end +end diff --git a/spec/rubocop/cop/layout/multiline_method_parameter_line_breaks_spec.rb b/spec/rubocop/cop/layout/multiline_method_parameter_line_breaks_spec.rb new file mode 100644 index 00000000000..70c4ff0e126 --- /dev/null +++ b/spec/rubocop/cop/layout/multiline_method_parameter_line_breaks_spec.rb @@ -0,0 +1,162 @@ +# frozen_string_literal: true + +RSpec.describe RuboCop::Cop::Layout::MultilineMethodParameterLineBreaks, :config do + context 'when one parameter on same line' do + it 'does not add any offenses' do + expect_no_offenses(<<~RUBY) + def taz(abc) + end + RUBY + end + end + + context 'when there are no parameters' do + it 'does not add any offenses' do + expect_no_offenses(<<~RUBY) + def taz + end + RUBY + end + end + + context 'when two parameters are on next line' do + it 'does not add any offenses' do + expect_no_offenses(<<~RUBY) + def taz( + foo, bar + ) + end + RUBY + end + end + + context 'when many parameter are on multiple lines, two on same line' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + def taz(abc, + foo, bar, + ^^^ Each parameter in a multi-line method definition must start on a separate line. + baz + ) + end + RUBY + + expect_correction(<<~RUBY) + def taz(abc, + foo,#{trailing_whitespace} + bar, + baz + ) + end + RUBY + end + end + + context 'when many parameters are on multiple lines, three on same line' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + def taz(abc, + foo, bar, barz, + ^^^^ Each parameter in a multi-line method definition must start on a separate line. + ^^^ Each parameter in a multi-line method definition must start on a separate line. + baz + ) + end + RUBY + + expect_correction(<<~RUBY) + def taz(abc, + foo,#{trailing_whitespace} + bar,#{trailing_whitespace} + barz, + baz + ) + end + RUBY + end + end + + context 'when many parameters including hash are on multiple lines, three on same line' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + def taz(abc, + foo, bar, z: "barz", + ^^^^^^^^^ Each parameter in a multi-line method definition must start on a separate line. + ^^^ Each parameter in a multi-line method definition must start on a separate line. + x: + ) + end + RUBY + + expect_correction(<<~RUBY) + def taz(abc, + foo,#{trailing_whitespace} + bar,#{trailing_whitespace} + z: "barz", + x: + ) + end + RUBY + end + end + + context 'when parameter\'s default value starts on same line but ends on different line' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + def taz(abc, foo = { + ^^^^^^^ Each parameter in a multi-line method definition must start on a separate line. + foo: "edf", + }) + end + RUBY + + expect_correction(<<~RUBY) + def taz(abc,#{trailing_whitespace} + foo = { + foo: "edf", + }) + end + RUBY + end + end + + context 'when second parameter starts on same line as end of first' do + it 'registers an offense and corrects' do + expect_offense(<<~RUBY) + def taz(abc = { + foo: "edf", + }, bar:) + ^^^^ Each parameter in a multi-line method definition must start on a separate line. + end + RUBY + + expect_correction(<<~RUBY) + def taz(abc = { + foo: "edf", + },#{trailing_whitespace} + bar:) + end + RUBY + end + end + + context 'when there are multiple parameters on the first line' do + it 'registers an offense and corrects starting from the 2nd argument' do + expect_offense(<<~RUBY) + def do_something(foo, bar, baz, + ^^^ Each parameter in a multi-line method definition must start on a separate line. + ^^^ Each parameter in a multi-line method definition must start on a separate line. + quux) + end + RUBY + + expect_correction(<<~RUBY) + def do_something(foo,#{trailing_whitespace} + bar,#{trailing_whitespace} + baz, + quux) + end + RUBY + end + end +end