rubocop 0.91.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +8 -5
  3. data/config/default.yml +143 -56
  4. data/lib/rubocop.rb +17 -5
  5. data/lib/rubocop/cached_data.rb +2 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/version.rb +1 -1
  8. data/lib/rubocop/comment_config.rb +1 -1
  9. data/lib/rubocop/config.rb +4 -0
  10. data/lib/rubocop/config_loader.rb +19 -2
  11. data/lib/rubocop/config_loader_resolver.rb +7 -5
  12. data/lib/rubocop/config_regeneration.rb +33 -0
  13. data/lib/rubocop/config_validator.rb +7 -6
  14. data/lib/rubocop/cop/badge.rb +9 -24
  15. data/lib/rubocop/cop/base.rb +16 -1
  16. data/lib/rubocop/cop/bundler/duplicated_gem.rb +23 -3
  17. data/lib/rubocop/cop/commissioner.rb +36 -22
  18. data/lib/rubocop/cop/corrector.rb +3 -1
  19. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  20. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  21. data/lib/rubocop/cop/force.rb +1 -1
  22. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
  23. data/lib/rubocop/cop/layout/array_alignment.rb +1 -0
  24. data/lib/rubocop/cop/layout/class_structure.rb +7 -0
  25. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  26. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  27. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -7
  28. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  29. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  30. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -11
  31. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  32. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  33. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  34. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  35. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  36. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  37. data/lib/rubocop/cop/lint/debugger.rb +2 -3
  38. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  39. data/lib/rubocop/cop/lint/empty_block.rb +46 -0
  40. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  41. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  42. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +17 -3
  43. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  44. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  45. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  47. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  48. data/lib/rubocop/cop/lint/to_enum_arguments.rb +95 -0
  49. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  50. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +185 -0
  51. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  52. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  53. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  54. data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
  55. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  56. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  57. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  58. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  59. data/lib/rubocop/cop/offense.rb +18 -5
  60. data/lib/rubocop/cop/security/open.rb +12 -10
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  62. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  63. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  64. data/lib/rubocop/cop/style/array_coercion.rb +4 -0
  65. data/lib/rubocop/cop/style/case_like_if.rb +20 -4
  66. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  67. data/lib/rubocop/cop/style/combinable_loops.rb +8 -1
  68. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  69. data/lib/rubocop/cop/style/date_time.rb +12 -1
  70. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +67 -0
  71. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  72. data/lib/rubocop/cop/style/for.rb +0 -4
  73. data/lib/rubocop/cop/style/format_string_token.rb +48 -3
  74. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  77. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  78. data/lib/rubocop/cop/style/mixin_usage.rb +7 -27
  79. data/lib/rubocop/cop/style/multiple_comparison.rb +54 -7
  80. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  81. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +11 -3
  82. data/lib/rubocop/cop/style/raise_args.rb +0 -3
  83. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  84. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  85. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  86. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  87. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +45 -24
  88. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  89. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  90. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  91. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  92. data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
  93. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  94. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  95. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  96. data/lib/rubocop/cop/team.rb +6 -1
  97. data/lib/rubocop/cop/util.rb +1 -1
  98. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  99. data/lib/rubocop/ext/regexp_node.rb +29 -10
  100. data/lib/rubocop/ext/regexp_parser.rb +77 -0
  101. data/lib/rubocop/formatter/disabled_config_formatter.rb +12 -5
  102. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  103. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  104. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  105. data/lib/rubocop/magic_comment.rb +2 -2
  106. data/lib/rubocop/options.rb +22 -17
  107. data/lib/rubocop/result_cache.rb +8 -2
  108. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  109. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  110. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  111. data/lib/rubocop/runner.rb +9 -5
  112. data/lib/rubocop/target_finder.rb +27 -26
  113. data/lib/rubocop/target_ruby.rb +1 -1
  114. data/lib/rubocop/version.rb +61 -6
  115. metadata +21 -16
  116. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
@@ -33,10 +33,31 @@ module RuboCop
33
33
  #
34
34
  # # bad
35
35
  # format('%<greeting>s', greeting: 'Hello')
36
- # format('%{greeting}', 'Hello')
36
+ # format('%{greeting}', greeting: 'Hello')
37
37
  #
38
38
  # # good
39
39
  # format('%s', 'Hello')
40
+ #
41
+ # It is allowed to contain unannotated token
42
+ # if the number of them is less than or equals to
43
+ # `MaxUnannotatedPlaceholdersAllowed`.
44
+ #
45
+ # @example MaxUnannotatedPlaceholdersAllowed: 0
46
+ #
47
+ # # bad
48
+ # format('%06d', 10)
49
+ # format('%s %s.', 'Hello', 'world')
50
+ #
51
+ # # good
52
+ # format('%<number>06d', number: 10)
53
+ #
54
+ # @example MaxUnannotatedPlaceholdersAllowed: 1 (default)
55
+ #
56
+ # # bad
57
+ # format('%s %s.', 'Hello', 'world')
58
+ #
59
+ # # good
60
+ # format('%06d', 10)
40
61
  class FormatStringToken < Base
41
62
  include ConfigurableEnforcedStyle
42
63
 
@@ -44,8 +65,12 @@ module RuboCop
44
65
  return unless node.value.include?('%')
45
66
  return if node.each_ancestor(:xstr, :regexp).any?
46
67
 
47
- tokens(node) do |detected_style, token_range|
48
- if detected_style == style || unannotated_format?(node, detected_style)
68
+ detections = collect_detections(node)
69
+ return if detections.empty?
70
+ return if allowed_unannotated?(detections)
71
+
72
+ detections.each do |detected_style, token_range|
73
+ if detected_style == style
49
74
  correct_style_detected
50
75
  else
51
76
  style_detected(detected_style)
@@ -112,6 +137,26 @@ module RuboCop
112
137
  yield(detected_style, token)
113
138
  end
114
139
  end
140
+
141
+ def collect_detections(node)
142
+ detections = []
143
+ tokens(node) do |detected_style, token_range|
144
+ unless unannotated_format?(node, detected_style)
145
+ detections << [detected_style, token_range]
146
+ end
147
+ end
148
+ detections
149
+ end
150
+
151
+ def allowed_unannotated?(detections)
152
+ return false if detections.size > max_unannotated_placeholders_allowed
153
+
154
+ detections.all? { |detected_style,| detected_style == :unannotated }
155
+ end
156
+
157
+ def max_unannotated_placeholders_allowed
158
+ cop_config['MaxUnannotatedPlaceholdersAllowed']
159
+ end
115
160
  end
116
161
  end
117
162
  end
@@ -144,25 +144,22 @@ module RuboCop
144
144
  # # good
145
145
  # Array 1
146
146
  class MethodCallWithArgsParentheses < Base
147
+ require_relative 'method_call_with_args_parentheses/omit_parentheses'
148
+ require_relative 'method_call_with_args_parentheses/require_parentheses'
149
+
147
150
  include ConfigurableEnforcedStyle
148
151
  include IgnoredMethods
149
152
  include IgnoredPattern
153
+ include RequireParentheses
154
+ include OmitParentheses
150
155
  extend AutoCorrector
151
156
 
152
- def initialize(*)
153
- super
154
- return unless style_configured?
155
-
156
- case style
157
- when :require_parentheses
158
- extend RequireParentheses
159
- when :omit_parentheses
160
- extend OmitParentheses
161
- end
157
+ def on_send(node)
158
+ send(style, node) # call require_parentheses or omit_parentheses
162
159
  end
163
-
164
- # @abstract Overridden in style modules
165
- def autocorrect(_node); end
160
+ alias on_csend on_send
161
+ alias on_super on_send
162
+ alias on_yield on_send
166
163
 
167
164
  private
168
165
 
@@ -7,15 +7,19 @@ module RuboCop
7
7
  # Style omit_parentheses
8
8
  module OmitParentheses
9
9
  TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
10
+ OMIT_MSG = 'Omit parentheses for method calls with arguments.'
11
+ private_constant :OMIT_MSG
10
12
 
11
- def on_send(node)
13
+ private
14
+
15
+ def omit_parentheses(node)
12
16
  return unless node.parenthesized?
13
17
  return if node.implicit_call?
14
18
  return if super_call_without_arguments?(node)
15
19
  return if allowed_camel_case_method_call?(node)
16
20
  return if legitimate_call_with_parentheses?(node)
17
21
 
18
- add_offense(offense_range(node)) do |corrector|
22
+ add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
19
23
  if parentheses_at_the_end_of_multiline_call?(node)
20
24
  corrector.replace(args_begin(node), ' \\')
21
25
  else
@@ -24,20 +28,11 @@ module RuboCop
24
28
  corrector.remove(node.loc.end)
25
29
  end
26
30
  end
27
- alias on_csend on_send
28
- alias on_super on_send
29
- alias on_yield on_send
30
-
31
- private
32
31
 
33
32
  def offense_range(node)
34
33
  node.loc.begin.join(node.loc.end)
35
34
  end
36
35
 
37
- def message(_range = nil)
38
- 'Omit parentheses for method calls with arguments.'
39
- end
40
-
41
36
  def super_call_without_arguments?(node)
42
37
  node.super_type? && node.arguments.none?
43
38
  end
@@ -6,27 +6,23 @@ module RuboCop
6
6
  class MethodCallWithArgsParentheses
7
7
  # Style require_parentheses
8
8
  module RequireParentheses
9
- def on_send(node)
9
+ REQUIRE_MSG = 'Use parentheses for method calls with arguments.'
10
+ private_constant :REQUIRE_MSG
11
+
12
+ private
13
+
14
+ def require_parentheses(node)
10
15
  return if ignored_method?(node.method_name)
11
16
  return if matches_ignored_pattern?(node.method_name)
12
17
  return if eligible_for_parentheses_omission?(node)
13
18
  return unless node.arguments? && !node.parenthesized?
14
19
 
15
- add_offense(node) do |corrector|
20
+ add_offense(node, message: REQUIRE_MSG) do |corrector|
16
21
  corrector.replace(args_begin(node), '(')
17
22
 
18
23
  corrector.insert_after(args_end(node), ')') unless args_parenthesized?(node)
19
24
  end
20
25
  end
21
- alias on_csend on_send
22
- alias on_super on_send
23
- alias on_yield on_send
24
-
25
- def message(_node = nil)
26
- 'Use parentheses for method calls with arguments.'
27
- end
28
-
29
- private
30
26
 
31
27
  def eligible_for_parentheses_omission?(node)
32
28
  node.operator_method? || node.setter_method? || ignored_macro?(node)
@@ -140,16 +140,12 @@ module RuboCop
140
140
  def missing_parentheses(node)
141
141
  location = node.arguments.source_range
142
142
 
143
- return unless unexpected_style_detected(:require_no_parentheses)
144
-
145
143
  add_offense(location, message: MSG_MISSING) do |corrector|
146
144
  correct_definition(node, corrector)
147
145
  end
148
146
  end
149
147
 
150
148
  def unwanted_parentheses(args)
151
- return unless unexpected_style_detected(:require_parentheses)
152
-
153
149
  add_offense(args, message: MSG_PRESENT) do |corrector|
154
150
  # offense is registered on args node when parentheses are unwanted
155
151
  correct_arguments(args, corrector)
@@ -50,41 +50,21 @@ module RuboCop
50
50
  const)
51
51
  PATTERN
52
52
 
53
- def_node_matcher :wrapped_macro_scope?, <<~PATTERN
54
- {({sclass class module block} ... ({begin if} ...))}
53
+ def_node_matcher :in_top_level_scope?, <<~PATTERN
54
+ {
55
+ root? # either at the top level
56
+ ^[ {kwbegin begin if def} # or wrapped within one of these
57
+ #in_top_level_scope? ] # that is in top level scope
58
+ }
55
59
  PATTERN
56
60
 
57
61
  def on_send(node)
58
62
  include_statement(node) do |statement|
59
- return if node.argument? ||
60
- accepted_include?(node) ||
61
- belongs_to_class_or_module?(node)
63
+ return unless in_top_level_scope?(node)
62
64
 
63
65
  add_offense(node, message: format(MSG, statement: statement))
64
66
  end
65
67
  end
66
-
67
- private
68
-
69
- def accepted_include?(node)
70
- node.parent && (node.macro? || ascend_macro_scope?(node.parent))
71
- end
72
-
73
- def ascend_macro_scope?(ancestor)
74
- return true if wrapped_macro_scope?(ancestor)
75
-
76
- ancestor.parent && ascend_macro_scope?(ancestor.parent)
77
- end
78
-
79
- def belongs_to_class_or_module?(node)
80
- if !node.parent
81
- false
82
- else
83
- return true if node.parent.class_type? || node.parent.module_type?
84
-
85
- belongs_to_class_or_module?(node.parent)
86
- end
87
- end
88
68
  end
89
69
  end
90
70
  end
@@ -4,7 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop checks against comparing a variable with multiple items, where
7
- # `Array#include?` could be used instead to avoid code repetition.
7
+ # `Array#include?`, `Set#include?` or a `case` could be used instead
8
+ # to avoid code repetition.
9
+ # It accepts comparisons of multiple method calls to avoid unnecessary method calls
10
+ # by default. It can be configured by `AllowMethodComparison` option.
8
11
  #
9
12
  # @example
10
13
  # # bad
@@ -14,25 +17,61 @@ module RuboCop
14
17
  # # good
15
18
  # a = 'a'
16
19
  # foo if ['a', 'b', 'c'].include?(a)
20
+ #
21
+ # VALUES = Set['a', 'b', 'c'].freeze
22
+ # # elsewhere...
23
+ # foo if VALUES.include?(a)
24
+ #
25
+ # case foo
26
+ # when 'a', 'b', 'c' then foo
27
+ # # ...
28
+ # end
29
+ #
30
+ # # accepted (but consider `case` as above)
31
+ # foo if a == b.lightweight || a == b.heavyweight
32
+ #
33
+ # @example AllowMethodComparison: true (default)
34
+ # # good
35
+ # foo if a == b.lightweight || a == b.heavyweight
36
+ #
37
+ # @example AllowMethodComparison: false
38
+ # # bad
39
+ # foo if a == b.lightweight || a == b.heavyweight
40
+ #
41
+ # # good
42
+ # foo if [b.lightweight, b.heavyweight].include?(a)
17
43
  class MultipleComparison < Base
44
+ extend AutoCorrector
45
+
18
46
  MSG = 'Avoid comparing a variable with multiple items ' \
19
47
  'in a conditional, use `Array#include?` instead.'
20
48
 
49
+ def on_new_investigation
50
+ @compared_elements = []
51
+ end
52
+
21
53
  def on_or(node)
22
54
  root_of_or_node = root_of_or_node(node)
23
55
 
24
56
  return unless node == root_of_or_node
25
57
  return unless nested_variable_comparison?(root_of_or_node)
26
58
 
27
- add_offense(node)
59
+ add_offense(node) do |corrector|
60
+ elements = @compared_elements.join(', ')
61
+ prefer_method = "[#{elements}].include?(#{variables_in_node(node).first})"
62
+
63
+ corrector.replace(node, prefer_method)
64
+ end
28
65
  end
29
66
 
30
67
  private
31
68
 
32
69
  def_node_matcher :simple_double_comparison?, '(send $lvar :== $lvar)'
33
- def_node_matcher :simple_comparison?, <<~PATTERN
34
- {(send $lvar :== _)
35
- (send _ :== $lvar)}
70
+ def_node_matcher :simple_comparison_lhs?, <<~PATTERN
71
+ (send $lvar :== $_)
72
+ PATTERN
73
+ def_node_matcher :simple_comparison_rhs?, <<~PATTERN
74
+ (send $_ :== $lvar)
36
75
  PATTERN
37
76
 
38
77
  def nested_variable_comparison?(node)
@@ -55,9 +94,13 @@ module RuboCop
55
94
  simple_double_comparison?(node) do |var1, var2|
56
95
  return [variable_name(var1), variable_name(var2)]
57
96
  end
58
- simple_comparison?(node) do |var|
97
+ if (var, obj = simple_comparison_lhs?(node)) || (obj, var = simple_comparison_rhs?(node))
98
+ return [] if allow_method_comparison? && obj.send_type?
99
+
100
+ @compared_elements << obj.source
59
101
  return [variable_name(var)]
60
102
  end
103
+
61
104
  []
62
105
  end
63
106
 
@@ -74,7 +117,7 @@ module RuboCop
74
117
  end
75
118
 
76
119
  def comparison?(node)
77
- simple_comparison?(node) || nested_comparison?(node)
120
+ simple_comparison_lhs?(node) || simple_comparison_rhs?(node) || nested_comparison?(node)
78
121
  end
79
122
 
80
123
  def root_of_or_node(or_node)
@@ -86,6 +129,10 @@ module RuboCop
86
129
  or_node
87
130
  end
88
131
  end
132
+
133
+ def allow_method_comparison?
134
+ cop_config.fetch('AllowMethodComparison', true)
135
+ end
89
136
  end
90
137
  end
91
138
  end
@@ -49,6 +49,8 @@ module RuboCop
49
49
  end
50
50
 
51
51
  def remove_parentheses(source)
52
+ return source unless source.start_with?('(')
53
+
52
54
  source.gsub(/\A\(/, '').gsub(/\)\z/, '')
53
55
  end
54
56
  end
@@ -4,7 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # This cop checks for places where keyword arguments can be used instead of
7
- # boolean arguments when defining methods.
7
+ # boolean arguments when defining methods. `respond_to_missing?` method is allowed by default.
8
+ # These are customizable with `AllowedMethods` option.
8
9
  #
9
10
  # @example
10
11
  # # bad
@@ -23,13 +24,20 @@ module RuboCop
23
24
  # puts bar
24
25
  # end
25
26
  #
27
+ # @example AllowedMethods: ['some_method']
28
+ # # good
29
+ # def some_method(bar = false)
30
+ # puts bar
31
+ # end
32
+ #
26
33
  class OptionalBooleanParameter < Base
34
+ include AllowedMethods
35
+
27
36
  MSG = 'Use keyword arguments when defining method with boolean argument.'
28
37
  BOOLEAN_TYPES = %i[true false].freeze
29
- METHODS_EXCLUDED = %i[respond_to_missing?].freeze
30
38
 
31
39
  def on_def(node)
32
- return if METHODS_EXCLUDED.include?(node.method_name)
40
+ return if allowed_method?(node.method_name)
33
41
 
34
42
  node.arguments.each do |arg|
35
43
  next unless arg.optarg_type?
@@ -84,8 +84,6 @@ module RuboCop
84
84
 
85
85
  def check_compact(node)
86
86
  if node.arguments.size > 1
87
- return unless opposite_style_detected
88
-
89
87
  add_offense(node, message: format(COMPACT_MSG, method: node.method_name)) do |corrector|
90
88
  replacement = correction_exploded_to_compact(node)
91
89
 
@@ -103,7 +101,6 @@ module RuboCop
103
101
 
104
102
  return unless first_arg.send_type? && first_arg.method?(:new)
105
103
  return if acceptable_exploded_args?(first_arg.arguments)
106
- return unless opposite_style_detected
107
104
 
108
105
  add_offense(node, message: format(EXPLODED_MSG, method: node.method_name)) do |corrector|
109
106
  replacement = correction_compact_to_exploded(node)
@@ -28,6 +28,14 @@ module RuboCop
28
28
  # end
29
29
  #
30
30
  # # bad
31
+ # begin
32
+ # do_something
33
+ # end
34
+ #
35
+ # # good
36
+ # do_something
37
+ #
38
+ # # bad
31
39
  # # When using Ruby 2.5 or later.
32
40
  # do_something do
33
41
  # begin
@@ -60,7 +68,9 @@ module RuboCop
60
68
  MSG = 'Redundant `begin` block detected.'
61
69
 
62
70
  def on_def(node)
63
- check(node)
71
+ return unless node.body&.kwbegin_type?
72
+
73
+ register_offense(node.body)
64
74
  end
65
75
  alias on_defs on_def
66
76
 
@@ -69,20 +79,38 @@ module RuboCop
69
79
 
70
80
  return if node.send_node.lambda_literal?
71
81
  return if node.braces?
82
+ return unless node.body&.kwbegin_type?
72
83
 
73
- check(node)
84
+ register_offense(node.body)
74
85
  end
75
86
 
76
- private
87
+ def on_kwbegin(node)
88
+ return if contain_rescue_or_ensure?(node) || valid_context_using_only_begin?(node)
77
89
 
78
- def check(node)
79
- return unless node.body&.kwbegin_type?
90
+ register_offense(node)
91
+ end
92
+
93
+ private
80
94
 
81
- add_offense(node.body.loc.begin) do |corrector|
82
- corrector.remove(node.body.loc.begin)
83
- corrector.remove(node.body.loc.end)
95
+ def register_offense(node)
96
+ add_offense(node.loc.begin) do |corrector|
97
+ corrector.remove(node.loc.begin)
98
+ corrector.remove(node.loc.end)
84
99
  end
85
100
  end
101
+
102
+ def contain_rescue_or_ensure?(node)
103
+ first_child = node.children.first
104
+
105
+ first_child.rescue_type? || first_child.ensure_type?
106
+ end
107
+
108
+ def valid_context_using_only_begin?(node)
109
+ parent = node.parent
110
+
111
+ node.each_ancestor.any?(&:assignment?) || parent&.post_condition_loop? ||
112
+ parent&.send_type? || parent&.operator_keyword?
113
+ end
86
114
  end
87
115
  end
88
116
  end