rubocop 1.90.0 → 1.92.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 (303) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +241 -11
  4. data/config/obsoletion.yml +9 -0
  5. data/lib/rubocop/cached_data.rb +47 -7
  6. data/lib/rubocop/changed_files.rb +134 -0
  7. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  8. data/lib/rubocop/cli/command/execute_runner.rb +23 -3
  9. data/lib/rubocop/cli/command/explain.rb +34 -0
  10. data/lib/rubocop/cli/command/show_cops.rb +6 -0
  11. data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
  12. data/lib/rubocop/cli.rb +11 -4
  13. data/lib/rubocop/comment_config/directive_range.rb +3 -1
  14. data/lib/rubocop/comment_config/disable_next.rb +49 -3
  15. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  16. data/lib/rubocop/comment_config.rb +32 -53
  17. data/lib/rubocop/config.rb +10 -0
  18. data/lib/rubocop/config_loader.rb +18 -7
  19. data/lib/rubocop/config_loader_resolver.rb +79 -12
  20. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  21. data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
  22. data/lib/rubocop/config_obsoletion.rb +1 -1
  23. data/lib/rubocop/config_validator.rb +29 -3
  24. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  25. data/lib/rubocop/cop/badge.rb +1 -1
  26. data/lib/rubocop/cop/base.rb +57 -24
  27. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  28. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  29. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  30. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  31. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  32. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +22 -7
  33. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  34. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +23 -2
  35. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  36. data/lib/rubocop/cop/correctors.rb +2 -2
  37. data/lib/rubocop/cop/documentation.rb +1 -1
  38. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  39. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  40. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  41. data/lib/rubocop/cop/generator.rb +1 -1
  42. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  43. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  44. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  45. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  46. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  47. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
  48. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  49. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  50. data/lib/rubocop/cop/layout/class_structure.rb +9 -1
  51. data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
  52. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  53. data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
  54. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +14 -5
  55. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  56. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  57. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  58. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -1
  59. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  60. data/lib/rubocop/cop/layout/end_alignment.rb +16 -10
  61. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
  62. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  63. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
  64. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +5 -3
  65. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  66. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  67. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  68. data/lib/rubocop/cop/layout/hash_alignment.rb +15 -10
  69. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -0
  70. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  71. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  72. data/lib/rubocop/cop/layout/indentation_width.rb +4 -4
  73. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  74. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  75. data/lib/rubocop/cop/layout/line_length.rb +71 -19
  76. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  77. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  78. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +7 -0
  79. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  80. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +14 -0
  81. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  82. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  83. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +15 -2
  84. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  85. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  86. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
  87. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  88. data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
  89. data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
  90. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  91. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
  92. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  93. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  94. data/lib/rubocop/cop/lint/argument_mismatch.rb +14 -4
  95. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
  96. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
  97. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  98. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  99. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  100. data/lib/rubocop/cop/lint/literal_as_condition.rb +23 -2
  101. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  102. data/lib/rubocop/cop/lint/loop.rb +24 -2
  103. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
  104. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -4
  105. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  106. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  107. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  108. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
  109. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +42 -17
  110. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  111. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  112. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  113. data/lib/rubocop/cop/lint/void.rb +8 -1
  114. data/lib/rubocop/cop/lint.rb +1 -0
  115. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  116. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  117. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  118. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  119. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  120. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  121. data/lib/rubocop/cop/mixin/check_line_breakable.rb +17 -14
  122. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +5 -1
  123. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  124. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  125. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +69 -0
  126. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  127. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +26 -2
  128. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +31 -3
  129. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  130. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  131. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  132. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  133. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  134. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  135. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +29 -3
  136. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  137. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  138. data/lib/rubocop/cop/mixin/statement_modifier.rb +22 -2
  139. data/lib/rubocop/cop/mixin.rb +4 -2
  140. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  141. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  142. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  143. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  144. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  145. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  146. data/lib/rubocop/cop/offense.rb +59 -3
  147. data/lib/rubocop/cop/registry.rb +10 -2
  148. data/lib/rubocop/cop/security/json_load.rb +16 -4
  149. data/lib/rubocop/cop/severity.rb +12 -0
  150. data/lib/rubocop/cop/style/access_modifier_declarations.rb +10 -2
  151. data/lib/rubocop/cop/style/alias.rb +18 -5
  152. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  153. data/lib/rubocop/cop/style/and_or.rb +4 -0
  154. data/lib/rubocop/cop/style/arguments_forwarding.rb +6 -3
  155. data/lib/rubocop/cop/style/attr.rb +3 -4
  156. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  157. data/lib/rubocop/cop/style/block_delimiters.rb +29 -3
  158. data/lib/rubocop/cop/style/case_like_if.rb +9 -6
  159. data/lib/rubocop/cop/style/class_and_module_children.rb +54 -16
  160. data/lib/rubocop/cop/style/class_methods_definitions.rb +8 -0
  161. data/lib/rubocop/cop/style/combinable_loops.rb +85 -11
  162. data/lib/rubocop/cop/style/concat_array_literals.rb +46 -7
  163. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  164. data/lib/rubocop/cop/style/constant_visibility.rb +6 -2
  165. data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
  166. data/lib/rubocop/cop/style/directive_scope.rb +154 -21
  167. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +118 -18
  168. data/lib/rubocop/cop/style/documentation_method.rb +1 -9
  169. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  170. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  171. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  172. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  173. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  174. data/lib/rubocop/cop/style/empty_method.rb +15 -3
  175. data/lib/rubocop/cop/style/endless_method.rb +91 -11
  176. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  177. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  178. data/lib/rubocop/cop/style/file_null.rb +2 -2
  179. data/lib/rubocop/cop/style/float_division.rb +28 -14
  180. data/lib/rubocop/cop/style/for.rb +44 -1
  181. data/lib/rubocop/cop/style/format_string.rb +28 -6
  182. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  183. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  184. data/lib/rubocop/cop/style/guard_clause.rb +13 -1
  185. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  186. data/lib/rubocop/cop/style/hash_lookup_method.rb +35 -9
  187. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  188. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  189. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  190. data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -20
  191. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  192. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  193. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  194. data/lib/rubocop/cop/style/it_block_parameter.rb +32 -3
  195. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  196. data/lib/rubocop/cop/style/lambda.rb +18 -0
  197. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  198. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  199. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  200. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +45 -10
  201. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -2
  202. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  203. data/lib/rubocop/cop/style/method_def_parentheses.rb +29 -3
  204. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  205. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  206. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  207. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  208. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  209. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  210. data/lib/rubocop/cop/style/mutable_constant.rb +28 -1
  211. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  212. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  213. data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
  214. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  215. data/lib/rubocop/cop/style/next.rb +24 -10
  216. data/lib/rubocop/cop/style/nil_comparison.rb +48 -8
  217. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  218. data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
  219. data/lib/rubocop/cop/style/not.rb +10 -3
  220. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  221. data/lib/rubocop/cop/style/numeric_predicate.rb +21 -5
  222. data/lib/rubocop/cop/style/one_line_conditional.rb +14 -3
  223. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  224. data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
  225. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  226. data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
  227. data/lib/rubocop/cop/style/redundant_begin.rb +20 -3
  228. data/lib/rubocop/cop/style/redundant_condition.rb +34 -17
  229. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -1
  230. data/lib/rubocop/cop/style/redundant_exception.rb +15 -3
  231. data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
  232. data/lib/rubocop/cop/style/redundant_freeze.rb +10 -1
  233. data/lib/rubocop/cop/style/redundant_parentheses.rb +23 -2
  234. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  235. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  236. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +25 -5
  237. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  238. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  239. data/lib/rubocop/cop/style/require_order.rb +9 -0
  240. data/lib/rubocop/cop/style/rescue_modifier.rb +5 -9
  241. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  242. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  243. data/lib/rubocop/cop/style/sample.rb +9 -8
  244. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  245. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  246. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  247. data/lib/rubocop/cop/style/single_line_methods.rb +16 -4
  248. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  249. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  250. data/lib/rubocop/cop/style/string_concatenation.rb +9 -3
  251. data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
  252. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  253. data/lib/rubocop/cop/style/symbol_proc.rb +23 -5
  254. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  255. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  256. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  257. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  258. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  259. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  260. data/lib/rubocop/cop/style.rb +0 -1
  261. data/lib/rubocop/cop/team.rb +10 -5
  262. data/lib/rubocop/cop/util.rb +1 -1
  263. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  264. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  265. data/lib/rubocop/cops_documentation_generator.rb +23 -2
  266. data/lib/rubocop/directive_comment.rb +37 -8
  267. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  268. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
  269. data/lib/rubocop/formatter/formatter_set.rb +6 -1
  270. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  271. data/lib/rubocop/formatter/html_formatter.rb +2 -2
  272. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  273. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  274. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  275. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  276. data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
  277. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  278. data/lib/rubocop/formatter.rb +1 -0
  279. data/lib/rubocop/lsp/runtime.rb +47 -11
  280. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  281. data/lib/rubocop/lsp.rb +5 -2
  282. data/lib/rubocop/magic_comment.rb +27 -0
  283. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  284. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  285. data/lib/rubocop/mcp/scope.rb +65 -0
  286. data/lib/rubocop/mcp/server.rb +184 -76
  287. data/lib/rubocop/mcp/source_files.rb +35 -0
  288. data/lib/rubocop/offense_limit.rb +30 -0
  289. data/lib/rubocop/options.rb +97 -4
  290. data/lib/rubocop/path_util.rb +1 -1
  291. data/lib/rubocop/plugin/configuration_integrator.rb +12 -5
  292. data/lib/rubocop/plugin/loader.rb +1 -1
  293. data/lib/rubocop/rake_task.rb +1 -1
  294. data/lib/rubocop/remote_config.rb +8 -5
  295. data/lib/rubocop/result_cache.rb +36 -18
  296. data/lib/rubocop/runner.rb +160 -49
  297. data/lib/rubocop/server/cache.rb +2 -2
  298. data/lib/rubocop/unified_diff.rb +257 -0
  299. data/lib/rubocop/util.rb +69 -0
  300. data/lib/rubocop/version.rb +1 -1
  301. data/lib/rubocop.rb +9 -1
  302. metadata +21 -6
  303. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -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
 
@@ -106,7 +110,7 @@ module RuboCop
106
110
  when :or
107
111
  find_target(node.lhs)
108
112
  when :match_with_lvasgn
109
- lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
113
+ lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring -- `match_with_lvasgn` has no named operand accessors
110
114
  if lhs.regexp_type?
111
115
  rhs
112
116
  elsif rhs.regexp_type?
@@ -171,7 +175,7 @@ module RuboCop
171
175
  return collect_conditions(node.lhs, target, conditions) &&
172
176
  collect_conditions(node.rhs, target, conditions)
173
177
  when :match_with_lvasgn
174
- lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
178
+ lhs, rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring -- `match_with_lvasgn` has no named operand accessors
175
179
  condition_from_binary_op(lhs, rhs, target)
176
180
  when :send
177
181
  condition_from_send_node(node, target)
@@ -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
@@ -258,7 +261,7 @@ module RuboCop
258
261
  def regexp_with_working_captures?(node)
259
262
  case node.type
260
263
  when :match_with_lvasgn
261
- lhs, _rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring
264
+ lhs, _rhs = *node # rubocop:disable InternalAffairs/NodeDestructuring -- `match_with_lvasgn` has no named operand accessors
262
265
  node.loc.selector.source == '=~' && regexp_with_named_captures?(lhs)
263
266
  when :send
264
267
  node.method?(:match) &&
@@ -65,6 +65,12 @@ 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` turns a class definition into a constant assignment in
69
+ # the same pass, which the compacted `class` keyword would then precede.
70
+ def self.autocorrect_incompatible_with
71
+ [Style::StructInheritance]
72
+ end
73
+
68
74
  def on_class(node)
69
75
  return if node.parent_class && style_for_classes != :nested
70
76
 
@@ -117,7 +123,10 @@ module RuboCop
117
123
  end
118
124
 
119
125
  def heuristic_namespace_keyword(node)
120
- class_definition = node.left_sibling&.each_node(:class)&.find do |class_node|
126
+ left_sibling = node.left_sibling
127
+ return 'module' unless left_sibling.is_a?(AST::Node)
128
+
129
+ class_definition = left_sibling.each_node(:class).find do |class_node|
121
130
  class_node.identifier == node.identifier.namespace
122
131
  end
123
132
 
@@ -194,33 +203,62 @@ module RuboCop
194
203
  "#{node.body.children.first.const_name}"
195
204
  end
196
205
 
197
- # rubocop:disable-next Metrics/AbcSize
198
206
  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)
207
+ range = if same_line?(body.loc.name, body.loc.end)
208
+ single_line_end_range(body)
209
+ else
210
+ multiline_end_range(body)
211
+ end
206
212
 
207
213
  corrector.remove(range)
208
214
  end
209
215
 
210
- def unindent(corrector, node)
211
- return unless node.body.children.last
216
+ def single_line_end_range(body)
217
+ raw = processed_source.raw_source
218
+ pos = body.loc.end.begin_pos
219
+ pos -= 1 while raw[pos - 1] == ' '
220
+ pos -= 1 if raw[pos - 1] == ';'
221
+ range_between(pos, body.loc.end.end_pos)
222
+ end
212
223
 
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)
224
+ def multiline_end_range(body)
225
+ remove_begin_pos = body.loc.end.begin_pos - leading_spaces(body).size
226
+ adjustment = processed_source.raw_source[remove_begin_pos] == ';' ? 0 : 1
227
+ range_between(remove_begin_pos, body.loc.end.end_pos + adjustment)
228
+ end
215
229
 
216
- column_delta = configured_indentation_width - spaces_size(last_child_leading_spaces)
217
- return if column_delta.zero?
230
+ def unindent(corrector, node)
231
+ column_delta = unindent_column_delta(node)
232
+ return unless column_delta&.negative?
233
+
234
+ range = unindentable_range(node.body)
235
+ return unless range
218
236
 
219
237
  AlignmentCorrector.correct(
220
- corrector, processed_source, node, column_delta, tab_indentation: true
238
+ corrector, processed_source, range, column_delta, tab_indentation: true
221
239
  )
222
240
  end
223
241
 
242
+ def unindent_column_delta(node)
243
+ body = node.body.children.last
244
+ return unless body
245
+
246
+ node_indentation = spaces_size(leading_spaces(node))
247
+ body_indentation = spaces_size(leading_spaces(body))
248
+
249
+ node_indentation + configured_indentation_width - body_indentation
250
+ end
251
+
252
+ def unindentable_range(body)
253
+ first_line = body.loc.name.line + 1
254
+ last_line = body.loc.end.line - 1
255
+ return if first_line > last_line
256
+
257
+ buffer = processed_source.buffer
258
+ range_between(buffer.line_range(first_line).begin_pos,
259
+ buffer.line_range(last_line).end_pos)
260
+ end
261
+
224
262
  def leading_spaces(node)
225
263
  node.source_range.source_line[/\A\s*/]
226
264
  end
@@ -72,6 +72,7 @@ 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|
77
78
  autocorrect_sclass(node, corrector)
@@ -112,6 +113,13 @@ module RuboCop
112
113
  end
113
114
  end
114
115
 
116
+ def no_code_after?(range)
117
+ trailing = processed_source.lines[range.last_line - 1][range.last_column..].to_s.strip
118
+ trailing = trailing.sub(/\A;+\s*/, '')
119
+
120
+ trailing.empty? || trailing.start_with?('#')
121
+ end
122
+
115
123
  def autocorrect_sclass(node, corrector)
116
124
  rewritten_defs = []
117
125
 
@@ -60,19 +60,18 @@ module RuboCop
60
60
  # end
61
61
  #
62
62
  class CombinableLoops < Base
63
+ include RangeHelp
63
64
  extend AutoCorrector
64
65
 
65
66
  MSG = 'Combine this loop with the previous loop.'
66
67
 
67
- # rubocop:disable-next Metrics/CyclomaticComplexity
68
68
  def on_block(node)
69
69
  return unless node.parent&.begin_type?
70
70
  return unless collection_looping_method?(node)
71
- return unless same_collection_looping_block?(node, node.left_sibling)
72
- return unless node.body && node.left_sibling.body
71
+ return unless combinable_looping_blocks?(node, node.left_sibling)
73
72
 
74
73
  add_offense(node) do |corrector|
75
- next unless node.arguments == node.left_sibling.arguments
74
+ next unless combinable_blocks?(node, node.left_sibling)
76
75
 
77
76
  combine_with_left_sibling(corrector, node)
78
77
  end
@@ -87,9 +86,7 @@ module RuboCop
87
86
  return unless node.body && node.left_sibling.body
88
87
 
89
88
  add_offense(node) do |corrector|
90
- # Combining loops with different iteration variables would leave the second
91
- # body referencing an undefined variable, so only autocorrect when they match.
92
- next unless node.variable == node.left_sibling.variable
89
+ next unless combinable_fors?(node, node.left_sibling)
93
90
 
94
91
  combine_with_left_sibling(corrector, node)
95
92
  end
@@ -110,25 +107,102 @@ module RuboCop
110
107
  sibling.send_node.arguments == node.send_node.arguments
111
108
  end
112
109
 
110
+ def combinable_looping_blocks?(node, sibling)
111
+ same_collection_looping_block?(node, sibling) && node.body && sibling.body
112
+ end
113
+
113
114
  def same_collection_looping_for?(node, sibling)
114
115
  sibling&.for_type? && node.collection == sibling.collection
115
116
  end
116
117
 
118
+ def combinable_blocks?(node, sibling)
119
+ return false unless node.arguments == sibling.arguments
120
+ # Numbered and `it` parameters are implicit, so `arguments` is empty for both.
121
+ return false unless node.argument_list == sibling.argument_list
122
+ return false if directive_after_opening?(node)
123
+
124
+ !node.body.type?(:rescue, :ensure) && !sibling.body.type?(:rescue, :ensure)
125
+ end
126
+
127
+ # Combining loops with different iteration variables would leave the second
128
+ # body referencing an undefined variable, so only autocorrect when they match.
129
+ def combinable_fors?(node, sibling)
130
+ node.variable == sibling.variable && !directive_after_opening?(node)
131
+ end
132
+
133
+ # The opening is removed, so a directive after it would end up on a line of
134
+ # its own, where it covers the rest of the file instead of a single line.
135
+ def directive_after_opening?(node)
136
+ opening_line = loop_opening(node).line
137
+ return false if opening_line == node.body.first_line
138
+
139
+ comment = processed_source.comment_at_line(opening_line)
140
+ comment && DirectiveComment.new(comment).start_with_marker?
141
+ end
142
+
143
+ # The block arguments or `{`/`do` of a block, or the `do` or collection of a `for` loop.
144
+ def loop_opening(node)
145
+ if node.for_type?
146
+ node.loc.begin || node.collection.source_range
147
+ else
148
+ (node.block_type? && node.arguments.source_range) || node.loc.begin
149
+ end
150
+ end
151
+
117
152
  def combine_with_left_sibling(corrector, node)
118
- corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
119
- corrector.remove(node.source_range.begin.join(node.body.source_range.begin))
153
+ corrector.remove(closing_with_preceding_space(node.left_sibling, node))
154
+ corrector.remove(opening_with_following_space(node))
120
155
 
121
156
  correct_end_of_block(corrector, node)
122
157
  end
123
158
 
159
+ # Comments and heredoc bodies before the closing delimiter are kept. When
160
+ # the next loop starts on the delimiter's line, the line break before the
161
+ # delimiter is kept too, so that a comment doesn't swallow the next loop.
162
+ def closing_with_preceding_space(loop, next_loop)
163
+ separate_lines = !same_line?(loop.loc.end, next_loop)
164
+
165
+ range_with_surrounding_space(
166
+ loop.loc.end, side: :left, newlines: separate_lines, whitespace: separate_lines
167
+ )
168
+ end
169
+
170
+ # Comments between the opening and the body are kept.
171
+ def opening_with_following_space(node)
172
+ opening = node.source_range.begin.join(loop_opening(node))
173
+
174
+ range_with_surrounding_space(opening, side: :right, whitespace: true)
175
+ end
176
+
124
177
  def correct_end_of_block(corrector, node)
125
178
  return unless node.left_sibling.respond_to?(:braces?)
126
- return if node.right_sibling&.any_block_type?
179
+ return if combined_with_right_sibling?(node)
127
180
 
128
- end_of_block = node.left_sibling.braces? ? '}' : ' end'
181
+ end_of_block = first_combined_block(node).braces? ? '}' : ' end'
129
182
  corrector.remove(node.loc.end)
130
183
  corrector.insert_before(node.source_range.end, end_of_block)
131
184
  end
185
+
186
+ def combined_with_right_sibling?(node)
187
+ sibling = node.right_sibling
188
+ return false unless sibling&.any_block_type?
189
+ # A disabled loop isn't corrected, so it doesn't get merged into this one.
190
+ return false unless enabled_lines?(sibling.source_range)
191
+
192
+ same_collection_looping_block?(sibling, node) && sibling.body &&
193
+ combinable_blocks?(sibling, node)
194
+ end
195
+
196
+ def first_combined_block(node)
197
+ first = node.left_sibling
198
+
199
+ while (previous = first.left_sibling) && enabled_lines?(first.source_range) &&
200
+ combinable_looping_blocks?(first, previous) && combinable_blocks?(first, previous)
201
+ first = previous
202
+ end
203
+
204
+ first
205
+ end
132
206
  end
133
207
  end
134
208
  end
@@ -23,6 +23,7 @@ module RuboCop
23
23
  # list.push(qux, quux, corge)
24
24
  #
25
25
  class ConcatArrayLiterals < Base
26
+ include RangeHelp
26
27
  extend AutoCorrector
27
28
 
28
29
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
@@ -30,6 +31,14 @@ module RuboCop
30
31
  'Use `push` with elements as arguments without array brackets instead of `%<current>s`.'
31
32
  RESTRICT_ON_SEND = %i[concat].freeze
32
33
 
34
+ # `Style/MethodCallWithArgsParentheses` removing the parentheses of the `push(...)`
35
+ # this cop writes in the same pass drops the line continuation a multiline call needs.
36
+ # `Style/TrailingCommaInArguments` adds a comma after the array literal this cop
37
+ # replaces with its elements in the same pass.
38
+ def self.autocorrect_incompatible_with
39
+ [Style::MethodCallWithArgsParentheses, Style::TrailingCommaInArguments]
40
+ end
41
+
33
42
  # rubocop:disable-next Metrics
34
43
  def on_send(node)
35
44
  return if node.arguments.empty?
@@ -54,17 +63,13 @@ module RuboCop
54
63
  if use_percent_literal
55
64
  next unless prefer
56
65
 
57
- corrector.replace(offense, prefer)
66
+ rebuild_call(corrector, offense, prefer)
58
67
  elsif node.arguments.any? { |argument| argument.children.empty? }
59
68
  # In-place bracket removal would leave dangling commas (e.g.
60
69
  # `concat([], [b])` -> `push(, b)`), so rebuild the call instead.
61
- corrector.replace(offense, preferred_method(node))
70
+ rebuild_call(corrector, offense, preferred_method(node))
62
71
  else
63
- corrector.replace(node.loc.selector, 'push')
64
- node.arguments.each do |argument|
65
- corrector.remove(argument.loc.begin)
66
- corrector.remove(argument.loc.end)
67
- end
72
+ remove_brackets(corrector, node)
68
73
  end
69
74
  end
70
75
  end
@@ -94,6 +99,40 @@ module RuboCop
94
99
  arg.children.all? { |child| child.type?(:str, :sym) }
95
100
  end
96
101
  end
102
+
103
+ def rebuild_call(corrector, offense, replacement)
104
+ # The comments inside the call would be lost.
105
+ return if comment_in?(offense)
106
+
107
+ corrector.replace(offense, replacement)
108
+ end
109
+
110
+ def comment_in?(range)
111
+ processed_source.each_comment_in_lines(range.line..range.last_line).any? do |comment|
112
+ range.contains?(comment.source_range)
113
+ end
114
+ end
115
+
116
+ def remove_brackets(corrector, node)
117
+ closing_ranges = node.arguments.map { |argument| closing_bracket_range(node, argument) }
118
+ # A comment or a heredoc body there can't be moved out of the way.
119
+ return unless closing_ranges.all? { |range| range.source.match?(/\A[\s,]*\]\z/) }
120
+
121
+ corrector.replace(node.loc.selector, 'push')
122
+ node.arguments.zip(closing_ranges) do |argument, closing_range|
123
+ corrector.remove(argument.loc.begin)
124
+ corrector.remove(closing_range)
125
+ end
126
+ end
127
+
128
+ # Whatever sits between the last element and the `]` of an array that isn't the
129
+ # last argument would end up before the following comma (a newline before it is a
130
+ # syntax error), so it's removed along with the `]`.
131
+ def closing_bracket_range(node, argument)
132
+ return argument.loc.end if argument.equal?(node.last_argument)
133
+
134
+ range_between(argument.children.last.source_range.end_pos, argument.loc.end.end_pos)
135
+ end
97
136
  end
98
137
  end
99
138
  end
@@ -223,6 +223,10 @@ module RuboCop
223
223
  }
224
224
  PATTERN
225
225
 
226
+ def self.autocorrect_incompatible_with
227
+ [Style::MethodCallWithArgsParentheses]
228
+ end
229
+
226
230
  ASSIGNMENT_TYPES.each do |type|
227
231
  define_method :"on_#{type}" do |node|
228
232
  return if part_of_ignored_node?(node)
@@ -82,12 +82,16 @@ module RuboCop
82
82
  end
83
83
  end
84
84
 
85
- # rubocop:disable-next Metrics/AbcSize
85
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity
86
86
  def visibility_declaration?(node)
87
87
  node.parent.each_child_node(:send).any? do |child|
88
88
  next false unless (arguments = visibility_declaration_for(child))
89
89
 
90
- arguments = arguments.first.children.first.to_a if arguments.first&.splat_type?
90
+ if (splat = arguments.first)&.splat_type?
91
+ next true unless splat.children.first.array_type?
92
+
93
+ arguments = splat.children.first.children
94
+ end
91
95
  constant_values = arguments.map do |argument|
92
96
  # `respond_to?(:value)` is too broad: `int`/`float` nodes respond to it
93
97
  # but their value is a `Numeric`, which has no `to_sym` (e.g.
@@ -10,6 +10,10 @@ module RuboCop
10
10
  # Autocorrection is unsafe because it will change the inheritance
11
11
  # tree (e.g. return value of `Module#ancestors`) of the constant.
12
12
  #
13
+ # It is also unsafe because constants that the class body resolves through its
14
+ # ancestors (e.g. one provided by an included module) fall out of scope inside
15
+ # the block.
16
+ #
13
17
  # @example
14
18
  # # bad
15
19
  # class Person < Data.define(:first_name, :last_name)
@@ -42,6 +46,7 @@ module RuboCop
42
46
 
43
47
  def on_class(node)
44
48
  return unless data_define?(node.parent_class)
49
+ return if defines_constants?(node.body)
45
50
 
46
51
  add_offense(node.parent_class) do |corrector|
47
52
  corrector.remove(range_with_surrounding_space(node.loc.keyword, newlines: false))
@@ -59,6 +64,15 @@ module RuboCop
59
64
 
60
65
  private
61
66
 
67
+ # Constants, including nested classes and modules, are scoped to the class when they
68
+ # are defined in a class body, but leak into the enclosing namespace once moved into a
69
+ # `Data.define` block, so such a class cannot be rewritten as an assignment.
70
+ def defines_constants?(class_body)
71
+ return false unless class_body
72
+
73
+ class_body.each_node(:casgn, :class, :module).any?
74
+ end
75
+
62
76
  def correct_parent(parent, corrector)
63
77
  if parent.block_type?
64
78
  # Convert a brace block to `do`, so the class's own `end` closes it