rubocop 1.84.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 (313) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +180 -86
  3. data/config/obsoletion.yml +26 -1
  4. data/lib/rubocop/cache_config.rb +1 -1
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +34 -2
  6. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  7. data/lib/rubocop/cli/command/mcp.rb +19 -0
  8. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  9. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  11. data/lib/rubocop/cli.rb +9 -7
  12. data/lib/rubocop/comment_config.rb +12 -15
  13. data/lib/rubocop/config.rb +14 -10
  14. data/lib/rubocop/config_finder.rb +1 -1
  15. data/lib/rubocop/config_loader.rb +17 -2
  16. data/lib/rubocop/config_loader_resolver.rb +13 -4
  17. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  18. data/lib/rubocop/config_store.rb +2 -2
  19. data/lib/rubocop/config_validator.rb +1 -1
  20. data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
  21. data/lib/rubocop/cop/base.rb +25 -4
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  23. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  24. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  25. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  26. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  27. data/lib/rubocop/cop/correctors.rb +28 -0
  28. data/lib/rubocop/cop/documentation.rb +2 -3
  29. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  30. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  31. data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
  32. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  33. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  34. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  35. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  36. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  37. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  38. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  39. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  40. data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
  41. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  42. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  43. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +23 -7
  44. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  45. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  46. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  47. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  48. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  49. data/lib/rubocop/cop/layout/end_alignment.rb +8 -5
  50. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  51. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  52. data/lib/rubocop/cop/layout/indentation_width.rb +12 -0
  53. data/lib/rubocop/cop/layout/line_length.rb +5 -3
  54. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  55. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  56. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
  57. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  58. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  59. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  60. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  61. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  62. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
  63. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  64. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  65. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  66. data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
  67. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  68. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  69. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  70. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  71. data/lib/rubocop/cop/lint/debugger.rb +0 -1
  72. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  73. data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
  74. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  75. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  76. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  77. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  78. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  79. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  80. data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
  81. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  82. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -5
  83. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  84. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  85. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
  86. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +5 -5
  87. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  88. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  89. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
  90. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  91. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  92. data/lib/rubocop/cop/lint/number_conversion.rb +19 -10
  93. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  94. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
  95. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  96. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  97. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  98. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  99. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
  100. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -12
  101. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  102. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +36 -12
  103. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
  104. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
  105. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  106. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  107. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  108. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  109. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  110. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  111. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  112. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  113. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -0
  114. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  115. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  116. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  117. data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
  118. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  119. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  120. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  121. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  122. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  123. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  124. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  125. data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
  126. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  127. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  128. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
  129. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  130. data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
  131. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  132. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  133. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -14
  134. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  135. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  136. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
  137. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  138. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  139. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
  140. data/lib/rubocop/cop/lint/void.rb +32 -12
  141. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  142. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  143. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  144. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  145. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  146. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  147. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  148. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  149. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  150. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  151. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  152. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  153. data/lib/rubocop/cop/mixin.rb +86 -0
  154. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  155. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  156. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  157. data/lib/rubocop/cop/naming/predicate_method.rb +2 -2
  158. data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
  159. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  160. data/lib/rubocop/cop/offense.rb +8 -0
  161. data/lib/rubocop/cop/registry.rb +62 -38
  162. data/lib/rubocop/cop/security/eval.rb +15 -2
  163. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  164. data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
  165. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  166. data/lib/rubocop/cop/style/alias.rb +15 -3
  167. data/lib/rubocop/cop/style/and_or.rb +2 -1
  168. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  169. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  170. data/lib/rubocop/cop/style/array_intersect.rb +4 -0
  171. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
  172. data/lib/rubocop/cop/style/array_join.rb +4 -2
  173. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  174. data/lib/rubocop/cop/style/attr.rb +5 -2
  175. data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
  176. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  177. data/lib/rubocop/cop/style/block_delimiters.rb +37 -31
  178. data/lib/rubocop/cop/style/case_equality.rb +18 -2
  179. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  180. data/lib/rubocop/cop/style/class_and_module_children.rb +18 -2
  181. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  182. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  183. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  184. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  185. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  186. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  187. data/lib/rubocop/cop/style/concat_array_literals.rb +7 -1
  188. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -5
  189. data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
  190. data/lib/rubocop/cop/style/copyright.rb +22 -11
  191. data/lib/rubocop/cop/style/date_time.rb +4 -4
  192. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  193. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  194. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  195. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  196. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  197. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  198. data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
  199. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  200. data/lib/rubocop/cop/style/encoding.rb +7 -1
  201. data/lib/rubocop/cop/style/end_block.rb +3 -1
  202. data/lib/rubocop/cop/style/endless_method.rb +8 -3
  203. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  204. data/lib/rubocop/cop/style/file_open.rb +84 -0
  205. data/lib/rubocop/cop/style/file_write.rb +21 -16
  206. data/lib/rubocop/cop/style/for.rb +3 -0
  207. data/lib/rubocop/cop/style/format_string.rb +4 -3
  208. data/lib/rubocop/cop/style/format_string_token.rb +29 -2
  209. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  210. data/lib/rubocop/cop/style/guard_clause.rb +9 -6
  211. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
  212. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  213. data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
  214. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  215. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  216. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  217. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  218. data/lib/rubocop/cop/style/if_unless_modifier.rb +15 -4
  219. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  220. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  221. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  222. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  223. data/lib/rubocop/cop/style/map_join.rb +123 -0
  224. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
  225. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  226. data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
  227. data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
  228. data/lib/rubocop/cop/style/mutable_constant.rb +106 -12
  229. data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
  230. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  231. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  232. data/lib/rubocop/cop/style/not.rb +2 -0
  233. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  234. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  235. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  236. data/lib/rubocop/cop/style/parallel_assignment.rb +12 -1
  237. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  238. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  239. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  240. data/lib/rubocop/cop/style/proc.rb +3 -2
  241. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  242. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  243. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  244. data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
  245. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  246. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  247. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  248. data/lib/rubocop/cop/style/redundant_format.rb +1 -0
  249. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  250. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  251. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  252. data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
  253. data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
  254. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  255. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  256. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  257. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  258. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  259. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  260. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  261. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  262. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  263. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  264. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  265. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  266. data/lib/rubocop/cop/style/semicolon.rb +18 -1
  267. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  268. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  269. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  270. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  271. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  272. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  273. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  274. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  275. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  276. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  277. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  278. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  279. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  280. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  281. data/lib/rubocop/cop/style/word_array.rb +1 -0
  282. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  283. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  284. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  285. data/lib/rubocop/cop/team.rb +86 -35
  286. data/lib/rubocop/cop/variable_force/branch.rb +2 -2
  287. data/lib/rubocop/directive_comment.rb +2 -1
  288. data/lib/rubocop/file_patterns.rb +9 -1
  289. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -9
  290. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  291. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  292. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  293. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  294. data/lib/rubocop/formatter.rb +22 -21
  295. data/lib/rubocop/lsp/diagnostic.rb +1 -0
  296. data/lib/rubocop/lsp/routes.rb +10 -3
  297. data/lib/rubocop/lsp/runtime.rb +1 -2
  298. data/lib/rubocop/mcp/server.rb +200 -0
  299. data/lib/rubocop/options.rb +35 -4
  300. data/lib/rubocop/path_util.rb +14 -2
  301. data/lib/rubocop/plugin/loader.rb +1 -1
  302. data/lib/rubocop/project_index_loader.rb +66 -0
  303. data/lib/rubocop/result_cache.rb +22 -10
  304. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  305. data/lib/rubocop/rspec/shared_contexts.rb +32 -2
  306. data/lib/rubocop/runner.rb +124 -53
  307. data/lib/rubocop/server/cache.rb +5 -7
  308. data/lib/rubocop/server/core.rb +8 -0
  309. data/lib/rubocop/target_finder.rb +14 -7
  310. data/lib/rubocop/target_ruby.rb +18 -12
  311. data/lib/rubocop/version.rb +21 -3
  312. data/lib/rubocop.rb +22 -96
  313. metadata +27 -5
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Naming
6
- # Makes sure that rescued exceptions variables are named as
6
+ # Makes sure that rescued exception variables are named as
7
7
  # expected.
8
8
  #
9
9
  # The `PreferredName` config option takes a `String`. It represents
@@ -98,6 +98,14 @@ module RuboCop
98
98
  freeze
99
99
  end
100
100
 
101
+ def marshal_dump
102
+ [@severity, @location, @message, @cop_name, @status]
103
+ end
104
+
105
+ def marshal_load(array)
106
+ @severity, @location, @message, @cop_name, @status = array
107
+ end
108
+
101
109
  # @api public
102
110
  #
103
111
  # @!attribute [r] correctable?
@@ -16,7 +16,7 @@ module RuboCop
16
16
  end
17
17
 
18
18
  # Registry that tracks all cops by their badge and department.
19
- class Registry
19
+ class Registry # rubocop:disable Metrics/ClassLength
20
20
  include Enumerable
21
21
 
22
22
  def self.all
@@ -46,18 +46,25 @@ module RuboCop
46
46
  global.qualify_badge(badge).first == badge
47
47
  end
48
48
 
49
- attr_reader :options
49
+ attr_reader :options, :warnings
50
50
 
51
51
  def initialize(cops = [], options = {})
52
- @registry = {}
53
- @departments = {}
54
- @cops_by_cop_name = Hash.new { |hash, key| hash[key] = [] }
52
+ @departments = Set.new
53
+ @cops_by_badge = {}
54
+ @lazy_loaded_cops_by_badge = {}
55
55
 
56
56
  @enrollment_queue = cops
57
57
  @options = options
58
58
 
59
59
  @enabled_cache = {}.compare_by_identity
60
60
  @disabled_cache = {}.compare_by_identity
61
+ @warnings = {}
62
+ end
63
+
64
+ def lazy_load(cop_name, constant_name)
65
+ badge = Badge.parse(cop_name)
66
+ @departments << badge.department
67
+ @lazy_loaded_cops_by_badge[badge] = constant_name
61
68
  end
62
69
 
63
70
  def enlist(cop)
@@ -71,22 +78,17 @@ module RuboCop
71
78
  # @return [Array<Symbol>] list of departments for current cops.
72
79
  def departments
73
80
  clear_enrollment_queue
74
- @departments.keys
81
+ @departments.to_a
75
82
  end
76
83
 
77
84
  # @return [Registry] Cops for that specific department.
78
85
  def with_department(department)
79
- clear_enrollment_queue
80
- with(@departments.fetch(department, []))
86
+ with(cops.select { |cop| cop.department == department })
81
87
  end
82
88
 
83
89
  # @return [Registry] Cops not for a specific department.
84
90
  def without_department(department)
85
- clear_enrollment_queue
86
- without_department = @departments.dup
87
- without_department.delete(department)
88
-
89
- with(without_department.values.flatten)
91
+ with(cops.reject { |cop| cop.department == department })
90
92
  end
91
93
 
92
94
  # @return [Boolean] Checks if given name is department
@@ -132,7 +134,7 @@ module RuboCop
132
134
  # @return [String] Qualified cop name
133
135
  def qualified_cop_name(name, path, warn: true)
134
136
  badge = Badge.parse(name)
135
- print_warning(name, path) if warn && department_missing?(badge, name)
137
+ print_department_missing_warning(name, path) if warn && department_missing?(badge, name)
136
138
  return name if registered?(badge)
137
139
 
138
140
  potential_badges = qualify_badge(badge)
@@ -148,42 +150,44 @@ module RuboCop
148
150
  !badge.qualified? && unqualified_cop_names.include?(name)
149
151
  end
150
152
 
151
- def print_warning(name, path)
152
- message = "#{path}: Warning: no department given for #{name}."
153
+ def print_department_missing_warning(name, path)
154
+ message = "no department given for #{name}."
153
155
  if path.end_with?('.rb')
154
156
  message += ' Run `rubocop -a --only Migration/DepartmentName` to fix.'
155
157
  end
156
- warn message
158
+ emit_warning(path, message)
157
159
  end
158
160
 
159
161
  def unqualified_cop_names
160
162
  clear_enrollment_queue
161
163
  @unqualified_cop_names ||=
162
- Set.new(@cops_by_cop_name.keys.map { |qn| File.basename(qn) }) <<
163
- 'RedundantCopDisableDirective'
164
+ (@cops_by_badge.keys | @lazy_loaded_cops_by_badge.keys)
165
+ .to_set { |badge| File.basename(badge.to_s) } << 'RedundantCopDisableDirective'
164
166
  end
165
167
 
166
168
  def qualify_badge(badge)
167
169
  clear_enrollment_queue
168
170
  @departments
169
- .map { |department, _| badge.with_department(department) }
171
+ .map { |department| badge.with_department(department) }
170
172
  .select { |potential_badge| registered?(potential_badge) }
171
173
  end
172
174
 
173
175
  # @return [Hash{String => Array<Class>}]
174
176
  def to_h
175
177
  clear_enrollment_queue
176
- @cops_by_cop_name
178
+ load_all_lazy_cops
179
+ @cops_by_badge.to_h { |_badge, cop| [cop.cop_name, [cop]] }
177
180
  end
178
181
 
179
182
  def cops
180
183
  clear_enrollment_queue
181
- @registry.values
184
+ load_all_lazy_cops
185
+ @cops_by_badge.values
182
186
  end
183
187
 
184
188
  def length
185
189
  clear_enrollment_queue
186
- @registry.size
190
+ @cops_by_badge.size + @lazy_loaded_cops_by_badge.size
187
191
  end
188
192
 
189
193
  def enabled(config)
@@ -218,7 +222,8 @@ module RuboCop
218
222
  end
219
223
 
220
224
  def names
221
- cops.map(&:cop_name)
225
+ clear_enrollment_queue
226
+ @cops_by_badge.keys.map(&:to_s) | @lazy_loaded_cops_by_badge.keys.map(&:to_s)
222
227
  end
223
228
 
224
229
  def cops_for_department(department)
@@ -235,7 +240,8 @@ module RuboCop
235
240
 
236
241
  def sort!
237
242
  clear_enrollment_queue
238
- @registry = @registry.sort_by { |badge, _| badge.cop_name }.to_h
243
+ load_all_lazy_cops
244
+ @cops_by_badge = @cops_by_badge.sort_by { |badge, _cop| badge.cop_name }.to_h
239
245
 
240
246
  self
241
247
  end
@@ -251,7 +257,9 @@ module RuboCop
251
257
  # @param [String] cop_name
252
258
  # @return [Class, nil]
253
259
  def find_by_cop_name(cop_name)
254
- to_h[cop_name].first
260
+ clear_enrollment_queue
261
+ badge = Badge.parse(cop_name)
262
+ @cops_by_badge[badge] || load_lazy_cop(badge)
255
263
  end
256
264
 
257
265
  # When a cop name is given returns a single-element array with the cop class.
@@ -264,6 +272,7 @@ module RuboCop
264
272
 
265
273
  def freeze
266
274
  clear_enrollment_queue
275
+ load_all_lazy_cops
267
276
  unqualified_cop_names # build cache
268
277
  super
269
278
  end
@@ -274,6 +283,10 @@ module RuboCop
274
283
  attr_reader :global
275
284
  end
276
285
 
286
+ def warnings?(path)
287
+ @warnings[path]
288
+ end
289
+
277
290
  private
278
291
 
279
292
  def initialize_copy(reg)
@@ -284,34 +297,45 @@ module RuboCop
284
297
  return if @enrollment_queue.empty?
285
298
 
286
299
  @enrollment_queue.each do |cop|
287
- @registry[cop.badge] = cop
288
- @departments[cop.department] ||= []
289
- @departments[cop.department] << cop
290
- @cops_by_cop_name[cop.cop_name] << cop
300
+ @cops_by_badge[cop.badge] = cop
301
+ @departments << cop.department
291
302
  end
292
303
  @enrollment_queue = []
293
304
  end
294
305
 
306
+ def load_all_lazy_cops
307
+ @lazy_loaded_cops_by_badge.each_key { |badge| load_lazy_cop(badge) }
308
+ end
309
+
310
+ def load_lazy_cop(badge)
311
+ constant_name = @lazy_loaded_cops_by_badge.delete(badge)
312
+ return unless constant_name
313
+
314
+ @cops_by_badge[badge] = Kernel.const_get(constant_name)
315
+ end
316
+
295
317
  def with(cops)
296
318
  self.class.new(cops)
297
319
  end
298
320
 
299
321
  def resolve_badge(given_badge, real_badge, source_path, warn: true)
300
- unless given_badge.match?(real_badge)
301
- path = PathUtil.smart_path(source_path)
302
-
303
- if warn
304
- warn("#{path}: #{given_badge} has the wrong namespace - " \
305
- "replace it with #{given_badge.with_department(real_badge.department)}")
306
- end
322
+ if warn && !given_badge.match?(real_badge)
323
+ emit_warning(source_path,
324
+ "#{given_badge} has the wrong namespace - " \
325
+ "replace it with #{given_badge.with_department(real_badge.department)}")
307
326
  end
308
327
 
309
328
  real_badge.to_s
310
329
  end
311
330
 
331
+ def emit_warning(path, message)
332
+ Registry.global.warnings[path] = true
333
+ warn "#{PathUtil.smart_path(path)}: Warning: #{message}"
334
+ end
335
+
312
336
  def registered?(badge)
313
337
  clear_enrollment_queue
314
- @registry.key?(badge)
338
+ @cops_by_badge.key?(badge) || @lazy_loaded_cops_by_badge.key?(badge)
315
339
  end
316
340
  end
317
341
  end
@@ -3,15 +3,28 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Security
6
- # Checks for the use of `Kernel#eval` and `Binding#eval`.
6
+ # Checks for the use of `Kernel#eval` and `Binding#eval` with
7
+ # dynamic strings as arguments. Evaluating non-literal strings
8
+ # can enable code injection attacks and makes it difficult to
9
+ # reason about what code will actually be executed.
10
+ #
11
+ # Calls to `eval` with literal strings are not flagged by this cop,
12
+ # as they do not pose the same injection risk.
7
13
  #
8
14
  # @example
9
15
  #
10
16
  # # bad
11
- #
12
17
  # eval(something)
13
18
  # binding.eval(something)
14
19
  # Kernel.eval(something)
20
+ #
21
+ # # good - use safer alternatives
22
+ # obj.public_send(method_name)
23
+ # obj.send(method_name, *args)
24
+ #
25
+ # # good - literal strings are allowed
26
+ # eval("1 + 1")
27
+ # binding.eval("foo")
15
28
  class Eval < Base
16
29
  MSG = 'The use of `eval` is a serious security risk.'
17
30
  RESTRICT_ON_SEND = %i[eval].freeze
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # a subprocess is created in the same way as `Kernel#open`, and its output is returned.
11
11
  # `Kernel#open` may allow unintentional command injection, which is the reason these
12
12
  # `IO` methods are a security risk.
13
- # Consider to use `File.read` to disable the behavior of subprocess invocation.
13
+ # Consider using `File.read` to disable the behavior of subprocess invocation.
14
14
  #
15
15
  # @safety
16
16
  # This cop is unsafe because false positive will occur if the variable passed as
@@ -348,8 +348,20 @@ module RuboCop
348
348
 
349
349
  def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
350
350
  def_node = begin_node.children[begin_node.children.index(modifier_node) + 1]
351
- range = modifier_node.source_range.begin.join(def_node.source_range.begin)
352
- corrector.remove(range)
351
+ # Stop the removal range at the first comment that precedes the def, if
352
+ # any exist. Without this, comments between the modifier and the def are
353
+ # dropped because they fall inside the removed range.
354
+ end_pos = first_comment_or_node_start(def_node)
355
+ corrector.remove(modifier_node.source_range.begin.join(end_pos))
356
+ end
357
+
358
+ def first_comment_or_node_start(node)
359
+ preceding = processed_source.ast_with_comments[node].select do |comment|
360
+ comment.loc.line < node.loc.line
361
+ end
362
+ return node.source_range.begin if preceding.empty?
363
+
364
+ preceding.first.source_range.begin
353
365
  end
354
366
 
355
367
  def def_source(node, def_nodes)
@@ -4,8 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for grouping of accessors in `class` and `module` bodies.
7
- # By default it enforces accessors to be placed in grouped declarations,
8
- # but it can be configured to enforce separating them in multiple declarations.
7
+ # By default it enforces accessors to be placed in grouped
8
+ # declarations, reducing boilerplate. It can also be configured
9
+ # to enforce separating them into individual declarations for
10
+ # easier diffing and per-attribute documentation.
9
11
  #
10
12
  # NOTE: If there is a method call before the accessor method it is always allowed
11
13
  # as it might be intended like Sorbet.
@@ -4,10 +4,13 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Enforces the use of either `#alias` or `#alias_method`
7
- # depending on configuration.
7
+ # depending on configuration. Consistent use of one or the
8
+ # other prevents confusion about their different semantics
9
+ # (e.g., `alias` is resolved at parse time, while `alias_method`
10
+ # is resolved at runtime).
8
11
  # It also flags uses of `alias :symbol` rather than `alias bareword`.
9
12
  #
10
- # However, it will always enforce `method_alias` when used `alias`
13
+ # However, it will always enforce `alias_method` when `alias` is used
11
14
  # in an instance method definition and in a singleton method definition.
12
15
  # If used in a block, always enforce `alias_method`
13
16
  # unless it is an `instance_eval` block.
@@ -42,6 +45,7 @@ module RuboCop
42
45
  return unless node.command?(:alias_method)
43
46
  return unless style == :prefer_alias && alias_keyword_possible?(node)
44
47
  return unless node.arguments.count == 2
48
+ return if alias_method_value_used?(node)
45
49
 
46
50
  msg = format(MSG_ALIAS_METHOD, current: lexical_scope_type(node))
47
51
  add_offense(node.loc.selector, message: msg) do |corrector|
@@ -77,6 +81,14 @@ module RuboCop
77
81
  scope_type(node) != :dynamic && node.arguments.all?(&:sym_type?)
78
82
  end
79
83
 
84
+ # `alias_method` is a method call whose return value can be used
85
+ # (e.g., as an argument to `public`/`module_function`, or as an assignment),
86
+ # but `alias` is a keyword statement that cannot appear in such positions.
87
+ # Detect these positions so the conversion does not produce a syntax error.
88
+ def alias_method_value_used?(node)
89
+ node.argument? || node.parent&.assignment?
90
+ end
91
+
80
92
  def alias_method_possible?(node)
81
93
  scope_type(node) != :instance_eval &&
82
94
  node.children.none?(&:gvar_type?) &&
@@ -101,7 +113,7 @@ module RuboCop
101
113
  return :lexical
102
114
  when :def, :defs
103
115
  return :dynamic
104
- when :block
116
+ when :block, :numblock, :itblock
105
117
  return :instance_eval if parent.method?(:instance_eval)
106
118
 
107
119
  return :dynamic
@@ -71,7 +71,7 @@ module RuboCop
71
71
  node.each_child_node do |expr|
72
72
  if expr.send_type?
73
73
  correct_send(expr, corrector)
74
- elsif expr.return_type? || expr.assignment?
74
+ elsif expr.type?(:return, :next, :break, :yield) || expr.assignment?
75
75
  correct_other(expr, corrector)
76
76
  end
77
77
  end
@@ -128,6 +128,7 @@ module RuboCop
128
128
 
129
129
  def correct_other(node, corrector)
130
130
  return if node.parenthesized_call?
131
+ return if node.children.empty? && node.equal?(node.parent.children.last)
131
132
 
132
133
  corrector.wrap(node, '(', ')')
133
134
  end
@@ -424,21 +424,39 @@ module RuboCop
424
424
  end
425
425
 
426
426
  def forwarded_rest_arg
427
- return nil if referenced_rest_arg?
427
+ return @forwarded_rest_arg if defined?(@forwarded_rest_arg)
428
428
 
429
- arguments.find { |arg| forwarded_rest_arg?(arg, @rest_arg_name) }
429
+ @forwarded_rest_arg = if referenced_rest_arg?
430
+ nil
431
+ else
432
+ arguments.find do |arg|
433
+ forwarded_rest_arg?(arg, @rest_arg_name)
434
+ end
435
+ end
430
436
  end
431
437
 
432
438
  def forwarded_kwrest_arg
433
- return nil if referenced_kwrest_arg?
439
+ return @forwarded_kwrest_arg if defined?(@forwarded_kwrest_arg)
434
440
 
435
- arguments.filter_map { |arg| extract_forwarded_kwrest_arg(arg, @kwrest_arg_name) }.first
441
+ @forwarded_kwrest_arg = if referenced_kwrest_arg?
442
+ nil
443
+ else
444
+ arguments.filter_map do |arg|
445
+ extract_forwarded_kwrest_arg(arg, @kwrest_arg_name)
446
+ end.first
447
+ end
436
448
  end
437
449
 
438
450
  def forwarded_block_arg
439
- return nil if referenced_block_arg?
440
-
441
- arguments.find { |arg| forwarded_block_arg?(arg, @block_arg_name) }
451
+ return @forwarded_block_arg if defined?(@forwarded_block_arg)
452
+
453
+ @forwarded_block_arg = if referenced_block_arg?
454
+ nil
455
+ else
456
+ arguments.find do |arg|
457
+ forwarded_block_arg?(arg, @block_arg_name)
458
+ end
459
+ end
442
460
  end
443
461
 
444
462
  def classification
@@ -40,8 +40,9 @@ module RuboCop
40
40
 
41
41
  node = innermost_braces_node(node)
42
42
  return if node.parent && brace_method?(node.parent)
43
+ return if compound_assignment_target?(node)
43
44
 
44
- preferred = (value.zero? ? 'first' : 'last')
45
+ preferred = preferred_name(value)
45
46
  offense_range = find_offense_range(node)
46
47
 
47
48
  add_offense(offense_range, message: format(MSG, preferred: preferred)) do |corrector|
@@ -53,6 +54,10 @@ module RuboCop
53
54
 
54
55
  private
55
56
 
57
+ def preferred_name(value)
58
+ value.zero? ? 'first' : 'last'
59
+ end
60
+
56
61
  def preferred_value(node, value)
57
62
  value = ".#{value}" unless node.loc.dot
58
63
  value
@@ -74,6 +79,12 @@ module RuboCop
74
79
  def brace_method?(node)
75
80
  node.send_type? && (node.method?(:[]) || node.method?(:[]=))
76
81
  end
82
+
83
+ # `arr[0] += 1` etc. would autocorrect to `arr.first += 1`, which calls the
84
+ # nonexistent `first=`/`last=` setter and raises `NoMethodError`.
85
+ def compound_assignment_target?(node)
86
+ node.parent&.type?(:op_asgn, :or_asgn, :and_asgn) && node.parent.children.first == node
87
+ end
77
88
  end
78
89
  end
79
90
  end
@@ -145,6 +145,10 @@ module RuboCop
145
145
 
146
146
  dot = dot_node.loc.dot.source
147
147
  bang = straight?(method_name) ? '' : '!'
148
+ # `a&.intersection(b)&.none?` returns `nil` when `a` is `nil`, but the negated
149
+ # rewrite `!a&.intersect?(b)` returns `true` there, flipping the result.
150
+ return if bang == '!' && dot == '&.'
151
+
148
152
  replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
149
153
 
150
154
  register_offense(node, replacement)
@@ -29,6 +29,9 @@ module RuboCop
29
29
  def on_send(node)
30
30
  array, element = single_element(node)
31
31
  return unless array
32
+ # `[*foo]` is not a single element: the splat can expand to any number of
33
+ # elements, so `intersect?([*foo])` is not equivalent to `include?(*foo)`.
34
+ return if element.splat_type?
32
35
 
33
36
  add_offense(
34
37
  node.source_range.with(begin_pos: node.loc.selector.begin_pos)
@@ -3,9 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of "*" as a substitute for _join_.
6
+ # Checks for uses of `*` as a substitute for `Array#join`.
7
+ # Using `join` is clearer about intent and more readable than
8
+ # overloading the `*` operator for string conversion.
7
9
  #
8
- # Not all cases can reliably checked, due to Ruby's dynamic
10
+ # Not all cases can be reliably checked, due to Ruby's dynamic
9
11
  # types, so we consider only cases when the first argument is an
10
12
  # array literal or the second is a string literal.
11
13
  #
@@ -4,8 +4,11 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for non-ascii (non-English) characters
7
- # in comments. You could set an array of allowed non-ascii chars in
8
- # `AllowedChars` attribute (copyright notice "©" by default).
7
+ # in comments. Non-ascii characters can cause issues with
8
+ # portability and encoding across different environments
9
+ # and editors. You could set an array of allowed non-ascii
10
+ # chars in `AllowedChars` attribute (copyright notice "©"
11
+ # by default).
9
12
  #
10
13
  # @example
11
14
  # # bad
@@ -49,7 +52,7 @@ module RuboCop
49
52
  end
50
53
 
51
54
  def allowed_non_ascii_chars
52
- cop_config['AllowedChars'] || []
55
+ @allowed_non_ascii_chars ||= (cop_config['AllowedChars'] || []).freeze
53
56
  end
54
57
  end
55
58
  end
@@ -3,10 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of Module#attr.
6
+ # Checks for uses of `Module#attr`. The `attr` method has confusing
7
+ # behavior: with a single argument it creates a reader (like `attr_reader`),
8
+ # but with a second boolean argument it creates an accessor (deprecated in
9
+ # Ruby 1.9). Use `attr_reader` or `attr_accessor` to make intent explicit.
7
10
  #
8
11
  # @example
9
- # # bad - creates a single attribute accessor (deprecated in Ruby 1.9)
12
+ # # bad
10
13
  # attr :something, true
11
14
  # attr :one, :two, :three # behaves as attr_reader
12
15
  #
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks if usage of %() or %Q() matches configuration.
6
+ # Checks if usage of `%()` or `%Q()` matches configuration.
7
+ # Consistent use of one style makes the codebase easier
8
+ # to read.
7
9
  #
8
10
  # @example EnforcedStyle: bare_percent (default)
9
11
  # # bad
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for BEGIN blocks.
6
+ # Checks for `BEGIN` blocks. They are Perl-style constructs that execute
7
+ # code before the rest of the file is parsed, making the control flow
8
+ # harder to follow and reason about.
7
9
  #
8
10
  # @example
9
11
  # # bad