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,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks that each source file defines at most one top-level class or module.
7
+ #
8
+ # Keeping one class or module per file makes it easier to find and navigate
9
+ # code, and follows the convention used by most Ruby projects.
10
+ #
11
+ # Classes and modules listed in `AllowedClasses` are not counted toward the
12
+ # limit. This is useful for small ancillary classes like custom exception
13
+ # classes that logically belong with the main class.
14
+ #
15
+ # @example
16
+ # # bad - Multiple top-level classes
17
+ # class Foo
18
+ # end
19
+ #
20
+ # class Bar
21
+ # end
22
+ #
23
+ # # bad - Multiple top-level modules
24
+ # module Foo
25
+ # end
26
+ #
27
+ # module Bar
28
+ # end
29
+ #
30
+ # # bad - A top-level class and a top-level module
31
+ # class Foo
32
+ # end
33
+ #
34
+ # module Bar
35
+ # end
36
+ #
37
+ # # good - A single top-level class
38
+ # class Foo
39
+ # end
40
+ #
41
+ # # good - A single top-level module
42
+ # module Foo
43
+ # end
44
+ #
45
+ # # good - Nested classes within a single top-level class
46
+ # class Foo
47
+ # class Bar
48
+ # end
49
+ # end
50
+ #
51
+ # # good - Multiple classes within a single top-level module
52
+ # module Foo
53
+ # class Bar
54
+ # end
55
+ #
56
+ # class Baz
57
+ # end
58
+ # end
59
+ #
60
+ # @example AllowedClasses: ['AllowedClass']
61
+ # # good
62
+ # class Foo
63
+ # end
64
+ #
65
+ # class AllowedClass
66
+ # end
67
+ #
68
+ class OneClassPerFile < Base
69
+ include RangeHelp
70
+
71
+ MSG = 'Do not define multiple classes/modules at the top level in a single file.'
72
+
73
+ def on_new_investigation
74
+ @top_level_definitions = []
75
+ end
76
+
77
+ def on_class(node)
78
+ check_top_level(node)
79
+ end
80
+
81
+ def on_module(node)
82
+ check_top_level(node)
83
+ end
84
+
85
+ private
86
+
87
+ def check_top_level(node)
88
+ return unless top_level_definition?(node)
89
+ return if allowed_class?(node)
90
+
91
+ @top_level_definitions << node
92
+ return unless @top_level_definitions.length > 1
93
+
94
+ add_offense(range_between(node.source_range.begin_pos, node.loc.name.end_pos))
95
+ end
96
+
97
+ def top_level_definition?(node)
98
+ if node.parent&.begin_type?
99
+ node.parent.root?
100
+ else
101
+ node.root?
102
+ end
103
+ end
104
+
105
+ def allowed_class?(node)
106
+ allowed_classes.include?(node.identifier.short_name)
107
+ end
108
+
109
+ def allowed_classes
110
+ @allowed_classes ||= cop_config.fetch('AllowedClasses', []).map(&:intern)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -3,9 +3,10 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of if/then/else/end constructs on a single line.
7
- # `AlwaysCorrectToMultiline` config option can be set to true to autocorrect all offenses to
8
- # multi-line constructs. When `AlwaysCorrectToMultiline` is false (default case) the
6
+ # Checks for uses of `if/then/else/end` constructs on a single line.
7
+ # A ternary operator (`?:`) or multi-line `if` is more readable.
8
+ # `AlwaysCorrectToMultiline` config option can be set to `true` to autocorrect all offenses to
9
+ # multi-line constructs. When `AlwaysCorrectToMultiline` is `false` (default case) the
9
10
  # autocorrect will first try converting them to ternary operators.
10
11
  #
11
12
  # @example
@@ -55,19 +56,21 @@ module RuboCop
55
56
  include OnNormalIfUnless
56
57
  extend AutoCorrector
57
58
 
58
- MSG = 'Favor the ternary operator (`?:`) or multi-line constructs ' \
59
- 'over single-line `%<keyword>s/then/else/end` constructs.'
59
+ MSG_SUFFIX = 'over single-line `%<keyword>s/then/else/end` constructs.'
60
+ MSG_TERNARY = "Favor the ternary operator (`?:`) #{MSG_SUFFIX}"
61
+ MSG_MULTILINE = "Favor multi-line `%<keyword>s` #{MSG_SUFFIX}"
60
62
 
61
63
  def on_normal_if_unless(node)
62
64
  return unless node.single_line?
63
65
  return unless node.else_branch
64
66
  return if node.elsif? || node.if_branch&.begin_type?
65
67
 
66
- message = message(node)
67
- add_offense(node, message: message) do |corrector|
68
+ multiline = multiline?(node)
69
+
70
+ add_offense(node, message: message(node, multiline)) do |corrector|
68
71
  next if part_of_ignored_node?(node)
69
72
 
70
- autocorrect(corrector, node)
73
+ autocorrect(corrector, node, multiline)
71
74
 
72
75
  ignore_node(node)
73
76
  end
@@ -75,12 +78,18 @@ module RuboCop
75
78
 
76
79
  private
77
80
 
78
- def message(node)
79
- format(MSG, keyword: node.keyword)
81
+ def multiline?(node)
82
+ always_multiline? || cannot_replace_to_ternary?(node)
83
+ end
84
+
85
+ def message(node, multiline)
86
+ template = multiline ? MSG_MULTILINE : MSG_TERNARY
87
+
88
+ format(template, keyword: node.keyword)
80
89
  end
81
90
 
82
- def autocorrect(corrector, node)
83
- if always_multiline? || cannot_replace_to_ternary?(node)
91
+ def autocorrect(corrector, node, multiline)
92
+ if multiline
84
93
  IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
85
94
  else
86
95
  corrector.replace(node, ternary_correction(node))
@@ -26,9 +26,10 @@ module RuboCop
26
26
  splat kwsplat forwarded_args forwarded_restarg forwarded_kwrestarg block_pass
27
27
  ].freeze
28
28
 
29
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
29
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
30
30
  def on_send(node)
31
31
  return unless (dot = node.loc.dot)
32
+ return if unary_method_no_operator?(node)
32
33
  return if node.receiver.const_type? || !node.arguments.one?
33
34
 
34
35
  return unless (rhs = node.first_argument)
@@ -43,10 +44,18 @@ module RuboCop
43
44
  corrector.insert_after(selector, ' ') if insert_space_after?(node)
44
45
  end
45
46
  end
46
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
47
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
47
48
 
48
49
  private
49
50
 
51
+ # `foo.~@` and `foo.!@` call the method `~` and `!` respectively. While those
52
+ # are operator methods, we don't want to actually consider them as such.
53
+ def unary_method_no_operator?(node)
54
+ return false unless node.nonmutating_unary_operator_method?
55
+
56
+ node.method_name.to_s != node.selector.source
57
+ end
58
+
50
59
  # Checks for an acceptable case of `foo.+(bar).baz`.
51
60
  def method_call_with_parenthesized_arg?(argument)
52
61
  return false unless argument.parent.parent&.send_type?
@@ -4,6 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for simple usages of parallel assignment.
7
+ # Parallel assignment is less readable than individual
8
+ # assignments and makes it harder to follow what each
9
+ # variable is being set to.
10
+ #
7
11
  # This will only complain when the number of variables
8
12
  # being assigned matched the number of assigning variables.
9
13
  #
@@ -14,7 +18,7 @@ module RuboCop
14
18
  #
15
19
  # # good
16
20
  # one, two = *foo
17
- # a, b = foo()
21
+ # a, b = foo
18
22
  # a, b = b, a
19
23
  #
20
24
  # a = 1
@@ -34,7 +38,7 @@ module RuboCop
34
38
  rhs_elements = Array(rhs).compact # edge case for one constant
35
39
 
36
40
  return if allowed_lhs?(node.assignments) || allowed_rhs?(rhs) ||
37
- allowed_masign?(node.assignments, rhs_elements)
41
+ allowed_masign?(node.assignments, rhs_elements) || contains_heredoc?(rhs)
38
42
 
39
43
  range = node.source_range.begin.join(rhs.source_range.end)
40
44
 
@@ -73,6 +77,13 @@ module RuboCop
73
77
  !node.array_type? || elements.any?(&:splat_type?)
74
78
  end
75
79
 
80
+ # Autocorrection splits the assignment into single assignments on
81
+ # consecutive lines, which would put following assignments into the
82
+ # heredoc body unless the heredoc bodies were moved along.
83
+ def contains_heredoc?(node)
84
+ node.each_descendant(:any_str).any?(&:heredoc?)
85
+ end
86
+
76
87
  def assignment_corrector(node, rhs, order)
77
88
  if node.parent&.rescue_type?
78
89
  _assignment, modifier = *node.parent
@@ -223,7 +234,7 @@ module RuboCop
223
234
  # __FILE__ is treated as a StrNode but has no begin
224
235
  if node.str_type? && loc.respond_to?(:begin) && loc.begin.nil?
225
236
  "'#{node.source}'"
226
- elsif node.sym_type? && loc.begin.nil?
237
+ elsif node.sym_type? && !node.loc?(:begin)
227
238
  ":#{node.source}"
228
239
  else
229
240
  node.source
@@ -0,0 +1,270 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for consecutive calls to `select`/`filter`/`find_all` and `reject`
7
+ # on the same receiver with the same block body, where `partition` could be
8
+ # used instead. Also detects two `select` or two `reject` calls where one
9
+ # block negates the other with `!`. Using `partition` reduces two collection
10
+ # traversals to one.
11
+ #
12
+ # @safety
13
+ # This cop is unsafe because:
14
+ #
15
+ # * `Hash#select` and `Hash#reject` return hashes, but `Hash#partition`
16
+ # returns nested arrays.
17
+ # * When the receiver has side effects, calling it once (with `partition`)
18
+ # versus twice (with `select` + `reject`) may produce different results.
19
+ # * Custom classes may override `select`/`reject` without providing a
20
+ # compatible `partition` method.
21
+ #
22
+ # @example
23
+ # # bad
24
+ # positives = array.select { |x| x > 0 }
25
+ # negatives = array.reject { |x| x > 0 }
26
+ #
27
+ # # bad
28
+ # positives = array.filter { |x| x > 0 }
29
+ # negatives = array.reject { |x| x > 0 }
30
+ #
31
+ # # bad
32
+ # negatives = array.reject { |x| x > 0 }
33
+ # positives = array.select { |x| x > 0 }
34
+ #
35
+ # # bad
36
+ # positives = array.select(&:positive?)
37
+ # negatives = array.reject(&:positive?)
38
+ #
39
+ # # bad
40
+ # positives = array.select(&:positive?)
41
+ # negatives = array.reject { |x| x.positive? }
42
+ #
43
+ # # bad
44
+ # positives = array.select { |x| x.positive? }
45
+ # non_positives = array.select { |x| !x.positive? }
46
+ #
47
+ # # good
48
+ # positives, negatives = array.partition { |x| x > 0 }
49
+ #
50
+ # # good
51
+ # positives, non_positives = array.partition { |x| x.positive? }
52
+ #
53
+ # # good
54
+ # positives, negatives = array.partition(&:positive?)
55
+ #
56
+ class PartitionInsteadOfDoubleSelect < Base
57
+ include RangeHelp
58
+ extend AutoCorrector
59
+
60
+ MSG = 'Use `partition` instead of consecutive `%<first>s` and `%<second>s` calls.'
61
+
62
+ SELECT_METHODS = %i[select filter find_all].freeze
63
+ CANDIDATE_METHODS = (SELECT_METHODS + %i[reject]).to_set.freeze
64
+ RESTRICT_ON_SEND = (SELECT_METHODS + %i[reject]).freeze
65
+
66
+ # @!method symbol_proc_method?(node)
67
+ def_node_matcher :symbol_proc_method?, <<~PATTERN
68
+ (block _ (args (arg _name)) (send (lvar _name) $_method_name))
69
+ PATTERN
70
+
71
+ def on_block(node)
72
+ return unless CANDIDATE_METHODS.include?(node.method_name)
73
+
74
+ find_and_register_offense(node)
75
+ end
76
+ alias on_numblock on_block
77
+ alias on_itblock on_block
78
+
79
+ def on_send(node)
80
+ return unless node.last_argument&.block_pass_type?
81
+
82
+ find_and_register_offense(node)
83
+ end
84
+ alias on_csend on_send
85
+
86
+ private
87
+
88
+ def find_and_register_offense(node)
89
+ container = node_container(node)
90
+ return unless container
91
+
92
+ sibling_container = container.left_sibling
93
+ sibling = find_matching_candidate(node, sibling_container)
94
+ return unless sibling
95
+
96
+ register_offense(node, sibling, container, sibling_container)
97
+ end
98
+
99
+ def node_container(node)
100
+ parent = node.parent
101
+ if parent&.begin_type?
102
+ node
103
+ elsif parent&.assignment? && parent.parent&.begin_type?
104
+ parent
105
+ end
106
+ end
107
+
108
+ def find_matching_candidate(node, sibling_container)
109
+ return unless sibling_container
110
+
111
+ sibling = extract_candidate(sibling_container)
112
+ return unless sibling
113
+ return unless node.receiver == sibling.receiver
114
+ return unless matching_pair?(node, sibling)
115
+
116
+ sibling
117
+ end
118
+
119
+ def matching_pair?(node, sibling)
120
+ (complementary_pair?(node, sibling) && equivalent_predicate?(node, sibling)) ||
121
+ (node.method?(sibling.method_name) && negated_predicate?(node, sibling))
122
+ end
123
+
124
+ def extract_candidate(container)
125
+ extract_block(container) || extract_block_pass_send(container)
126
+ end
127
+
128
+ def extract_block(container)
129
+ if container.any_block_type?
130
+ container
131
+ elsif container.assignment?
132
+ rhs = container.children.last
133
+ rhs if rhs&.any_block_type?
134
+ end
135
+ end
136
+
137
+ def extract_block_pass_send(container)
138
+ node = container.assignment? ? container.children.last : container
139
+ return unless node&.type?(:call)
140
+ return unless node.last_argument&.block_pass_type?
141
+
142
+ node
143
+ end
144
+
145
+ def complementary_pair?(node1, node2)
146
+ m1 = node1.method_name
147
+ m2 = node2.method_name
148
+ (SELECT_METHODS.include?(m1) && m2 == :reject) ||
149
+ (m1 == :reject && SELECT_METHODS.include?(m2))
150
+ end
151
+
152
+ def equivalent_predicate?(node1, node2)
153
+ if node1.any_block_type? && node2.any_block_type?
154
+ same_block_contents?(node1, node2)
155
+ elsif node1.any_block_type?
156
+ block_matches_block_pass?(node1, node2)
157
+ elsif node2.any_block_type?
158
+ block_matches_block_pass?(node2, node1)
159
+ else
160
+ node1.last_argument == node2.last_argument
161
+ end
162
+ end
163
+
164
+ def same_block_contents?(block1, block2)
165
+ return false unless block1.type == block2.type
166
+
167
+ if block1.block_type?
168
+ block1.arguments == block2.arguments &&
169
+ block1.body == block2.body
170
+ else
171
+ block1.body == block2.body
172
+ end
173
+ end
174
+
175
+ def block_matches_block_pass?(block_node, send_node)
176
+ method_name = symbol_proc_method?(block_node)
177
+ return false unless method_name
178
+
179
+ sym_node = send_node.last_argument.children.first
180
+ sym_node.sym_type? && sym_node.children.first == method_name
181
+ end
182
+
183
+ def negated_predicate?(node1, node2)
184
+ return false unless node1.any_block_type? && node2.any_block_type?
185
+ return false unless node1.type == node2.type
186
+ return false if node1.block_type? && node1.arguments != node2.arguments
187
+
188
+ negated_body?(node1.body, node2.body) || negated_body?(node2.body, node1.body)
189
+ end
190
+
191
+ def negated_body?(body1, body2)
192
+ body1&.send_type? && body1.method?(:!) && body1.receiver == body2
193
+ end
194
+
195
+ def register_offense(node, sibling, container, sibling_container)
196
+ message = format(MSG, first: sibling.method_name, second: node.method_name)
197
+
198
+ add_offense(container, message: message) do |corrector|
199
+ next unless both_lvasgn?(container, sibling_container)
200
+
201
+ autocorrect(corrector, node, sibling, container, sibling_container)
202
+ end
203
+ end
204
+
205
+ def both_lvasgn?(container, sibling_container)
206
+ container.lvasgn_type? && sibling_container.lvasgn_type?
207
+ end
208
+
209
+ def autocorrect(corrector, node, sibling, container, sibling_container)
210
+ if complementary_pair?(node, sibling)
211
+ select_var, reject_var =
212
+ complementary_variable_order(sibling, container, sibling_container)
213
+ partition_node = select_node_for(sibling, container)
214
+ else
215
+ select_var, reject_var, partition_node =
216
+ negation_partition_args(node, sibling, container, sibling_container)
217
+ end
218
+
219
+ partition_call = build_partition_call(partition_node)
220
+ replacement = "#{select_var}, #{reject_var} = #{partition_call}"
221
+
222
+ corrector.replace(sibling_container, replacement)
223
+ range = range_by_whole_lines(container.source_range, include_final_newline: true)
224
+ corrector.remove(range)
225
+ end
226
+
227
+ def complementary_variable_order(sibling, container, sibling_container)
228
+ if SELECT_METHODS.include?(sibling.method_name)
229
+ [sibling_container.children.first, container.children.first]
230
+ else
231
+ [container.children.first, sibling_container.children.first]
232
+ end
233
+ end
234
+
235
+ def negation_partition_args(node, sibling, container, sibling_container)
236
+ node_is_negated = negated_body?(node.body, sibling.body)
237
+ is_select = SELECT_METHODS.include?(node.method_name)
238
+ # For select: non-negated is truthy (first). For reject: negated is truthy (first).
239
+ node_is_truthy = is_select != node_is_negated
240
+ partition_node = node_is_negated ? sibling : node
241
+
242
+ if node_is_truthy
243
+ [container.children.first, sibling_container.children.first, partition_node]
244
+ else
245
+ [sibling_container.children.first, container.children.first, partition_node]
246
+ end
247
+ end
248
+
249
+ def select_node_for(sibling, container)
250
+ if SELECT_METHODS.include?(sibling.method_name)
251
+ sibling
252
+ else
253
+ container.children.last
254
+ end
255
+ end
256
+
257
+ def build_partition_call(node)
258
+ source = node.source
259
+ send_node = node.any_block_type? ? node.send_node : node
260
+ selector = send_node.loc.selector
261
+ offset = node.source_range.begin_pos
262
+ method_start = selector.begin_pos - offset
263
+ method_end = selector.end_pos - offset
264
+
265
+ "#{source[0...method_start]}partition#{source[method_end..]}"
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
@@ -4,6 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Enforces the consistent usage of `%`-literal delimiters.
7
+ # Using consistent delimiters across the codebase reduces
8
+ # cognitive load when reading `%`-literals.
7
9
  #
8
10
  # Specify the 'default' key to set all preferred delimiters at once. You
9
11
  # can continue to specify individual preferred delimiters to override the
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Looks for uses of `any?`, `all?`, `none?`, or `one?` with a block
7
+ # containing only an `is_a?`, `kind_of?`, or `instance_of?` check, and
8
+ # suggests using the predicate method with the class argument directly.
9
+ #
10
+ # @safety
11
+ # This cop is unsafe because `instance_of?` checks for an exact class
12
+ # match, while the pattern argument uses `===` which also matches
13
+ # subclasses. For `is_a?` and `kind_of?`, the behavior is equivalent.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # array.any? { |x| x.is_a?(Integer) }
18
+ # array.all? { |x| x.kind_of?(String) }
19
+ # array.none? { |x| x.is_a?(Float) }
20
+ # array.one? { |x| x.instance_of?(Symbol) }
21
+ #
22
+ # # good
23
+ # array.any?(Integer)
24
+ # array.all?(String)
25
+ # array.none?(Float)
26
+ # array.one?(Symbol)
27
+ class PredicateWithKind < Base
28
+ extend AutoCorrector
29
+ include RangeHelp
30
+
31
+ MSG = 'Prefer `%<replacement>s` to `%<original>s` with a kind check.'
32
+ RESTRICT_ON_SEND = %i[any? all? none? one?].freeze
33
+ KIND_METHODS = %i[is_a? kind_of? instance_of?].to_set.freeze
34
+
35
+ # @!method kind_check?(node)
36
+ def_node_matcher :kind_check?, <<~PATTERN
37
+ {
38
+ (block call (args (arg $_)) $(send (lvar _) %KIND_METHODS _))
39
+ (numblock call $1 $(send (lvar _) %KIND_METHODS _))
40
+ (itblock call $_ $(send (lvar _) %KIND_METHODS _))
41
+ }
42
+ PATTERN
43
+
44
+ # @!method kind_call?(node, name)
45
+ def_node_matcher :kind_call?, <<~PATTERN
46
+ (send (lvar %1) %KIND_METHODS _)
47
+ PATTERN
48
+
49
+ def on_send(node)
50
+ return unless (block_node = node.block_node)
51
+ return if block_node.body&.begin_type?
52
+ return unless (kind_check_node = extract_send_node(block_node))
53
+
54
+ klass = kind_check_node.first_argument
55
+ replacement = "#{node.method_name}(#{klass.source})"
56
+
57
+ register_offense(node, block_node, klass, replacement)
58
+ end
59
+ alias on_csend on_send
60
+
61
+ private
62
+
63
+ def extract_send_node(block_node)
64
+ return unless (block_arg_name, kind_check_node = kind_check?(block_node))
65
+
66
+ block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
67
+ block_arg_name = :it if block_node.type?(:itblock)
68
+
69
+ kind_check_node if kind_call?(kind_check_node, block_arg_name)
70
+ end
71
+
72
+ def register_offense(node, block_node, klass, replacement)
73
+ original = "#{node.method_name} { ... }"
74
+ message = format(MSG, replacement: replacement, original: original)
75
+
76
+ add_offense(block_node, message: message) do |corrector|
77
+ range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
78
+ corrector.replace(range, "#{node.method_name}(#{klass.source})")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -14,22 +14,22 @@ module RuboCop
14
14
  # is a `Hash` or responds to the replacement methods.
15
15
  #
16
16
  # @example EnforcedStyle: short (default)
17
- # # bad
18
- # Hash#has_key?
19
- # Hash#has_value?
17
+ # # bad
18
+ # Hash#has_key?
19
+ # Hash#has_value?
20
20
  #
21
- # # good
22
- # Hash#key?
23
- # Hash#value?
21
+ # # good
22
+ # Hash#key?
23
+ # Hash#value?
24
24
  #
25
25
  # @example EnforcedStyle: verbose
26
- # # bad
27
- # Hash#key?
28
- # Hash#value?
26
+ # # bad
27
+ # Hash#key?
28
+ # Hash#value?
29
29
  #
30
- # # good
31
- # Hash#has_key?
32
- # Hash#has_value?
30
+ # # good
31
+ # Hash#has_key?
32
+ # Hash#has_value?
33
33
  class PreferredHashMethods < Base
34
34
  include ConfigurableEnforcedStyle
35
35
  extend AutoCorrector
@@ -3,8 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of Proc.new where Kernel#proc
7
- # would be more appropriate.
6
+ # Checks for uses of `Proc.new` where `Kernel#proc`
7
+ # would be more appropriate. `proc` is the shorter and
8
+ # more idiomatic way to create procs in Ruby.
8
9
  #
9
10
  # @example
10
11
  # # bad
@@ -51,7 +51,7 @@ module RuboCop
51
51
  EXPLODED_MSG = 'Provide an exception class and message as arguments to `%<method>s`.'
52
52
  COMPACT_MSG = 'Provide an exception object as an argument to `%<method>s`.'
53
53
  ACCEPTABLE_ARG_TYPES = %i[
54
- hash forwarded_restarg splat forwarded_restarg forwarded_args
54
+ hash forwarded_restarg splat forwarded_kwrestarg forwarded_args
55
55
  ].freeze
56
56
 
57
57
  RESTRICT_ON_SEND = %i[raise fail].freeze