rubocop 1.53.0 → 1.60.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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +5 -3
  4. data/config/default.yml +80 -11
  5. data/config/obsoletion.yml +5 -0
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  7. data/lib/rubocop/cli.rb +1 -1
  8. data/lib/rubocop/config.rb +0 -2
  9. data/lib/rubocop/config_finder.rb +2 -2
  10. data/lib/rubocop/config_loader.rb +0 -1
  11. data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
  12. data/lib/rubocop/config_obsoletion.rb +11 -8
  13. data/lib/rubocop/config_validator.rb +0 -2
  14. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  15. data/lib/rubocop/cop/base.rb +7 -1
  16. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  17. data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
  18. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  19. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  20. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
  21. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  22. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  23. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  24. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -21
  26. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  27. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  28. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  29. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  30. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  31. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  32. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  33. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  34. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  35. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
  36. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +26 -3
  37. data/lib/rubocop/cop/layout/end_alignment.rb +12 -2
  38. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  39. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  40. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  41. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  42. data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -1
  43. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  44. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  45. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
  46. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  47. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  48. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  49. data/lib/rubocop/cop/layout/redundant_line_break.rb +15 -4
  50. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  51. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  52. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  53. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  54. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  55. data/lib/rubocop/cop/layout/space_around_operators.rb +53 -21
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  57. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
  58. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  59. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  60. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  61. data/lib/rubocop/cop/lint/debugger.rb +18 -4
  62. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  63. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  64. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  65. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  66. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  67. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  68. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  69. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  70. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -1
  71. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  72. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
  73. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  74. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  75. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +10 -0
  76. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  77. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +63 -4
  78. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  79. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  80. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +14 -8
  81. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  82. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  83. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
  84. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  85. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  86. data/lib/rubocop/cop/lint/symbol_conversion.rb +8 -3
  87. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  88. data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
  89. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  92. data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
  93. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  94. data/lib/rubocop/cop/lint/void.rb +46 -21
  95. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  96. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  97. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  98. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  99. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
  100. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  101. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  102. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  103. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  104. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  105. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  106. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  107. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +6 -8
  108. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  109. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  110. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  111. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  112. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  113. data/lib/rubocop/cop/naming/file_name.rb +1 -1
  114. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  116. data/lib/rubocop/cop/registry.rb +4 -4
  117. data/lib/rubocop/cop/security/open.rb +2 -2
  118. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  119. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  120. data/lib/rubocop/cop/style/alias.rb +9 -8
  121. data/lib/rubocop/cop/style/arguments_forwarding.rb +355 -63
  122. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  123. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  124. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  125. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  126. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  127. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  128. data/lib/rubocop/cop/style/class_check.rb +1 -0
  129. data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -0
  130. data/lib/rubocop/cop/style/collection_compact.rb +7 -6
  131. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  132. data/lib/rubocop/cop/style/combinable_loops.rb +17 -9
  133. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -1
  134. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  135. data/lib/rubocop/cop/style/date_time.rb +5 -4
  136. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  137. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  138. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  139. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  140. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  141. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  142. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  143. data/lib/rubocop/cop/style/for.rb +1 -1
  144. data/lib/rubocop/cop/style/format_string.rb +24 -3
  145. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
  146. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  147. data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
  148. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  149. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  150. data/lib/rubocop/cop/style/identical_conditional_branches.rb +28 -3
  151. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  152. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  153. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  154. data/lib/rubocop/cop/style/lambda.rb +3 -3
  155. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  156. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  157. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  158. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +22 -6
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  160. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  161. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  162. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  163. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  164. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  165. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +2 -4
  166. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  167. data/lib/rubocop/cop/style/next.rb +1 -1
  168. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  169. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  170. data/lib/rubocop/cop/style/operator_method_call.rb +8 -2
  171. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  172. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  173. data/lib/rubocop/cop/style/redundant_argument.rb +9 -3
  174. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  175. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  176. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  177. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  178. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  179. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  180. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  181. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  182. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  183. data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -21
  184. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +18 -4
  185. data/lib/rubocop/cop/style/redundant_return.rb +8 -3
  186. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  187. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
  188. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  189. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  190. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  191. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  192. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +23 -9
  193. data/lib/rubocop/cop/style/sample.rb +2 -1
  194. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  195. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  196. data/lib/rubocop/cop/style/semicolon.rb +8 -3
  197. data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
  198. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  199. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  200. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  201. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  202. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  203. data/lib/rubocop/cop/style/strip.rb +7 -4
  204. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  205. data/lib/rubocop/cop/style/symbol_array.rb +35 -15
  206. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  207. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  208. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  209. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  210. data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
  211. data/lib/rubocop/cop/variable_force/assignment.rb +14 -5
  212. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  213. data/lib/rubocop/ext/regexp_node.rb +9 -4
  214. data/lib/rubocop/file_finder.rb +4 -7
  215. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  216. data/lib/rubocop/formatter/html_formatter.rb +5 -4
  217. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  218. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  219. data/lib/rubocop/formatter.rb +1 -1
  220. data/lib/rubocop/lsp/routes.rb +42 -19
  221. data/lib/rubocop/lsp/runtime.rb +22 -2
  222. data/lib/rubocop/lsp/server.rb +10 -4
  223. data/lib/rubocop/magic_comment.rb +12 -10
  224. data/lib/rubocop/options.rb +0 -8
  225. data/lib/rubocop/result_cache.rb +4 -1
  226. data/lib/rubocop/rspec/shared_contexts.rb +8 -3
  227. data/lib/rubocop/rspec/support.rb +1 -0
  228. data/lib/rubocop/runner.rb +6 -4
  229. data/lib/rubocop/server/client_command/exec.rb +1 -1
  230. data/lib/rubocop/string_interpreter.rb +3 -3
  231. data/lib/rubocop/target_finder.rb +7 -3
  232. data/lib/rubocop/target_ruby.rb +9 -5
  233. data/lib/rubocop/version.rb +1 -1
  234. data/lib/rubocop.rb +6 -0
  235. metadata +20 -13
  236. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -17,10 +17,16 @@ module RuboCop
17
17
  # @example
18
18
  # # bad
19
19
  # hash.keys.each { |k| p k }
20
- # hash.values.each { |v| p v }
20
+ # hash.each { |k, unused_value| p k }
21
21
  #
22
22
  # # good
23
23
  # hash.each_key { |k| p k }
24
+ #
25
+ # # bad
26
+ # hash.values.each { |v| p v }
27
+ # hash.each { |unused_key, v| p v }
28
+ #
29
+ # # good
24
30
  # hash.each_value { |v| p v }
25
31
  #
26
32
  # @example AllowedReceivers: ['execute']
@@ -33,22 +39,46 @@ module RuboCop
33
39
  extend AutoCorrector
34
40
 
35
41
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
42
+ UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
36
43
 
37
44
  # @!method kv_each(node)
38
45
  def_node_matcher :kv_each, <<~PATTERN
39
- ({block numblock} $(send (send _ ${:keys :values}) :each) ...)
46
+ ({block numblock} $(call (call _ ${:keys :values}) :each) ...)
47
+ PATTERN
48
+
49
+ # @!method each_arguments(node)
50
+ def_node_matcher :each_arguments, <<~PATTERN
51
+ (block (call _ :each)(args $_key $_value) ...)
40
52
  PATTERN
41
53
 
42
54
  # @!method kv_each_with_block_pass(node)
43
55
  def_node_matcher :kv_each_with_block_pass, <<~PATTERN
44
- (send $(send _ ${:keys :values}) :each (block_pass (sym _)))
56
+ (call $(call _ ${:keys :values}) :each (block_pass (sym _)))
45
57
  PATTERN
46
58
 
59
+ # rubocop:disable Metrics/AbcSize
47
60
  def on_block(node)
61
+ return unless handleable?(node)
62
+
48
63
  kv_each(node) do |target, method|
49
- register_kv_offense(target, method)
64
+ register_kv_offense(target, method) and return
65
+ end
66
+
67
+ return unless (key, value = each_arguments(node))
68
+
69
+ if unused_block_arg_exist?(node, value)
70
+ message = message('each_key', node.method_name, value.source)
71
+ unused_range = key.source_range.end.join(value.source_range.end)
72
+
73
+ register_each_args_offense(node, message, 'each_key', unused_range)
74
+ elsif unused_block_arg_exist?(node, key)
75
+ message = message('each_value', node.method_name, key.source)
76
+ unused_range = key.source_range.begin.join(value.source_range.begin)
77
+
78
+ register_each_args_offense(node, message, 'each_value', unused_range)
50
79
  end
51
80
  end
81
+ # rubocop:enable Metrics/AbcSize
52
82
 
53
83
  alias on_numblock on_block
54
84
 
@@ -60,27 +90,70 @@ module RuboCop
60
90
 
61
91
  private
62
92
 
93
+ def handleable?(node)
94
+ return false unless (root_receiver = root_receiver(node))
95
+
96
+ !root_receiver.literal? || root_receiver.hash_type?
97
+ end
98
+
63
99
  def register_kv_offense(target, method)
64
100
  return unless (parent_receiver = target.receiver.receiver)
65
101
  return if allowed_receiver?(parent_receiver)
66
102
 
67
- add_offense(kv_range(target), message: format_message(method)) do |corrector|
103
+ current = target.receiver.loc.selector.join(target.source_range.end).source
104
+
105
+ add_offense(kv_range(target), message: format_message(method, current)) do |corrector|
68
106
  correct_key_value_each(target, corrector)
69
107
  end
70
108
  end
71
109
 
110
+ def unused_block_arg_exist?(node, block_arg)
111
+ lvar_sources = node.body.each_descendant(:lvar).map(&:source)
112
+
113
+ if block_arg.mlhs_type?
114
+ block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
115
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
116
+ end
117
+ else
118
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
119
+ end
120
+ end
121
+
122
+ def message(prefer, method_name, unused_code)
123
+ format(
124
+ UNUSED_BLOCK_ARG_MSG, prefer: prefer, current: method_name, unused_code: unused_code
125
+ )
126
+ end
127
+
128
+ def register_each_args_offense(node, message, prefer, unused_range)
129
+ add_offense(node, message: message) do |corrector|
130
+ corrector.replace(node.send_node.loc.selector, prefer)
131
+ corrector.remove(unused_range)
132
+ end
133
+ end
134
+
72
135
  def register_kv_with_block_pass_offense(node, target, method)
73
136
  return unless (parent_receiver = node.parent.receiver.receiver)
74
137
  return if allowed_receiver?(parent_receiver)
75
138
 
76
- range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
77
- add_offense(range, message: format_message(method)) do |corrector|
139
+ range = target.loc.selector.join(node.parent.loc.selector.end)
140
+
141
+ add_offense(range, message: format_message(method, range.source)) do |corrector|
78
142
  corrector.replace(range, "each_#{method[0..-2]}")
79
143
  end
80
144
  end
81
145
 
82
- def format_message(method_name)
83
- format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
146
+ def root_receiver(node)
147
+ receiver = node.receiver
148
+ if receiver&.receiver
149
+ root_receiver(receiver)
150
+ else
151
+ receiver
152
+ end
153
+ end
154
+
155
+ def format_message(method_name, current)
156
+ format(MSG, prefer: "each_#{method_name[0..-2]}", current: current)
84
157
  end
85
158
 
86
159
  def check_argument(variable)
@@ -103,7 +176,7 @@ module RuboCop
103
176
  name = "each_#{node.receiver.method_name.to_s.chop}"
104
177
  return correct_implicit(node, corrector, name) unless receiver
105
178
 
106
- new_source = receiver.source + ".#{name}"
179
+ new_source = receiver.source + "#{node.loc.dot.source}#{name}"
107
180
  corrector.replace(node, new_source)
108
181
  end
109
182
 
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # @!method bad_method_with_poro?(node)
44
44
  def_node_matcher :bad_method_with_poro?, <<~PATTERN
45
45
  (block
46
- (send _ _)
46
+ (call _ _)
47
47
  (args
48
48
  $(arg _)
49
49
  (arg _))
@@ -86,6 +86,7 @@ module RuboCop
86
86
  corrector.replace(range, preferred_method)
87
87
  end
88
88
  end
89
+ alias on_csend on_send
89
90
 
90
91
  private
91
92
 
@@ -136,7 +136,7 @@ module RuboCop
136
136
 
137
137
  private
138
138
 
139
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
139
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
140
140
  def check_branches(node, branches)
141
141
  # return if any branch is empty. An empty branch can be an `if`
142
142
  # without an `else` or a branch that contains only comments.
@@ -149,9 +149,26 @@ module RuboCop
149
149
  branches.any? { |branch| single_child_branch?(branch) }
150
150
 
151
151
  heads = branches.map { |branch| head(branch) }
152
- check_expressions(node, heads, :before_condition) if duplicated_expressions?(node, heads)
152
+
153
+ return unless duplicated_expressions?(node, heads)
154
+
155
+ condition_variable = assignable_condition_value(node)
156
+
157
+ head = heads.first
158
+ if head.assignment?
159
+ # The `send` node is used instead of the `indexasgn` node, so `name` cannot be used.
160
+ # https://github.com/rubocop/rubocop-ast/blob/v1.29.0/lib/rubocop/ast/node/indexasgn_node.rb
161
+ #
162
+ # FIXME: It would be better to update `RuboCop::AST::OpAsgnNode` or its subclasses to
163
+ # handle `self.foo ||= value` as a solution, instead of using `head.node_parts[0].to_s`.
164
+ assigned_value = head.send_type? ? head.receiver.source : head.node_parts[0].to_s
165
+
166
+ return if condition_variable == assigned_value
167
+ end
168
+
169
+ check_expressions(node, heads, :before_condition)
153
170
  end
154
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
171
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
155
172
 
156
173
  def duplicated_expressions?(node, expressions)
157
174
  unique_expressions = expressions.uniq
@@ -164,6 +181,14 @@ module RuboCop
164
181
  node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
165
182
  end
166
183
 
184
+ def assignable_condition_value(node)
185
+ if node.condition.call_type?
186
+ (receiver = node.condition.receiver) ? receiver.source : node.condition.source
187
+ elsif node.condition.variable?
188
+ node.condition.source
189
+ end
190
+ end
191
+
167
192
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
168
193
  def check_expressions(node, expressions, insert_position)
169
194
  return if expressions.any?(&:nil?)
@@ -47,7 +47,7 @@ module RuboCop
47
47
  def correct_elsif(node)
48
48
  <<~RUBY.chop
49
49
  if #{node.condition.source}
50
- #{node.if_branch.source}
50
+ #{node.if_branch&.source}
51
51
  #{build_else_branch(node.else_branch).chop}
52
52
  end
53
53
  RUBY
@@ -56,7 +56,7 @@ module RuboCop
56
56
  def build_else_branch(second_condition)
57
57
  result = <<~RUBY
58
58
  elsif #{second_condition.condition.source}
59
- #{second_condition.if_branch.source}
59
+ #{second_condition.if_branch&.source}
60
60
  RUBY
61
61
 
62
62
  if second_condition.else_branch
@@ -60,17 +60,17 @@ module RuboCop
60
60
  # @!method inverse_candidate?(node)
61
61
  def_node_matcher :inverse_candidate?, <<~PATTERN
62
62
  {
63
- (send $(send $(...) $_ $...) :!)
64
- (send ({block numblock} $(send $(...) $_) $...) :!)
65
- (send (begin $(send $(...) $_ $...)) :!)
63
+ (send $(call $(...) $_ $...) :!)
64
+ (send ({block numblock} $(call $(...) $_) $...) :!)
65
+ (send (begin $(call $(...) $_ $...)) :!)
66
66
  }
67
67
  PATTERN
68
68
 
69
69
  # @!method inverse_block?(node)
70
70
  def_node_matcher :inverse_block?, <<~PATTERN
71
- ({block numblock} $(send (...) $_) ... { $(send ... :!)
71
+ ({block numblock} $(call (...) $_) ... { $(call ... :!)
72
72
  $(send (...) {:!= :!~} ...)
73
- (begin ... $(send ... :!))
73
+ (begin ... $(call ... :!))
74
74
  (begin ... $(send (...) {:!= :!~} ...))
75
75
  })
76
76
  PATTERN
@@ -87,6 +87,7 @@ module RuboCop
87
87
  end
88
88
  end
89
89
  end
90
+ alias on_csend on_send
90
91
 
91
92
  def on_block(node)
92
93
  inverse_block?(node) do |_method_call, method, block|
@@ -51,7 +51,7 @@ module RuboCop
51
51
  class InvertibleUnlessCondition < Base
52
52
  extend AutoCorrector
53
53
 
54
- MSG = 'Favor `if` with inverted condition over `unless`.'
54
+ MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
55
55
 
56
56
  def on_if(node)
57
57
  return unless node.unless?
@@ -59,7 +59,10 @@ module RuboCop
59
59
  condition = node.condition
60
60
  return unless invertible?(condition)
61
61
 
62
- add_offense(node) do |corrector|
62
+ message = format(MSG, prefer: "#{node.inverse_keyword} #{preferred_condition(condition)}",
63
+ current: "#{node.keyword} #{condition.source}")
64
+
65
+ add_offense(node, message: message) do |corrector|
63
66
  corrector.replace(node.loc.keyword, node.inverse_keyword)
64
67
  autocorrect(corrector, condition)
65
68
  end
@@ -88,6 +91,40 @@ module RuboCop
88
91
  (argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
89
92
  end
90
93
 
94
+ def preferred_condition(node)
95
+ case node.type
96
+ when :begin then "(#{preferred_condition(node.children.first)})"
97
+ when :send then preferred_send_condition(node)
98
+ when :or, :and then preferred_logical_condition(node)
99
+ end
100
+ end
101
+
102
+ def preferred_send_condition(node)
103
+ receiver_source = node.receiver.source
104
+ return receiver_source if node.method?(:!)
105
+
106
+ inverse_method_name = inverse_methods[node.method_name]
107
+ return "#{receiver_source}.#{inverse_method_name}" unless node.arguments?
108
+
109
+ argument_list = node.arguments.map(&:source).join(', ')
110
+ if node.operator_method?
111
+ return "#{receiver_source} #{inverse_method_name} #{argument_list}"
112
+ end
113
+
114
+ if node.parenthesized?
115
+ return "#{receiver_source}.#{inverse_method_name}(#{argument_list})"
116
+ end
117
+
118
+ "#{receiver_source}.#{inverse_method_name} #{argument_list}"
119
+ end
120
+
121
+ def preferred_logical_condition(node)
122
+ preferred_lhs = preferred_condition(node.lhs)
123
+ preferred_rhs = preferred_condition(node.rhs)
124
+
125
+ "#{preferred_lhs} #{node.inverse_operator} #{preferred_rhs}"
126
+ end
127
+
91
128
  def autocorrect(corrector, node)
92
129
  case node.type
93
130
  when :begin
@@ -69,10 +69,10 @@ module RuboCop
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
71
  add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
72
- if node.send_node.source == 'lambda'
73
- autocorrect_method_to_literal(corrector, node)
74
- else
72
+ if node.send_node.lambda_literal?
75
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
+ else
75
+ autocorrect_method_to_literal(corrector, node)
76
76
  end
77
77
  end
78
78
  end
@@ -20,6 +20,7 @@ module RuboCop
20
20
  # lambda.(x, y)
21
21
  class LambdaCall < Base
22
22
  include ConfigurableEnforcedStyle
23
+ include IgnoredNode
23
24
  extend AutoCorrector
24
25
 
25
26
  MSG = 'Prefer the use of `%<prefer>s` over `%<current>s`.'
@@ -33,8 +34,12 @@ module RuboCop
33
34
  current = node.source
34
35
 
35
36
  add_offense(node, message: format(MSG, prefer: prefer, current: current)) do |corrector|
37
+ next if part_of_ignored_node?(node)
38
+
36
39
  opposite_style_detected
37
40
  corrector.replace(node, prefer)
41
+
42
+ ignore_node(node)
38
43
  end
39
44
  else
40
45
  correct_style_detected
@@ -44,9 +44,9 @@ module RuboCop
44
44
 
45
45
  # @!method map_and_compact?(node)
46
46
  def_node_matcher :map_and_compact?, <<~RUBY
47
- (send
47
+ (call
48
48
  (block
49
- (send _ :map)
49
+ (call _ :map)
50
50
  (args
51
51
  $(arg _))
52
52
  {
@@ -85,6 +85,7 @@ module RuboCop
85
85
  end
86
86
  end
87
87
  end
88
+ alias on_csend on_send
88
89
 
89
90
  private
90
91
 
@@ -34,21 +34,25 @@ module RuboCop
34
34
 
35
35
  minimum_target_ruby_version 2.6
36
36
 
37
- MSG = 'Pass a block to `to_h` instead of calling `%<method>s.to_h`.'
37
+ MSG = 'Pass a block to `to_h` instead of calling `%<method>s%<dot>sto_h`.'
38
38
  RESTRICT_ON_SEND = %i[to_h].freeze
39
39
 
40
- # @!method map_to_h?(node)
41
- def_node_matcher :map_to_h?, <<~PATTERN
40
+ # @!method map_to_h(node)
41
+ def_node_matcher :map_to_h, <<~PATTERN
42
42
  {
43
- $(send ({block numblock} $(send _ {:map :collect}) ...) :to_h)
44
- $(send $(send _ {:map :collect} (block_pass sym)) :to_h)
43
+ $(call ({block numblock} $(call _ {:map :collect}) ...) :to_h)
44
+ $(call $(call _ {:map :collect} (block_pass sym)) :to_h)
45
45
  }
46
46
  PATTERN
47
47
 
48
+ def self.autocorrect_incompatible_with
49
+ [Layout::SingleLineBlockChain]
50
+ end
51
+
48
52
  def on_send(node)
49
- return unless (to_h_node, map_node = map_to_h?(node))
53
+ return unless (to_h_node, map_node = map_to_h(node))
50
54
 
51
- message = format(MSG, method: map_node.loc.selector.source)
55
+ message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
52
56
  add_offense(map_node.loc.selector, message: message) do |corrector|
53
57
  # If the `to_h` call already has a block, do not autocorrect.
54
58
  next if to_h_node.block_node
@@ -56,15 +60,21 @@ module RuboCop
56
60
  autocorrect(corrector, to_h_node, map_node)
57
61
  end
58
62
  end
63
+ alias on_csend on_send
59
64
 
60
65
  private
61
66
 
67
+ # rubocop:disable Metrics/AbcSize
62
68
  def autocorrect(corrector, to_h, map)
63
69
  removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
64
70
 
65
71
  corrector.remove(range_with_surrounding_space(removal_range, side: :left))
72
+ if (map_dot = map.loc.dot)
73
+ corrector.replace(map_dot, to_h.loc.dot.source)
74
+ end
66
75
  corrector.replace(map.loc.selector, 'to_h')
67
76
  end
77
+ # rubocop:enable Metrics/AbcSize
68
78
  end
69
79
  end
70
80
  end
@@ -86,6 +86,7 @@ module RuboCop
86
86
 
87
87
  def legitimate_call_with_parentheses?(node) # rubocop:disable Metrics/PerceivedComplexity
88
88
  call_in_literals?(node) ||
89
+ node.parent&.when_type? ||
89
90
  call_with_ambiguous_arguments?(node) ||
90
91
  call_in_logical_operators?(node) ||
91
92
  call_in_optional_arguments?(node) ||
@@ -124,24 +125,39 @@ module RuboCop
124
125
  node.parent&.class_type? && node.parent&.single_line?
125
126
  end
126
127
 
127
- def call_with_ambiguous_arguments?(node)
128
+ def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
128
129
  call_with_braced_block?(node) ||
130
+ call_in_argument_with_block?(node) ||
129
131
  call_as_argument_or_chain?(node) ||
132
+ call_in_match_pattern?(node) ||
130
133
  hash_literal_in_arguments?(node) ||
131
134
  node.descendants.any? do |n|
132
- n.forwarded_args_type? || ambiguous_literal?(n) || logical_operator?(n) ||
133
- call_with_braced_block?(n)
135
+ n.forwarded_args_type? || n.block_type? ||
136
+ ambiguous_literal?(n) || logical_operator?(n)
134
137
  end
135
138
  end
136
139
 
137
140
  def call_with_braced_block?(node)
138
- (node.send_type? || node.super_type?) && node.block_node&.braces?
141
+ (node.call_type? || node.super_type?) && node.block_node&.braces?
142
+ end
143
+
144
+ def call_in_argument_with_block?(node)
145
+ parent = node.parent&.block_type? && node.parent&.parent
146
+ return false unless parent
147
+
148
+ parent.call_type? || parent.super_type? || parent.yield_type?
139
149
  end
140
150
 
141
151
  def call_as_argument_or_chain?(node)
142
152
  node.parent &&
143
- ((node.parent.send_type? && !assigned_before?(node.parent, node)) ||
144
- node.parent.csend_type? || node.parent.super_type? || node.parent.yield_type?)
153
+ (node.parent.call_type? || node.parent.super_type? || node.parent.yield_type?) &&
154
+ !assigned_before?(node.parent, node)
155
+ end
156
+
157
+ def call_in_match_pattern?(node)
158
+ return false unless (parent = node.parent)
159
+
160
+ parent.match_pattern_type? || parent.match_pattern_p_type?
145
161
  end
146
162
 
147
163
  def hash_literal_in_arguments?(node)
@@ -218,15 +218,13 @@ module RuboCop
218
218
  send(style, node) # call require_parentheses or omit_parentheses
219
219
  end
220
220
  alias on_csend on_send
221
- alias on_super on_send
222
221
  alias on_yield on_send
223
222
 
224
223
  private
225
224
 
226
225
  def args_begin(node)
227
226
  loc = node.loc
228
- selector =
229
- node.super_type? || node.yield_type? ? loc.keyword : loc.selector
227
+ selector = node.yield_type? ? loc.keyword : loc.selector
230
228
 
231
229
  resize_by = args_parenthesized?(node) ? 2 : 1
232
230
  selector.end.resize(resize_by)
@@ -239,7 +237,7 @@ module RuboCop
239
237
  def args_parenthesized?(node)
240
238
  return false unless node.arguments.one?
241
239
 
242
- first_node = node.arguments.first
240
+ first_node = node.first_argument
243
241
  first_node.begin_type? && first_node.parenthesized_call?
244
242
  end
245
243
  end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # This cop can be customized allowed methods with `AllowedMethods`.
9
9
  # By default, there are no methods to allowed.
10
10
  #
11
+ # NOTE: This cop allows the use of `it()` without arguments in blocks,
12
+ # as in `0.times { it() }`, following `Lint/ItWithoutArgumentsInBlock` cop.
13
+ #
11
14
  # @example
12
15
  # # bad
13
16
  # object.some_method()
@@ -30,15 +33,18 @@ module RuboCop
30
33
 
31
34
  MSG = 'Do not use parentheses for method calls with no arguments.'
32
35
 
36
+ # rubocop:disable Metrics/CyclomaticComplexity
33
37
  def on_send(node)
34
38
  return unless !node.arguments? && node.parenthesized?
35
39
  return if ineligible_node?(node)
36
40
  return if default_argument?(node)
37
41
  return if allowed_method_name?(node.method_name)
38
42
  return if same_name_assignment?(node)
43
+ return if parenthesized_it_method_in_block?(node)
39
44
 
40
45
  register_offense(node)
41
46
  end
47
+ # rubocop:enable Metrics/CyclomaticComplexity
42
48
 
43
49
  private
44
50
 
@@ -71,6 +77,20 @@ module RuboCop
71
77
  end
72
78
  end
73
79
 
80
+ # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
81
+ #
82
+ # $ ruby -e '0.times { begin; it; end }'
83
+ # -e:1: warning: `it` calls without arguments will refer to the first block param in
84
+ # Ruby 3.4; use it() or self.it
85
+ #
86
+ def parenthesized_it_method_in_block?(node)
87
+ return false unless node.method?(:it)
88
+ return false unless (block_node = node.each_ancestor(:block).first)
89
+ return false unless block_node.arguments.empty_and_without_delimiters?
90
+
91
+ !node.receiver && node.arguments.empty? && !node.block_literal?
92
+ end
93
+
74
94
  def any_assignment?(node)
75
95
  node.each_ancestor(*AST::Node::ASSIGNMENTS).any? do |asgn_node|
76
96
  # `obj.method = value` parses as (send ... :method= ...), and will
@@ -170,7 +170,7 @@ module RuboCop
170
170
  return true if node.arguments.any? do |arg|
171
171
  arg.forward_arg_type? || arg.restarg_type? || arg.kwrestarg_type?
172
172
  end
173
- return false unless (last_argument = node.arguments.last)
173
+ return false unless (last_argument = node.last_argument)
174
174
 
175
175
  last_argument.blockarg_type? && last_argument.name.nil?
176
176
  end
@@ -7,12 +7,12 @@ module RuboCop
7
7
  # defining `respond_to_missing?`.
8
8
  #
9
9
  # @example
10
- # #bad
10
+ # # bad
11
11
  # def method_missing(name, *args)
12
12
  # # ...
13
13
  # end
14
14
  #
15
- # #good
15
+ # # good
16
16
  # def respond_to_missing?(name, include_private)
17
17
  # # ...
18
18
  # end
@@ -40,7 +40,7 @@ module RuboCop
40
40
  def on_class(node)
41
41
  begin_node = node.child_nodes.find(&:begin_type?) || node
42
42
  begin_node.each_child_node(:send).select(&:macro?).each do |macro|
43
- next unless MIXIN_METHODS.include?(macro.method_name)
43
+ next if !MIXIN_METHODS.include?(macro.method_name) || macro.arguments.empty?
44
44
 
45
45
  check(macro)
46
46
  end
@@ -28,7 +28,7 @@ module RuboCop
28
28
  MSG = 'Avoid multi-line chains of blocks.'
29
29
 
30
30
  def on_block(node)
31
- node.send_node.each_node(:send) do |send_node|
31
+ node.send_node.each_node(:send, :csend) do |send_node|
32
32
  receiver = send_node.receiver
33
33
 
34
34
  next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
41
41
  MSG_SINGLE_LINE = 'Avoid multi-line ternary operators, use single-line instead.'
42
- SINGLE_LINE_TYPES = %i[return break next send].freeze
42
+ SINGLE_LINE_TYPES = %i[return break next send csend].freeze
43
43
 
44
44
  def on_if(node)
45
45
  return unless offense?(node)
@@ -54,12 +54,10 @@ module RuboCop
54
54
  private
55
55
 
56
56
  def offense?(node)
57
- node.ternary? && node.multiline?
57
+ node.ternary? && node.multiline? && node.source != replacement(node)
58
58
  end
59
59
 
60
60
  def autocorrect(corrector, node)
61
- return unless offense?(node)
62
-
63
61
  corrector.replace(node, replacement(node))
64
62
  return unless (parent = node.parent)
65
63
  return unless (comments_in_condition = comments_in_condition(node))