rubocop 1.79.2 → 1.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +259 -90
  5. data/config/obsoletion.yml +30 -1
  6. data/exe/rubocop +1 -8
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cli/command/auto_generate_config.rb +36 -4
  9. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  10. data/lib/rubocop/cli/command/lsp.rb +1 -1
  11. data/lib/rubocop/cli/command/mcp.rb +19 -0
  12. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  13. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  14. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  15. data/lib/rubocop/cli.rb +35 -9
  16. data/lib/rubocop/comment_config.rb +59 -17
  17. data/lib/rubocop/config.rb +14 -10
  18. data/lib/rubocop/config_finder.rb +1 -1
  19. data/lib/rubocop/config_loader.rb +37 -23
  20. data/lib/rubocop/config_loader_resolver.rb +20 -10
  21. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  22. data/lib/rubocop/config_store.rb +7 -2
  23. data/lib/rubocop/config_validator.rb +1 -1
  24. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  25. data/lib/rubocop/cop/base.rb +25 -4
  26. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  27. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  28. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  29. data/lib/rubocop/cop/correctors/alignment_corrector.rb +26 -7
  30. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  31. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  32. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  33. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  34. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  35. data/lib/rubocop/cop/correctors.rb +28 -0
  36. data/lib/rubocop/cop/documentation.rb +2 -3
  37. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  38. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  39. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  40. data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  42. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  43. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  44. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  45. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  46. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  47. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  48. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  49. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  50. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  51. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  52. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  53. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  54. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  55. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  56. data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
  57. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  58. data/lib/rubocop/cop/layout/class_structure.rb +14 -7
  59. data/lib/rubocop/cop/layout/dot_position.rb +2 -2
  60. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +26 -7
  61. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
  62. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
  63. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  64. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  65. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  66. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  67. data/lib/rubocop/cop/layout/end_alignment.rb +10 -3
  68. data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
  69. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  70. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  71. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  72. data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
  73. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  74. data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
  75. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  76. data/lib/rubocop/cop/layout/indentation_width.rb +123 -7
  77. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  78. data/lib/rubocop/cop/layout/line_length.rb +26 -9
  79. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  80. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  81. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  82. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  83. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  84. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +229 -39
  85. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  86. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  87. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  88. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  89. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  90. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  91. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  92. data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
  93. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  94. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  95. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  96. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  97. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  98. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  99. data/lib/rubocop/cop/lint/circular_argument_reference.rb +45 -3
  100. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +4 -3
  101. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  102. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  103. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  104. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  105. data/lib/rubocop/cop/lint/debugger.rb +0 -3
  106. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  107. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  108. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  109. data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
  110. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  111. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  112. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  113. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  114. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  115. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  116. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  117. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  118. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  119. data/lib/rubocop/cop/lint/float_comparison.rb +2 -1
  120. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  121. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -5
  122. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  123. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
  124. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  125. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -12
  126. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -10
  127. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  128. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  129. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
  130. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  131. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  132. data/lib/rubocop/cop/lint/number_conversion.rb +19 -10
  133. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  134. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
  135. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  136. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  137. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  138. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  139. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -10
  140. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -12
  141. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  142. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  143. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +36 -12
  144. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +12 -2
  145. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
  146. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  147. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  148. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  149. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  150. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  151. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  152. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  153. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  154. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  155. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -0
  156. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  157. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  158. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  159. data/lib/rubocop/cop/lint/self_assignment.rb +39 -7
  160. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  161. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  162. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  163. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  164. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  165. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  166. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  167. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  168. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  169. data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
  170. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  171. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  172. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  173. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
  174. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  175. data/lib/rubocop/cop/lint/unreachable_code.rb +7 -5
  176. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  177. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  178. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  179. data/lib/rubocop/cop/lint/useless_assignment.rb +53 -25
  180. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  181. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  182. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  183. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
  184. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  185. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  186. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +37 -11
  187. data/lib/rubocop/cop/lint/void.rb +39 -12
  188. data/lib/rubocop/cop/message_annotator.rb +1 -1
  189. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  190. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  191. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  192. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  193. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  194. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  195. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  196. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  197. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
  198. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  199. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  200. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  201. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
  202. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  203. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  204. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  205. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  206. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  207. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  208. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  209. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  210. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  211. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  212. data/lib/rubocop/cop/mixin.rb +86 -0
  213. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  214. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  215. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  216. data/lib/rubocop/cop/naming/method_name.rb +5 -3
  217. data/lib/rubocop/cop/naming/predicate_method.rb +32 -8
  218. data/lib/rubocop/cop/naming/predicate_prefix.rb +12 -12
  219. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  220. data/lib/rubocop/cop/offense.rb +17 -1
  221. data/lib/rubocop/cop/registry.rb +62 -38
  222. data/lib/rubocop/cop/security/eval.rb +15 -2
  223. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  224. data/lib/rubocop/cop/security/json_load.rb +33 -11
  225. data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
  226. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  227. data/lib/rubocop/cop/style/alias.rb +15 -3
  228. data/lib/rubocop/cop/style/and_or.rb +2 -1
  229. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  230. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  231. data/lib/rubocop/cop/style/array_intersect.rb +50 -12
  232. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +50 -0
  233. data/lib/rubocop/cop/style/array_join.rb +4 -2
  234. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  235. data/lib/rubocop/cop/style/attr.rb +5 -2
  236. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  237. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  238. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  239. data/lib/rubocop/cop/style/block_delimiters.rb +39 -32
  240. data/lib/rubocop/cop/style/case_equality.rb +29 -15
  241. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  242. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -2
  243. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  244. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  245. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  246. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  247. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  248. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  249. data/lib/rubocop/cop/style/concat_array_literals.rb +7 -1
  250. data/lib/rubocop/cop/style/conditional_assignment.rb +14 -19
  251. data/lib/rubocop/cop/style/constant_visibility.rb +20 -12
  252. data/lib/rubocop/cop/style/copyright.rb +22 -11
  253. data/lib/rubocop/cop/style/date_time.rb +4 -4
  254. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  255. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  256. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  257. data/lib/rubocop/cop/style/documentation.rb +6 -6
  258. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  259. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  260. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  261. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  262. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  263. data/lib/rubocop/cop/style/empty_class_definition.rb +119 -0
  264. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  265. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  266. data/lib/rubocop/cop/style/encoding.rb +7 -1
  267. data/lib/rubocop/cop/style/end_block.rb +3 -1
  268. data/lib/rubocop/cop/style/endless_method.rb +23 -5
  269. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  270. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  271. data/lib/rubocop/cop/style/file_open.rb +84 -0
  272. data/lib/rubocop/cop/style/file_write.rb +21 -16
  273. data/lib/rubocop/cop/style/float_division.rb +15 -1
  274. data/lib/rubocop/cop/style/for.rb +3 -0
  275. data/lib/rubocop/cop/style/format_string.rb +4 -3
  276. data/lib/rubocop/cop/style/format_string_token.rb +49 -5
  277. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  278. data/lib/rubocop/cop/style/guard_clause.rb +27 -22
  279. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
  280. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  281. data/lib/rubocop/cop/style/hash_lookup_method.rb +106 -0
  282. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  283. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  284. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  285. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  286. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  287. data/lib/rubocop/cop/style/if_unless_modifier.rb +58 -18
  288. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  289. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  290. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  291. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  292. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  293. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  294. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  295. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  296. data/lib/rubocop/cop/style/map_join.rb +123 -0
  297. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  298. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  299. data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
  300. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  301. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  302. data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
  303. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  304. data/lib/rubocop/cop/style/mutable_constant.rb +106 -12
  305. data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
  306. data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
  307. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  308. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  309. data/lib/rubocop/cop/style/not.rb +2 -0
  310. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  311. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  312. data/lib/rubocop/cop/style/one_line_conditional.rb +21 -12
  313. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  314. data/lib/rubocop/cop/style/parallel_assignment.rb +14 -3
  315. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  316. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  317. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  318. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  319. data/lib/rubocop/cop/style/proc.rb +3 -2
  320. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  321. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  322. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  323. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  324. data/lib/rubocop/cop/style/redundant_begin.rb +37 -3
  325. data/lib/rubocop/cop/style/redundant_condition.rb +6 -3
  326. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  327. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  328. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  329. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  330. data/lib/rubocop/cop/style/redundant_format.rb +27 -5
  331. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  332. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  333. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  334. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  335. data/lib/rubocop/cop/style/redundant_parentheses.rb +36 -30
  336. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  337. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  338. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  339. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  340. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  341. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  342. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  343. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  344. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  345. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  346. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  347. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  348. data/lib/rubocop/cop/style/safe_navigation.rb +25 -8
  349. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  350. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  351. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  352. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  353. data/lib/rubocop/cop/style/semicolon.rb +41 -8
  354. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  355. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  356. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  357. data/lib/rubocop/cop/style/sole_nested_conditional.rb +12 -3
  358. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  359. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  360. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  361. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  362. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  363. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  364. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  365. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  366. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  367. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  368. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  369. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  370. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  371. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  372. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  373. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  374. data/lib/rubocop/cop/style/word_array.rb +1 -0
  375. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  376. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  377. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  378. data/lib/rubocop/cop/team.rb +87 -36
  379. data/lib/rubocop/cop/util.rb +2 -3
  380. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  381. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  382. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  383. data/lib/rubocop/cop/variable_force.rb +9 -7
  384. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  385. data/lib/rubocop/directive_comment.rb +48 -4
  386. data/lib/rubocop/file_patterns.rb +9 -1
  387. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  388. data/lib/rubocop/formatter/disabled_config_formatter.rb +38 -14
  389. data/lib/rubocop/formatter/formatter_set.rb +2 -2
  390. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  391. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  392. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  393. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  394. data/lib/rubocop/formatter.rb +22 -21
  395. data/lib/rubocop/lsp/diagnostic.rb +18 -33
  396. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  397. data/lib/rubocop/lsp/routes.rb +43 -7
  398. data/lib/rubocop/lsp/runtime.rb +13 -4
  399. data/lib/rubocop/lsp/stdin_runner.rb +8 -17
  400. data/lib/rubocop/magic_comment.rb +20 -0
  401. data/lib/rubocop/mcp/server.rb +200 -0
  402. data/lib/rubocop/options.rb +35 -4
  403. data/lib/rubocop/path_util.rb +14 -2
  404. data/lib/rubocop/plugin/loader.rb +1 -1
  405. data/lib/rubocop/project_index_loader.rb +66 -0
  406. data/lib/rubocop/rake_task.rb +1 -1
  407. data/lib/rubocop/remote_config.rb +10 -8
  408. data/lib/rubocop/result_cache.rb +61 -38
  409. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  410. data/lib/rubocop/rspec/shared_contexts.rb +39 -5
  411. data/lib/rubocop/rspec/support.rb +2 -1
  412. data/lib/rubocop/runner.rb +134 -57
  413. data/lib/rubocop/server/cache.rb +6 -29
  414. data/lib/rubocop/server/core.rb +8 -0
  415. data/lib/rubocop/target_finder.rb +17 -10
  416. data/lib/rubocop/target_ruby.rb +31 -14
  417. data/lib/rubocop/version.rb +21 -3
  418. data/lib/rubocop.rb +28 -96
  419. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  420. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  421. metadata +38 -9
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'mcp'
5
+
6
+ required_mcp_version = '0.6.0'
7
+
8
+ if Gem::Version.new(required_mcp_version) > Gem::Version.new(MCP::VERSION)
9
+ # While `mcp` is not a runtime dependency, users may have an outdated version installed.
10
+ warn <<~MESSAGE
11
+ Error: `mcp` gem version #{MCP::VERSION} was loaded, but `rubocop --mcp` requires #{required_mcp_version}.
12
+ - If you're using Bundler and don't yet have `gem 'mcp'` as a dependency, add it now.
13
+ - If you're using Bundler and already have `gem 'mcp'` as a dependency, update it to the most recent version.
14
+ - If you don't use Bundler, run `gem update mcp`.
15
+ MESSAGE
16
+ exit!
17
+ end
18
+ rescue LoadError => e
19
+ raise unless e.path == 'mcp'
20
+
21
+ warn <<~MESSAGE
22
+ Error: Unable to load `mcp` gem. Add `gem 'mcp', '~> 0.6'` to your Gemfile, or run `gem install mcp`.
23
+ MESSAGE
24
+
25
+ exit!
26
+ end
27
+
28
+ require_relative '../lsp'
29
+ require_relative '../lsp/runtime'
30
+
31
+ module RuboCop
32
+ module MCP
33
+ # RuboCop MCP Server.
34
+ # @api private
35
+ class Server
36
+ def initialize(config_store)
37
+ @config_store = config_store
38
+ @runtime = RuboCop::LSP::Runtime.new(@config_store)
39
+ @options = {}
40
+ end
41
+
42
+ def start
43
+ # No `protocol_version` is specified because draft feature by default can be used.
44
+ server = ::MCP::Server.new(
45
+ name: 'rubocop_mcp_server',
46
+ version: RuboCop::Version::STRING,
47
+ tools: [inspection_tool, autocorrection_tool]
48
+ )
49
+
50
+ ::MCP::Server::Transports::StdioTransport.new(server).open
51
+ end
52
+
53
+ private
54
+
55
+ def inspection_tool
56
+ build_tool(
57
+ name: 'rubocop_inspection',
58
+ description: 'Inspect Ruby code for offenses. ' \
59
+ 'Provide `source_code` to check inline code or `path` to check files.',
60
+ title: "RuboCop's inspection",
61
+ destructive_hint: false,
62
+ idempotent_hint: true,
63
+ read_only_hint: true,
64
+ safety_required: false
65
+ ) do |path, source_code|
66
+ run_inspection(path, source_code)
67
+ end
68
+ end
69
+
70
+ def autocorrection_tool
71
+ build_tool(
72
+ name: 'rubocop_autocorrection',
73
+ description: 'Autocorrect RuboCop offenses in Ruby code. ' \
74
+ 'Provide `source_code` to correct inline code or `path` to correct files. ' \
75
+ 'Set `safety` to false to include unsafe corrections.',
76
+ title: "RuboCop's autocorrection",
77
+ destructive_hint: true,
78
+ idempotent_hint: false,
79
+ read_only_hint: false,
80
+ safety_required: true
81
+ ) do |path, source_code, safety|
82
+ run_autocorrection(path, source_code, safety)
83
+ end
84
+ end
85
+
86
+ def run_inspection(path, source_code)
87
+ if source_code
88
+ offenses = @runtime.offenses(path || 'example.rb', source_code, source_code.encoding)
89
+ offenses.to_json
90
+ else
91
+ process_files(path, filter_empty: true) do |file, source|
92
+ offenses = @runtime.offenses(file, source, source.encoding)
93
+
94
+ { path: PathUtil.relative_path(file), offenses: offenses }
95
+ end
96
+ end
97
+ end
98
+
99
+ def run_autocorrection(path, source_code, safety)
100
+ command = safety ? 'rubocop.formatAutocorrects' : 'rubocop.formatAutocorrectsAll'
101
+
102
+ if source_code
103
+ @runtime.format(path || 'example.rb', source_code, command: command).tap do |corrected|
104
+ write_file(path, corrected) if path
105
+ end
106
+ else
107
+ process_files(path) do |file, source|
108
+ @runtime.format(file, source, command: command).then do |corrected|
109
+ write_file(file, corrected)
110
+
111
+ { path: PathUtil.relative_path(file), corrected: source != corrected }
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ def process_files(path, filter_empty: false)
118
+ target_finder = RuboCop::TargetFinder.new(@config_store, @options)
119
+ target_files = target_finder.find(path ? [path] : [], :only_recognized_file_types)
120
+ all_files = target_files.map { |file| yield(file, read_file(file)) }
121
+ files = filter_empty ? all_files.reject { |f| f[:offenses]&.empty? } : all_files
122
+
123
+ { files: files, summary: build_summary(target_files, all_files) }.to_json
124
+ end
125
+
126
+ def read_file(file)
127
+ config = @config_store.for_file(file)
128
+ RuboCop::ProcessedSource.from_file(
129
+ file, config.target_ruby_version, parser_engine: config.parser_engine
130
+ ).raw_source
131
+ rescue Errno::ENOENT
132
+ raise RuboCop::Error, "No such file or directory: #{file}"
133
+ end
134
+
135
+ def write_file(file, content)
136
+ File.write(file, content)
137
+ rescue Errno::EACCES
138
+ raise RuboCop::Error, "Permission denied: #{file}"
139
+ rescue Errno::ENOSPC
140
+ raise RuboCop::Error, "No space left on device: #{file}"
141
+ rescue Errno::EROFS
142
+ raise RuboCop::Error, "Read-only file system: #{file}"
143
+ end
144
+
145
+ # NOTE: It is useful for RuboCop's result summary to be shown in the LLM's responses
146
+ # during interactions, so the summary is returned in a form that is easy for the LLM
147
+ # to reason about. Since LLM execution is non-deterministic, it is also sensible to
148
+ # compute the summary deterministically at this stage.
149
+ def build_summary(target_files, files)
150
+ summary = { target_file_count: target_files.count }
151
+ if files.first&.key?(:offenses)
152
+ summary[:offense_count] = files.sum { |f| f[:offenses].size }
153
+ else
154
+ summary[:corrected_file_count] = files.count { |f| f[:corrected] }
155
+ end
156
+ summary
157
+ end
158
+
159
+ # rubocop:disable Metrics/MethodLength, Metrics/ParameterLists
160
+ def build_tool(
161
+ name:, description:,
162
+ title:, destructive_hint:, idempotent_hint:, read_only_hint:, safety_required:
163
+ )
164
+ if safety_required
165
+ safety_property = { safety: { type: 'boolean' } }
166
+ required = ['safety']
167
+ else
168
+ safety_property = {}
169
+ required = nil
170
+ end
171
+
172
+ ::MCP::Tool.define(
173
+ name: name,
174
+ description: description,
175
+ input_schema: {
176
+ properties: {
177
+ path: { type: 'string' },
178
+ source_code: { type: 'string' }
179
+ }.merge(safety_property),
180
+ required: required
181
+ }.compact,
182
+ annotations: {
183
+ title: title,
184
+ destructive_hint: destructive_hint,
185
+ idempotent_hint: idempotent_hint,
186
+ open_world_hint: false,
187
+ read_only_hint: read_only_hint
188
+ }
189
+ ) do |path: nil, source_code: nil, safety: true|
190
+ result = yield(path, source_code, safety)
191
+
192
+ ::MCP::Tool::Response.new([{ type: 'text', text: result }])
193
+ rescue RuboCop::Error => e
194
+ ::MCP::Tool::Response.new([{ type: 'text', text: e.message }], error: true)
195
+ end
196
+ end
197
+ # rubocop:enable Metrics/MethodLength, Metrics/ParameterLists
198
+ end
199
+ end
200
+ end
@@ -16,7 +16,9 @@ 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[
20
+ version verbose_version show_cops list_enabled_cops_for show_docs_url lsp mcp
21
+ ].freeze
20
22
  DEFAULT_MAXIMUM_EXCLUSION_ITEMS = 15
21
23
 
22
24
  def initialize
@@ -57,6 +59,7 @@ module RuboCop
57
59
  add_check_options(opts)
58
60
  add_cache_options(opts)
59
61
  add_lsp_option(opts)
62
+ add_mcp_option(opts)
60
63
  add_server_options(opts)
61
64
  add_output_options(opts)
62
65
  add_autocorrection_options(opts)
@@ -88,6 +91,8 @@ module RuboCop
88
91
  option(opts, '-F', '--fail-fast')
89
92
  option(opts, '--disable-pending-cops')
90
93
  option(opts, '--enable-pending-cops')
94
+ option(opts, '--disable-all-cops')
95
+ option(opts, '--enable-all-cops')
91
96
  option(opts, '--ignore-disable-comments')
92
97
  option(opts, '--force-exclusion')
93
98
  option(opts, '--only-recognized-file-types')
@@ -215,6 +220,12 @@ module RuboCop
215
220
  end
216
221
  end
217
222
 
223
+ def add_mcp_option(opts)
224
+ section(opts, 'MCP Option') do
225
+ option(opts, '--mcp')
226
+ end
227
+ end
228
+
218
229
  def add_server_options(opts)
219
230
  section(opts, 'Server Options') do
220
231
  option(opts, '--[no-]server')
@@ -229,6 +240,7 @@ module RuboCop
229
240
  def add_additional_modes(opts)
230
241
  section(opts, 'Additional Modes') do
231
242
  option(opts, '-L', '--list-target-files')
243
+ option(opts, '--list-enabled-cops-for PATH')
232
244
  option(opts, '--show-cops [COP1,COP2,...]') do |list|
233
245
  @options[:show_cops] = list.nil? ? [] : list.split(',')
234
246
  end
@@ -386,6 +398,7 @@ module RuboCop
386
398
  validate_display_only_failed_and_display_only_correctable
387
399
  validate_display_only_correctable_and_autocorrect
388
400
  validate_lsp_and_editor_mode
401
+ validate_enable_all_cops_and_disable_all_cops
389
402
  disable_parallel_when_invalid_option_combo
390
403
 
391
404
  return if incompatible_options.size <= 1
@@ -439,6 +452,13 @@ module RuboCop
439
452
  raise OptionArgumentError, 'Do not specify `--editor-mode` as it is redundant in `--lsp`.'
440
453
  end
441
454
 
455
+ def validate_enable_all_cops_and_disable_all_cops
456
+ return if !@options.key?(:enable_all_cops) || !@options.key?(:disable_all_cops)
457
+
458
+ raise OptionArgumentError,
459
+ '--enable-all-cops cannot be used together with --disable-all-cops.'
460
+ end
461
+
442
462
  def validate_autocorrect
443
463
  if @options.key?(:safe_autocorrect) && @options.key?(:autocorrect_all)
444
464
  message = Rainbow(<<~MESSAGE).red
@@ -467,8 +487,7 @@ module RuboCop
467
487
  end
468
488
 
469
489
  def invalid_arguments_for_parallel
470
- [('--auto-gen-config' if @options.key?(:auto_gen_config)),
471
- ('-F/--fail-fast' if @options.key?(:fail_fast)),
490
+ [('-F/--fail-fast' if @options.key?(:fail_fast)),
472
491
  ('--profile' if @options[:profile]),
473
492
  ('--memory' if @options[:memory]),
474
493
  ('--cache false' if @options > { cache: 'false' })].compact
@@ -592,7 +611,7 @@ module RuboCop
592
611
  display_only_correctable: ['Only output correctable offense messages.'],
593
612
  display_only_safe_correctable: ['Only output safe-correctable offense messages',
594
613
  'when combined with --display-only-correctable.'],
595
- show_cops: ['Shows the given cops, or all cops by',
614
+ show_cops: ['Show the given cops, or all cops by',
596
615
  'default, and their configurations for the',
597
616
  'current directory.',
598
617
  'You can use `*` as a wildcard.'],
@@ -612,8 +631,16 @@ module RuboCop
612
631
  display_cop_names: ['Display cop names in offense messages.',
613
632
  'Default is true.'],
614
633
  disable_pending_cops: 'Run without pending cops.',
634
+ disable_all_cops: ['Run with all cops disabled by default,',
635
+ 'except `Lint/Syntax`. Overrides',
636
+ '`AllCops/EnabledByDefault` and',
637
+ '`AllCops/DisabledByDefault` in config files.'],
615
638
  display_style_guide: 'Display style guide URLs in offense messages.',
616
639
  enable_pending_cops: 'Run with pending cops.',
640
+ enable_all_cops: ['Run with all cops enabled, including those',
641
+ 'disabled by default. Overrides',
642
+ '`AllCops/EnabledByDefault` and',
643
+ '`AllCops/DisabledByDefault` in config files.'],
617
644
  extra_details: 'Display extra details in offense messages.',
618
645
  lint: 'Run only lint cops.',
619
646
  safe: 'Run only safe cops.',
@@ -621,6 +648,8 @@ module RuboCop
621
648
  'autocorrected source. This is especially useful',
622
649
  'when combined with --autocorrect and --stdin.'],
623
650
  list_target_files: 'List all files RuboCop will inspect.',
651
+ list_enabled_cops_for: ['List which cops will inspect a given file or',
652
+ 'directory.'],
624
653
  autocorrect: 'Autocorrect offenses (only when it\'s safe).',
625
654
  auto_correct: '(same, deprecated)',
626
655
  safe_auto_correct: '(same, deprecated)',
@@ -651,6 +680,8 @@ module RuboCop
651
680
  server_status: 'Show server status.',
652
681
  no_detach: 'Run the server process in the foreground.',
653
682
  lsp: 'Start a language server listening on STDIN.',
683
+ mcp: ['Start an MCP (Model Context Protocol) server that',
684
+ 'communicates over stdio.'],
654
685
  raise_cop_error: ['Raise cop-related errors with cause and location.',
655
686
  'This is used to prevent cops from failing silently.',
656
687
  '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)
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # Defensive loader for the optional `rubydex` gem.
5
+ #
6
+ # When `AllCops/UseProjectIndex` is enabled in the user's configuration, RuboCop builds
7
+ # a project-wide index using `Rubydex::Graph` and exposes it to cops that opt in.
8
+ # The gem is intentionally not a runtime dependency; if it is not installed,
9
+ # or if the running Ruby is older than what `rubydex` supports, RuboCop falls back to
10
+ # its standard file-local behavior.
11
+ module ProjectIndexLoader
12
+ MINIMUM_RUBY_VERSION = '3.2'
13
+
14
+ module_function
15
+
16
+ # Returns whether the `rubydex` gem can be loaded. The result is memoized
17
+ # for the lifetime of the process.
18
+ def available?
19
+ return @available if defined?(@available)
20
+
21
+ @available = supported_ruby? && try_require
22
+ end
23
+
24
+ def supported_ruby?
25
+ RUBY_VERSION >= MINIMUM_RUBY_VERSION
26
+ end
27
+
28
+ def warn_unavailable
29
+ return if @warned
30
+
31
+ @warned = true
32
+
33
+ if supported_ruby?
34
+ warn Rainbow(<<~MSG).yellow
35
+ `AllCops/UseProjectIndex` is enabled but the `rubydex` gem is not installed.
36
+ Analyses that use the project index will be skipped. Add `gem 'rubydex'` to your Gemfile.
37
+ MSG
38
+ else
39
+ warn Rainbow(<<~MSG).yellow
40
+ `AllCops/UseProjectIndex` is enabled but `rubydex` requires Ruby #{MINIMUM_RUBY_VERSION} or later (current: #{RUBY_VERSION}).
41
+ Analyses that use the project index will be skipped.
42
+ MSG
43
+ end
44
+ end
45
+
46
+ def try_require
47
+ require 'rubydex'
48
+ true
49
+ rescue LoadError
50
+ false
51
+ end
52
+
53
+ # Builds and resolves a `Rubydex::Graph` for the given file paths. Returns the resolved graph,
54
+ # or `nil` if the build fails. This is the only place in RuboCop that depends on the concrete
55
+ # `Rubydex::Graph` API, so callers (e.g. `Runner`) do not need to know which Rubydex classes
56
+ # or methods are involved.
57
+ def build_index(paths)
58
+ graph = Rubydex::Graph.new
59
+ graph.index_all(paths.map(&:to_s))
60
+ graph.resolve
61
+ graph
62
+ rescue StandardError => e
63
+ warn Rainbow("rubydex index build failed: #{e.message}. Continuing without it.").yellow
64
+ end
65
+ end
66
+ end
@@ -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