rubocop 1.91.0 → 1.92.2

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 (278) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +4 -1
  4. data/lib/rubocop/cached_data.rb +47 -7
  5. data/lib/rubocop/changed_files.rb +35 -13
  6. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +2 -2
  8. data/lib/rubocop/cli/command/explain.rb +34 -0
  9. data/lib/rubocop/cli/command/show_cops.rb +6 -0
  10. data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
  11. data/lib/rubocop/cli.rb +3 -2
  12. data/lib/rubocop/comment_config.rb +17 -8
  13. data/lib/rubocop/config.rb +0 -4
  14. data/lib/rubocop/config_loader.rb +6 -3
  15. data/lib/rubocop/config_loader_resolver.rb +37 -12
  16. data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
  17. data/lib/rubocop/config_obsoletion.rb +9 -0
  18. data/lib/rubocop/config_validator.rb +11 -0
  19. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  20. data/lib/rubocop/cop/badge.rb +1 -1
  21. data/lib/rubocop/cop/base.rb +42 -21
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  23. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  24. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  25. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  26. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  27. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
  28. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  29. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
  30. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
  31. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  32. data/lib/rubocop/cop/documentation.rb +1 -1
  33. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  34. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  35. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  36. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  37. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  38. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  39. data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
  40. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  41. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  42. data/lib/rubocop/cop/layout/class_structure.rb +29 -7
  43. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  44. data/lib/rubocop/cop/layout/dot_position.rb +25 -15
  45. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
  46. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  47. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  48. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  49. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
  50. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  51. data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
  52. data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
  53. data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
  54. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
  55. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
  56. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  57. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  58. data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
  59. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
  60. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  61. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  62. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  63. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  64. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  65. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  66. data/lib/rubocop/cop/layout/line_length.rb +176 -26
  67. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  68. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  69. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
  70. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  71. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
  72. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  73. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  74. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
  75. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  76. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  77. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
  78. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  79. data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
  80. data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
  81. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  82. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
  83. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
  84. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  85. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  86. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  87. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  88. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  89. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  90. data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
  91. data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
  92. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  93. data/lib/rubocop/cop/lint/loop.rb +24 -2
  94. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
  95. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  96. data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
  97. data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
  98. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
  99. data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
  100. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
  101. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  102. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
  103. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
  104. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
  105. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  106. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  107. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  108. data/lib/rubocop/cop/lint/void.rb +8 -1
  109. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  110. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  111. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  112. data/lib/rubocop/cop/mixin/alignment.rb +4 -0
  113. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
  114. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
  115. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  116. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  117. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  118. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
  119. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  120. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
  121. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
  122. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  123. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  124. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  125. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  126. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  127. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
  128. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  129. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  130. data/lib/rubocop/cop/mixin/statement_modifier.rb +39 -5
  131. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  132. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  133. data/lib/rubocop/cop/mixin.rb +2 -0
  134. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  135. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  136. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  137. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  138. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  139. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  140. data/lib/rubocop/cop/offense.rb +59 -3
  141. data/lib/rubocop/cop/registry.rb +1 -1
  142. data/lib/rubocop/cop/security/json_load.rb +16 -4
  143. data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
  144. data/lib/rubocop/cop/style/alias.rb +18 -5
  145. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  146. data/lib/rubocop/cop/style/and_or.rb +37 -10
  147. data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
  148. data/lib/rubocop/cop/style/attr.rb +3 -4
  149. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  150. data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
  151. data/lib/rubocop/cop/style/case_equality.rb +14 -3
  152. data/lib/rubocop/cop/style/case_like_if.rb +6 -3
  153. data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
  154. data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
  155. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  156. data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
  157. data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
  158. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  159. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
  160. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  161. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  162. data/lib/rubocop/cop/style/empty_method.rb +3 -1
  163. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
  164. data/lib/rubocop/cop/style/endless_method.rb +104 -11
  165. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  166. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  167. data/lib/rubocop/cop/style/float_division.rb +37 -2
  168. data/lib/rubocop/cop/style/for.rb +53 -2
  169. data/lib/rubocop/cop/style/format_string.rb +28 -6
  170. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  171. data/lib/rubocop/cop/style/guard_clause.rb +31 -22
  172. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
  173. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  174. data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -9
  175. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  176. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  177. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  178. data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
  179. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  180. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  181. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  182. data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
  183. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  184. data/lib/rubocop/cop/style/lambda.rb +34 -1
  185. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  186. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  187. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
  188. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
  189. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  190. data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
  191. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  192. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  193. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  194. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  195. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  196. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  197. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  198. data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
  199. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  200. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  201. data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
  202. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  203. data/lib/rubocop/cop/style/next.rb +24 -10
  204. data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
  205. data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
  206. data/lib/rubocop/cop/style/not.rb +10 -5
  207. data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
  208. data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
  209. data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
  210. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  211. data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
  212. data/lib/rubocop/cop/style/raise_args.rb +80 -11
  213. data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
  214. data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
  215. data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
  216. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
  217. data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
  218. data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
  219. data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
  220. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
  221. data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
  222. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  223. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
  224. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  225. data/lib/rubocop/cop/style/redundant_self.rb +9 -1
  226. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
  227. data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
  228. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  229. data/lib/rubocop/cop/style/require_order.rb +25 -8
  230. data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
  231. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  232. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  233. data/lib/rubocop/cop/style/sample.rb +9 -8
  234. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  235. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  236. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  237. data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
  238. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  239. data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
  240. data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
  241. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  242. data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
  243. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  244. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  245. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  246. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  247. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  248. data/lib/rubocop/cop/team.rb +10 -5
  249. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  250. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  251. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  252. data/lib/rubocop/formatter/formatter_set.rb +4 -0
  253. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  254. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  255. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  256. data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
  257. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  258. data/lib/rubocop/lsp/routes.rb +23 -23
  259. data/lib/rubocop/lsp/runtime.rb +47 -11
  260. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  261. data/lib/rubocop/lsp.rb +5 -2
  262. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  263. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  264. data/lib/rubocop/mcp/scope.rb +65 -0
  265. data/lib/rubocop/mcp/server.rb +184 -76
  266. data/lib/rubocop/mcp/source_files.rb +35 -0
  267. data/lib/rubocop/offense_limit.rb +30 -0
  268. data/lib/rubocop/options.rb +45 -10
  269. data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
  270. data/lib/rubocop/remote_config.rb +4 -1
  271. data/lib/rubocop/result_cache.rb +20 -13
  272. data/lib/rubocop/runner.rb +110 -42
  273. data/lib/rubocop/server/cache.rb +6 -6
  274. data/lib/rubocop/target_ruby.rb +21 -9
  275. data/lib/rubocop/util.rb +69 -0
  276. data/lib/rubocop/version.rb +1 -1
  277. data/lib/rubocop.rb +5 -0
  278. metadata +16 -5
@@ -60,7 +60,7 @@ module RuboCop
60
60
  add_offense(node.loc.keyword, message: MSG_ALIAS) do |corrector|
61
61
  autocorrect(corrector, node)
62
62
  end
63
- elsif node.children.none? { |arg| bareword?(arg) }
63
+ elsif node.children.all? { |arg| !bareword?(arg) && bareword_symbol?(arg) }
64
64
  add_offense_for_args(node) { |corrector| autocorrect(corrector, node) }
65
65
  end
66
66
  end
@@ -97,7 +97,7 @@ module RuboCop
97
97
 
98
98
  def add_offense_for_args(node, &block)
99
99
  existing_args = node.children.map(&:source).join(' ')
100
- preferred_args = node.children.map { |a| a.source[1..] }.join(' ')
100
+ preferred_args = node.children.map { |node| bareword_for(node) }.join(' ')
101
101
  arg_ranges = node.children.map(&:source_range)
102
102
  msg = format(MSG_SYMBOL_ARGS, prefer: preferred_args, current: existing_args)
103
103
  add_offense(arg_ranges.reduce(&:join), message: msg, &block)
@@ -138,6 +138,15 @@ module RuboCop
138
138
  !sym_node.source.start_with?(':') || sym_node.dsym_type?
139
139
  end
140
140
 
141
+ def bareword_symbol?(sym_node)
142
+ return false unless sym_node.sym_type?
143
+
144
+ value = sym_node.value.to_s
145
+ return true if value.match?(/\A[A-Za-z_]\w*[?!=]?\z/)
146
+
147
+ !sym_node.source.start_with?(':"', ":'") && !value.start_with?('@', '$')
148
+ end
149
+
141
150
  def correct_alias_method_to_alias(corrector, send_node)
142
151
  new, old = *send_node.arguments
143
152
  replacement = "alias #{identifier(new)} #{identifier(old)}"
@@ -153,17 +162,21 @@ module RuboCop
153
162
  end
154
163
 
155
164
  def correct_alias_with_symbol_args(corrector, node)
156
- corrector.replace(node.new_identifier, node.new_identifier.source[1..])
157
- corrector.replace(node.old_identifier, node.old_identifier.source[1..])
165
+ corrector.replace(node.new_identifier, bareword_for(node.new_identifier))
166
+ corrector.replace(node.old_identifier, bareword_for(node.old_identifier))
158
167
  end
159
168
 
160
169
  def identifier(node)
161
- if node.sym_type?
170
+ if bareword_symbol?(node)
162
171
  ":#{node.children.first}"
163
172
  else
164
173
  node.source
165
174
  end
166
175
  end
176
+
177
+ def bareword_for(sym_node)
178
+ sym_node.value.to_s
179
+ end
167
180
  end
168
181
  end
169
182
  end
@@ -39,9 +39,9 @@ module RuboCop
39
39
  # @!method ambiguous_endless_method_body(node)
40
40
  def_node_matcher :ambiguous_endless_method_body, <<~PATTERN
41
41
  ^${
42
- (if _ <def _>)
43
- ({and or} def _)
44
- ({while until} _ def)
42
+ (if _ <any_def _>)
43
+ ({and or} any_def _)
44
+ ({while until} _ any_def)
45
45
  }
46
46
  PATTERN
47
47
 
@@ -57,6 +57,7 @@ module RuboCop
57
57
  correct_to_multiline(corrector, node)
58
58
  end
59
59
  end
60
+ alias on_defs on_def
60
61
 
61
62
  private
62
63
 
@@ -48,6 +48,10 @@ module RuboCop
48
48
 
49
49
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
50
50
 
51
+ def self.autocorrect_incompatible_with
52
+ [Style::MethodCallWithArgsParentheses, Style::ReturnNil, Style::ReturnNilInPredicateMethodDefinition]
53
+ end
54
+
51
55
  def on_and(node)
52
56
  process_logical_operator(node) if style == :always
53
57
  end
@@ -68,13 +72,7 @@ module RuboCop
68
72
 
69
73
  message = message(node)
70
74
  add_offense(node.loc.operator, message: message) do |corrector|
71
- node.each_child_node do |expr|
72
- if expr.send_type?
73
- correct_send(expr, corrector)
74
- elsif expr.type?(:return, :next, :break, :yield) || expr.assignment?
75
- correct_other(expr, corrector)
76
- end
77
- end
75
+ node.each_child_node { |expr| correct_operand(expr, corrector) }
78
76
 
79
77
  corrector.replace(node.loc.operator, node.alternate_operator)
80
78
 
@@ -82,6 +80,22 @@ module RuboCop
82
80
  end
83
81
  end
84
82
 
83
+ def correct_operand(expr, corrector)
84
+ expr = expr.send_node if expr.any_block_type?
85
+
86
+ if expr.type?(:call, :super)
87
+ correct_send(expr, corrector)
88
+ elsif wrap_in_parentheses?(expr)
89
+ correct_other(expr, corrector)
90
+ end
91
+ end
92
+
93
+ # These bind looser than `&&` and `||`, or can't be their operands at all.
94
+ def wrap_in_parentheses?(node)
95
+ node.type?(:return, :next, :break, :yield, :any_match_pattern, :range, :defined?) ||
96
+ node.assignment? || (node.if_type? && node.ternary?)
97
+ end
98
+
85
99
  def on_conditionals(node)
86
100
  node.condition.each_node(*AST::Node::OPERATOR_KEYWORDS) do |operator|
87
101
  process_logical_operator(operator)
@@ -97,6 +111,7 @@ module RuboCop
97
111
  return correct_setter(node, corrector) if node.setter_method?
98
112
  return correct_other(node, corrector) if node.comparison_method?
99
113
 
114
+ correct_block_receiver(node, corrector)
100
115
  return unless correctable_send?(node)
101
116
 
102
117
  whitespace_before_arg_range = whitespace_before_arg(node)
@@ -116,7 +131,8 @@ module RuboCop
116
131
  # however, 'not x' also parses as (send x :!)
117
132
  def correct_not(node, receiver, corrector)
118
133
  if node.prefix_bang?
119
- return unless receiver.send_type?
134
+ receiver = receiver.send_node if receiver.any_block_type?
135
+ return unless receiver.type?(:call, :super)
120
136
 
121
137
  correct_send(receiver, corrector)
122
138
  elsif node.prefix_not?
@@ -141,12 +157,23 @@ module RuboCop
141
157
  end
142
158
  end
143
159
 
160
+ # A method chained onto a `do` block, as in `bar baz do ... end.qux`,
161
+ # still needs parentheses around the arguments of the call that takes the block.
162
+ def correct_block_receiver(node, corrector)
163
+ receiver = node.receiver
164
+ receiver = receiver.receiver while receiver&.call_type?
165
+ correct_operand(receiver, corrector) if receiver&.any_block_type?
166
+ end
167
+
144
168
  def correctable_send?(node)
145
- !node.parenthesized? && node.arguments? && !node.method?(:[])
169
+ # Operator syntax such as `a + b` or `a[b]` already binds tighter than `&&` and `||`.
170
+ return false if node.operator_method? && !node.loc.dot
171
+
172
+ !node.parenthesized? && node.arguments?
146
173
  end
147
174
 
148
175
  def whitespace_before_arg(node)
149
- begin_paren = node.loc.selector.end_pos
176
+ begin_paren = node.selector.end_pos
150
177
  end_paren = begin_paren
151
178
  # Increment position of parenthesis, unless message is a predicate
152
179
  # method followed by a non-whitespace char (e.g. is_a?String).
@@ -153,7 +153,7 @@ module RuboCop
153
153
  BLOCK_MSG = 'Use anonymous block arguments forwarding (`&`).'
154
154
 
155
155
  def self.autocorrect_incompatible_with
156
- [Naming::BlockForwarding, Style::MethodDefParentheses]
156
+ [Naming::BlockForwarding, Style::EndlessMethod, Style::MethodDefParentheses]
157
157
  end
158
158
 
159
159
  def on_def(node)
@@ -169,7 +169,7 @@ module RuboCop
169
169
 
170
170
  return if send_classifications.empty?
171
171
 
172
- if only_forwards_all?(send_classifications)
172
+ if forward_all?(send_nodes, send_classifications)
173
173
  add_forward_all_offenses(node, send_classifications, forwardable_args)
174
174
  elsif target_ruby_version >= 3.2
175
175
  add_post_ruby_32_offenses(node, send_classifications, forwardable_args)
@@ -192,11 +192,33 @@ module RuboCop
192
192
  [restarg_node, kwrestarg_node, blockarg_node]
193
193
  end
194
194
 
195
+ def forward_all?(send_nodes, send_classifications)
196
+ only_forwards_all?(send_classifications) &&
197
+ !anonymous_forwarding_elsewhere?(send_nodes, send_classifications)
198
+ end
199
+
195
200
  def only_forwards_all?(send_classifications)
196
201
  all_classifications = %i[all all_anonymous].freeze
197
202
  send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
198
203
  end
199
204
 
205
+ def anonymous_forwarding_elsewhere?(send_nodes, send_classifications)
206
+ classified = send_classifications.map(&:first)
207
+
208
+ send_nodes.any? do |send_node|
209
+ classified.none? { |node| node.equal?(send_node) } &&
210
+ forwards_anonymous_args?(send_node)
211
+ end
212
+ end
213
+
214
+ def forwards_anonymous_args?(send_node)
215
+ send_node.arguments.any? do |arg|
216
+ next arg.children.any?(&:forwarded_kwrestarg_type?) if arg.hash_type?
217
+
218
+ arg.forwarded_restarg_type?
219
+ end
220
+ end
221
+
200
222
  # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
201
223
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
202
224
  rest_arg, kwrest_arg, block_arg = *forwardable_args
@@ -376,11 +398,16 @@ module RuboCop
376
398
 
377
399
  def add_parens_if_missing(node, corrector)
378
400
  return if parentheses?(node)
379
- return if node.send_type? && node.method?(:[])
401
+ return if node.call_type? && operator_or_setter_call?(node)
402
+ return unless (@parenthesized_nodes ||= Set.new.compare_by_identity).add?(node)
380
403
 
381
404
  add_parentheses(node, corrector)
382
405
  end
383
406
 
407
+ def operator_or_setter_call?(node)
408
+ node.setter_method? || (node.operator_method? && !node.loc.dot)
409
+ end
410
+
384
411
  # Classifies send nodes for possible rest/kwrest/all (including block) forwarding.
385
412
  class SendNodeClassifier
386
413
  extend NodePattern::Macros
@@ -499,6 +526,8 @@ module RuboCop
499
526
  end
500
527
 
501
528
  def offensive_block_forwarding?
529
+ return false if @send_node.block_literal?
530
+
502
531
  @block_arg ? forwarded_block_arg : allow_offense_for_no_block?
503
532
  end
504
533
 
@@ -50,10 +50,9 @@ module RuboCop
50
50
  def autocorrect(corrector, node)
51
51
  attr_name, setter = *node.arguments
52
52
 
53
- node_expr = node.source_range
54
- attr_expr = attr_name.source_range
55
-
56
- remove = range_between(attr_expr.end_pos, node_expr.end_pos) if setter&.boolean_type?
53
+ if setter&.boolean_type?
54
+ remove = range_between(attr_name.source_range.end_pos, setter.source_range.end_pos)
55
+ end
57
56
 
58
57
  corrector.replace(node.loc.selector, replacement_method(node))
59
58
  corrector.remove(remove) if remove
@@ -66,16 +66,18 @@ module RuboCop
66
66
 
67
67
  # @!method bit_operation?(node)
68
68
  def_node_matcher :bit_operation?, <<~PATTERN
69
- (begin
70
- (send _ :& _))
69
+ {
70
+ (begin (send _ :& _))
71
+ (send _ :& _)
72
+ }
71
73
  PATTERN
72
74
 
73
75
  # rubocop:disable-next Metrics/AbcSize
74
76
  def on_send(node)
75
- return unless node.receiver&.begin_type?
77
+ return unless bit_operation?(node.receiver)
76
78
  return unless (preferred_method = preferred_method(node))
77
79
 
78
- bit_operation = node.receiver.children.first
80
+ bit_operation = node.receiver.begin_type? ? node.receiver.children.first : node.receiver
79
81
  lhs, _operator, rhs = *bit_operation
80
82
 
81
83
  preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
@@ -177,7 +177,7 @@ module RuboCop
177
177
  BRACES_REQUIRED_MESSAGE = "Brace delimiters `{...}` required for '%<method_name>s' method."
178
178
 
179
179
  def self.autocorrect_incompatible_with
180
- [Style::RedundantBegin]
180
+ [Layout::SpaceBeforeBlockBraces, Style::MethodCallWithArgsParentheses, Style::RedundantBegin]
181
181
  end
182
182
 
183
183
  def on_send(node)
@@ -196,6 +196,8 @@ module RuboCop
196
196
  end
197
197
  end
198
198
  alias on_csend on_send
199
+ alias on_super on_send
200
+ alias on_yield on_send
199
201
 
200
202
  def on_block(node)
201
203
  return if part_of_ignored_node?(node)
@@ -292,7 +294,7 @@ module RuboCop
292
294
  corrector.replace(b, '{')
293
295
  corrector.replace(e, '}')
294
296
 
295
- corrector.wrap(node.body, "begin\n", "\nend") if begin_required?(node)
297
+ wrap_body_with_begin(corrector, node.body) if begin_required?(node)
296
298
  end
297
299
 
298
300
  def whitespace_before?(range)
@@ -304,6 +306,8 @@ module RuboCop
304
306
  end
305
307
 
306
308
  def move_comment_before_block(corrector, comment, block_node, closing_brace)
309
+ return if directive_comment?(comment)
310
+
307
311
  range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
308
312
 
309
313
  # It is possible that there is code between the block and the comment
@@ -317,6 +321,10 @@ module RuboCop
317
321
  corrector.insert_before(block_node, "#{comment.text}\n")
318
322
  end
319
323
 
324
+ def directive_comment?(comment)
325
+ comment.text.match?(DirectiveComment::DIRECTIVE_COMMENT_REGEXP)
326
+ end
327
+
320
328
  def source_range_before_comment(range, comment)
321
329
  range = range.end.join(comment.source_range.begin)
322
330
 
@@ -372,6 +380,7 @@ module RuboCop
372
380
  # rubocop:disable-next Metrics/CyclomaticComplexity -- inlined special_method checks to avoid double evaluation
373
381
  def proper_block_style?(node)
374
382
  return true if require_do_end?(node)
383
+ return true if collection_of_for_loop?(node)
375
384
 
376
385
  method_name = node.method_name
377
386
  return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
@@ -398,6 +407,10 @@ module RuboCop
398
407
  !modifier_rescue?(body)
399
408
  end
400
409
 
410
+ def collection_of_for_loop?(node)
411
+ node.parent&.for_type? && node.parent.collection == node
412
+ end
413
+
401
414
  def modifier_rescue?(rescue_node)
402
415
  return false if rescue_node.body.nil? || rescue_node.else_branch
403
416
  return false unless rescue_node.resbody_branches.one?
@@ -438,11 +451,20 @@ module RuboCop
438
451
  end
439
452
 
440
453
  def correction_would_break_code?(node)
441
- return false unless node.keywords?
454
+ return loop_condition?(node) if node.braces?
442
455
 
443
456
  node.send_node.arguments? && !node.send_node.parenthesized?
444
457
  end
445
458
 
459
+ # A `do` in the condition of `while` or `until` is taken as the start of
460
+ # the loop body. Inside the body of another block it is unambiguous again.
461
+ def loop_condition?(node)
462
+ ancestors = node.each_ancestor.take_while { |ancestor| !ancestor.any_block_type? }
463
+ [node, *ancestors].each_cons(2).any? do |child, parent|
464
+ parent.type?(:while, :until) && parent.condition.equal?(child)
465
+ end
466
+ end
467
+
446
468
  def functional_method?(method_name)
447
469
  functional_methods.include?(method_name)
448
470
  end
@@ -490,6 +512,16 @@ module RuboCop
490
512
  node.type?(:array, :range)
491
513
  end
492
514
 
515
+ def wrap_body_with_begin(corrector, body)
516
+ heredoc_ends = body.each_node(:any_str).select(&:heredoc?).map do |str|
517
+ str.loc.heredoc_end
518
+ end
519
+ body_end = [body.source_range, *heredoc_ends].max_by(&:end_pos)
520
+
521
+ corrector.insert_before(body, "begin\n")
522
+ corrector.insert_after(body_end, "\nend")
523
+ end
524
+
493
525
  def begin_required?(block_node)
494
526
  # If the block contains `rescue` or `ensure`, it needs to be wrapped in
495
527
  # `begin`...`end` when changing `do-end` to `{}`.
@@ -7,7 +7,7 @@ module RuboCop
7
7
  # The `===` operator has different behavior depending on the
8
8
  # receiver and its use outside of `case`/`when` is confusing.
9
9
  # Prefer more explicit alternatives like `is_a?`, `include?`,
10
- # or `match?`.
10
+ # `cover?`, or `match?`.
11
11
  #
12
12
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
13
13
  # the case equality operator is a constant.
@@ -22,9 +22,11 @@ module RuboCop
22
22
  # @example
23
23
  # # bad
24
24
  # (1..100) === 7
25
+ # ('a'..'z') === char
25
26
  #
26
27
  # # good
27
28
  # (1..100).include?(7)
29
+ # ('a'..'z').cover?(char)
28
30
  #
29
31
  # @example AllowOnConstant: false (default)
30
32
  # # bad
@@ -90,9 +92,18 @@ module RuboCop
90
92
  end
91
93
 
92
94
  def begin_replacement(lhs, rhs)
93
- return unless lhs.children.first&.range_type?
95
+ range = lhs.children.first
96
+ return unless range&.range_type?
94
97
 
95
- "#{lhs.source}.include?(#{rhs.source})"
98
+ method = cover_semantics?(range) ? 'cover?' : 'include?'
99
+
100
+ "#{lhs.source}.#{method}(#{rhs.source})"
101
+ end
102
+
103
+ # `Range#===` behaves like `Range#cover?` since Ruby 2.7 (since Ruby 2.6 for ranges
104
+ # that aren't string ranges), and `Range#include?` only matches that for numeric ranges.
105
+ def cover_semantics?(range)
106
+ target_ruby_version >= 2.7 && !range.children.compact.all?(&:numeric_type?)
96
107
  end
97
108
 
98
109
  def const_replacement(lhs, rhs)
@@ -54,6 +54,10 @@ module RuboCop
54
54
 
55
55
  MSG = 'Convert `if-elsif` to `case-when`.'
56
56
 
57
+ def self.autocorrect_incompatible_with
58
+ [Style::IfInsideElse]
59
+ end
60
+
57
61
  def on_if(node)
58
62
  return unless should_check?(node)
59
63
 
@@ -224,10 +228,9 @@ module RuboCop
224
228
  end
225
229
 
226
230
  def branch_conditions(node)
227
- conditions = []
228
- while node&.if_type? && !node.ternary?
231
+ conditions = [node.condition]
232
+ while (node = node.else_branch)&.if_type? && node.elsif?
229
233
  conditions << node.condition
230
- node = node.else_branch
231
234
  end
232
235
  conditions
233
236
  end
@@ -65,6 +65,13 @@ module RuboCop
65
65
  NESTED_MSG = 'Use nested module/class definitions instead of compact style.'
66
66
  COMPACT_MSG = 'Use compact module/class definition instead of nested style.'
67
67
 
68
+ # `Style/StructInheritance` and `Style/DataInheritance` turn a class definition into
69
+ # a constant assignment in the same pass, which the compacted `class` keyword would
70
+ # then precede.
71
+ def self.autocorrect_incompatible_with
72
+ [Style::DataInheritance, Style::StructInheritance]
73
+ end
74
+
68
75
  def on_class(node)
69
76
  return if node.parent_class && style_for_classes != :nested
70
77
 
@@ -117,7 +124,10 @@ module RuboCop
117
124
  end
118
125
 
119
126
  def heuristic_namespace_keyword(node)
120
- class_definition = node.left_sibling&.each_node(:class)&.find do |class_node|
127
+ left_sibling = node.left_sibling
128
+ return 'module' unless left_sibling.is_a?(AST::Node)
129
+
130
+ class_definition = left_sibling.each_node(:class).find do |class_node|
121
131
  class_node.identifier == node.identifier.namespace
122
132
  end
123
133
 
@@ -143,7 +153,9 @@ module RuboCop
143
153
  # making autocorrection ping-pong between the two forms.
144
154
  return if style_for_kind(node.body.type) == :nested
145
155
  return unless compactible_namespace?(node)
156
+ return if node.each_ancestor(:class, :module).any? { |ancestor| ignored_node?(ancestor) }
146
157
 
158
+ ignore_node(node)
147
159
  compact_node(corrector, node)
148
160
  remove_end(corrector, node.body)
149
161
  unindent(corrector, node)
@@ -194,33 +206,62 @@ module RuboCop
194
206
  "#{node.body.children.first.const_name}"
195
207
  end
196
208
 
197
- # rubocop:disable-next Metrics/AbcSize
198
209
  def remove_end(corrector, body)
199
- remove_begin_pos = if same_line?(body.loc.name, body.loc.end)
200
- body.loc.name.end_pos
201
- else
202
- body.loc.end.begin_pos - leading_spaces(body).size
203
- end
204
- adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
205
- range = range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
210
+ range = if same_line?(body.loc.name, body.loc.end)
211
+ single_line_end_range(body)
212
+ else
213
+ multiline_end_range(body)
214
+ end
206
215
 
207
216
  corrector.remove(range)
208
217
  end
209
218
 
210
- def unindent(corrector, node)
211
- return unless node.body.children.last
219
+ def single_line_end_range(body)
220
+ raw = processed_source.raw_source
221
+ pos = body.loc.end.begin_pos
222
+ pos -= 1 while raw[pos - 1] == ' '
223
+ pos -= 1 if raw[pos - 1] == ';'
224
+ range_between(pos, body.loc.end.end_pos)
225
+ end
212
226
 
213
- last_child_leading_spaces = leading_spaces(node.body.children.last)
214
- return if spaces_size(leading_spaces(node)) == spaces_size(last_child_leading_spaces)
227
+ def multiline_end_range(body)
228
+ remove_begin_pos = body.loc.end.begin_pos - leading_spaces(body).size
229
+ adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
230
+ range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
231
+ end
215
232
 
216
- column_delta = configured_indentation_width - spaces_size(last_child_leading_spaces)
217
- return if column_delta.zero?
233
+ def unindent(corrector, node)
234
+ column_delta = unindent_column_delta(node)
235
+ return unless column_delta&.negative?
236
+
237
+ range = unindentable_range(node.body)
238
+ return unless range
218
239
 
219
240
  AlignmentCorrector.correct(
220
- corrector, processed_source, node, column_delta, tab_indentation: true
241
+ corrector, processed_source, range, column_delta, tab_indentation: true
221
242
  )
222
243
  end
223
244
 
245
+ def unindent_column_delta(node)
246
+ body = node.body.children.last
247
+ return unless body
248
+
249
+ node_indentation = spaces_size(leading_spaces(node))
250
+ body_indentation = spaces_size(leading_spaces(body))
251
+
252
+ node_indentation + configured_indentation_width - body_indentation
253
+ end
254
+
255
+ def unindentable_range(body)
256
+ first_line = body.loc.name.line + 1
257
+ last_line = body.loc.end.line - 1
258
+ return if first_line > last_line
259
+
260
+ buffer = processed_source.buffer
261
+ range_between(buffer.line_range(first_line).begin_pos,
262
+ buffer.line_range(last_line).end_pos)
263
+ end
264
+
224
265
  def leading_spaces(node)
225
266
  node.source_range.source_line[/\A\s*/]
226
267
  end
@@ -72,9 +72,13 @@ module RuboCop
72
72
  return unless def_self_style?
73
73
  return unless node.identifier.self_type?
74
74
  return unless all_methods_public?(node)
75
+ return unless def_nodes(node).all? { |def_node| no_code_after?(def_node.source_range) }
75
76
 
76
77
  add_offense(node, message: MSG_SCLASS) do |corrector|
78
+ next if inside_corrected_sclass?(node)
79
+
77
80
  autocorrect_sclass(node, corrector)
81
+ ignore_node(node)
78
82
  end
79
83
  end
80
84
 
@@ -112,6 +116,17 @@ module RuboCop
112
116
  end
113
117
  end
114
118
 
119
+ def no_code_after?(range)
120
+ trailing = processed_source.lines[range.last_line - 1][range.last_column..].to_s.strip
121
+ trailing = trailing.sub(/\A;+\s*/, '')
122
+
123
+ trailing.empty? || trailing.start_with?('#')
124
+ end
125
+
126
+ def inside_corrected_sclass?(node)
127
+ node.each_ancestor(:sclass).any? { |ancestor| ignored_node?(ancestor) }
128
+ end
129
+
115
130
  def autocorrect_sclass(node, corrector)
116
131
  rewritten_defs = []
117
132