rubocop 1.67.0 → 1.69.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (178) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +81 -6
  4. data/lib/rubocop/cached_data.rb +12 -4
  5. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  6. data/lib/rubocop/cli/command/version.rb +2 -2
  7. data/lib/rubocop/cop/autocorrect_logic.rb +22 -2
  8. data/lib/rubocop/cop/base.rb +1 -1
  9. data/lib/rubocop/cop/bundler/gem_filename.rb +0 -1
  10. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +0 -1
  11. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  12. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  14. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +1 -2
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +0 -2
  16. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +2 -4
  17. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +1 -1
  18. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +46 -0
  19. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +0 -2
  20. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  21. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -2
  22. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  23. data/lib/rubocop/cop/layout/begin_end_alignment.rb +0 -1
  24. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  25. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  26. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -3
  27. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +3 -4
  28. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +3 -1
  29. data/lib/rubocop/cop/layout/indentation_width.rb +7 -7
  30. data/lib/rubocop/cop/layout/leading_comment_space.rb +44 -1
  31. data/lib/rubocop/cop/layout/line_length.rb +118 -4
  32. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  33. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +1 -1
  34. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -3
  35. data/lib/rubocop/cop/layout/parameter_alignment.rb +3 -4
  36. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -35
  37. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -2
  38. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  39. data/lib/rubocop/cop/layout/space_around_operators.rb +16 -17
  40. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  41. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +6 -0
  42. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  43. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -0
  44. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +0 -1
  45. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +11 -12
  46. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -0
  47. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  48. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  49. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -1
  50. data/lib/rubocop/cop/lint/empty_file.rb +0 -2
  51. data/lib/rubocop/cop/lint/ensure_return.rb +1 -1
  52. data/lib/rubocop/cop/lint/float_comparison.rb +14 -6
  53. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -3
  54. data/lib/rubocop/cop/lint/hash_new_with_keyword_arguments_as_default.rb +55 -0
  55. data/lib/rubocop/cop/lint/interpolation_check.rb +9 -0
  56. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +3 -0
  57. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  58. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -5
  59. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  60. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +2 -2
  61. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +8 -1
  62. data/lib/rubocop/cop/lint/number_conversion.rb +0 -1
  63. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -2
  64. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +106 -0
  65. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -2
  66. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  67. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  68. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +12 -7
  69. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -7
  70. data/lib/rubocop/cop/lint/regexp_as_condition.rb +0 -1
  71. data/lib/rubocop/cop/lint/rescue_type.rb +3 -7
  72. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -0
  73. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +5 -1
  74. data/lib/rubocop/cop/lint/self_assignment.rb +8 -10
  75. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  76. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  77. data/lib/rubocop/cop/lint/unused_method_argument.rb +18 -2
  78. data/lib/rubocop/cop/lint/useless_defined.rb +55 -0
  79. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  80. data/lib/rubocop/cop/lint/useless_setter_call.rb +14 -25
  81. data/lib/rubocop/cop/lint/void.rb +3 -2
  82. data/lib/rubocop/cop/metrics/class_length.rb +7 -7
  83. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
  84. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  85. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -2
  86. data/lib/rubocop/cop/mixin/check_assignment.rb +4 -12
  87. data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
  88. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +49 -0
  89. data/lib/rubocop/cop/mixin/dig_help.rb +27 -0
  90. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  91. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +3 -1
  92. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -9
  93. data/lib/rubocop/cop/mixin/range_help.rb +0 -1
  94. data/lib/rubocop/cop/mixin/target_ruby_version.rb +17 -1
  95. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  96. data/lib/rubocop/cop/naming/constant_name.rb +6 -7
  97. data/lib/rubocop/cop/naming/file_name.rb +0 -2
  98. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +11 -12
  99. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -11
  100. data/lib/rubocop/cop/naming/variable_name.rb +3 -4
  101. data/lib/rubocop/cop/naming/variable_number.rb +2 -3
  102. data/lib/rubocop/cop/offense.rb +2 -3
  103. data/lib/rubocop/cop/style/access_modifier_declarations.rb +53 -24
  104. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  105. data/lib/rubocop/cop/style/array_intersect.rb +5 -4
  106. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  107. data/lib/rubocop/cop/style/block_delimiters.rb +18 -3
  108. data/lib/rubocop/cop/style/case_like_if.rb +8 -11
  109. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  110. data/lib/rubocop/cop/style/commented_keyword.rb +11 -1
  111. data/lib/rubocop/cop/style/conditional_assignment.rb +19 -21
  112. data/lib/rubocop/cop/style/constant_visibility.rb +3 -12
  113. data/lib/rubocop/cop/style/dig_chain.rb +90 -0
  114. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  115. data/lib/rubocop/cop/style/file_null.rb +73 -0
  116. data/lib/rubocop/cop/style/file_touch.rb +75 -0
  117. data/lib/rubocop/cop/style/for.rb +0 -1
  118. data/lib/rubocop/cop/style/global_vars.rb +1 -3
  119. data/lib/rubocop/cop/style/guard_clause.rb +15 -2
  120. data/lib/rubocop/cop/style/hash_conversion.rb +1 -2
  121. data/lib/rubocop/cop/style/if_inside_else.rb +0 -1
  122. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -2
  123. data/lib/rubocop/cop/style/if_with_semicolon.rb +14 -5
  124. data/lib/rubocop/cop/style/inverse_methods.rb +0 -1
  125. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  126. data/lib/rubocop/cop/style/lambda_call.rb +0 -1
  127. data/lib/rubocop/cop/style/map_into_array.rb +6 -1
  128. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  129. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +7 -11
  130. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +33 -3
  131. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  132. data/lib/rubocop/cop/style/multiple_comparison.rb +28 -39
  133. data/lib/rubocop/cop/style/mutable_constant.rb +4 -5
  134. data/lib/rubocop/cop/style/negated_if_else_condition.rb +6 -4
  135. data/lib/rubocop/cop/style/nested_ternary_operator.rb +5 -4
  136. data/lib/rubocop/cop/style/not.rb +1 -1
  137. data/lib/rubocop/cop/style/one_line_conditional.rb +25 -4
  138. data/lib/rubocop/cop/style/operator_method_call.rb +5 -6
  139. data/lib/rubocop/cop/style/or_assignment.rb +3 -6
  140. data/lib/rubocop/cop/style/parallel_assignment.rb +8 -13
  141. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  142. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -1
  143. data/lib/rubocop/cop/style/redundant_condition.rb +36 -21
  144. data/lib/rubocop/cop/style/redundant_line_continuation.rb +21 -2
  145. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -11
  146. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -0
  147. data/lib/rubocop/cop/style/redundant_return.rb +2 -2
  148. data/lib/rubocop/cop/style/redundant_self.rb +7 -14
  149. data/lib/rubocop/cop/style/redundant_self_assignment.rb +7 -5
  150. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +4 -4
  151. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  152. data/lib/rubocop/cop/style/rescue_modifier.rb +2 -3
  153. data/lib/rubocop/cop/style/safe_navigation.rb +13 -1
  154. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  155. data/lib/rubocop/cop/style/select_by_regexp.rb +1 -1
  156. data/lib/rubocop/cop/style/self_assignment.rb +11 -17
  157. data/lib/rubocop/cop/style/signal_exception.rb +2 -3
  158. data/lib/rubocop/cop/style/single_argument_dig.rb +9 -5
  159. data/lib/rubocop/cop/style/single_line_do_end_block.rb +13 -3
  160. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -3
  161. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  162. data/lib/rubocop/cop/style/string_concatenation.rb +0 -1
  163. data/lib/rubocop/cop/style/swap_values.rb +4 -15
  164. data/lib/rubocop/cop/style/ternary_parentheses.rb +25 -4
  165. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +4 -4
  166. data/lib/rubocop/cop/style/variable_interpolation.rb +1 -2
  167. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  168. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  169. data/lib/rubocop/cop/variable_force/variable.rb +5 -1
  170. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  171. data/lib/rubocop/cop/variable_force.rb +4 -10
  172. data/lib/rubocop/cops_documentation_generator.rb +20 -10
  173. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
  174. data/lib/rubocop/runner.rb +16 -8
  175. data/lib/rubocop/target_ruby.rb +1 -1
  176. data/lib/rubocop/version.rb +27 -8
  177. data/lib/rubocop.rb +16 -0
  178. metadata +28 -12
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Help methods for working with `Enumerable#dig` in cops.
6
+ # Used by `Style::DigChain` and `Style::SingleArgumentDig`
7
+ module DigHelp
8
+ extend NodePattern::Macros
9
+
10
+ # @!method dig?(node)
11
+ def_node_matcher :dig?, <<~PATTERN
12
+ (call _ :dig !{hash block_pass}+)
13
+ PATTERN
14
+
15
+ # @!method single_argument_dig?(node)
16
+ def_node_matcher :single_argument_dig?, <<~PATTERN
17
+ (send _ :dig $!splat)
18
+ PATTERN
19
+
20
+ private
21
+
22
+ def dig_chain_enabled?
23
+ @config.for_cop('Style/DigChain')['Enabled']
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for rewriting endless methods to normal method definitions
6
+ module EndlessMethodRewriter
7
+ def correct_to_multiline(corrector, node)
8
+ replacement = <<~RUBY.strip
9
+ def #{node.method_name}#{arguments(node)}
10
+ #{node.body.source}
11
+ end
12
+ RUBY
13
+
14
+ corrector.replace(node, replacement)
15
+ end
16
+
17
+ private
18
+
19
+ def arguments(node, missing = '')
20
+ node.arguments.any? ? node.arguments.source : missing
21
+ end
22
+ end
23
+ end
24
+ end
@@ -29,7 +29,9 @@ module RuboCop
29
29
  end
30
30
 
31
31
  def uninterpolated_string?(node)
32
- node.str_type? || (node.dstr_type? && node.each_descendant(:begin).none?)
32
+ node.str_type? || (
33
+ node.dstr_type? && node.each_descendant(:begin, :ivar, :cvar, :gvar).none?
34
+ )
33
35
  end
34
36
 
35
37
  def uninterpolated_heredoc?(node)
@@ -121,12 +121,10 @@ module RuboCop
121
121
 
122
122
  def indented_keyword_expression(node)
123
123
  if node.for_type?
124
- expression = node.collection
124
+ node.collection
125
125
  else
126
- expression, = *node
126
+ node.children.first
127
127
  end
128
-
129
- expression
130
128
  end
131
129
 
132
130
  def argument_in_method_call(node, kind) # rubocop:todo Metrics/CyclomaticComplexity
@@ -187,12 +185,10 @@ module RuboCop
187
185
 
188
186
  def assignment_rhs(node)
189
187
  case node.type
190
- when :casgn then _scope, _lhs, rhs = *node
191
- when :op_asgn then _lhs, _op, rhs = *node
192
- when :send, :csend then rhs = node.last_argument
193
- else _lhs, rhs = *node
188
+ when :casgn, :op_asgn then node.rhs
189
+ when :send, :csend then node.last_argument
190
+ else node.children.last
194
191
  end
195
- rhs
196
192
  end
197
193
 
198
194
  def not_for_this_cop?(node)
@@ -56,7 +56,6 @@ module RuboCop
56
56
  range: NOT_GIVEN, side: :both, newlines: true,
57
57
  whitespace: false, continuations: false,
58
58
  buffer: @processed_source.buffer)
59
-
60
59
  range = range_positional unless range_positional == NOT_GIVEN
61
60
 
62
61
  src = buffer.source
@@ -8,12 +8,28 @@ module RuboCop
8
8
  @minimum_target_ruby_version
9
9
  end
10
10
 
11
+ def required_maximum_ruby_version
12
+ @maximum_target_ruby_version
13
+ end
14
+
11
15
  def minimum_target_ruby_version(version)
12
16
  @minimum_target_ruby_version = version
13
17
  end
14
18
 
19
+ def maximum_target_ruby_version(version)
20
+ @maximum_target_ruby_version = version
21
+ end
22
+
15
23
  def support_target_ruby_version?(version)
16
- required_minimum_ruby_version <= version
24
+ # By default, no minimum or maximum versions of ruby are required
25
+ # to run any cop. In order to do a simple numerical comparison of
26
+ # the requested version against any requirements, we use 0 and
27
+ # Infinity as the default values to indicate no minimum (0) and no
28
+ # maximum (Infinity).
29
+ min = required_minimum_ruby_version || 0
30
+ max = required_maximum_ruby_version || Float::INFINITY
31
+
32
+ min <= version && max >= version
17
33
  end
18
34
  end
19
35
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  MSG = 'Use %<style>s block forwarding.'
49
49
 
50
50
  def self.autocorrect_incompatible_with
51
- [Lint::AmbiguousOperator, Style::ArgumentsForwarding]
51
+ [Lint::AmbiguousOperator, Style::ArgumentsForwarding, Style::ExplicitBlockArgument]
52
52
  end
53
53
 
54
54
  def on_def(node)
@@ -31,12 +31,11 @@ module RuboCop
31
31
  PATTERN
32
32
 
33
33
  def on_casgn(node)
34
- if node.parent&.or_asgn_type?
35
- lhs, value = *node.parent
36
- _scope, const_name = *lhs
37
- else
38
- _scope, const_name, value = *node
39
- end
34
+ value = if node.parent&.or_asgn_type?
35
+ node.parent.expression
36
+ else
37
+ node.expression
38
+ end
40
39
 
41
40
  # We cannot know the result of method calls like
42
41
  # NewClass = something_that_returns_a_class
@@ -46,7 +45,7 @@ module RuboCop
46
45
  # SomeClass = Class.new(...)
47
46
  # SomeClass = Struct.new(...)
48
47
  return if allowed_assignment?(value)
49
- return if SNAKE_CASE.match?(const_name)
48
+ return if SNAKE_CASE.match?(node.name)
50
49
 
51
50
  add_offense(node.loc.name)
52
51
  end
@@ -37,8 +37,6 @@ module RuboCop
37
37
  #
38
38
  # anything/using_snake_case.rake
39
39
  class FileName < Base
40
- include RangeHelp
41
-
42
40
  MSG_SNAKE_CASE = 'The name of this source file (`%<basename>s`) should use snake_case.'
43
41
  MSG_NO_DEFINITION = '`%<basename>s` should define a class or module called `%<namespace>s`.'
44
42
  MSG_REGEX = '`%<basename>s` should match `%<regex>s`.'
@@ -155,6 +155,7 @@ module RuboCop
155
155
  UNDERSCORE_REQUIRED = 'Memoized variable `%<var>s` does not start ' \
156
156
  'with `_`. Use `@%<suggested_var>s` instead.'
157
157
  DYNAMIC_DEFINE_METHODS = %i[define_method define_singleton_method].to_set.freeze
158
+ INITIALIZE_METHODS = %i[initialize initialize_clone initialize_copy initialize_dup].freeze
158
159
 
159
160
  # @!method method_definition?(node)
160
161
  def_node_matcher :method_definition?, <<~PATTERN
@@ -168,7 +169,7 @@ module RuboCop
168
169
  # rubocop:disable Metrics/AbcSize
169
170
  # rubocop:disable Metrics/MethodLength
170
171
  def on_or_asgn(node)
171
- lhs, _value = *node
172
+ lhs = node.lhs
172
173
  return unless lhs.ivasgn_type?
173
174
 
174
175
  method_node, method_name = find_definition(node)
@@ -181,8 +182,8 @@ module RuboCop
181
182
 
182
183
  suggested_var = suggested_var(method_name)
183
184
  msg = format(
184
- message(lhs.children.first.to_s),
185
- var: lhs.children.first.to_s,
185
+ message(lhs.name),
186
+ var: lhs.name,
186
187
  suggested_var: suggested_var,
187
188
  method: method_name
188
189
  )
@@ -209,14 +210,13 @@ module RuboCop
209
210
  method_node, method_name = find_definition(node)
210
211
  return false unless method_node
211
212
 
212
- var_name = arg.children.first
213
- defined_memoized?(method_node.body, var_name) do |defined_ivar, return_ivar, ivar_assign|
213
+ defined_memoized?(method_node.body, arg.name) do |defined_ivar, return_ivar, ivar_assign|
214
214
  return false if matches?(method_name, ivar_assign)
215
215
 
216
216
  suggested_var = suggested_var(method_name)
217
217
  msg = format(
218
- message(var_name.to_s),
219
- var: var_name.to_s,
218
+ message(arg.name),
219
+ var: arg.name,
220
220
  suggested_var: suggested_var,
221
221
  method: method_name
222
222
  )
@@ -251,11 +251,10 @@ module RuboCop
251
251
  end
252
252
 
253
253
  def matches?(method_name, ivar_assign)
254
- return true if ivar_assign.nil? || method_name == :initialize
254
+ return true if ivar_assign.nil? || INITIALIZE_METHODS.include?(method_name)
255
255
 
256
- method_name = method_name.to_s.delete('!?')
257
- variable = ivar_assign.children.first
258
- variable_name = variable.to_s.sub('@', '')
256
+ method_name = method_name.to_s.delete('!?=')
257
+ variable_name = ivar_assign.name.to_s.sub('@', '')
259
258
 
260
259
  variable_name_candidates(method_name).include?(variable_name)
261
260
  end
@@ -269,7 +268,7 @@ module RuboCop
269
268
  end
270
269
 
271
270
  def suggested_var(method_name)
272
- suggestion = method_name.to_s.delete('!?')
271
+ suggestion = method_name.to_s.delete('!?=')
273
272
 
274
273
  style == :required ? "_#{suggestion}" : suggestion
275
274
  end
@@ -109,7 +109,7 @@ module RuboCop
109
109
  variable_name_matches?(lvasgn_node, name)
110
110
  end
111
111
  else
112
- node.children.first == name
112
+ node.name == name
113
113
  end
114
114
  end
115
115
 
@@ -141,12 +141,7 @@ module RuboCop
141
141
  # Further `lvar` nodes will not be corrected though since they now refer to a
142
142
  # different variable.
143
143
  def correct_reassignment(corrector, node, offending_name, preferred_name)
144
- if node.lvasgn_type?
145
- correct_node(corrector, node.child_nodes.first, offending_name, preferred_name)
146
- elsif node.masgn_type?
147
- # With multiple assign, the assignments are in an array as the last child
148
- correct_node(corrector, node.children.last, offending_name, preferred_name)
149
- end
144
+ correct_node(corrector, node.rhs, offending_name, preferred_name)
150
145
  end
151
146
 
152
147
  def preferred_name(variable_name)
@@ -159,10 +154,7 @@ module RuboCop
159
154
  end
160
155
 
161
156
  def variable_name(node)
162
- asgn_node = node.exception_variable
163
- return unless asgn_node
164
-
165
- asgn_node.children.last
157
+ node.exception_variable&.name
166
158
  end
167
159
 
168
160
  def message(node)
@@ -40,11 +40,10 @@ module RuboCop
40
40
  end
41
41
 
42
42
  def on_lvasgn(node)
43
- name, = *node
44
- return unless name
45
- return if allowed_identifier?(name)
43
+ return unless node.name
44
+ return if allowed_identifier?(node.name)
46
45
 
47
- check_name(node, name, node.loc.name)
46
+ check_name(node, node.name, node.loc.name)
48
47
  end
49
48
  alias on_ivasgn on_lvasgn
50
49
  alias on_cvasgn on_lvasgn
@@ -113,10 +113,9 @@ module RuboCop
113
113
 
114
114
  def on_arg(node)
115
115
  @node = node
116
- name, = *node
117
- return if allowed_identifier?(name)
116
+ return if allowed_identifier?(node.name)
118
117
 
119
- check_name(node, name, node.loc.name)
118
+ check_name(node, node.name, node.loc.name)
120
119
  end
121
120
  alias on_lvasgn on_arg
122
121
  alias on_ivasgn on_arg
@@ -43,11 +43,10 @@ module RuboCop
43
43
  # @!attribute [r] cop_name
44
44
  #
45
45
  # @return [String]
46
- # a cop class name without department.
47
- # i.e. type of the violation.
46
+ # the cop name as a String for which this offense is for.
48
47
  #
49
48
  # @example
50
- # 'LineLength'
49
+ # 'Layout/LineLength'
51
50
  attr_reader :cop_name
52
51
 
53
52
  # @api private
@@ -70,6 +70,7 @@ module RuboCop
70
70
  # private :bar, :baz
71
71
  # private *%i[qux quux]
72
72
  # private *METHOD_NAMES
73
+ # private *private_methods
73
74
  #
74
75
  # end
75
76
  #
@@ -80,6 +81,7 @@ module RuboCop
80
81
  # private :bar, :baz
81
82
  # private *%i[qux quux]
82
83
  # private *METHOD_NAMES
84
+ # private *private_methods
83
85
  #
84
86
  # end
85
87
  #
@@ -133,21 +135,18 @@ module RuboCop
133
135
  # @!method access_modifier_with_symbol?(node)
134
136
  def_node_matcher :access_modifier_with_symbol?, <<~PATTERN
135
137
  (send nil? {:private :protected :public :module_function}
136
- {(sym _) (splat {#percent_symbol_array? const})}
138
+ {(sym _)+ (splat {#percent_symbol_array? const send})}
137
139
  )
138
140
  PATTERN
139
141
 
140
142
  # @!method access_modifier_with_attr?(node)
141
143
  def_node_matcher :access_modifier_with_attr?, <<~PATTERN
142
144
  (send nil? {:private :protected :public :module_function}
143
- (send nil? {:attr :attr_reader :attr_writer :attr_accessor} _))
145
+ (send nil? {:attr :attr_reader :attr_writer :attr_accessor} _+))
144
146
  PATTERN
145
147
 
146
148
  def on_send(node)
147
- return unless node.access_modifier?
148
- return if ALLOWED_NODE_TYPES.include?(node.parent&.type)
149
- return if allow_modifiers_on_symbols?(node)
150
- return if allow_modifiers_on_attrs?(node)
149
+ return if allowed?(node)
151
150
 
152
151
  if offense?(node)
153
152
  add_offense(node.loc.selector) do |corrector|
@@ -161,15 +160,22 @@ module RuboCop
161
160
 
162
161
  private
163
162
 
163
+ def allowed?(node)
164
+ !node.access_modifier? ||
165
+ ALLOWED_NODE_TYPES.include?(node.parent&.type) ||
166
+ allow_modifiers_on_symbols?(node) ||
167
+ allow_modifiers_on_attrs?(node)
168
+ end
169
+
164
170
  def autocorrect(corrector, node)
165
171
  case style
166
172
  when :group
167
- def_node = find_corresponding_def_node(node)
168
- return unless def_node
173
+ def_nodes = find_corresponding_def_nodes(node)
174
+ return unless def_nodes.any?
169
175
 
170
- replace_def(corrector, node, def_node)
176
+ replace_defs(corrector, node, def_nodes)
171
177
  when :inline
172
- remove_node(corrector, node)
178
+ remove_nodes(corrector, node)
173
179
  select_grouped_def_nodes(node).each do |grouped_def_node|
174
180
  insert_inline_modifier(corrector, grouped_def_node, node.method_name)
175
181
  end
@@ -194,6 +200,13 @@ module RuboCop
194
200
  (inline_style? && access_modifier_is_not_inlined?(node))
195
201
  end
196
202
 
203
+ def correctable_group_offense?(node)
204
+ return false unless group_style?
205
+ return false if allowed?(node)
206
+
207
+ access_modifier_is_inlined?(node) && find_corresponding_def_nodes(node).any?
208
+ end
209
+
197
210
  def group_style?
198
211
  style == :group
199
212
  end
@@ -212,7 +225,11 @@ module RuboCop
212
225
 
213
226
  def right_siblings_same_inline_method?(node)
214
227
  node.right_siblings.any? do |sibling|
215
- sibling.send_type? && sibling.method?(node.method_name) && !sibling.arguments.empty?
228
+ sibling.send_type? &&
229
+ correctable_group_offense?(sibling) &&
230
+ sibling.method?(node.method_name) &&
231
+ !sibling.arguments.empty? &&
232
+ find_corresponding_def_nodes(sibling).any?
216
233
  end
217
234
  end
218
235
 
@@ -226,14 +243,22 @@ module RuboCop
226
243
  end
227
244
  end
228
245
 
229
- def find_corresponding_def_node(node)
246
+ def find_corresponding_def_nodes(node)
230
247
  if access_modifier_with_symbol?(node)
231
- method_name = node.first_argument.respond_to?(:value) && node.first_argument.value
232
- node.parent.each_child_node(:def).find do |child|
233
- child.method?(method_name)
248
+ method_names = node.arguments.filter_map do |argument|
249
+ next unless argument.sym_type?
250
+
251
+ argument.respond_to?(:value) && argument.value
252
+ end
253
+
254
+ def_nodes = node.parent.each_child_node(:def).select do |child|
255
+ method_names.include?(child.method_name)
234
256
  end
257
+
258
+ # If there isn't a `def` node for each symbol, we will skip autocorrection.
259
+ def_nodes.size == method_names.size ? def_nodes : []
235
260
  else
236
- node.first_argument
261
+ [node.first_argument]
237
262
  end
238
263
  end
239
264
 
@@ -251,8 +276,8 @@ module RuboCop
251
276
  end.select(&:def_type?)
252
277
  end
253
278
 
254
- def replace_def(corrector, node, def_node)
255
- source = def_source(node, def_node)
279
+ def replace_defs(corrector, node, def_nodes)
280
+ source = def_source(node, def_nodes)
256
281
  argument_less_modifier_node = find_argument_less_modifier_node(node)
257
282
  if argument_less_modifier_node
258
283
  corrector.insert_after(argument_less_modifier_node, "\n\n#{source}")
@@ -264,20 +289,24 @@ module RuboCop
264
289
  return
265
290
  end
266
291
 
267
- remove_node(corrector, def_node)
268
- remove_node(corrector, node)
292
+ remove_nodes(corrector, *def_nodes, node)
269
293
  end
270
294
 
271
295
  def insert_inline_modifier(corrector, node, modifier_name)
272
296
  corrector.insert_before(node, "#{modifier_name} ")
273
297
  end
274
298
 
275
- def remove_node(corrector, node)
276
- corrector.remove(range_with_comments_and_lines(node))
299
+ def remove_nodes(corrector, *nodes)
300
+ nodes.each do |node|
301
+ corrector.remove(range_with_comments_and_lines(node))
302
+ end
277
303
  end
278
304
 
279
- def def_source(node, def_node)
280
- [*processed_source.ast_with_comments[node].map(&:text), def_node.source].join("\n")
305
+ def def_source(node, def_nodes)
306
+ [
307
+ *processed_source.ast_with_comments[node].map(&:text),
308
+ *def_nodes.map(&:source)
309
+ ].join("\n")
281
310
  end
282
311
  end
283
312
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Looks for endless methods inside operations of lower precedence (`and`, `or`, and
7
+ # modifier forms of `if`, `unless`, `while`, `until`) that are ambiguous due to
8
+ # lack of parentheses. This may lead to unexpected behavior as the code may appear
9
+ # to use these keywords as part of the method but in fact they modify
10
+ # the method definition itself.
11
+ #
12
+ # In these cases, using a normal method definition is more clear.
13
+ #
14
+ # @example
15
+ #
16
+ # # bad
17
+ # def foo = true if bar
18
+ #
19
+ # # good - using a non-endless method is more explicit
20
+ # def foo
21
+ # true
22
+ # end if bar
23
+ #
24
+ # # ok - method body is explicit
25
+ # def foo = (true if bar)
26
+ #
27
+ # # ok - method definition is explicit
28
+ # (def foo = true) if bar
29
+ class AmbiguousEndlessMethodDefinition < Base
30
+ extend TargetRubyVersion
31
+ extend AutoCorrector
32
+ include EndlessMethodRewriter
33
+ include RangeHelp
34
+
35
+ minimum_target_ruby_version 3.0
36
+
37
+ MSG = 'Avoid using `%<keyword>s` statements with endless methods.'
38
+
39
+ # @!method ambiguous_endless_method_body(node)
40
+ def_node_matcher :ambiguous_endless_method_body, <<~PATTERN
41
+ ^${
42
+ (if _ <def _>)
43
+ ({and or} def _)
44
+ ({while until} _ def)
45
+ }
46
+ PATTERN
47
+
48
+ def on_def(node)
49
+ return unless node.endless?
50
+
51
+ operation = ambiguous_endless_method_body(node)
52
+ return unless operation
53
+
54
+ return unless modifier_form?(operation)
55
+
56
+ add_offense(operation, message: format(MSG, keyword: keyword(operation))) do |corrector|
57
+ correct_to_multiline(corrector, node)
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ def modifier_form?(operation)
64
+ return true if operation.operator_keyword?
65
+
66
+ operation.modifier_form?
67
+ end
68
+
69
+ def keyword(operation)
70
+ if operation.respond_to?(:keyword)
71
+ operation.keyword
72
+ else
73
+ operation.operator
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -28,6 +28,7 @@ module RuboCop
28
28
  # # bad
29
29
  # (array1 & array2).any?
30
30
  # (array1 & array2).empty?
31
+ # (array1 & array2).none?
31
32
  #
32
33
  # # good
33
34
  # array1.intersect?(array2)
@@ -57,7 +58,7 @@ module RuboCop
57
58
  (send
58
59
  (begin
59
60
  (send $(...) :& $(...))
60
- ) ${:any? :empty?}
61
+ ) ${:any? :empty? :none?}
61
62
  )
62
63
  PATTERN
63
64
 
@@ -66,18 +67,18 @@ module RuboCop
66
67
  (send
67
68
  (begin
68
69
  (send $(...) :& $(...))
69
- ) ${:present? :any? :blank? :empty?}
70
+ ) ${:present? :any? :blank? :empty? :none?}
70
71
  )
71
72
  PATTERN
72
73
 
73
74
  MSG = 'Use `%<negated>s%<receiver>s.intersect?(%<argument>s)` ' \
74
75
  'instead of `(%<receiver>s & %<argument>s).%<method_name>s`.'
75
76
  STRAIGHT_METHODS = %i[present? any?].freeze
76
- NEGATED_METHODS = %i[blank? empty?].freeze
77
+ NEGATED_METHODS = %i[blank? empty? none?].freeze
77
78
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
78
79
 
79
80
  def on_send(node)
80
- return if (parent = node.parent) && (parent.block_type? || parent.numblock_type?)
81
+ return if node.block_literal?
81
82
  return unless (receiver, argument, method_name = bad_intersection_check?(node))
82
83
 
83
84
  message = message(receiver.source, argument.source, method_name)