rubocop 1.80.2 → 1.86.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 (310) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +170 -19
  5. data/config/obsoletion.yml +9 -0
  6. data/lib/rubocop/cache_config.rb +29 -0
  7. data/lib/rubocop/cli/command/auto_generate_config.rb +3 -3
  8. data/lib/rubocop/cli/command/lsp.rb +1 -1
  9. data/lib/rubocop/cli/command/mcp.rb +19 -0
  10. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  11. data/lib/rubocop/cli/command/show_docs_url.rb +1 -1
  12. data/lib/rubocop/cli.rb +28 -6
  13. data/lib/rubocop/comment_config.rb +62 -17
  14. data/lib/rubocop/config.rb +14 -10
  15. data/lib/rubocop/config_finder.rb +1 -1
  16. data/lib/rubocop/config_loader.rb +20 -21
  17. data/lib/rubocop/config_loader_resolver.rb +9 -7
  18. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  19. data/lib/rubocop/config_store.rb +6 -1
  20. data/lib/rubocop/config_validator.rb +1 -1
  21. data/lib/rubocop/cop/autocorrect_logic.rb +8 -4
  22. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  23. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  24. data/lib/rubocop/cop/correctors/alignment_corrector.rb +22 -6
  25. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  26. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  27. data/lib/rubocop/cop/documentation.rb +2 -3
  28. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  29. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  30. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
  31. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  32. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  33. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  34. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  35. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  36. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  37. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  38. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  39. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  40. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  41. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  42. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  43. data/lib/rubocop/cop/layout/class_structure.rb +13 -6
  44. data/lib/rubocop/cop/layout/dot_position.rb +2 -2
  45. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +12 -2
  46. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
  47. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  48. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  49. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  50. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  51. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  52. data/lib/rubocop/cop/layout/end_alignment.rb +8 -1
  53. data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
  54. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  55. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  56. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  57. data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
  58. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  59. data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
  60. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  61. data/lib/rubocop/cop/layout/indentation_width.rb +111 -7
  62. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  63. data/lib/rubocop/cop/layout/line_length.rb +26 -9
  64. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  65. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  66. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  67. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  68. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +204 -39
  69. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -4
  70. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  71. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  72. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  73. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  74. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  75. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  76. data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
  77. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  78. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  79. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  80. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  81. data/lib/rubocop/cop/lint/constant_reassignment.rb +59 -9
  82. data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
  83. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  84. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  85. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  86. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  87. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  88. data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
  89. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  90. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  91. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  92. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  93. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  94. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  95. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  96. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  97. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
  98. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  99. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +16 -6
  100. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  101. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  102. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
  103. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  104. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  105. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
  106. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  107. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
  108. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -2
  109. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  110. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
  111. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  112. data/lib/rubocop/cop/lint/self_assignment.rb +10 -2
  113. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  114. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  115. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  116. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  117. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
  118. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  119. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
  120. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  121. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  122. data/lib/rubocop/cop/lint/useless_assignment.rb +45 -17
  123. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  124. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  125. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  126. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +24 -9
  127. data/lib/rubocop/cop/lint/void.rb +39 -12
  128. data/lib/rubocop/cop/message_annotator.rb +1 -1
  129. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  130. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  131. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  132. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  133. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
  134. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  135. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
  136. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  137. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  138. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  139. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  140. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  141. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  142. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  143. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  144. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  145. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  146. data/lib/rubocop/cop/naming/method_name.rb +4 -2
  147. data/lib/rubocop/cop/naming/predicate_method.rb +27 -4
  148. data/lib/rubocop/cop/naming/predicate_prefix.rb +11 -11
  149. data/lib/rubocop/cop/offense.rb +9 -1
  150. data/lib/rubocop/cop/registry.rb +20 -13
  151. data/lib/rubocop/cop/security/eval.rb +15 -2
  152. data/lib/rubocop/cop/security/json_load.rb +33 -11
  153. data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
  154. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  155. data/lib/rubocop/cop/style/alias.rb +4 -1
  156. data/lib/rubocop/cop/style/and_or.rb +1 -0
  157. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  158. data/lib/rubocop/cop/style/array_intersect.rb +2 -2
  159. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  160. data/lib/rubocop/cop/style/array_join.rb +4 -2
  161. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  162. data/lib/rubocop/cop/style/attr.rb +5 -2
  163. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  164. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  165. data/lib/rubocop/cop/style/block_delimiters.rb +27 -34
  166. data/lib/rubocop/cop/style/case_equality.rb +15 -13
  167. data/lib/rubocop/cop/style/class_and_module_children.rb +11 -2
  168. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  169. data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
  170. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
  171. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -18
  172. data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
  173. data/lib/rubocop/cop/style/copyright.rb +1 -1
  174. data/lib/rubocop/cop/style/documentation.rb +6 -6
  175. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  176. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  177. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  178. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  179. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  180. data/lib/rubocop/cop/style/empty_class_definition.rb +119 -0
  181. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  182. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  183. data/lib/rubocop/cop/style/encoding.rb +7 -1
  184. data/lib/rubocop/cop/style/end_block.rb +3 -1
  185. data/lib/rubocop/cop/style/endless_method.rb +23 -5
  186. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  187. data/lib/rubocop/cop/style/file_open.rb +84 -0
  188. data/lib/rubocop/cop/style/float_division.rb +15 -1
  189. data/lib/rubocop/cop/style/for.rb +3 -0
  190. data/lib/rubocop/cop/style/format_string_token.rb +49 -5
  191. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  192. data/lib/rubocop/cop/style/guard_clause.rb +27 -22
  193. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
  194. data/lib/rubocop/cop/style/hash_lookup_method.rb +101 -0
  195. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  196. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  197. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  198. data/lib/rubocop/cop/style/if_inside_else.rb +1 -5
  199. data/lib/rubocop/cop/style/if_unless_modifier.rb +57 -17
  200. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  201. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  202. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  203. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  204. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  205. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  206. data/lib/rubocop/cop/style/magic_comment_format.rb +2 -2
  207. data/lib/rubocop/cop/style/map_join.rb +123 -0
  208. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  209. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  210. data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
  211. data/lib/rubocop/cop/style/module_member_existence_check.rb +107 -0
  212. data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
  213. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  214. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  215. data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
  216. data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
  217. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  218. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  219. data/lib/rubocop/cop/style/not.rb +2 -0
  220. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  221. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  222. data/lib/rubocop/cop/style/one_line_conditional.rb +21 -12
  223. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  224. data/lib/rubocop/cop/style/parallel_assignment.rb +6 -2
  225. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  226. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  227. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  228. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  229. data/lib/rubocop/cop/style/proc.rb +3 -2
  230. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  231. data/lib/rubocop/cop/style/reduce_to_hash.rb +184 -0
  232. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  233. data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
  234. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  235. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  236. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  237. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  238. data/lib/rubocop/cop/style/redundant_format.rb +26 -5
  239. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  240. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  241. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  242. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  243. data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -22
  244. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  245. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  246. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  247. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  248. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  249. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  250. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  251. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  252. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  253. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  254. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  255. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  256. data/lib/rubocop/cop/style/semicolon.rb +25 -7
  257. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  258. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  259. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  260. data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
  261. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  262. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  263. data/lib/rubocop/cop/style/symbol_proc.rb +4 -3
  264. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  265. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  266. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  267. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  268. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  269. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  270. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  271. data/lib/rubocop/cop/team.rb +4 -4
  272. data/lib/rubocop/cop/util.rb +2 -3
  273. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  274. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  275. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  276. data/lib/rubocop/directive_comment.rb +48 -4
  277. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  278. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  279. data/lib/rubocop/formatter/formatter_set.rb +2 -2
  280. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  281. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  282. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  283. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  284. data/lib/rubocop/formatter.rb +22 -21
  285. data/lib/rubocop/lsp/diagnostic.rb +18 -33
  286. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  287. data/lib/rubocop/lsp/routes.rb +12 -5
  288. data/lib/rubocop/lsp/runtime.rb +13 -3
  289. data/lib/rubocop/lsp/stdin_runner.rb +8 -17
  290. data/lib/rubocop/magic_comment.rb +20 -0
  291. data/lib/rubocop/mcp/server.rb +200 -0
  292. data/lib/rubocop/options.rb +10 -1
  293. data/lib/rubocop/path_util.rb +14 -2
  294. data/lib/rubocop/plugin/loader.rb +1 -1
  295. data/lib/rubocop/rake_task.rb +1 -1
  296. data/lib/rubocop/remote_config.rb +10 -8
  297. data/lib/rubocop/result_cache.rb +60 -37
  298. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  299. data/lib/rubocop/rspec/shared_contexts.rb +18 -5
  300. data/lib/rubocop/rspec/support.rb +2 -1
  301. data/lib/rubocop/runner.rb +12 -3
  302. data/lib/rubocop/server/cache.rb +6 -29
  303. data/lib/rubocop/server/core.rb +2 -0
  304. data/lib/rubocop/target_finder.rb +1 -1
  305. data/lib/rubocop/target_ruby.rb +31 -14
  306. data/lib/rubocop/version.rb +2 -2
  307. data/lib/rubocop.rb +20 -0
  308. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  309. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  310. metadata +33 -9
@@ -16,7 +16,7 @@ module RuboCop
16
16
  'root of the project. RuboCop will use this path to determine which ' \
17
17
  'cops are enabled (via eg. Include/Exclude), and so that certain cops ' \
18
18
  'like Naming/FileName can be checked.'
19
- EXITING_OPTIONS = %i[version verbose_version show_cops show_docs_url lsp].freeze
19
+ EXITING_OPTIONS = %i[version verbose_version show_cops show_docs_url lsp mcp].freeze
20
20
  DEFAULT_MAXIMUM_EXCLUSION_ITEMS = 15
21
21
 
22
22
  def initialize
@@ -57,6 +57,7 @@ module RuboCop
57
57
  add_check_options(opts)
58
58
  add_cache_options(opts)
59
59
  add_lsp_option(opts)
60
+ add_mcp_option(opts)
60
61
  add_server_options(opts)
61
62
  add_output_options(opts)
62
63
  add_autocorrection_options(opts)
@@ -215,6 +216,12 @@ module RuboCop
215
216
  end
216
217
  end
217
218
 
219
+ def add_mcp_option(opts)
220
+ section(opts, 'MCP Option') do
221
+ option(opts, '--mcp')
222
+ end
223
+ end
224
+
218
225
  def add_server_options(opts)
219
226
  section(opts, 'Server Options') do
220
227
  option(opts, '--[no-]server')
@@ -651,6 +658,8 @@ module RuboCop
651
658
  server_status: 'Show server status.',
652
659
  no_detach: 'Run the server process in the foreground.',
653
660
  lsp: 'Start a language server listening on STDIN.',
661
+ mcp: ['Start an MCP (Model Context Protocol) server that',
662
+ 'communicates over stdio.'],
654
663
  raise_cop_error: ['Raise cop-related errors with cause and location.',
655
664
  'This is used to prevent cops from failing silently.',
656
665
  'Default is false.'],
@@ -10,7 +10,19 @@ module RuboCop
10
10
 
11
11
  module_function
12
12
 
13
- def relative_path(path, base_dir = Dir.pwd)
13
+ # Returns the current working directory, cached for the duration of a run.
14
+ # Dir.pwd is a syscall; caching it avoids repeated overhead since RuboCop
15
+ # never changes the working directory during a run.
16
+ def pwd
17
+ @pwd ||= Dir.pwd
18
+ end
19
+
20
+ # Reset the cached pwd. Only needed in tests that use Dir.chdir.
21
+ def reset_pwd
22
+ @pwd = nil
23
+ end
24
+
25
+ def relative_path(path, base_dir = PathUtil.pwd)
14
26
  PathUtil.relative_paths_cache[base_dir][path] ||=
15
27
  # Optimization for the common case where path begins with the base
16
28
  # dir. Just cut off the first part.
@@ -41,7 +53,7 @@ module RuboCop
41
53
  path.uri.to_s
42
54
  else
43
55
  # Ideally, we calculate this relative to the project root.
44
- base_dir = Dir.pwd
56
+ base_dir = PathUtil.pwd
45
57
 
46
58
  if path.start_with? base_dir
47
59
  relative_path(path, base_dir)
@@ -84,7 +84,7 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def require_plugin(require_path)
87
- FeatureLoader.load(config_directory_path: Dir.pwd, feature: require_path)
87
+ FeatureLoader.load(config_directory_path: PathUtil.pwd, feature: require_path)
88
88
  end
89
89
 
90
90
  def constantize_plugin_from_gemspec_metadata(plugin_name)
@@ -75,7 +75,7 @@ module RuboCop
75
75
  def setup_subtasks(name, *args, &task_block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
76
76
  namespace(name) do
77
77
  # rubocop:todo Naming/InclusiveLanguage
78
- task(:auto_correct, *args) do
78
+ task(:auto_correct, *args) do |_, task_args|
79
79
  require 'rainbow'
80
80
  warn Rainbow(
81
81
  'rubocop:auto_correct task is deprecated; ' \
@@ -11,13 +11,13 @@ module RuboCop
11
11
 
12
12
  CACHE_LIFETIME = 24 * 60 * 60
13
13
 
14
- def initialize(url, base_dir)
14
+ def initialize(url, cache_root)
15
15
  begin
16
16
  @uri = URI.parse(url)
17
17
  rescue URI::InvalidURIError
18
18
  raise ConfigNotFoundError, "Failed to resolve configuration: '#{url}' is not a valid URI"
19
19
  end
20
- @base_dir = base_dir
20
+ @cache_root = cache_root
21
21
  end
22
22
 
23
23
  def file
@@ -27,16 +27,17 @@ module RuboCop
27
27
  next if response.is_a?(Net::HTTPNotModified)
28
28
  next if response.is_a?(SocketError)
29
29
 
30
+ FileUtils.mkdir_p(File.dirname(cache_path))
30
31
  File.write(cache_path, response.body)
31
32
  end
32
33
 
33
34
  cache_path
34
35
  end
35
36
 
36
- def inherit_from_remote(file, path)
37
+ def inherit_from_remote(file)
37
38
  new_uri = @uri.dup
38
39
  new_uri.path.gsub!(%r{/[^/]*$}, "/#{file.delete_prefix('./')}")
39
- RemoteConfig.new(new_uri.to_s, File.dirname(path))
40
+ RemoteConfig.new(new_uri.to_s, @cache_root)
40
41
  end
41
42
 
42
43
  private
@@ -80,7 +81,7 @@ module RuboCop
80
81
  end
81
82
 
82
83
  def cache_path
83
- File.expand_path(".rubocop-#{cache_name_from_uri}", @base_dir)
84
+ @cache_path ||= File.expand_path(cache_name_from_uri, @cache_root)
84
85
  end
85
86
 
86
87
  def cache_path_exists?
@@ -97,9 +98,10 @@ module RuboCop
97
98
  end
98
99
 
99
100
  def cache_name_from_uri
100
- uri = cloned_url
101
- uri.query = nil
102
- uri.to_s.gsub!(/[^0-9A-Za-z]/, '-')
101
+ # The md5 checksum suffix is 37 bytes, so we play it save and
102
+ # allow 254 bytes total - this should be safe on Linux/macOS/Windows
103
+ filename = File.basename(@uri.path).gsub(/\.ya?ml\z/i, '').byteslice(0, 217).scrub('')
104
+ "#{filename}-#{Digest::MD5.hexdigest(@uri.to_s)}.yml"
103
105
  end
104
106
 
105
107
  def cloned_url
@@ -22,19 +22,22 @@ module RuboCop
22
22
  # Remove old files so that the cache doesn't grow too big. When the
23
23
  # threshold MaxFilesInCache has been exceeded, the oldest 50% of all the
24
24
  # files in the cache are removed. The reason for removing so much is that
25
- # cleaning should be done relatively seldom, since there is a slight risk
25
+ # removing should be done relatively seldom, since there is a slight risk
26
26
  # that some other RuboCop process was just about to read the file, when
27
27
  # there's parallel execution and the cache is shared.
28
28
  def self.cleanup(config_store, verbose, cache_root_override = nil)
29
29
  return if inhibit_cleanup # OPTIMIZE: For faster testing
30
+ return unless config_store.for_pwd.for_all_cops['MaxFilesInCache']
30
31
 
31
32
  rubocop_cache_dir = cache_root(config_store, cache_root_override)
32
33
  return unless File.exist?(rubocop_cache_dir)
33
34
 
34
- files, dirs = Find.find(rubocop_cache_dir).partition { |path| File.file?(path) }
35
+ # We know the cache entries are 3 level deep, so globing
36
+ # for `*/*/*` only returns files.
37
+ files = Dir[File.join(rubocop_cache_dir, '*/*/*')]
35
38
  return unless requires_file_removal?(files.length, config_store)
36
39
 
37
- remove_oldest_files(files, dirs, rubocop_cache_dir, verbose)
40
+ remove_oldest_files(files, rubocop_cache_dir, verbose)
38
41
  end
39
42
 
40
43
  class << self
@@ -49,39 +52,57 @@ module RuboCop
49
52
  file_count > 1 && file_count > config_store.for_pwd.for_all_cops['MaxFilesInCache']
50
53
  end
51
54
 
52
- def remove_oldest_files(files, dirs, rubocop_cache_dir, verbose)
55
+ def remove_oldest_files(files, rubocop_cache_dir, verbose)
53
56
  # Add 1 to half the number of files, so that we remove the file if
54
57
  # there's only 1 left.
55
58
  remove_count = (files.length / 2) + 1
56
59
  puts "Removing the #{remove_count} oldest files from #{rubocop_cache_dir}" if verbose
57
60
  sorted = files.sort_by { |path| File.mtime(path) }
58
- remove_files(sorted, dirs, remove_count)
61
+ remove_files(sorted, remove_count)
59
62
  rescue Errno::ENOENT
60
63
  # This can happen if parallel RuboCop invocations try to remove the
61
64
  # same files. No problem.
62
65
  puts $ERROR_INFO if verbose
63
66
  end
64
67
 
65
- def remove_files(files, dirs, remove_count)
68
+ def remove_files(files, remove_count)
66
69
  # Batch file deletions, deleting over 130,000+ files will crash
67
70
  # File.delete.
68
71
  files[0, remove_count].each_slice(10_000).each do |files_slice|
69
72
  File.delete(*files_slice)
70
73
  end
71
- dirs.each { |dir| Dir.rmdir(dir) if Dir["#{dir}/*"].empty? }
74
+
75
+ dirs = files.map { |f| File.dirname(f) }.uniq
76
+ until dirs.empty?
77
+ dirs.select! do |dir|
78
+ Dir.rmdir(dir)
79
+ true
80
+ rescue SystemCallError # ENOTEMPTY etc
81
+ false
82
+ end
83
+ dirs = dirs.map { |f| File.dirname(f) }.uniq
84
+ end
72
85
  end
73
86
  end
74
87
 
75
88
  def self.cache_root(config_store, cache_root_override = nil)
76
- CacheConfig.root_dir do
89
+ return @cache_root if @cache_root && !cache_root_override
90
+
91
+ result = CacheConfig.root_dir do
77
92
  cache_root_override || config_store.for_pwd.for_all_cops['CacheRootDirectory']
78
93
  end
94
+ @cache_root = result unless cache_root_override
95
+ result
79
96
  end
80
97
 
81
98
  def self.allow_symlinks_in_cache_location?(config_store)
82
99
  config_store.for_pwd.for_all_cops['AllowSymlinksInCacheRootDirectory']
83
100
  end
84
101
 
102
+ def self.reset_config_cache
103
+ @cache_root = nil
104
+ end
105
+
85
106
  attr_reader :path
86
107
 
87
108
  def initialize(file, team, options, config_store, cache_root_override = nil)
@@ -90,7 +111,7 @@ module RuboCop
90
111
  @allow_symlinks_in_cache_location =
91
112
  ResultCache.allow_symlinks_in_cache_location?(config_store)
92
113
  @path = File.join(rubocop_cache_dir,
93
- rubocop_checksum,
114
+ self.class.source_checksum,
94
115
  context_checksum(team, options),
95
116
  file_checksum(file, config_store))
96
117
  @cached_data = CachedData.new(file)
@@ -167,13 +188,11 @@ module RuboCop
167
188
  end
168
189
 
169
190
  class << self
170
- attr_accessor :source_checksum, :inhibit_cleanup
171
- end
191
+ attr_accessor :inhibit_cleanup
172
192
 
173
- # The checksum of the RuboCop program running the inspection.
174
- def rubocop_checksum
175
- ResultCache.source_checksum ||=
176
- begin
193
+ # The checksum of the RuboCop program running the inspection.
194
+ def source_checksum
195
+ @source_checksum ||= begin
177
196
  digest = Digest::SHA1.new
178
197
  rubocop_extra_features
179
198
  .select { |path| File.file?(path) }
@@ -184,21 +203,33 @@ module RuboCop
184
203
  digest << RuboCop::Version::STRING << RuboCop::AST::Version::STRING
185
204
  digest.hexdigest
186
205
  end
187
- end
206
+ end
188
207
 
189
- def digest(path)
190
- content = if path.end_with?(*DL_EXTENSIONS)
191
- # Shared libraries often contain timestamps of when
192
- # they were compiled and other non-stable data.
193
- File.basename(path)
194
- else
195
- File.binread(path) # mtime not reliable
196
- end
197
- Zlib.crc32(content).to_s
198
- end
208
+ # Return a hash of the options given at invocation, minus the ones that have
209
+ # no effect on which offenses and disabled line ranges are found, and thus
210
+ # don't affect caching.
211
+ def relevant_options_digest(options)
212
+ @relevant_options_digest ||= {}
213
+ @relevant_options_digest[options] ||= begin
214
+ options = options.reject { |key, _| NON_CHANGING.include?(key) }
215
+ options.to_s.gsub(/[^a-z]+/i, '_')
216
+ end
217
+ end
218
+
219
+ private
199
220
 
200
- def rubocop_extra_features
201
- @rubocop_extra_features ||= begin
221
+ def digest(path)
222
+ content = if path.end_with?(*DL_EXTENSIONS)
223
+ # Shared libraries often contain timestamps of when
224
+ # they were compiled and other non-stable data.
225
+ File.basename(path)
226
+ else
227
+ File.binread(path) # mtime not reliable
228
+ end
229
+ Zlib.crc32(content).to_s
230
+ end
231
+
232
+ def rubocop_extra_features
202
233
  lib_root = File.join(File.dirname(__FILE__), '..')
203
234
  exe_root = File.join(lib_root, '..', 'exe')
204
235
 
@@ -216,20 +247,12 @@ module RuboCop
216
247
  end
217
248
  end
218
249
 
219
- # Return a hash of the options given at invocation, minus the ones that have
220
- # no effect on which offenses and disabled line ranges are found, and thus
221
- # don't affect caching.
222
- def relevant_options_digest(options)
223
- options = options.reject { |key, _| NON_CHANGING.include?(key) }
224
- options.to_s.gsub(/[^a-z]+/i, '_')
225
- end
226
-
227
250
  # We combine team and options into a single "context" checksum to avoid
228
251
  # making file names that are too long for some filesystems to handle.
229
252
  # This context is for anything that's not (1) the RuboCop executable
230
253
  # checksum or (2) the inspected file checksum.
231
254
  def context_checksum(team, options)
232
- keys = [team.external_dependency_checksum, relevant_options_digest(options)]
255
+ keys = [team.external_dependency_checksum, self.class.relevant_options_digest(options)]
233
256
  Digest::SHA1.hexdigest(keys.join)
234
257
  end
235
258
  end
@@ -6,6 +6,12 @@ require 'tempfile'
6
6
  module CopHelper
7
7
  extend RSpec::SharedContext
8
8
 
9
+ @integrated_plugins = false
10
+
11
+ class << self
12
+ attr_accessor :integrated_plugins
13
+ end
14
+
9
15
  let(:ruby_version) do
10
16
  # The minimum version Prism can parse is 3.3.
11
17
  ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : RuboCop::TargetRuby::DEFAULT_VERSION
@@ -18,11 +24,13 @@ module CopHelper
18
24
 
19
25
  before(:all) do
20
26
  next if ENV['RUBOCOP_CORE_DEVELOPMENT']
27
+ next if CopHelper.integrated_plugins
21
28
 
22
29
  plugins = Gem.loaded_specs.filter_map do |feature_name, feature_specification|
23
30
  feature_name if feature_specification.metadata['default_lint_roller_plugin']
24
31
  end
25
32
  RuboCop::Plugin.integrate_plugins(RuboCop::Config.new, plugins)
33
+ CopHelper.integrated_plugins = true
26
34
  end
27
35
 
28
36
  def inspect_source(source, file = nil)
@@ -2,8 +2,12 @@
2
2
 
3
3
  require 'tmpdir'
4
4
 
5
+ # Reset cached PathUtil.pwd before each example so that tests using Dir.chdir
6
+ # or stubbing Dir.pwd get a fresh value.
7
+ RSpec.configure { |c| c.before { RuboCop::PathUtil.reset_pwd } }
8
+
5
9
  RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLength
6
- around do |example|
10
+ around do |example| # rubocop:disable Metrics/BlockLength
7
11
  Dir.mktmpdir do |tmpdir|
8
12
  original_home = Dir.home
9
13
  original_xdg_config_home = ENV.fetch('XDG_CONFIG_HOME', nil)
@@ -26,12 +30,17 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
26
30
  begin
27
31
  FileUtils.mkdir_p(working_dir)
28
32
 
29
- Dir.chdir(working_dir) { example.run }
33
+ Dir.chdir(working_dir) do
34
+ RuboCop::PathUtil.reset_pwd
35
+ RuboCop::ResultCache.reset_config_cache
36
+ example.run
37
+ end
30
38
  ensure
31
39
  ENV['HOME'] = original_home
32
40
  ENV['XDG_CONFIG_HOME'] = original_xdg_config_home
33
41
 
34
- RuboCop::FileFinder.root_level = nil
42
+ RuboCop::ResultCache.reset_config_cache
43
+ RuboCop::ConfigLoader.clear_options # This also resets RuboCop::FileFinder.root_level
35
44
  end
36
45
  end
37
46
  end
@@ -266,6 +275,10 @@ RSpec.shared_context 'ruby 3.4' do
266
275
  let(:ruby_version) { 3.4 }
267
276
  end
268
277
 
269
- RSpec.shared_context 'ruby 3.5' do
270
- let(:ruby_version) { 3.5 }
278
+ RSpec.shared_context 'ruby 4.0' do
279
+ let(:ruby_version) { 4.0 }
280
+ end
281
+
282
+ RSpec.shared_context 'ruby 4.1' do
283
+ let(:ruby_version) { 4.1 }
271
284
  end
@@ -30,5 +30,6 @@ RSpec.configure do |config|
30
30
  config.include_context 'ruby 3.2', :ruby32
31
31
  config.include_context 'ruby 3.3', :ruby33
32
32
  config.include_context 'ruby 3.4', :ruby34
33
- config.include_context 'ruby 3.5', :ruby35
33
+ config.include_context 'ruby 4.0', :ruby40
34
+ config.include_context 'ruby 4.1', :ruby41
34
35
  end
@@ -65,6 +65,10 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def run(paths)
68
+ # Compute the cache source checksum once to avoid potential
69
+ # inconsistencies between workers.
70
+ ResultCache.source_checksum
71
+
68
72
  target_files = find_target_files(paths)
69
73
  if @options[:list_target_files]
70
74
  list_files(target_files)
@@ -190,7 +194,7 @@ module RuboCop
190
194
 
191
195
  if real_run_needed
192
196
  offenses = yield
193
- save_in_cache(cache, offenses)
197
+ save_in_cache(cache, offenses) unless Cop::Registry.global.warnings?(file)
194
198
  end
195
199
 
196
200
  offenses
@@ -346,7 +350,9 @@ module RuboCop
346
350
 
347
351
  def inspect_file(processed_source, team = mobilize_team(processed_source))
348
352
  extracted_ruby_sources = extract_ruby_sources(processed_source)
349
- offenses = extracted_ruby_sources.flat_map do |extracted_ruby_source|
353
+ offenses = []
354
+
355
+ extracted_ruby_sources.each do |extracted_ruby_source|
350
356
  report = team.investigate(
351
357
  extracted_ruby_source[:processed_source],
352
358
  offset: extracted_ruby_source[:offset],
@@ -354,8 +360,11 @@ module RuboCop
354
360
  )
355
361
  @errors.concat(team.errors)
356
362
  @warnings.concat(team.warnings)
357
- report.offenses
363
+ offenses.concat(report.offenses)
364
+
365
+ break if team.updated_source_file?
358
366
  end
367
+
359
368
  [offenses, team.updated_source_file?]
360
369
  end
361
370
 
@@ -73,33 +73,12 @@ module RuboCop
73
73
  cache_root_dir = if cache_root_path
74
74
  File.join(cache_root_path, 'rubocop_cache')
75
75
  else
76
- cache_root_dir_from_config
76
+ CacheConfig.root_dir_from_toplevel_config
77
77
  end
78
78
 
79
79
  File.expand_path(File.join(cache_root_dir, 'server'))
80
80
  end
81
81
 
82
- def cache_root_dir_from_config
83
- CacheConfig.root_dir do
84
- # `RuboCop::ConfigStore` has heavy dependencies, this is a lightweight implementation
85
- # so that only the necessary `CacheRootDirectory` can be obtained.
86
- config_path = ConfigFinder.find_config_path(Dir.pwd)
87
- file_contents = File.read(config_path)
88
-
89
- # Returns early if `CacheRootDirectory` is not used before requiring `erb` or `yaml`.
90
- next unless file_contents.include?('CacheRootDirectory')
91
-
92
- config_yaml = load_erb_templated_yaml(file_contents)
93
-
94
- # For compatibility with Ruby 3.0 or lower.
95
- if Gem::Version.new(Psych::VERSION) < Gem::Version.new('4.0.0')
96
- config_yaml == false ? nil : config_yaml
97
- end
98
-
99
- config_yaml&.dig('AllCops', 'CacheRootDirectory')
100
- end
101
- end
102
-
103
82
  def port_path
104
83
  dir.join('port')
105
84
  end
@@ -135,13 +114,11 @@ module RuboCop
135
114
  end
136
115
 
137
116
  def acquire_lock
138
- lock_file = File.open(lock_path, File::CREAT)
139
- # flock returns 0 if successful, and false if not.
140
- flock_result = lock_file.flock(File::LOCK_EX | File::LOCK_NB)
141
- yield flock_result != false
142
- ensure
143
- lock_file.flock(File::LOCK_UN)
144
- lock_file.close
117
+ File.open(lock_path, File::CREAT) do |lock_file|
118
+ # flock returns 0 if successful, and false if not.
119
+ flock_result = lock_file.flock(File::LOCK_EX | File::LOCK_NB)
120
+ yield flock_result != false
121
+ end
145
122
  end
146
123
 
147
124
  def write_port_and_token_files(port:, token:)
@@ -45,6 +45,8 @@ module RuboCop
45
45
  write_port_and_token_files
46
46
 
47
47
  pid = fork do
48
+ # NOTE: As of Ruby 4.0.0, ZJIT is still under development, while YJIT is production-ready,
49
+ # so support for ZJIT is deferred.
48
50
  if defined?(RubyVM::YJIT.enable)
49
51
  RubyVM::YJIT.enable
50
52
  end
@@ -38,7 +38,7 @@ module RuboCop
38
38
  # @param base_dir Root directory under which to search for
39
39
  # ruby source files
40
40
  # @return [Array] Array of filenames
41
- def target_files_in_dir(base_dir = Dir.pwd)
41
+ def target_files_in_dir(base_dir = PathUtil.pwd)
42
42
  # Support Windows: Backslashes from command-line -> forward slashes
43
43
  base_dir = base_dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
44
44
  all_files = find_files(base_dir, File::FNM_DOTMATCH)
@@ -4,7 +4,9 @@ module RuboCop
4
4
  # The kind of Ruby that code inspected by RuboCop is written in.
5
5
  # @api private
6
6
  class TargetRuby
7
- KNOWN_RUBIES = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5].freeze
7
+ KNOWN_RUBIES = [
8
+ 2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 4.0, 4.1
9
+ ].freeze
8
10
  DEFAULT_VERSION = 2.7
9
11
 
10
12
  OBSOLETE_RUBIES = {
@@ -110,8 +112,17 @@ module RuboCop
110
112
  end
111
113
 
112
114
  def version_from_gemspec_file(file)
115
+ # When using parser_prism, we need to use a Ruby version that Prism supports (3.3+)
116
+ # for parsing the gemspec file. This doesn't affect the detected Ruby version,
117
+ # it's just for the parsing step.
118
+ ruby_version_for_parsing = if @config.parser_engine == :parser_prism
119
+ 3.3
120
+ else
121
+ DEFAULT_VERSION
122
+ end
123
+
113
124
  processed_source = ProcessedSource.from_file(
114
- file, DEFAULT_VERSION, parser_engine: @config.parser_engine
125
+ file, ruby_version_for_parsing, parser_engine: @config.parser_engine
115
126
  )
116
127
  return unless processed_source.valid_syntax?
117
128
 
@@ -149,21 +160,18 @@ module RuboCop
149
160
  # The target ruby version may be found in a .ruby-version file.
150
161
  # @api private
151
162
  class RubyVersionFile < Source
152
- RUBY_VERSION_FILENAME = '.ruby-version'
153
- RUBY_VERSION_PATTERN = /\A(?:ruby-)?(?<version>\d+\.\d+)/.freeze
154
-
155
163
  def name
156
- "`#{RUBY_VERSION_FILENAME}`"
164
+ "`#{filename}`"
157
165
  end
158
166
 
159
167
  private
160
168
 
161
169
  def filename
162
- RUBY_VERSION_FILENAME
170
+ '.ruby-version'
163
171
  end
164
172
 
165
173
  def pattern
166
- RUBY_VERSION_PATTERN
174
+ /\A(?:ruby-)?(?<version>\d+\.\d+)/.freeze
167
175
  end
168
176
 
169
177
  def find_version
@@ -182,21 +190,29 @@ module RuboCop
182
190
  # starting with `ruby`.
183
191
  # @api private
184
192
  class ToolVersionsFile < RubyVersionFile
185
- TOOL_VERSIONS_FILENAME = '.tool-versions'
186
- TOOL_VERSIONS_PATTERN = /^(?:ruby )(?<version>\d+\.\d+)/.freeze
193
+ private
187
194
 
188
- def name
189
- "`#{TOOL_VERSIONS_FILENAME}`"
195
+ def filename
196
+ '.tool-versions'
190
197
  end
191
198
 
199
+ def pattern
200
+ /^(?:ruby )(?<version>\d+\.\d+)/.freeze
201
+ end
202
+ end
203
+
204
+ # The target ruby version may be found in a mise.toml file, in a line
205
+ # starting with `ruby = "`.
206
+ # @api private
207
+ class MiseTomlFile < RubyVersionFile
192
208
  private
193
209
 
194
210
  def filename
195
- TOOL_VERSIONS_FILENAME
211
+ 'mise.toml'
196
212
  end
197
213
 
198
214
  def pattern
199
- TOOL_VERSIONS_PATTERN
215
+ /^ruby = "(?<version>\d+\.\d+)/.freeze
200
216
  end
201
217
  end
202
218
 
@@ -264,6 +280,7 @@ module RuboCop
264
280
  RuboCopConfig,
265
281
  GemspecFile,
266
282
  RubyVersionFile,
283
+ MiseTomlFile,
267
284
  ToolVersionsFile,
268
285
  BundlerLockFile,
269
286
  Default
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.80.2'
6
+ STRING = '1.86.1'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
@@ -55,7 +55,7 @@ module RuboCop
55
55
 
56
56
  # @api private
57
57
  def self.parser_version(target_ruby_version)
58
- config_path = ConfigFinder.find_config_path(Dir.pwd)
58
+ config_path = ConfigFinder.find_config_path(PathUtil.pwd)
59
59
  yaml = Util.silence_warnings do
60
60
  ConfigLoader.load_yaml_configuration(config_path)
61
61
  end