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
@@ -9,7 +9,7 @@ module RuboCop
9
9
  # Provides functionality for caching RuboCop runs.
10
10
  # @api private
11
11
  class ResultCache
12
- NON_CHANGING = %i[color format formatters out debug fail_level
12
+ NON_CHANGING = %i[color format formatters out debug display_time fail_level
13
13
  fix_layout autocorrect safe_autocorrect autocorrect_all
14
14
  cache fail_fast stdin parallel].freeze
15
15
 
@@ -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
@@ -203,6 +212,27 @@ RSpec.shared_context 'lsp' do
203
212
  end
204
213
  end
205
214
 
215
+ RSpec.shared_context 'with exclude limit tracking' do
216
+ around do |example|
217
+ Dir.mktmpdir('rubocop-exclude-limit') do |dir|
218
+ RuboCop::ExcludeLimit.tmp_dir = Pathname.new(dir)
219
+ example.run
220
+ ensure
221
+ RuboCop::ExcludeLimit.tmp_dir = nil
222
+ end
223
+ end
224
+
225
+ # Reads exclude_limit values from the tmp files written by ExcludeLimit.
226
+ # Returns a hash like { 'Max' => 81 } or nil if no values were written.
227
+ def read_exclude_limit(cop, parameter_name = nil)
228
+ if parameter_name
229
+ read_exclude_limit(cop)[parameter_name]
230
+ else
231
+ RuboCop::ExcludeLimit.read_limits(cop.class.badge.to_s)
232
+ end
233
+ end
234
+ end
235
+
206
236
  RSpec.shared_context 'ruby 2.0' do
207
237
  # Prism supports parsing Ruby 3.3+.
208
238
  let(:ruby_version) { ENV['PARSER_ENGINE'] == 'parser_prism' ? 3.3 : 2.0 }
@@ -266,6 +296,10 @@ RSpec.shared_context 'ruby 3.4' do
266
296
  let(:ruby_version) { 3.4 }
267
297
  end
268
298
 
269
- RSpec.shared_context 'ruby 3.5' do
270
- let(:ruby_version) { 3.5 }
299
+ RSpec.shared_context 'ruby 4.0' do
300
+ let(:ruby_version) { 4.0 }
301
+ end
302
+
303
+ RSpec.shared_context 'ruby 4.1' do
304
+ let(:ruby_version) { 4.1 }
271
305
  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
@@ -62,14 +62,21 @@ module RuboCop
62
62
  @errors = []
63
63
  @warnings = []
64
64
  @aborting = false
65
+ @inspected_files = []
66
+ @report_queue = {}
65
67
  end
66
68
 
67
69
  def run(paths)
70
+ # Compute the cache source checksum once to avoid potential
71
+ # inconsistencies between workers.
72
+ ResultCache.source_checksum
73
+
68
74
  target_files = find_target_files(paths)
75
+ @project_index = ProjectIndexLoader.build_index(target_files) if project_index_enabled?
76
+
69
77
  if @options[:list_target_files]
70
78
  list_files(target_files)
71
79
  else
72
- warm_cache(target_files) if @options[:parallel]
73
80
  inspect_files(target_files)
74
81
  end
75
82
  rescue Interrupt
@@ -86,21 +93,6 @@ module RuboCop
86
93
 
87
94
  private
88
95
 
89
- # Warms up the RuboCop cache by forking a suitable number of RuboCop
90
- # instances that each inspects its allotted group of files.
91
- def warm_cache(target_files)
92
- saved_options = @options.dup
93
- if target_files.length <= 1
94
- puts 'Skipping parallel inspection: only a single file needs inspection' if @options[:debug]
95
- return
96
- end
97
- puts 'Running parallel inspection' if @options[:debug]
98
- %i[autocorrect safe_autocorrect].each { |opt| @options[opt] = false }
99
- Parallel.each(target_files) { |target_file| file_offenses(target_file) }
100
- ensure
101
- @options = saved_options
102
- end
103
-
104
96
  def find_target_files(paths)
105
97
  target_finder = TargetFinder.new(@config_store, @options)
106
98
  mode = if @options[:only_recognized_file_types]
@@ -112,12 +104,26 @@ module RuboCop
112
104
  target_files.each(&:freeze).freeze
113
105
  end
114
106
 
115
- def inspect_files(files)
116
- inspected_files = []
107
+ def project_index_enabled?
108
+ return false unless @config_store.for_pwd.for_all_cops['UseProjectIndex']
117
109
 
110
+ unless ProjectIndexLoader.available?
111
+ ProjectIndexLoader.warn_unavailable
112
+ return false
113
+ end
114
+
115
+ true
116
+ end
117
+
118
+ def inspect_files(files) # rubocop:disable Metrics/AbcSize
118
119
  formatter_set.started(files)
120
+ file_iterator(files) do |file|
121
+ offenses = process_file(file)
122
+ succeeded = offenses.none? { |o| considered_failure?(o) && offense_displayed?(o) }
123
+ raise Parallel::Break if @options[:fail_fast] && !succeeded
119
124
 
120
- each_inspected_file(files) { |file| inspected_files << file }
125
+ [offenses, succeeded]
126
+ end
121
127
  ensure
122
128
  # OPTIMIZE: Calling `ResultCache.cleanup` takes time. This optimization
123
129
  # mainly targets editors that integrates RuboCop. When RuboCop is run
@@ -125,23 +131,88 @@ module RuboCop
125
131
  if files.size > 1 && cached_run?
126
132
  ResultCache.cleanup(@config_store, @options[:debug], @options[:cache_root])
127
133
  end
128
-
129
- formatter_set.finished(inspected_files.freeze)
134
+ formatter_set.finished(@inspected_files.freeze)
130
135
  formatter_set.close_output_files
131
136
  end
132
137
 
133
- def each_inspected_file(files)
134
- files.reduce(true) do |all_passed, file|
135
- offenses = process_file(file)
136
- yield file
138
+ def file_iterator(files, &block)
139
+ all_passed = true
137
140
 
138
- if offenses.any? { |o| considered_failure?(o) && offense_displayed?(o) }
139
- break false if @options[:fail_fast]
141
+ on_start = ->(file, _index) { file_started(file) }
142
+ on_finish = lambda do |file, index, (offenses, passed)|
143
+ all_passed &&= passed
144
+ finished_report(file, index, offenses)
145
+ end
140
146
 
141
- next false
142
- end
147
+ if run_in_parallel?(files)
148
+ parallel_file_iterator(files, on_start, on_finish, &block)
149
+ else
150
+ serial_file_iterator(files, on_start, on_finish, &block)
151
+ end
152
+
153
+ process_remaining_report_queue
154
+
155
+ all_passed
156
+ end
157
+
158
+ def finished_report(file, index, offenses)
159
+ @report_queue[index] = [file, offenses]
160
+ @next_index_to_report ||= 0
161
+ while @report_queue.key?(@next_index_to_report)
162
+ process_report_queue_entry(@next_index_to_report)
163
+ @next_index_to_report += 1
164
+ end
165
+ end
166
+
167
+ def process_report_queue_entry(index)
168
+ file, offenses = @report_queue.delete(index)
169
+ file_finished(file, offenses)
170
+ end
171
+
172
+ def process_remaining_report_queue
173
+ @report_queue.keys.sort.each do |index|
174
+ process_report_queue_entry(index)
175
+ end
176
+ end
143
177
 
144
- all_passed
178
+ def run_in_parallel?(files)
179
+ return false if @options[:auto_gen_config]
180
+ return false unless @options[:parallel]
181
+
182
+ if files.size <= 1
183
+ puts 'Skipping parallel inspection: only a single file needs inspection' if @options[:debug]
184
+ return false
185
+ end
186
+
187
+ return false if project_index_disables_parallel?
188
+
189
+ puts 'Running parallel inspection' if @options[:debug]
190
+
191
+ true
192
+ end
193
+
194
+ def project_index_disables_parallel?
195
+ return false if @project_index.nil? || !Gem.win_platform?
196
+
197
+ if @options[:debug]
198
+ puts 'Skipping parallel inspection: the project index is enabled and parallel ' \
199
+ 'inspection is not yet supported on Windows.'
200
+ end
201
+
202
+ true
203
+ end
204
+
205
+ def parallel_file_iterator(files, on_start, on_finish, &block)
206
+ Parallel.each(files, start: on_start, finish: on_finish, &block)
207
+ end
208
+
209
+ def serial_file_iterator(files, on_start, on_finish, &block)
210
+ files.each_with_index do |file, index|
211
+ on_start.call(file, index)
212
+ result = yield file
213
+ on_finish.call(file, index, result)
214
+ rescue Parallel::Break
215
+ break
145
216
  end
146
217
  end
147
218
 
@@ -150,16 +221,13 @@ module RuboCop
150
221
  end
151
222
 
152
223
  def process_file(file)
153
- file_started(file)
154
- offenses = file_offenses(file)
224
+ file_offenses(file)
155
225
  rescue InfiniteCorrectionLoop => e
156
226
  raise e if @options[:raise_cop_error]
157
227
 
158
228
  errors << e
159
229
  warn Rainbow(e.message).red
160
- offenses = e.offenses.compact.sort.freeze
161
- ensure
162
- file_finished(file, offenses || [])
230
+ e.offenses.compact.sort.freeze
163
231
  end
164
232
 
165
233
  def file_offenses(file)
@@ -190,7 +258,7 @@ module RuboCop
190
258
 
191
259
  if real_run_needed
192
260
  offenses = yield
193
- save_in_cache(cache, offenses)
261
+ save_in_cache(cache, offenses) unless Cop::Registry.global.warnings?(file)
194
262
  end
195
263
 
196
264
  offenses
@@ -246,6 +314,7 @@ module RuboCop
246
314
  end
247
315
 
248
316
  def file_finished(file, offenses)
317
+ @inspected_files << file
249
318
  offenses = offenses_to_report(offenses)
250
319
  formatter_set.file_finished(file, offenses)
251
320
  end
@@ -254,10 +323,6 @@ module RuboCop
254
323
  @cached_run ||=
255
324
  (@options[:cache] == 'true' ||
256
325
  (@options[:cache] != 'false' && @config_store.for_pwd.for_all_cops['UseCache'])) &&
257
- # When running --auto-gen-config, there's some processing done in the
258
- # cops related to calculating the Max parameters for Metrics cops. We
259
- # need to do that processing and cannot use caching.
260
- !@options[:auto_gen_config] &&
261
326
  # We can't cache results from code which is piped in to stdin
262
327
  !@options[:stdin]
263
328
  end
@@ -273,7 +338,8 @@ module RuboCop
273
338
  end
274
339
 
275
340
  def do_inspection_loop(file)
276
- processed_source = get_processed_source(file)
341
+ # We can reuse the prism result since the source did not change yet.
342
+ processed_source = get_processed_source(file, @prism_result)
277
343
  # This variable is 2d array used to track corrected offenses after each
278
344
  # inspection iteration. This is used to output meaningful infinite loop
279
345
  # error message.
@@ -295,7 +361,8 @@ module RuboCop
295
361
  # loop if we find any.
296
362
  break unless updated_source_file
297
363
 
298
- processed_source = get_processed_source(file)
364
+ # Autocorrect has happened, don't use the prism result since it is stale.
365
+ processed_source = get_processed_source(file, nil)
299
366
  end
300
367
 
301
368
  # Return summary of corrected offenses after all iterations
@@ -344,16 +411,9 @@ module RuboCop
344
411
 
345
412
  def inspect_file(processed_source, team = mobilize_team(processed_source))
346
413
  extracted_ruby_sources = extract_ruby_sources(processed_source)
347
- offenses = extracted_ruby_sources.flat_map do |extracted_ruby_source|
348
- report = team.investigate(
349
- extracted_ruby_source[:processed_source],
350
- offset: extracted_ruby_source[:offset],
351
- original: processed_source
352
- )
353
- @errors.concat(team.errors)
354
- @warnings.concat(team.warnings)
355
- report.offenses
356
- end
414
+ offenses = team.investigate_fragments(extracted_ruby_sources, original: processed_source)
415
+ @errors.concat(team.errors)
416
+ @warnings.concat(team.warnings)
357
417
  [offenses, team.updated_source_file?]
358
418
  end
359
419
 
@@ -373,7 +433,15 @@ module RuboCop
373
433
 
374
434
  def mobilize_team(processed_source)
375
435
  config = @config_store.for_file(processed_source.path)
376
- Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
436
+ team = Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
437
+
438
+ if @project_index
439
+ team.cops.each do |cop|
440
+ cop.project_index = @project_index
441
+ end
442
+ end
443
+
444
+ team
377
445
  end
378
446
 
379
447
  def mobilized_cop_classes(config) # rubocop:disable Metrics/AbcSize
@@ -482,7 +550,7 @@ module RuboCop
482
550
  end
483
551
 
484
552
  # rubocop:disable Metrics/MethodLength
485
- def get_processed_source(file)
553
+ def get_processed_source(file, prism_result)
486
554
  config = @config_store.for_file(file)
487
555
  ruby_version = config.target_ruby_version
488
556
  parser_engine = config.parser_engine
@@ -493,7 +561,7 @@ module RuboCop
493
561
  ruby_version,
494
562
  file,
495
563
  parser_engine: parser_engine,
496
- prism_result: @prism_result
564
+ prism_result: prism_result
497
565
  )
498
566
  else
499
567
  begin
@@ -516,8 +584,17 @@ module RuboCop
516
584
  # level caching in ResultCache.
517
585
  def standby_team(config)
518
586
  @team_by_config ||= {}.compare_by_identity
519
- @team_by_config[config] ||=
520
- Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
587
+ @team_by_config[config] ||= begin
588
+ team = Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
589
+
590
+ if @project_index
591
+ team.cops.each do |cop|
592
+ cop.project_index = @project_index
593
+ end
594
+ end
595
+
596
+ team
597
+ end
521
598
  end
522
599
  end
523
600
  end
@@ -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
@@ -55,6 +57,12 @@ module RuboCop
55
57
  end
56
58
 
57
59
  Process.waitpid(pid)
60
+
61
+ # The daemon writes its pid file asynchronously after forking, so wait until
62
+ # the server is actually running before returning. This prevents a race where
63
+ # a subsequent command (e.g. `--restart-server`) observes an inconsistent
64
+ # state right after `--start-server` returns.
65
+ Server.wait_for_running_status!(true)
58
66
  end
59
67
 
60
68
  def write_port_and_token_files