Skip to content

Commit

Permalink
Add RequiredBracesMethods param to BlockDelimiters
Browse files Browse the repository at this point in the history
  • Loading branch information
Buildkite authored and bbatsov committed Feb 9, 2020
1 parent b9a290d commit aab3252
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
* [#7619](https://github.com/rubocop-hq/rubocop/issues/7619): Support autocorrect of legacy cop names for `Migration/DepartmentName`. ([@koic][])
* [#7659](https://github.com/rubocop-hq/rubocop/pull/7659): Layout/LineLength autocorrect now breaks up long lines with blocks. ([@maxh][])
* [#7677](https://github.com/rubocop-hq/rubocop/pull/7677): Add a cop for `Hash#each_key` and `Hash#each_value`. ([@jemmaissroff][])
* Add `BracesRequiredMethods` parameter to `Style/BlockDelimiters` to require braces for specific methods such as Sorbet's `sig`. ([@maxh][])

### Bug fixes

Expand Down
6 changes: 6 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2357,6 +2357,12 @@ Style/BlockDelimiters:
# # also good
# collection.each do |element| puts element end
AllowBracesOnProceduralOneLiners: false
# The BracesRequiredMethods overrides all other configurations except
# IgnoredMethods. It can be used to enforce that all blocks for specific
# methods use braces. For example, you can use this to enforce Sorbet
# signatures use braces even when the rest of your codebase enforces
# the `line_count_based` style.
BracesRequiredMethods: []

Style/CaseEquality:
Description: 'Avoid explicit use of the case equality operator(===).'
Expand Down
61 changes: 60 additions & 1 deletion lib/rubocop/cop/style/block_delimiters.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# frozen_string_literal: true

# rubocop:disable Metrics/ClassLength
module RuboCop
module Cop
module Style
Expand Down Expand Up @@ -106,12 +107,41 @@ module Style
# word.flip.flop
# }
#
# @example BracesRequiredMethods: ['sig']
#
# # Methods listed in the BracesRequiredMethods list, such as 'sig'
# # in this example, will require `{...}` braces. This option takes
# # precedence over all other configurations except IgnoredMethods.
#
# # bad
# sig do
# params(
# foo: string,
# ).void
# end
# def bar(foo)
# puts foo
# end
#
# # good
# sig {
# params(
# foo: string,
# ).void
# }
# def bar(foo)
# puts foo
# end
#
class BlockDelimiters < Cop
include ConfigurableEnforcedStyle
include IgnoredMethods

ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'

BRACES_REQUIRED_MESSAGE = 'Brace delimiters `{...}` required for ' \
"'%<method_name>s' method."

def on_send(node)
return unless node.arguments?
return if node.parenthesized?
Expand Down Expand Up @@ -175,7 +205,15 @@ def braces_for_chaining_message(node)
end
end

def braces_required_message(node)
format(BRACES_REQUIRED_MESSAGE, method_name: node.method_name.to_s)
end

def message(node)
if braces_required_method?(node.method_name)
return braces_required_message(node)
end

case style
when :line_count_based then line_count_based_message(node)
when :semantic then semantic_message(node)
Expand Down Expand Up @@ -238,7 +276,9 @@ def get_blocks(node, &block)
# rubocop:enable Metrics/CyclomaticComplexity

def proper_block_style?(node)
return true if ignored_method?(node.method_name)
if special_method?(node.method_name)
return special_method_proper_block_style?(node)
end

case style
when :line_count_based then line_count_based_block_style?(node)
Expand All @@ -248,6 +288,24 @@ def proper_block_style?(node)
end
end

def special_method?(method_name)
ignored_method?(method_name) || braces_required_method?(method_name)
end

def special_method_proper_block_style?(node)
method_name = node.method_name
return true if ignored_method?(method_name)
return node.braces? if braces_required_method?(method_name)
end

def braces_required_method?(method_name)
braces_required_methods.include?(method_name.to_s)
end

def braces_required_methods
cop_config.fetch('BracesRequiredMethods', [])
end

def line_count_based_block_style?(node)
node.multiline? ^ node.braces?
end
Expand Down Expand Up @@ -329,3 +387,4 @@ def array_or_range?(node)
end
end
end
# rubocop:enable Metrics/ClassLength
28 changes: 28 additions & 0 deletions manual/cops_style.md
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,33 @@ words.each { |word|
word.flip.flop
}
```
#### BracesRequiredMethods: ['sig']

```ruby
# Methods listed in the BracesRequiredMethods list, such as 'sig'
# in this example, will require `{...}` braces. This option takes
# precedence over all other configurations except IgnoredMethods.

# bad
sig do
params(
foo: string,
).void
end
def bar(foo)
puts foo
end

# good
sig {
params(
foo: string,
).void
}
def bar(foo)
puts foo
end
```

### Configurable attributes

Expand All @@ -483,6 +510,7 @@ ProceduralMethods | `benchmark`, `bm`, `bmbm`, `create`, `each_with_object`, `me
FunctionalMethods | `let`, `let!`, `subject`, `watch` | Array
IgnoredMethods | `lambda`, `proc`, `it` | Array
AllowBracesOnProceduralOneLiners | `false` | Boolean
BracesRequiredMethods | `[]` | Array

### References

Expand Down
83 changes: 83 additions & 0 deletions spec/rubocop/cop/style/block_delimiters_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -691,4 +691,87 @@
end
end
end

context 'BracesRequiredMethods' do
cop_config = {
'EnforcedStyle' => 'line_count_based',
'BracesRequiredMethods' => %w[sig]
}

let(:cop_config) { cop_config }

describe 'BracesRequiredMethods methods' do
it 'allows braces' do
expect_no_offenses(<<~RUBY)
sig {
params(
foo: string,
).void
}
def consume(foo)
foo
end
RUBY
end

it 'registers an offense with do' do
expect_offense(<<~RUBY)
sig do
^^ Brace delimiters `{...}` required for 'sig' method.
params(
foo: string,
).void
end
def consume(foo)
foo
end
RUBY

expect_correction(<<~RUBY)
sig {
params(
foo: string,
).void
}
def consume(foo)
foo
end
RUBY
end
end

describe 'other methods' do
it 'allows braces' do
expect_no_offenses(<<~RUBY)
other_method do
params(
foo: string,
).void
end
def consume(foo)
foo
end
RUBY
end

it 'auto-corrects { and } to do and end' do
source = <<~RUBY
each{ |x|
some_method
other_method
}
RUBY

expected_source = <<~RUBY
each do |x|
some_method
other_method
end
RUBY

new_source = autocorrect_source(source)
expect(new_source).to eq(expected_source)
end
end
end
end

0 comments on commit aab3252

Please sign in to comment.