rubocop 1.81.7 → 1.92.1

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 (621) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +626 -111
  5. data/config/obsoletion.yml +39 -1
  6. data/exe/rubocop +1 -1
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cached_data.rb +50 -5
  9. data/lib/rubocop/changed_files.rb +134 -0
  10. data/lib/rubocop/cli/command/auto_generate_config.rb +34 -2
  11. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  12. data/lib/rubocop/cli/command/execute_runner.rb +54 -3
  13. data/lib/rubocop/cli/command/explain.rb +34 -0
  14. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  15. data/lib/rubocop/cli/command/lsp.rb +1 -1
  16. data/lib/rubocop/cli/command/mcp.rb +19 -0
  17. data/lib/rubocop/cli/command/show_cops.rb +8 -2
  18. data/lib/rubocop/cli/command/show_docs_url.rb +8 -10
  19. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  20. data/lib/rubocop/cli.rb +41 -14
  21. data/lib/rubocop/comment_config/directive_range.rb +34 -0
  22. data/lib/rubocop/comment_config/disable_next.rb +143 -0
  23. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  24. data/lib/rubocop/comment_config.rb +97 -44
  25. data/lib/rubocop/config.rb +100 -15
  26. data/lib/rubocop/config_finder.rb +1 -1
  27. data/lib/rubocop/config_loader.rb +49 -27
  28. data/lib/rubocop/config_loader_resolver.rb +95 -20
  29. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  30. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  31. data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
  32. data/lib/rubocop/config_obsoletion.rb +1 -1
  33. data/lib/rubocop/config_store.rb +2 -2
  34. data/lib/rubocop/config_validator.rb +69 -7
  35. data/lib/rubocop/cop/autocorrect_logic.rb +24 -6
  36. data/lib/rubocop/cop/badge.rb +10 -2
  37. data/lib/rubocop/cop/base.rb +128 -35
  38. data/lib/rubocop/cop/bundler/gem_comment.rb +5 -4
  39. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  40. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -10
  41. data/lib/rubocop/cop/bundler.rb +19 -0
  42. data/lib/rubocop/cop/commissioner.rb +13 -11
  43. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  44. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  45. data/lib/rubocop/cop/corrector.rb +4 -4
  46. data/lib/rubocop/cop/correctors/alignment_corrector.rb +87 -9
  47. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  48. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
  49. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  50. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +31 -4
  51. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +35 -5
  52. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
  53. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +70 -2
  54. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  55. data/lib/rubocop/cop/correctors.rb +28 -0
  56. data/lib/rubocop/cop/documentation.rb +3 -4
  57. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  58. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  59. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  60. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +14 -4
  61. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -8
  62. data/lib/rubocop/cop/gemspec/require_mfa.rb +9 -6
  63. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  64. data/lib/rubocop/cop/gemspec.rb +22 -0
  65. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  66. data/lib/rubocop/cop/generator.rb +5 -1
  67. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  68. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  69. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  70. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  71. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +12 -16
  72. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  73. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  74. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  75. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  76. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  77. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  78. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  79. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  80. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  81. data/lib/rubocop/cop/layout/argument_alignment.rb +9 -3
  82. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  83. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  84. data/lib/rubocop/cop/layout/block_alignment.rb +84 -4
  85. data/lib/rubocop/cop/layout/case_indentation.rb +28 -10
  86. data/lib/rubocop/cop/layout/class_structure.rb +156 -41
  87. data/lib/rubocop/cop/layout/comment_indentation.rb +17 -7
  88. data/lib/rubocop/cop/layout/condition_position.rb +13 -3
  89. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  90. data/lib/rubocop/cop/layout/dot_position.rb +26 -16
  91. data/lib/rubocop/cop/layout/else_alignment.rb +2 -14
  92. data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
  93. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +25 -7
  94. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  95. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
  96. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +15 -2
  97. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  98. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
  99. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -0
  100. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +21 -5
  101. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  102. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  103. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  104. data/lib/rubocop/cop/layout/end_alignment.rb +23 -15
  105. data/lib/rubocop/cop/layout/extra_spacing.rb +109 -3
  106. data/lib/rubocop/cop/layout/first_argument_indentation.rb +81 -2
  107. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +18 -5
  108. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +41 -0
  109. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +36 -23
  110. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  111. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  112. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  113. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  114. data/lib/rubocop/cop/layout/hash_alignment.rb +16 -11
  115. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +8 -4
  116. data/lib/rubocop/cop/layout/heredoc_indentation.rb +48 -3
  117. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  118. data/lib/rubocop/cop/layout/indentation_style.rb +5 -2
  119. data/lib/rubocop/cop/layout/indentation_width.rb +158 -12
  120. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  121. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +10 -4
  122. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  123. data/lib/rubocop/cop/layout/line_length.rb +265 -29
  124. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +69 -57
  125. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +8 -2
  126. data/lib/rubocop/cop/layout/multiline_block_layout.rb +10 -1
  127. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +65 -56
  128. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +7 -1
  129. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +268 -38
  130. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  131. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  132. data/lib/rubocop/cop/layout/redundant_line_break.rb +102 -2
  133. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +19 -5
  134. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  135. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  136. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  137. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  138. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
  139. data/lib/rubocop/cop/layout/space_around_keyword.rb +14 -2
  140. data/lib/rubocop/cop/layout/space_around_operators.rb +18 -3
  141. data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
  142. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  143. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  144. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +44 -34
  145. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
  146. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  147. data/lib/rubocop/cop/layout.rb +114 -0
  148. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  149. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -17
  150. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +72 -1
  151. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  152. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +3 -11
  153. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  154. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  155. data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
  156. data/lib/rubocop/cop/lint/assignment_in_condition.rb +15 -1
  157. data/lib/rubocop/cop/lint/circular_argument_reference.rb +45 -3
  158. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  159. data/lib/rubocop/cop/lint/constant_reassignment.rb +94 -15
  160. data/lib/rubocop/cop/lint/constant_resolution.rb +31 -6
  161. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +165 -15
  162. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  163. data/lib/rubocop/cop/lint/debugger.rb +13 -2
  164. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  165. data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
  166. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  167. data/lib/rubocop/cop/lint/duplicate_methods.rb +405 -28
  168. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  169. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  170. data/lib/rubocop/cop/lint/empty_conditional_body.rb +9 -2
  171. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  172. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  173. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  174. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  175. data/lib/rubocop/cop/lint/float_comparison.rb +2 -1
  176. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  177. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  178. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  179. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  180. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  181. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -6
  182. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  183. data/lib/rubocop/cop/lint/literal_as_condition.rb +36 -9
  184. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  185. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +75 -37
  186. data/lib/rubocop/cop/lint/loop.rb +24 -2
  187. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
  188. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +46 -18
  189. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  190. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  191. data/lib/rubocop/cop/lint/name_typo.rb +270 -0
  192. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  193. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  194. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
  195. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  196. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  197. data/lib/rubocop/cop/lint/number_conversion.rb +22 -11
  198. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  199. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +10 -0
  200. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  201. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
  202. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +20 -14
  203. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  204. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  205. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +160 -39
  206. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +53 -19
  207. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +28 -6
  208. data/lib/rubocop/cop/lint/redundant_require_statement.rb +5 -4
  209. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +37 -14
  210. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +13 -4
  211. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +11 -5
  212. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -3
  213. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  214. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  215. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  216. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  217. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  218. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  219. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  220. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +24 -0
  221. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +56 -6
  222. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  223. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  224. data/lib/rubocop/cop/lint/self_assignment.rb +33 -2
  225. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  226. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  227. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  228. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  229. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  230. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  231. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +13 -2
  232. data/lib/rubocop/cop/lint/symbol_conversion.rb +22 -4
  233. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  234. data/lib/rubocop/cop/lint/to_enum_arguments.rb +72 -4
  235. data/lib/rubocop/cop/lint/to_json.rb +20 -17
  236. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  237. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  238. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +54 -11
  239. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  240. data/lib/rubocop/cop/lint/unreachable_code.rb +15 -9
  241. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  242. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  243. data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
  244. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  245. data/lib/rubocop/cop/lint/useless_assignment.rb +61 -27
  246. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +40 -4
  247. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  248. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  249. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
  250. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  251. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  252. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +10 -4
  253. data/lib/rubocop/cop/lint/useless_setter_call.rb +11 -5
  254. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  255. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +94 -14
  256. data/lib/rubocop/cop/lint/void.rb +59 -22
  257. data/lib/rubocop/cop/lint.rb +179 -0
  258. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  259. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  260. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  261. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  262. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  263. data/lib/rubocop/cop/metrics/method_length.rb +2 -2
  264. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  265. data/lib/rubocop/cop/metrics/perceived_complexity.rb +37 -7
  266. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  267. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  268. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  269. data/lib/rubocop/cop/metrics.rb +31 -0
  270. data/lib/rubocop/cop/migration/department_name.rb +11 -5
  271. data/lib/rubocop/cop/migration.rb +13 -0
  272. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  273. data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
  274. data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
  275. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  276. data/lib/rubocop/cop/mixin/check_line_breakable.rb +47 -25
  277. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +12 -8
  278. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  279. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  280. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  281. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  282. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  283. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
  284. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  285. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  286. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
  287. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +58 -12
  288. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +37 -10
  289. data/lib/rubocop/cop/mixin/hash_subset.rb +9 -2
  290. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  291. data/lib/rubocop/cop/mixin/hash_transform_method.rb +14 -60
  292. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  293. data/lib/rubocop/cop/mixin/line_length_help.rb +28 -3
  294. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  295. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  296. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  297. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  298. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +10 -1
  299. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  300. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  301. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +79 -8
  302. data/lib/rubocop/cop/mixin/project_index_help.rb +203 -0
  303. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  304. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +181 -0
  305. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  306. data/lib/rubocop/cop/mixin/statement_modifier.rb +69 -12
  307. data/lib/rubocop/cop/mixin/trailing_comma.rb +7 -6
  308. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  309. data/lib/rubocop/cop/mixin.rb +93 -0
  310. data/lib/rubocop/cop/naming/accessor_method_name.rb +8 -0
  311. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +17 -1
  312. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  313. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  314. data/lib/rubocop/cop/naming/file_name.rb +4 -3
  315. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  316. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  317. data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
  318. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +12 -5
  319. data/lib/rubocop/cop/naming/method_name.rb +6 -3
  320. data/lib/rubocop/cop/naming/predicate_method.rb +13 -2
  321. data/lib/rubocop/cop/naming/predicate_prefix.rb +30 -17
  322. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +12 -7
  323. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  324. data/lib/rubocop/cop/naming.rb +31 -0
  325. data/lib/rubocop/cop/offense.rb +108 -9
  326. data/lib/rubocop/cop/registry.rb +232 -66
  327. data/lib/rubocop/cop/security/eval.rb +15 -2
  328. data/lib/rubocop/cop/security/io_methods.rb +2 -2
  329. data/lib/rubocop/cop/security/json_load.rb +16 -4
  330. data/lib/rubocop/cop/security/marshal_load.rb +1 -1
  331. data/lib/rubocop/cop/security.rb +19 -0
  332. data/lib/rubocop/cop/severity.rb +12 -0
  333. data/lib/rubocop/cop/style/access_modifier_declarations.rb +55 -8
  334. data/lib/rubocop/cop/style/accessor_grouping.rb +16 -5
  335. data/lib/rubocop/cop/style/alias.rb +33 -8
  336. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  337. data/lib/rubocop/cop/style/and_or.rb +8 -3
  338. data/lib/rubocop/cop/style/arguments_forwarding.rb +61 -17
  339. data/lib/rubocop/cop/style/array_first_last.rb +13 -3
  340. data/lib/rubocop/cop/style/array_intersect.rb +28 -5
  341. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
  342. data/lib/rubocop/cop/style/array_join.rb +4 -2
  343. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  344. data/lib/rubocop/cop/style/attr.rb +8 -6
  345. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  346. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  347. data/lib/rubocop/cop/style/bitwise_predicate.rb +7 -6
  348. data/lib/rubocop/cop/style/block_delimiters.rb +74 -38
  349. data/lib/rubocop/cop/style/case_equality.rb +29 -15
  350. data/lib/rubocop/cop/style/case_like_if.rb +11 -10
  351. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  352. data/lib/rubocop/cop/style/class_and_module_children.rb +151 -35
  353. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  354. data/lib/rubocop/cop/style/class_methods_definitions.rb +26 -5
  355. data/lib/rubocop/cop/style/collection_compact.rb +37 -17
  356. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  357. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  358. data/lib/rubocop/cop/style/combinable_loops.rb +91 -9
  359. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  360. data/lib/rubocop/cop/style/concat_array_literals.rb +65 -9
  361. data/lib/rubocop/cop/style/conditional_assignment.rb +34 -18
  362. data/lib/rubocop/cop/style/constant_visibility.rb +13 -7
  363. data/lib/rubocop/cop/style/copyright.rb +27 -12
  364. data/lib/rubocop/cop/style/data_inheritance.rb +18 -0
  365. data/lib/rubocop/cop/style/date_time.rb +4 -4
  366. data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
  367. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  368. data/lib/rubocop/cop/style/dir_empty.rb +4 -0
  369. data/lib/rubocop/cop/style/directive_scope.rb +304 -0
  370. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +184 -20
  371. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  372. data/lib/rubocop/cop/style/documentation.rb +53 -6
  373. data/lib/rubocop/cop/style/documentation_method.rb +8 -12
  374. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  375. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  376. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  377. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  378. data/lib/rubocop/cop/style/empty_case_condition.rb +13 -4
  379. data/lib/rubocop/cop/style/empty_class_definition.rb +126 -0
  380. data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
  381. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  382. data/lib/rubocop/cop/style/empty_literal.rb +8 -3
  383. data/lib/rubocop/cop/style/empty_method.rb +15 -9
  384. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +74 -22
  385. data/lib/rubocop/cop/style/encoding.rb +7 -1
  386. data/lib/rubocop/cop/style/end_block.rb +3 -1
  387. data/lib/rubocop/cop/style/endless_method.rb +100 -21
  388. data/lib/rubocop/cop/style/env_home.rb +4 -0
  389. data/lib/rubocop/cop/style/even_odd.rb +11 -1
  390. data/lib/rubocop/cop/style/exact_regexp_match.rb +52 -8
  391. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  392. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  393. data/lib/rubocop/cop/style/file_null.rb +6 -4
  394. data/lib/rubocop/cop/style/file_open.rb +84 -0
  395. data/lib/rubocop/cop/style/file_write.rb +21 -16
  396. data/lib/rubocop/cop/style/float_division.rb +28 -14
  397. data/lib/rubocop/cop/style/for.rb +60 -1
  398. data/lib/rubocop/cop/style/format_string.rb +44 -9
  399. data/lib/rubocop/cop/style/format_string_token.rb +50 -7
  400. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  401. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  402. data/lib/rubocop/cop/style/guard_clause.rb +57 -40
  403. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +31 -9
  404. data/lib/rubocop/cop/style/hash_conversion.rb +39 -11
  405. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  406. data/lib/rubocop/cop/style/hash_lookup_method.rb +128 -0
  407. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  408. data/lib/rubocop/cop/style/hash_syntax.rb +19 -5
  409. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  410. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  411. data/lib/rubocop/cop/style/identical_conditional_branches.rb +38 -10
  412. data/lib/rubocop/cop/style/if_inside_else.rb +36 -8
  413. data/lib/rubocop/cop/style/if_unless_modifier.rb +80 -76
  414. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +17 -14
  415. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  416. data/lib/rubocop/cop/style/if_with_semicolon.rb +61 -8
  417. data/lib/rubocop/cop/style/inline_comment.rb +5 -2
  418. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  419. data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
  420. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  421. data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
  422. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
  423. data/lib/rubocop/cop/style/keyword_parameters_order.rb +10 -4
  424. data/lib/rubocop/cop/style/lambda.rb +45 -2
  425. data/lib/rubocop/cop/style/lambda_call.rb +24 -8
  426. data/lib/rubocop/cop/style/magic_comment_format.rb +4 -4
  427. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
  428. data/lib/rubocop/cop/style/map_into_array.rb +2 -3
  429. data/lib/rubocop/cop/style/map_join.rb +123 -0
  430. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  431. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +102 -20
  432. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  433. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +42 -6
  434. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +36 -8
  435. data/lib/rubocop/cop/style/method_def_parentheses.rb +36 -8
  436. data/lib/rubocop/cop/style/min_max_comparison.rb +4 -1
  437. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  438. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +40 -7
  439. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  440. data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
  441. data/lib/rubocop/cop/style/module_function.rb +4 -2
  442. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  443. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  444. data/lib/rubocop/cop/style/multiline_if_then.rb +10 -4
  445. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  446. data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
  447. data/lib/rubocop/cop/style/multiline_method_signature.rb +14 -13
  448. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  449. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  450. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  451. data/lib/rubocop/cop/style/mutable_constant.rb +158 -14
  452. data/lib/rubocop/cop/style/negated_if_else_condition.rb +3 -3
  453. data/lib/rubocop/cop/style/negative_array_index.rb +228 -0
  454. data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
  455. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  456. data/lib/rubocop/cop/style/next.rb +24 -10
  457. data/lib/rubocop/cop/style/nil_comparison.rb +50 -12
  458. data/lib/rubocop/cop/style/nil_lambda.rb +9 -1
  459. data/lib/rubocop/cop/style/non_nil_check.rb +21 -11
  460. data/lib/rubocop/cop/style/not.rb +12 -3
  461. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  462. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  463. data/lib/rubocop/cop/style/numeric_predicate.rb +22 -6
  464. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  465. data/lib/rubocop/cop/style/one_line_conditional.rb +18 -6
  466. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  467. data/lib/rubocop/cop/style/operator_method_call.rb +10 -2
  468. data/lib/rubocop/cop/style/option_hash.rb +1 -1
  469. data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
  470. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -7
  471. data/lib/rubocop/cop/style/parentheses_around_condition.rb +7 -2
  472. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  473. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +18 -3
  474. data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
  475. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  476. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  477. data/lib/rubocop/cop/style/proc.rb +3 -2
  478. data/lib/rubocop/cop/style/raise_args.rb +20 -10
  479. data/lib/rubocop/cop/style/reduce_to_hash.rb +216 -0
  480. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  481. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  482. data/lib/rubocop/cop/style/redundant_assignment.rb +12 -2
  483. data/lib/rubocop/cop/style/redundant_begin.rb +25 -7
  484. data/lib/rubocop/cop/style/redundant_condition.rb +41 -23
  485. data/lib/rubocop/cop/style/redundant_constant_base.rb +42 -6
  486. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
  487. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +17 -3
  488. data/lib/rubocop/cop/style/redundant_each.rb +4 -5
  489. data/lib/rubocop/cop/style/redundant_exception.rb +21 -3
  490. data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
  491. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  492. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  493. data/lib/rubocop/cop/style/redundant_format.rb +43 -7
  494. data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
  495. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
  496. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +49 -10
  497. data/lib/rubocop/cop/style/redundant_line_continuation.rb +82 -115
  498. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  499. data/lib/rubocop/cop/style/redundant_parentheses.rb +105 -83
  500. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  501. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +13 -2
  502. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  503. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +27 -7
  504. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -4
  505. data/lib/rubocop/cop/style/redundant_return.rb +4 -3
  506. data/lib/rubocop/cop/style/redundant_self.rb +11 -2
  507. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  508. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +27 -4
  509. data/lib/rubocop/cop/style/redundant_sort.rb +45 -11
  510. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  511. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +133 -0
  512. data/lib/rubocop/cop/style/regexp_literal.rb +106 -28
  513. data/lib/rubocop/cop/style/require_order.rb +25 -8
  514. data/lib/rubocop/cop/style/rescue_modifier.rb +15 -14
  515. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  516. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  517. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  518. data/lib/rubocop/cop/style/safe_navigation.rb +18 -9
  519. data/lib/rubocop/cop/style/sample.rb +19 -13
  520. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  521. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  522. data/lib/rubocop/cop/style/select_by_regexp.rb +52 -23
  523. data/lib/rubocop/cop/style/self_assignment.rb +13 -16
  524. data/lib/rubocop/cop/style/semicolon.rb +39 -13
  525. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  526. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  527. data/lib/rubocop/cop/style/single_line_do_end_block.rb +19 -7
  528. data/lib/rubocop/cop/style/single_line_methods.rb +24 -5
  529. data/lib/rubocop/cop/style/sole_nested_conditional.rb +25 -7
  530. data/lib/rubocop/cop/style/special_global_vars.rb +7 -2
  531. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +31 -5
  532. data/lib/rubocop/cop/style/static_class.rb +21 -0
  533. data/lib/rubocop/cop/style/string_concatenation.rb +25 -6
  534. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  535. data/lib/rubocop/cop/style/struct_inheritance.rb +36 -1
  536. data/lib/rubocop/cop/style/super_arguments.rb +6 -4
  537. data/lib/rubocop/cop/style/symbol_proc.rb +32 -14
  538. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  539. data/lib/rubocop/cop/style/ternary_parentheses.rb +34 -1
  540. data/lib/rubocop/cop/style/time_now.rb +41 -0
  541. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  542. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  543. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  544. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +35 -32
  545. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  546. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  547. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  548. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  549. data/lib/rubocop/cop/style/word_array.rb +1 -0
  550. data/lib/rubocop/cop/style/yoda_condition.rb +26 -12
  551. data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
  552. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  553. data/lib/rubocop/cop/style.rb +311 -0
  554. data/lib/rubocop/cop/team.rb +171 -48
  555. data/lib/rubocop/cop/util.rb +5 -8
  556. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  557. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  558. data/lib/rubocop/cop/variable_force/variable.rb +33 -9
  559. data/lib/rubocop/cops_documentation_generator.rb +27 -9
  560. data/lib/rubocop/directive_comment.rb +116 -7
  561. data/lib/rubocop/feature_loader.rb +0 -6
  562. data/lib/rubocop/file_patterns.rb +9 -1
  563. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  564. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  565. data/lib/rubocop/formatter/disabled_config_formatter.rb +39 -10
  566. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  567. data/lib/rubocop/formatter/formatter_set.rb +7 -2
  568. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  569. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  570. data/lib/rubocop/formatter/json_formatter.rb +82 -7
  571. data/lib/rubocop/formatter/junit_formatter.rb +27 -10
  572. data/lib/rubocop/formatter/offense_count_formatter.rb +4 -3
  573. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  574. data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
  575. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -5
  576. data/lib/rubocop/formatter/tap_formatter.rb +8 -3
  577. data/lib/rubocop/formatter/worst_offenders_formatter.rb +5 -4
  578. data/lib/rubocop/formatter.rb +23 -21
  579. data/lib/rubocop/lockfile.rb +18 -0
  580. data/lib/rubocop/lsp/diagnostic.rb +22 -42
  581. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  582. data/lib/rubocop/lsp/routes.rb +130 -83
  583. data/lib/rubocop/lsp/runtime.rb +64 -15
  584. data/lib/rubocop/lsp/server.rb +6 -2
  585. data/lib/rubocop/lsp/stdin_runner.rb +35 -2
  586. data/lib/rubocop/lsp.rb +5 -2
  587. data/lib/rubocop/magic_comment.rb +47 -0
  588. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  589. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  590. data/lib/rubocop/mcp/scope.rb +65 -0
  591. data/lib/rubocop/mcp/server.rb +307 -0
  592. data/lib/rubocop/mcp/source_files.rb +35 -0
  593. data/lib/rubocop/offense_limit.rb +30 -0
  594. data/lib/rubocop/options.rb +141 -13
  595. data/lib/rubocop/path_util.rb +16 -5
  596. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  597. data/lib/rubocop/plugin/configuration_integrator.rb +13 -3
  598. data/lib/rubocop/plugin/loader.rb +2 -2
  599. data/lib/rubocop/project_index_loader.rb +82 -0
  600. data/lib/rubocop/rake_task.rb +2 -3
  601. data/lib/rubocop/remote_config.rb +18 -13
  602. data/lib/rubocop/result_cache.rb +102 -59
  603. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  604. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  605. data/lib/rubocop/rspec/shared_contexts.rb +53 -5
  606. data/lib/rubocop/rspec/support.rb +2 -1
  607. data/lib/rubocop/runner.rb +362 -106
  608. data/lib/rubocop/server/cache.rb +21 -40
  609. data/lib/rubocop/server/cli.rb +2 -4
  610. data/lib/rubocop/server/core.rb +8 -0
  611. data/lib/rubocop/server.rb +14 -0
  612. data/lib/rubocop/target_finder.rb +18 -8
  613. data/lib/rubocop/target_ruby.rb +42 -22
  614. data/lib/rubocop/todo_audit.rb +111 -0
  615. data/lib/rubocop/unified_diff.rb +257 -0
  616. data/lib/rubocop/util.rb +69 -0
  617. data/lib/rubocop/version.rb +23 -7
  618. data/lib/rubocop.rb +34 -701
  619. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  620. metadata +80 -14
  621. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -6,6 +6,14 @@ module RuboCop
6
6
  # Checks whether some constant value isn't a
7
7
  # mutable literal (e.g. array or hash).
8
8
  #
9
+ # When the `Recursive` option is enabled, mutable literals nested inside
10
+ # arrays and hashes are also frozen, so an offense on the outermost
11
+ # unfrozen literal will autocorrect every nested mutable literal as well.
12
+ # When the outer literal already has `.freeze` appended, the cop descends
13
+ # into it and reports each outermost unfrozen literal underneath. The
14
+ # option is disabled by default to preserve existing behavior; opt in to
15
+ # get strict nested freezing.
16
+ #
9
17
  # Strict mode can be used to freeze all constants, rather than
10
18
  # just literals.
11
19
  # Strict mode is considered an experimental feature. It has not been
@@ -49,6 +57,17 @@ module RuboCop
49
57
  # CONST = Something.new
50
58
  #
51
59
  #
60
+ # @example Recursive: false (default)
61
+ # # good - only the outer container needs to be frozen
62
+ # CONST = [{ a: [], b: 'foo' }].freeze
63
+ #
64
+ # @example Recursive: true
65
+ # # bad - nested mutable literals must be frozen too
66
+ # CONST = [{ a: [], b: 'foo' }].freeze
67
+ #
68
+ # # good
69
+ # CONST = [{ a: [].freeze, b: 'foo'.freeze }.freeze].freeze
70
+ #
52
71
  # @example EnforcedStyle: strict
53
72
  # # bad
54
73
  # CONST = Something.new
@@ -70,6 +89,9 @@ module RuboCop
70
89
  # end
71
90
  # end.freeze
72
91
  #
92
+ # # good - `Data.define` declares an immutable value type
93
+ # CONST = Data.define(:foo, :bar)
94
+ #
73
95
  # @example
74
96
  # # Magic comment - shareable_constant_value: literal
75
97
  #
@@ -124,8 +146,15 @@ module RuboCop
124
146
 
125
147
  MSG = 'Freeze mutable objects assigned to constants.'
126
148
 
149
+ # With `Recursive: true`, `.freeze` is appended to a nested literal. When the literal
150
+ # is the last element and spans multiple lines, the trailing comma lands between the literal
151
+ # and the `.freeze` appended in the same pass.
152
+ def self.autocorrect_incompatible_with
153
+ [Style::HashAsLastArrayItem, Style::TrailingCommaInArrayLiteral, Style::TrailingCommaInHashLiteral]
154
+ end
155
+
127
156
  def on_casgn(node)
128
- if node.expression.nil? # This is only the case for `CONST += ...` or similarg66
157
+ if node.expression.nil? # This is only the case for `CONST += ...` or similar
129
158
  parent = node.parent
130
159
  return unless parent.or_asgn_type? # We only care about `CONST ||= ...`
131
160
 
@@ -138,31 +167,55 @@ module RuboCop
138
167
  private
139
168
 
140
169
  def on_assignment(value)
141
- if style == :strict
142
- strict_check(value)
170
+ nodes = mutable_nodes(value) do |node|
171
+ if style == :strict
172
+ strict_check(node)
173
+ else
174
+ literal_check(node)
175
+ end
176
+ end
177
+
178
+ nodes.each do |node|
179
+ add_offense(node) { |corrector| autocorrect(corrector, node) }
180
+ end
181
+ end
182
+
183
+ def mutable_nodes(value, &block)
184
+ if recursive? && explicitly_frozen_literal?(value)
185
+ literal_children(value.receiver).flat_map { |c| mutable_nodes(c, &block) }
143
186
  else
144
- check(value)
187
+ node_offending = yield(value)
188
+
189
+ if node_offending
190
+ [value]
191
+ else
192
+ []
193
+ end
145
194
  end
146
195
  end
147
196
 
148
197
  def strict_check(value)
198
+ return if value.assignment?
149
199
  return if immutable_literal?(value)
150
200
  return if operation_produces_immutable_object?(value)
151
201
  return if frozen_string_literal?(value)
152
202
  return if shareable_constant_value?(value)
203
+ return if parenthesized_immutable_value?(value)
153
204
 
154
- add_offense(value) { |corrector| autocorrect(corrector, value) }
205
+ true
155
206
  end
156
207
 
157
- def check(value)
158
- range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
159
- return unless mutable_literal?(value) ||
160
- (target_ruby_version <= 2.7 && range_enclosed_in_parentheses)
161
-
208
+ def literal_check(value)
209
+ return unless mutable_or_unfrozen_range?(value)
162
210
  return if frozen_string_literal?(value)
163
211
  return if shareable_constant_value?(value)
164
212
 
165
- add_offense(value) { |corrector| autocorrect(corrector, value) }
213
+ true
214
+ end
215
+
216
+ def mutable_or_unfrozen_range?(value)
217
+ mutable_literal?(value) ||
218
+ (target_ruby_version <= 2.7 && range_enclosed_in_parentheses?(value))
166
219
  end
167
220
 
168
221
  def autocorrect(corrector, node)
@@ -171,13 +224,84 @@ module RuboCop
171
224
  splat_value = splat_value(node)
172
225
  if splat_value
173
226
  correct_splat_expansion(corrector, expr, splat_value)
174
- elsif node.array_type? && !node.bracketed?
227
+ corrector.insert_after(expr, '.freeze')
228
+ return
229
+ end
230
+
231
+ return expand_omitted_hash_value(corrector, node.parent) if omitted_hash_value?(node)
232
+
233
+ wrap_for_freeze(corrector, node, expr)
234
+ corrector.insert_after(expr, '.freeze')
235
+
236
+ freeze_nested_literals(corrector, node) if recursive?
237
+ end
238
+
239
+ def omitted_hash_value?(node)
240
+ node.parent&.pair_type? && node.parent.value_omission?
241
+ end
242
+
243
+ def expand_omitted_hash_value(corrector, pair)
244
+ key = pair.key.value
245
+
246
+ corrector.replace(pair, "#{key}: #{key}.freeze")
247
+ end
248
+
249
+ def wrap_for_freeze(corrector, node, expr)
250
+ if node.array_type? && !node.bracketed?
175
251
  corrector.wrap(expr, '[', ']')
252
+ elsif node.hash_type? && !node.braces?
253
+ corrector.wrap(expr, '{ ', ' }')
176
254
  elsif requires_parentheses?(node)
177
255
  corrector.wrap(expr, '(', ')')
178
256
  end
257
+ end
179
258
 
180
- corrector.insert_after(expr, '.freeze')
259
+ # Recursively freezes every nested mutable literal inside an array or
260
+ # hash literal. Already-frozen subtrees are not re-frozen, but their
261
+ # children are still inspected for unfrozen literals deeper down.
262
+ def freeze_nested_literals(corrector, node)
263
+ literal_children(node).each do |child|
264
+ if explicitly_frozen_literal?(child)
265
+ freeze_nested_literals(corrector, child.receiver)
266
+ elsif freezable_nested_literal?(child)
267
+ autocorrect(corrector, child)
268
+ end
269
+ end
270
+ end
271
+
272
+ def freezable_nested_literal?(node)
273
+ return false if frozen_string_literal?(node)
274
+ return false if shareable_constant_value?(node)
275
+
276
+ mutable_literal?(node)
277
+ end
278
+
279
+ # Returns the child literals of an array or hash node that may
280
+ # themselves need freezing. For hashes, both keys and values are
281
+ # included. Percent-literal arrays (e.g. `%w(a b)`) are skipped because
282
+ # `.freeze` cannot be appended to their contents. Splat elements are
283
+ # skipped because the splatted object is not stored in the array.
284
+ def literal_children(node)
285
+ case node.type
286
+ when :array
287
+ return [] if node.percent_literal?
288
+
289
+ node.children.reject(&:splat_type?)
290
+ when :hash
291
+ node.children.flat_map { |child| child.pair_type? ? child.children : [] }
292
+ else
293
+ []
294
+ end
295
+ end
296
+
297
+ def explicitly_frozen_literal?(node)
298
+ return false unless node.send_type? && node.method?(:freeze)
299
+
300
+ node.receiver && mutable_literal?(node.receiver)
301
+ end
302
+
303
+ def recursive?
304
+ cop_config.fetch('Recursive', false)
181
305
  end
182
306
 
183
307
  def mutable_literal?(value)
@@ -196,12 +320,30 @@ module RuboCop
196
320
  recent_shareable_value? node
197
321
  end
198
322
 
323
+ def parenthesized_immutable_value?(value)
324
+ return false if !value.begin_type? || !value.children.one?
325
+
326
+ inner = value.children.first
327
+
328
+ !inner.assignment? && !strict_check(inner)
329
+ end
330
+
199
331
  def frozen_regexp_or_range_literals?(node)
200
332
  target_ruby_version >= 3.0 && node.type?(:regexp, :range)
201
333
  end
202
334
 
203
335
  def requires_parentheses?(node)
204
- node.range_type? || (node.send_type? && node.loc.dot.nil?)
336
+ if node.range_type? || node.operator_keyword? || unparenthesized_method_call?(node)
337
+ return true
338
+ end
339
+
340
+ node.if_type? && node.ternary?
341
+ end
342
+
343
+ def unparenthesized_method_call?(node)
344
+ return false unless node.call_type?
345
+
346
+ node.loc.dot.nil? || (node.arguments.any? && !node.parenthesized?)
205
347
  end
206
348
 
207
349
  def correct_splat_expansion(corrector, expr, splat_value)
@@ -225,6 +367,8 @@ module RuboCop
225
367
  (const _ _)
226
368
  (send (const {nil? cbase} :Struct) :new ...)
227
369
  (block (send (const {nil? cbase} :Struct) :new ...) ...)
370
+ (send (const {nil? cbase} :Data) :define ...)
371
+ (block (send (const {nil? cbase} :Data) :define ...) ...)
228
372
  (send _ :freeze)
229
373
  (send {float int} {:+ :- :* :** :/ :% :<<} _)
230
374
  (send _ {:+ :- :* :** :/ :%} {float int})
@@ -46,7 +46,7 @@ module RuboCop
46
46
  @corrected_nodes = nil
47
47
  end
48
48
 
49
- # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity
49
+ # rubocop:disable-next Metrics/AbcSize,Metrics/CyclomaticComplexity
50
50
  def on_if(node)
51
51
  return unless if_else?(node)
52
52
  return unless (condition = unwrap_begin_nodes(node.condition))
@@ -64,7 +64,6 @@ module RuboCop
64
64
  end
65
65
  end
66
66
  end
67
- # rubocop:enable Metrics/AbcSize,Metrics/CyclomaticComplexity
68
67
 
69
68
  private
70
69
 
@@ -119,7 +118,8 @@ module RuboCop
119
118
  if node.ternary?
120
119
  node.if_branch
121
120
  else
122
- range_between(node.condition.source_range.end_pos, node.loc.else.begin_pos)
121
+ if_start = node.loc.begin || node.condition.source_range
122
+ range_between(if_start.end_pos, node.loc.else.begin_pos)
123
123
  end
124
124
  end
125
125
 
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies usages of `arr[arr.length - n]`, `arr[arr.size - n]`, or
7
+ # `arr[arr.count - n]` and suggests to change them to use `arr[-n]` instead.
8
+ # Also handles range patterns like `arr[0..(arr.length - n)]`.
9
+ #
10
+ # The cop recognizes preserving methods (`sort`, `reverse`, `shuffle`, `rotate`)
11
+ # and their combinations, allowing safe replacement when the receiver matches.
12
+ # It works with variables, instance variables, class variables, and constants.
13
+ #
14
+ # @safety
15
+ # This cop is unsafe because the receiver may not be an Array. For a Hash,
16
+ # `hash[hash.size - 1]` looks up the key `hash.size - 1`, which `hash[-1]`
17
+ # does not. Even for an Array, `arr[arr.length - n]` wraps around to an
18
+ # element from the end when `n` exceeds the length, while `arr[-n]`
19
+ # returns `nil`.
20
+ #
21
+ # @example
22
+ # # bad
23
+ # arr[arr.count - 2]
24
+ # arr[0..(arr.length - 2)]
25
+ # arr[0...(arr.length - 4)]
26
+ # arr.sort[arr.reverse.length - 2]
27
+ # arr.sort.reverse[arr.sort.size - 2]
28
+ #
29
+ # # good
30
+ # arr[-2]
31
+ # arr[0..-2]
32
+ # arr[0...-4]
33
+ # arr.sort[-2]
34
+ # arr.sort.reverse[-2]
35
+ #
36
+ class NegativeArrayIndex < Base
37
+ extend AutoCorrector
38
+ include RangeHelp
39
+
40
+ MSG = 'Use `%<receiver>s[-%<index>s]` instead of `%<current>s`.'
41
+ MSG_RANGE = 'Use `%<receiver>s[%<start>s%<range_op>s-%<index>s]` instead of `%<current>s`.'
42
+ RESTRICT_ON_SEND = %i[[]].freeze
43
+
44
+ LENGTH_METHODS = %i[length size count].freeze
45
+
46
+ PRESERVING_METHODS = %i[sort reverse shuffle rotate].freeze
47
+
48
+ # @!method length_subtraction?(node)
49
+ def_node_matcher :length_subtraction?, <<~PATTERN
50
+ (send
51
+ (send $_ {:length :size :count}) :-
52
+ (int $_))
53
+ PATTERN
54
+
55
+ def on_send(node)
56
+ return if node.arguments.empty?
57
+
58
+ index_arg = node.first_argument
59
+ range_node = extract_range_from_begin(index_arg)
60
+ if range_with_length_subtraction?(range_node, node.receiver)
61
+ receiver = node.receiver.source
62
+ return handle_range_pattern(receiver, range_node, index_arg)
63
+ end
64
+
65
+ handle_simple_index_pattern(node, index_arg)
66
+ end
67
+
68
+ alias on_csend on_send
69
+
70
+ private
71
+
72
+ def handle_simple_index_pattern(node, index_arg)
73
+ length_receiver, negative_index = length_subtraction?(index_arg)
74
+
75
+ return unless negative_index&.positive?
76
+ return unless receivers_match?(length_receiver, node.receiver)
77
+
78
+ add_offense_for_subtraction(node, index_arg, negative_index)
79
+ end
80
+
81
+ def extract_range_from_begin(node)
82
+ node.begin_type? ? node.children.first : node
83
+ end
84
+
85
+ def extract_inner_end(node)
86
+ node.children.size == 1 ? node.children.first : node
87
+ end
88
+
89
+ def add_offense_for_subtraction(node, index_arg, negative_index)
90
+ receiver = node.receiver.source
91
+ offense_range = index_arg.source_range
92
+ current = "#{receiver}[#{index_arg.source}]"
93
+
94
+ message = format(MSG, receiver: receiver, index: negative_index, current: current)
95
+
96
+ add_offense(offense_range, message: message) do |corrector|
97
+ corrector.replace(offense_range, "-#{negative_index}")
98
+ end
99
+ end
100
+
101
+ # rubocop:disable-next Metrics/CyclomaticComplexity
102
+ def range_with_length_subtraction?(range_node, array_receiver)
103
+ return false unless range_node&.range_type?
104
+
105
+ range_end = range_node.end
106
+ range_start = range_node.begin
107
+ return false unless range_end && range_start
108
+
109
+ return false unless preserving_method?(range_start)
110
+
111
+ inner_end = extract_inner_end(range_end)
112
+ length_receiver, negative_index = length_subtraction?(inner_end)
113
+
114
+ return false unless negative_index&.positive?
115
+
116
+ receivers_match_strict?(length_receiver, array_receiver)
117
+ end
118
+
119
+ def handle_range_pattern(receiver, range_node, index_arg)
120
+ range_end = range_node.end
121
+ inner_end = extract_inner_end(range_end)
122
+ _length_receiver, negative_index = length_subtraction?(inner_end)
123
+
124
+ message, replacement = build_range_offense_data(
125
+ receiver, range_node, range_end, inner_end, negative_index, index_arg
126
+ )
127
+
128
+ add_offense(range_end, message: message) do |corrector|
129
+ corrector.replace(index_arg, replacement)
130
+ end
131
+ end
132
+
133
+ # rubocop:disable-next Metrics/ParameterLists
134
+ def build_range_offense_data(receiver, range_node, range_end, inner_end, negative_index,
135
+ index_arg)
136
+ range_op = range_node.erange_type? ? '...' : '..'
137
+ range_start = range_node.begin.source
138
+
139
+ range_without_parens =
140
+ build_range_without_parens(range_start, range_op, range_end, inner_end)
141
+ current_source = build_current_source(receiver, range_without_parens, index_arg)
142
+ start, index = format_range_message_parts(range_start, negative_index, index_arg)
143
+
144
+ message = build_message_for_range(receiver, start, range_op, index, current_source)
145
+ replacement = build_replacement_string(range_start, range_op, negative_index, index_arg)
146
+
147
+ [message, replacement]
148
+ end
149
+
150
+ def format_range_message_parts(range_start, negative_index, index_arg)
151
+ has_parentheses = index_arg.begin_type?
152
+ start = has_parentheses ? "(#{range_start}" : range_start
153
+ index = has_parentheses ? "#{negative_index})" : negative_index
154
+
155
+ [start, index]
156
+ end
157
+
158
+ def build_message_for_range(receiver, start, range_op, index, current)
159
+ format(
160
+ MSG_RANGE,
161
+ receiver: receiver, start: start, range_op: range_op, index: index, current: current
162
+ )
163
+ end
164
+
165
+ def build_replacement_string(range_start, range_op, negative_index, index_arg)
166
+ has_parentheses = index_arg.begin_type?
167
+
168
+ if has_parentheses
169
+ "(#{range_start}#{range_op}-#{negative_index})"
170
+ else
171
+ "#{range_start}#{range_op}-#{negative_index}"
172
+ end
173
+ end
174
+
175
+ def build_current_source(receiver, range_without_parens, index_arg)
176
+ has_parentheses = index_arg.begin_type?
177
+
178
+ if has_parentheses
179
+ "#{receiver}[(#{range_without_parens})]"
180
+ else
181
+ "#{receiver}[#{range_without_parens}]"
182
+ end
183
+ end
184
+
185
+ def build_range_without_parens(range_start, range_op, range_end, inner_end)
186
+ end_expression = range_end.begin_type? ? range_end.source : inner_end.source
187
+
188
+ "#{range_start}#{range_op}#{end_expression}"
189
+ end
190
+
191
+ def receivers_match?(length_receiver, array_receiver)
192
+ return array_receiver.self_type? unless length_receiver
193
+
194
+ unless preserving_method?(array_receiver) && preserving_method?(length_receiver)
195
+ return false
196
+ end
197
+ return true if length_receiver.source == array_receiver.source
198
+ return false unless (base_receiver = extract_base_receiver(array_receiver))
199
+
200
+ base_receiver.source == (extract_base_receiver(length_receiver) || length_receiver).source
201
+ end
202
+
203
+ def receivers_match_strict?(length_receiver, array_receiver)
204
+ preserving_method?(array_receiver) &&
205
+ length_receiver.source == array_receiver.source
206
+ end
207
+
208
+ def extract_base_receiver(node)
209
+ receiver = node.receiver
210
+
211
+ return nil unless receiver
212
+ return receiver unless receiver.receiver
213
+
214
+ extract_base_receiver(receiver)
215
+ end
216
+
217
+ def preserving_method?(node)
218
+ return true if node.receiver.nil?
219
+
220
+ method_name = node.method_name
221
+ return false unless PRESERVING_METHODS.include?(method_name)
222
+
223
+ preserving_method?(node.receiver)
224
+ end
225
+ end
226
+ end
227
+ end
228
+ end
@@ -19,6 +19,19 @@ module RuboCop
19
19
 
20
20
  MSG = 'Avoid using nested modifiers.'
21
21
 
22
+ # Operands that would otherwise bind differently next to `&&`/`||`, or
23
+ # not parse at all.
24
+ # @!method low_precedence?(node)
25
+ def_node_matcher :low_precedence?, <<~PATTERN
26
+ {
27
+ [{and or} semantic_operator?]
28
+ [send prefix_not?]
29
+ [if ternary?]
30
+ assignment?
31
+ [{csend yield super} arguments? !parenthesized? !operator_method?]
32
+ }
33
+ PATTERN
34
+
22
35
  def on_while(node)
23
36
  check(node)
24
37
  end
@@ -63,25 +76,31 @@ module RuboCop
63
76
  end
64
77
 
65
78
  def left_hand_operand(node, operator)
66
- expr = node.condition.source
67
- expr = "(#{expr})" if node.condition.or_type? && operator == '&&'
79
+ expr = operand_source(node.condition)
80
+ if (node.condition.or_type? && operator == '&&') || low_precedence?(node.condition)
81
+ expr = "(#{expr})"
82
+ end
68
83
  expr
69
84
  end
70
85
 
71
86
  def right_hand_operand(node, left_hand_keyword)
72
87
  condition = node.condition
88
+ negated = left_hand_keyword != node.keyword
73
89
 
74
- expr = if condition.send_type? && !condition.arguments.empty? &&
75
- !condition.operator_method?
76
- add_parentheses_to_method_arguments(condition)
77
- else
78
- condition.source
79
- end
80
- expr = "(#{expr})" if requires_parens?(condition)
81
- expr = "!#{expr}" unless left_hand_keyword == node.keyword
90
+ expr = operand_source(condition)
91
+ expr = "(#{expr})" if requires_parens?(condition, negated)
92
+ expr = "!#{expr}" if negated
82
93
  expr
83
94
  end
84
95
 
96
+ def operand_source(condition)
97
+ if condition.send_type? && !condition.arguments.empty? && !condition.operator_method?
98
+ add_parentheses_to_method_arguments(condition)
99
+ else
100
+ condition.source
101
+ end
102
+ end
103
+
85
104
  def add_parentheses_to_method_arguments(send_node)
86
105
  expr = +''
87
106
  expr << "#{send_node.receiver.source}." if send_node.receiver
@@ -91,8 +110,13 @@ module RuboCop
91
110
  expr
92
111
  end
93
112
 
94
- def requires_parens?(node)
95
- node.or_type? || !(RuboCop::AST::Node::COMPARISON_OPERATORS & node.children).empty?
113
+ def requires_parens?(node, negated)
114
+ # A negated `&&`/`||` operand must be wrapped so the `!` applies to the
115
+ # whole expression (e.g. `!(a && b)`, not `!a && b`).
116
+ (negated && node.operator_keyword?) ||
117
+ node.or_type? ||
118
+ low_precedence?(node) ||
119
+ !(RuboCop::AST::Node::COMPARISON_OPERATORS & node.children).empty?
96
120
  end
97
121
  end
98
122
  end
@@ -26,7 +26,7 @@ module RuboCop
26
26
 
27
27
  node.each_descendant(:if).select(&:ternary?).each do |nested_ternary|
28
28
  add_offense(nested_ternary) do |corrector|
29
- next if part_of_ignored_node?(node)
29
+ next if part_of_ignored_node?(node) || contains_heredoc?(node)
30
30
 
31
31
  autocorrect(corrector, node)
32
32
  ignore_node(node)
@@ -36,21 +36,39 @@ module RuboCop
36
36
 
37
37
  private
38
38
 
39
+ def contains_heredoc?(node)
40
+ node.each_descendant(:any_str).any?(&:heredoc?)
41
+ end
42
+
39
43
  def autocorrect(corrector, if_node)
40
44
  replace_loc_and_whitespace(corrector, if_node.loc.question, "\n")
41
45
  replace_loc_and_whitespace(corrector, if_node.loc.colon, "\nelse\n")
42
- corrector.replace(if_node.if_branch, remove_parentheses(if_node.if_branch.source))
43
- corrector.wrap(if_node, 'if ', "\nend")
44
- end
46
+ remove_parentheses(corrector, if_node.if_branch)
45
47
 
46
- def remove_parentheses(source)
47
- if source.start_with?('(') && source.end_with?(')')
48
- source.delete_prefix('(').delete_suffix(')')
48
+ if modifier_position?(if_node)
49
+ corrector.wrap(if_node, '(if ', "\nend)")
49
50
  else
50
- source
51
+ corrector.wrap(if_node, 'if ', "\nend")
51
52
  end
52
53
  end
53
54
 
55
+ def remove_parentheses(corrector, node)
56
+ return unless node.begin_type? && node.parenthesized_call?
57
+
58
+ corrector.remove(node.loc.begin)
59
+ corrector.remove(node.loc.end)
60
+ end
61
+
62
+ # An `if` right after `return`, as the operand of `not` or `defined?`, or as
63
+ # the first argument of a call without parentheses would be parsed as a modifier.
64
+ def modifier_position?(node)
65
+ return false unless (parent = node.parent)
66
+ return true if parent.type?(:return, :break, :next)
67
+ return false if !parent.type?(:call, :super, :yield, :defined?) || parent.parenthesized?
68
+
69
+ parent.prefix_not? || parent.first_argument.equal?(node)
70
+ end
71
+
54
72
  def replace_loc_and_whitespace(corrector, range, replacement)
55
73
  corrector.replace(
56
74
  range_with_surrounding_space(range: range, whitespace: true),