rubocop 0.91.1 → 1.1.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 (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