diff --git a/CHANGELOG.md b/CHANGELOG.md index cc23cf116e3..90641f968d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## master (unreleased) +### New features + +* [#6393](https://github.com/rubocop-hq/rubocop/issues/6393): Add AllowBracesOnProceduralOneLiners option to fine-tune Style/BlockDelimiter's semantic mode. ([@davearonson][]) + ### Bug fixes * [#5934](https://github.com/rubocop-hq/rubocop/issues/5934): Handle the combination of `--auto-gen-config` and `--config FILE` correctly. ([@jonas054][]) @@ -3636,3 +3640,4 @@ [@andrew-aladev]: https://github.com/andrew-aladev [@y-yagi]: https://github.com/y-yagi [@DiscoStarslayer]: https://github.com/DiscoStarslayer +[@davearonson]: https://github.com/davearonson diff --git a/config/default.yml b/config/default.yml index 00804942e6d..e015f5c8978 100644 --- a/config/default.yml +++ b/config/default.yml @@ -2719,8 +2719,9 @@ Style/BlockDelimiters: - line_count_based # The `semantic` style enforces braces around functional blocks, where the # primary purpose of the block is to return a value and do..end for - # procedural blocks, where the primary purpose of the block is its - # side-effects. + # multi-line procedural blocks, where the primary purpose of the block is + # its side-effects. Single-line procedural blocks may only use do-end, + # unless AllowBracesOnProceduralOneLiners has a truthy value (see below). # # This looks at the usage of a block's method to determine its type (e.g. is # the result of a `map` assigned to a variable or passed to another @@ -2781,6 +2782,28 @@ Style/BlockDelimiters: - lambda - proc - it + # The AllowBracesOnProceduralOneLiners option is ignored unless the + # EnforcedStyle is set to `semantic`. If so: + # + # If AllowBracesOnProceduralOneLiners is unspecified, or set to any + # falsey value, then semantic purity is maintained, so one-line + # procedural blocks must use do-end, not braces. + # + # # bad + # collection.each { |element| puts element } + # + # # good + # collection.each do |element| puts element end + # + # If AllowBracesOnProceduralOneLiners is set to any truthy value, + # then one-line procedural blocks may use either style. + # + # # good + # collection.each { |element| puts element } + # + # # also good + # collection.each do |element| puts element end + AllowBracesOnProceduralOneLiners: false Style/BracesAroundHashParameters: Description: 'Enforce braces style around hash parameters.' diff --git a/lib/rubocop/cop/style/block_delimiters.rb b/lib/rubocop/cop/style/block_delimiters.rb index c530e08971b..9bf8b6117ba 100644 --- a/lib/rubocop/cop/style/block_delimiters.rb +++ b/lib/rubocop/cop/style/block_delimiters.rb @@ -60,6 +60,30 @@ module Style # x # }.inspect # + # # The AllowBracesOnProceduralOneLiners option is ignored unless the + # # EnforcedStyle is set to `semantic`. If so: + # + # # If the AllowBracesOnProceduralOneLiners option is unspecified, or + # # set to `false` or any other falsey value, then semantic purity is + # # maintained, so one-line procedural blocks must use do-end, not + # # braces. + # + # # bad + # collection.each { |element| puts element } + # + # # good + # collection.each do |element| puts element end + # + # # If the AllowBracesOnProceduralOneLiners option is set to `true`, or + # # any other truthy value, then one-line procedural blocks may use + # # either style. (There is no setting for requiring braces on them.) + # + # # good + # collection.each { |element| puts element } + # + # # also good + # collection.each do |element| puts element end + # # @example EnforcedStyle: braces_for_chaining # # bad # words.each do |word| @@ -216,7 +240,8 @@ def semantic_block_style?(node) method_name = node.method_name if node.braces? - functional_method?(method_name) || functional_block?(node) + functional_method?(method_name) || functional_block?(node) || + (procedural_oneliners_may_have_braces? && !node.multiline?) else procedural_method?(method_name) || !return_value_used?(node) end @@ -250,6 +275,10 @@ def functional_block?(node) return_value_used?(node) || return_value_of_scope?(node) end + def procedural_oneliners_may_have_braces? + cop_config['AllowBracesOnProceduralOneLiners'] + end + def procedural_method?(method_name) cop_config['ProceduralMethods'].map(&:to_sym).include?(method_name) end diff --git a/manual/cops_style.md b/manual/cops_style.md index 6e6048ee95c..632fbe7fe3b 100644 --- a/manual/cops_style.md +++ b/manual/cops_style.md @@ -415,6 +415,30 @@ foo = map { |x| map { |x| x }.inspect + +# The AllowBracesOnProceduralOneLiners option is ignored unless the +# EnforcedStyle is set to `semantic`. If so: + +# If the AllowBracesOnProceduralOneLiners option is unspecified, or +# set to `false` or any other falsey value, then semantic purity is +# maintained, so one-line procedural blocks must use do-end, not +# braces. + +# bad +collection.each { |element| puts element } + +# good +collection.each do |element| puts element end + +# If the AllowBracesOnProceduralOneLiners option is set to `true`, or +# any other truthy value, then one-line procedural blocks may use +# either style. (There is no setting for requiring braces on them.) + +# good +collection.each { |element| puts element } + +# also good +collection.each do |element| puts element end ``` #### EnforcedStyle: braces_for_chaining @@ -438,6 +462,7 @@ EnforcedStyle | `line_count_based` | `line_count_based`, `semantic`, `braces_for ProceduralMethods | `benchmark`, `bm`, `bmbm`, `create`, `each_with_object`, `measure`, `new`, `realtime`, `tap`, `with_object` | Array FunctionalMethods | `let`, `let!`, `subject`, `watch` | Array IgnoredMethods | `lambda`, `proc`, `it` | Array +AllowBracesOnProceduralOneLiners | `false` | Boolean ### References diff --git a/spec/rubocop/cop/style/block_delimiters_spec.rb b/spec/rubocop/cop/style/block_delimiters_spec.rb index d34d03cfbe9..51e4b466999 100644 --- a/spec/rubocop/cop/style/block_delimiters_spec.rb +++ b/spec/rubocop/cop/style/block_delimiters_spec.rb @@ -169,18 +169,36 @@ RUBY end - it 'registers an offense for a single line procedural block' do - expect_offense(<<-RUBY.strip_indent) - each { |x| puts x } - ^ Prefer `do...end` over `{...}` for procedural blocks. - RUBY - end + context 'with a procedural one-line block' do + context 'with AllowBracesOnProceduralOneLiners false or unset' do + it 'registers an offense for a single line procedural block' do + expect_offense(<<-RUBY.strip_indent) + each { |x| puts x } + ^ Prefer `do...end` over `{...}` for procedural blocks. + RUBY + end + + it 'accepts a single line block with do-end if it is procedural' do + expect_no_offenses('each do |x| puts x; end') + end + end + + context 'with AllowBracesOnProceduralOneLiners true' do + let(:cop_config) do + cop_config.merge('AllowBracesOnProceduralOneLiners' => true) + end - it 'accepts a single line block with do-end if it is procedural' do - expect_no_offenses('each do |x| puts x; end') + it 'accepts a single line procedural block with braces' do + expect_no_offenses('each { |x| puts x }') + end + + it 'accepts a single line procedural do-end block' do + expect_no_offenses('each do |x| puts x; end') + end + end end - context 'with a procedural block' do + context 'with a procedural multi-line block' do let(:corrected_source) do <<-RUBY.strip_indent each do |x|