rubocop 0.77.0 → 0.81.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -3
  4. data/config/default.yml +136 -60
  5. data/lib/rubocop.rb +20 -4
  6. data/lib/rubocop/ast/builder.rb +45 -42
  7. data/lib/rubocop/ast/node.rb +11 -18
  8. data/lib/rubocop/ast/node/block_node.rb +5 -1
  9. data/lib/rubocop/ast/node/case_match_node.rb +56 -0
  10. data/lib/rubocop/ast/node/def_node.rb +11 -0
  11. data/lib/rubocop/ast/node/forward_args_node.rb +18 -0
  12. data/lib/rubocop/ast/node/regexp_node.rb +2 -4
  13. data/lib/rubocop/ast/traversal.rb +29 -10
  14. data/lib/rubocop/cli/command/auto_genenerate_config.rb +7 -7
  15. data/lib/rubocop/cli/command/show_cops.rb +11 -4
  16. data/lib/rubocop/comment_config.rb +6 -1
  17. data/lib/rubocop/config.rb +28 -10
  18. data/lib/rubocop/config_loader.rb +19 -19
  19. data/lib/rubocop/config_obsoletion.rb +6 -4
  20. data/lib/rubocop/config_validator.rb +55 -95
  21. data/lib/rubocop/cop/autocorrect_logic.rb +7 -4
  22. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +2 -2
  23. data/lib/rubocop/cop/cop.rb +3 -1
  24. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  25. data/lib/rubocop/cop/generator.rb +3 -4
  26. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  27. data/lib/rubocop/cop/layout/array_alignment.rb +53 -10
  28. data/lib/rubocop/cop/layout/block_end_newline.rb +5 -3
  29. data/lib/rubocop/cop/layout/else_alignment.rb +8 -0
  30. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  31. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  32. data/lib/rubocop/cop/layout/hash_alignment.rb +8 -4
  33. data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -4
  34. data/lib/rubocop/cop/layout/leading_comment_space.rb +33 -2
  35. data/lib/rubocop/cop/{metrics → layout}/line_length.rb +35 -79
  36. data/lib/rubocop/cop/layout/multiline_block_layout.rb +14 -5
  37. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
  38. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +1 -1
  39. data/lib/rubocop/cop/layout/space_around_operators.rb +49 -6
  40. data/lib/rubocop/cop/layout/space_before_block_braces.rb +17 -0
  41. data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
  42. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
  43. data/lib/rubocop/cop/lint/boolean_symbol.rb +12 -0
  44. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  45. data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
  46. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  47. data/lib/rubocop/cop/lint/loop.rb +6 -4
  48. data/lib/rubocop/cop/lint/nested_method_definition.rb +2 -2
  49. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +89 -0
  50. data/lib/rubocop/cop/lint/raise_exception.rb +39 -0
  51. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +3 -3
  52. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +13 -8
  53. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -1
  54. data/lib/rubocop/cop/lint/struct_new_override.rb +58 -0
  55. data/lib/rubocop/cop/lint/suppressed_exception.rb +12 -22
  56. data/lib/rubocop/cop/lint/unused_method_argument.rb +32 -6
  57. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -0
  58. data/lib/rubocop/cop/migration/department_name.rb +47 -6
  59. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  60. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
  61. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +6 -1
  62. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +7 -7
  63. data/lib/rubocop/cop/mixin/hash_transform_method.rb +171 -0
  64. data/lib/rubocop/cop/mixin/line_length_help.rb +88 -0
  65. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -0
  66. data/lib/rubocop/cop/mixin/rational_literal.rb +18 -0
  67. data/lib/rubocop/cop/mixin/statement_modifier.rb +2 -2
  68. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -12
  69. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  70. data/lib/rubocop/cop/naming/method_name.rb +30 -0
  71. data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
  72. data/lib/rubocop/cop/offense.rb +11 -0
  73. data/lib/rubocop/cop/registry.rb +7 -2
  74. data/lib/rubocop/cop/style/access_modifier_declarations.rb +26 -6
  75. data/lib/rubocop/cop/style/attr.rb +8 -0
  76. data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
  77. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  78. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  79. data/lib/rubocop/cop/style/documentation.rb +43 -5
  80. data/lib/rubocop/cop/style/end_block.rb +6 -0
  81. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
  82. data/lib/rubocop/cop/style/guard_clause.rb +3 -2
  83. data/lib/rubocop/cop/style/hash_each_methods.rb +89 -0
  84. data/lib/rubocop/cop/style/hash_transform_keys.rb +83 -0
  85. data/lib/rubocop/cop/style/hash_transform_values.rb +83 -0
  86. data/lib/rubocop/cop/style/if_unless_modifier.rb +38 -3
  87. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  88. data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
  89. data/lib/rubocop/cop/style/lambda.rb +1 -0
  90. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -205
  91. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +169 -0
  92. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +54 -0
  93. data/lib/rubocop/cop/style/module_function.rb +56 -10
  94. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  95. data/lib/rubocop/cop/style/multiline_when_then.rb +5 -1
  96. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -4
  97. data/lib/rubocop/cop/style/numeric_predicate.rb +4 -3
  98. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -2
  99. data/lib/rubocop/cop/style/or_assignment.rb +3 -2
  100. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +7 -7
  101. data/lib/rubocop/cop/style/redundant_condition.rb +17 -4
  102. data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
  103. data/lib/rubocop/cop/style/symbol_array.rb +2 -2
  104. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  105. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +34 -22
  106. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +41 -0
  107. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +85 -0
  108. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +44 -0
  109. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -1
  110. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  111. data/lib/rubocop/cop/style/yoda_condition.rb +16 -1
  112. data/lib/rubocop/cop/variable_force.rb +4 -1
  113. data/lib/rubocop/formatter/base_formatter.rb +2 -2
  114. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  115. data/lib/rubocop/formatter/formatter_set.rb +1 -0
  116. data/lib/rubocop/formatter/json_formatter.rb +6 -5
  117. data/lib/rubocop/formatter/junit_formatter.rb +74 -0
  118. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  119. data/lib/rubocop/node_pattern.rb +97 -11
  120. data/lib/rubocop/options.rb +8 -8
  121. data/lib/rubocop/processed_source.rb +1 -1
  122. data/lib/rubocop/result_cache.rb +2 -0
  123. data/lib/rubocop/rspec/shared_contexts.rb +5 -0
  124. data/lib/rubocop/runner.rb +5 -1
  125. data/lib/rubocop/target_ruby.rb +151 -0
  126. data/lib/rubocop/version.rb +1 -1
  127. metadata +38 -10
  128. data/lib/rubocop/cop/lint/end_in_method.rb +0 -40
  129. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Help methods for determining if a line is too long.
6
+ module LineLengthHelp
7
+ private
8
+
9
+ def ignore_cop_directives?
10
+ config.for_cop('Layout/LineLength')['IgnoreCopDirectives']
11
+ end
12
+
13
+ def directive_on_source_line?(line_index)
14
+ source_line_number = line_index + processed_source.buffer.first_line
15
+ comment =
16
+ processed_source.comments
17
+ .detect { |e| e.location.line == source_line_number }
18
+
19
+ return false unless comment
20
+
21
+ comment.text.match(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
22
+ end
23
+
24
+ def allow_uri?
25
+ config.for_cop('Layout/LineLength')['AllowURI']
26
+ end
27
+
28
+ def allowed_uri_position?(line, uri_range)
29
+ uri_range.begin < max_line_length &&
30
+ (uri_range.end == line_length(line) ||
31
+ uri_range.end == line_length(line) - 1)
32
+ end
33
+
34
+ def line_length(line)
35
+ line.length + indentation_difference(line)
36
+ end
37
+
38
+ def find_excessive_uri_range(line)
39
+ last_uri_match = match_uris(line).last
40
+ return nil unless last_uri_match
41
+
42
+ begin_position, end_position = last_uri_match.offset(0).map do |pos|
43
+ pos + indentation_difference(line)
44
+ end
45
+ return nil if begin_position < max_line_length &&
46
+ end_position < max_line_length
47
+
48
+ begin_position...end_position
49
+ end
50
+
51
+ def match_uris(string)
52
+ matches = []
53
+ string.scan(uri_regexp) do
54
+ matches << $LAST_MATCH_INFO if valid_uri?($LAST_MATCH_INFO[0])
55
+ end
56
+ matches
57
+ end
58
+
59
+ def indentation_difference(line)
60
+ return 0 unless tab_indentation_width
61
+
62
+ line.match(/^\t*/)[0].size * (tab_indentation_width - 1)
63
+ end
64
+
65
+ def tab_indentation_width
66
+ config.for_cop('Layout/Tab')['IndentationWidth']
67
+ end
68
+
69
+ def uri_regexp
70
+ @uri_regexp ||=
71
+ URI::DEFAULT_PARSER
72
+ .make_regexp(config.for_cop('Layout/LineLength')['URISchemes'])
73
+ end
74
+
75
+ def valid_uri?(uri_ish_string)
76
+ URI.parse(uri_ish_string)
77
+ true
78
+ rescue URI::InvalidURIError, NoMethodError
79
+ false
80
+ end
81
+
82
+ def line_length_without_directive(line)
83
+ before_comment, = line.split(CommentConfig::COMMENT_DIRECTIVE_REGEXP)
84
+ before_comment.rstrip.length
85
+ end
86
+ end
87
+ end
88
+ end
@@ -5,15 +5,20 @@ module RuboCop
5
5
  # This module handles measurement and reporting of complexity in methods.
6
6
  module MethodComplexity
7
7
  include ConfigurableMax
8
+ include IgnoredMethods
8
9
  extend NodePattern::Macros
9
10
 
10
11
  def on_def(node)
12
+ return if ignored_method?(node.method_name)
13
+
11
14
  check_complexity(node, node.method_name)
12
15
  end
13
16
  alias on_defs on_def
14
17
 
15
18
  def on_block(node)
16
19
  define_method?(node) do |name|
20
+ return if ignored_method?(name)
21
+
17
22
  check_complexity(node, name)
18
23
  end
19
24
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for handling Rational literals.
6
+ module RationalLiteral
7
+ extend NodePattern::Macros
8
+
9
+ private
10
+
11
+ def_node_matcher :rational_literal?, <<~PATTERN
12
+ (send
13
+ (int _) :/
14
+ (rational _))
15
+ PATTERN
16
+ end
17
+ end
18
+ end
@@ -52,9 +52,9 @@ module RuboCop
52
52
  end
53
53
 
54
54
  def max_line_length
55
- return unless config.for_cop('Metrics/LineLength')['Enabled']
55
+ return unless config.for_cop('Layout/LineLength')['Enabled']
56
56
 
57
- config.for_cop('Metrics/LineLength')['Max']
57
+ config.for_cop('Layout/LineLength')['Max']
58
58
  end
59
59
 
60
60
  def indentation_multiplier
@@ -23,7 +23,7 @@ module RuboCop
23
23
  if comma_offset && !inside_comment?(after_last_item, comma_offset)
24
24
  check_comma(node, kind, after_last_item.begin_pos + comma_offset)
25
25
  elsif should_have_comma?(style, node)
26
- put_comma(node, items, kind)
26
+ put_comma(items, kind)
27
27
  end
28
28
  end
29
29
 
@@ -93,9 +93,12 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def method_name_and_arguments_on_same_line?(node)
96
- %i[send csend].include?(node.type) &&
97
- node.loc.selector.line == node.arguments.last.last_line &&
98
- node.last_line == node.arguments.last.last_line
96
+ return false unless node.call_type?
97
+
98
+ line = node.loc.selector.nil? ? node.loc.line : node.loc.selector.line
99
+
100
+ line == node.last_argument.last_line &&
101
+ node.last_line == node.last_argument.last_line
99
102
  end
100
103
 
101
104
  # A single argument with the closing bracket on the same line as the end
@@ -142,9 +145,7 @@ module RuboCop
142
145
  add_offense(range, location: range, message: msg)
143
146
  end
144
147
 
145
- def put_comma(node, items, kind)
146
- return if avoid_autocorrect?(elements(node))
147
-
148
+ def put_comma(items, kind)
148
149
  last_item = items.last
149
150
  return if last_item.block_pass_type?
150
151
 
@@ -166,11 +167,6 @@ module RuboCop
166
167
  range_between(expr.begin_pos + ix, expr.end_pos)
167
168
  end
168
169
 
169
- # By default, there's no reason to avoid auto-correct.
170
- def avoid_autocorrect?(_nodes)
171
- false
172
- end
173
-
174
170
  def any_heredoc?(items)
175
171
  items.any? { |item| heredoc?(item) }
176
172
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # directive. It can be configured to allow for memoized instance variables
11
11
  # prefixed with an underscore. Prefixing ivars with an underscore is a
12
12
  # convention that is used to implicitly indicate that an ivar should not
13
- # be set or referencd outside of the memoization method.
13
+ # be set or referenced outside of the memoization method.
14
14
  #
15
15
  # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
16
16
  # # bad
@@ -31,9 +31,28 @@ module RuboCop
31
31
  class MethodName < Cop
32
32
  include ConfigurableNaming
33
33
  include IgnoredPattern
34
+ include RangeHelp
34
35
 
35
36
  MSG = 'Use %<style>s for method names.'
36
37
 
38
+ def_node_matcher :attr?, <<~PATTERN
39
+ (send nil? ${:attr_reader :attr_writer :attr_accessor :attr} $...)
40
+ PATTERN
41
+
42
+ def_node_matcher :sym_name, '(sym $_name)'
43
+ def_node_matcher :str_name, '(str $_name)'
44
+
45
+ def on_send(node)
46
+ return unless (attrs = attr?(node))
47
+
48
+ attrs.last.each do |name_item|
49
+ name = attr_name(name_item)
50
+ next if !name || matches_ignored_pattern?(name)
51
+
52
+ check_name(node, name, range_position(node))
53
+ end
54
+ end
55
+
37
56
  def on_def(node)
38
57
  return if node.operator_method? ||
39
58
  matches_ignored_pattern?(node.method_name)
@@ -44,6 +63,17 @@ module RuboCop
44
63
 
45
64
  private
46
65
 
66
+ def attr_name(name_item)
67
+ sym_name(name_item) || str_name(name_item)
68
+ end
69
+
70
+ def range_position(node)
71
+ selector_end_pos = node.loc.selector.end_pos + 1
72
+ expr_end_pos = node.loc.expression.end_pos
73
+
74
+ range_between(selector_end_pos, expr_end_pos)
75
+ end
76
+
47
77
  def message(style)
48
78
  format(MSG, style: style)
49
79
  end
@@ -49,7 +49,7 @@ module RuboCop
49
49
  def on_def(node)
50
50
  return unless node.arguments?
51
51
 
52
- check(node, node.arguments)
52
+ check(node, node.arguments.reject(&:forward_args_type?))
53
53
  end
54
54
  alias on_defs on_def
55
55
  end
@@ -65,6 +65,17 @@ module RuboCop
65
65
  freeze
66
66
  end
67
67
 
68
+ # @api public
69
+ #
70
+ # @!attribute [r] correctable?
71
+ #
72
+ # @return [Boolean]
73
+ # whether this offense can be automatically corrected via
74
+ # autocorrect or a todo.
75
+ def correctable?
76
+ @status != :unsupported
77
+ end
78
+
68
79
  # @api public
69
80
  #
70
81
  # @!attribute [r] corrected?
@@ -146,10 +146,15 @@ module RuboCop
146
146
 
147
147
  def enabled?(cop, config, only_safe)
148
148
  cfg = config.for_cop(cop)
149
+
150
+ # cfg['Enabled'] might be a string `pending`, which is considered
151
+ # disabled
152
+ cop_enabled = cfg.fetch('Enabled') == true
153
+
149
154
  if only_safe
150
- cfg.fetch('Enabled') && cfg.fetch('Safe', true)
155
+ cop_enabled && cfg.fetch('Safe', true)
151
156
  else
152
- cfg.fetch('Enabled')
157
+ cop_enabled
153
158
  end
154
159
  end
155
160
 
@@ -5,11 +5,12 @@ module RuboCop
5
5
  module Style
6
6
  # Access modifiers should be declared to apply to a group of methods
7
7
  # or inline before each method, depending on configuration.
8
+ # EnforcedStyle config covers only method definitions.
9
+ # Applications of visibility methods to symbols can be controlled
10
+ # using AllowModifiersOnSymbols config.
8
11
  #
9
12
  # @example EnforcedStyle: group (default)
10
- #
11
13
  # # bad
12
- #
13
14
  # class Foo
14
15
  #
15
16
  # private def bar; end
@@ -18,7 +19,6 @@ module RuboCop
18
19
  # end
19
20
  #
20
21
  # # good
21
- #
22
22
  # class Foo
23
23
  #
24
24
  # private
@@ -27,10 +27,9 @@ module RuboCop
27
27
  # def baz; end
28
28
  #
29
29
  # end
30
- # @example EnforcedStyle: inline
31
30
  #
31
+ # @example EnforcedStyle: inline
32
32
  # # bad
33
- #
34
33
  # class Foo
35
34
  #
36
35
  # private
@@ -41,13 +40,28 @@ module RuboCop
41
40
  # end
42
41
  #
43
42
  # # good
44
- #
45
43
  # class Foo
46
44
  #
47
45
  # private def bar; end
48
46
  # private def baz; end
49
47
  #
50
48
  # end
49
+ #
50
+ # @example AllowModifiersOnSymbols: true
51
+ # # good
52
+ # class Foo
53
+ #
54
+ # private :bar, :baz
55
+ #
56
+ # end
57
+ #
58
+ # @example AllowModifiersOnSymbols: false
59
+ # # bad
60
+ # class Foo
61
+ #
62
+ # private :bar, :baz
63
+ #
64
+ # end
51
65
  class AccessModifierDeclarations < Cop
52
66
  include ConfigurableEnforcedStyle
53
67
 
@@ -61,9 +75,15 @@ module RuboCop
61
75
  'inlined in method definitions.'
62
76
  ].join(' ')
63
77
 
78
+ def_node_matcher :access_modifier_with_symbol?, <<~PATTERN
79
+ (send nil? {:private :protected :public} (sym _))
80
+ PATTERN
81
+
64
82
  def on_send(node)
65
83
  return unless node.access_modifier?
66
84
  return if node.parent.pair_type?
85
+ return if cop_config['AllowModifiersOnSymbols'] &&
86
+ access_modifier_with_symbol?(node)
67
87
 
68
88
  if offense?(node)
69
89
  add_offense(node, location: :selector) do
@@ -21,6 +21,10 @@ module RuboCop
21
21
 
22
22
  def on_send(node)
23
23
  return unless node.command?(:attr) && node.arguments?
24
+ # check only for method definitions in class/module body
25
+ return if node.parent &&
26
+ !node.parent.class_type? &&
27
+ !class_eval?(node.parent)
24
28
 
25
29
  add_offense(node, location: :selector)
26
30
  end
@@ -56,6 +60,10 @@ module RuboCop
56
60
  'attr_reader'
57
61
  end
58
62
  end
63
+
64
+ def_node_matcher :class_eval?, <<~PATTERN
65
+ (block (send _ {:class_eval :module_eval}) ...)
66
+ PATTERN
59
67
  end
60
68
  end
61
69
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  module RuboCop
4
5
  module Cop
5
6
  module Style
@@ -106,12 +107,41 @@ module RuboCop
106
107
  # word.flip.flop
107
108
  # }
108
109
  #
110
+ # @example BracesRequiredMethods: ['sig']
111
+ #
112
+ # # Methods listed in the BracesRequiredMethods list, such as 'sig'
113
+ # # in this example, will require `{...}` braces. This option takes
114
+ # # precedence over all other configurations except IgnoredMethods.
115
+ #
116
+ # # bad
117
+ # sig do
118
+ # params(
119
+ # foo: string,
120
+ # ).void
121
+ # end
122
+ # def bar(foo)
123
+ # puts foo
124
+ # end
125
+ #
126
+ # # good
127
+ # sig {
128
+ # params(
129
+ # foo: string,
130
+ # ).void
131
+ # }
132
+ # def bar(foo)
133
+ # puts foo
134
+ # end
135
+ #
109
136
  class BlockDelimiters < Cop
110
137
  include ConfigurableEnforcedStyle
111
138
  include IgnoredMethods
112
139
 
113
140
  ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'
114
141
 
142
+ BRACES_REQUIRED_MESSAGE = 'Brace delimiters `{...}` required for ' \
143
+ "'%<method_name>s' method."
144
+
115
145
  def on_send(node)
116
146
  return unless node.arguments?
117
147
  return if node.parenthesized?
@@ -175,7 +205,15 @@ module RuboCop
175
205
  end
176
206
  end
177
207
 
208
+ def braces_required_message(node)
209
+ format(BRACES_REQUIRED_MESSAGE, method_name: node.method_name.to_s)
210
+ end
211
+
178
212
  def message(node)
213
+ if braces_required_method?(node.method_name)
214
+ return braces_required_message(node)
215
+ end
216
+
179
217
  case style
180
218
  when :line_count_based then line_count_based_message(node)
181
219
  when :semantic then semantic_message(node)
@@ -238,7 +276,9 @@ module RuboCop
238
276
  # rubocop:enable Metrics/CyclomaticComplexity
239
277
 
240
278
  def proper_block_style?(node)
241
- return true if ignored_method?(node.method_name)
279
+ if special_method?(node.method_name)
280
+ return special_method_proper_block_style?(node)
281
+ end
242
282
 
243
283
  case style
244
284
  when :line_count_based then line_count_based_block_style?(node)
@@ -248,6 +288,24 @@ module RuboCop
248
288
  end
249
289
  end
250
290
 
291
+ def special_method?(method_name)
292
+ ignored_method?(method_name) || braces_required_method?(method_name)
293
+ end
294
+
295
+ def special_method_proper_block_style?(node)
296
+ method_name = node.method_name
297
+ return true if ignored_method?(method_name)
298
+ return node.braces? if braces_required_method?(method_name)
299
+ end
300
+
301
+ def braces_required_method?(method_name)
302
+ braces_required_methods.include?(method_name.to_s)
303
+ end
304
+
305
+ def braces_required_methods
306
+ cop_config.fetch('BracesRequiredMethods', [])
307
+ end
308
+
251
309
  def line_count_based_block_style?(node)
252
310
  node.multiline? ^ node.braces?
253
311
  end
@@ -329,3 +387,4 @@ module RuboCop
329
387
  end
330
388
  end
331
389
  end
390
+ # rubocop:enable Metrics/ClassLength