rubocop 1.79.2 → 1.88.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (421) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +259 -90
  5. data/config/obsoletion.yml +30 -1
  6. data/exe/rubocop +1 -8
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cli/command/auto_generate_config.rb +36 -4
  9. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  10. data/lib/rubocop/cli/command/lsp.rb +1 -1
  11. data/lib/rubocop/cli/command/mcp.rb +19 -0
  12. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  13. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  14. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  15. data/lib/rubocop/cli.rb +35 -9
  16. data/lib/rubocop/comment_config.rb +59 -17
  17. data/lib/rubocop/config.rb +14 -10
  18. data/lib/rubocop/config_finder.rb +1 -1
  19. data/lib/rubocop/config_loader.rb +37 -23
  20. data/lib/rubocop/config_loader_resolver.rb +20 -10
  21. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  22. data/lib/rubocop/config_store.rb +7 -2
  23. data/lib/rubocop/config_validator.rb +1 -1
  24. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  25. data/lib/rubocop/cop/base.rb +25 -4
  26. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  27. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  28. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  29. data/lib/rubocop/cop/correctors/alignment_corrector.rb +26 -7
  30. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  31. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  32. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  33. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  34. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  35. data/lib/rubocop/cop/correctors.rb +28 -0
  36. data/lib/rubocop/cop/documentation.rb +2 -3
  37. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  38. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  39. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  40. data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  42. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  43. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  44. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  45. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  46. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  47. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  48. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  49. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  50. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  51. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  52. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  53. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  54. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  55. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  56. data/lib/rubocop/cop/layout/block_alignment.rb +41 -4
  57. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  58. data/lib/rubocop/cop/layout/class_structure.rb +14 -7
  59. data/lib/rubocop/cop/layout/dot_position.rb +2 -2
  60. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +26 -7
  61. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
  62. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
  63. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  64. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  65. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  66. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  67. data/lib/rubocop/cop/layout/end_alignment.rb +10 -3
  68. data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
  69. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  70. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  71. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  72. data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
  73. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  74. data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
  75. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  76. data/lib/rubocop/cop/layout/indentation_width.rb +123 -7
  77. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  78. data/lib/rubocop/cop/layout/line_length.rb +26 -9
  79. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  80. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  81. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  82. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  83. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  84. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +229 -39
  85. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  86. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  87. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  88. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  89. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  90. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  91. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  92. data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
  93. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  94. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  95. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  96. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  97. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  98. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  99. data/lib/rubocop/cop/lint/circular_argument_reference.rb +45 -3
  100. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +4 -3
  101. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  102. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  103. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  104. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  105. data/lib/rubocop/cop/lint/debugger.rb +0 -3
  106. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  107. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  108. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  109. data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
  110. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  111. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  112. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  113. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  114. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  115. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  116. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  117. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  118. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  119. data/lib/rubocop/cop/lint/float_comparison.rb +2 -1
  120. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  121. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -5
  122. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  123. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
  124. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  125. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -12
  126. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -10
  127. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  128. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  129. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
  130. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  131. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  132. data/lib/rubocop/cop/lint/number_conversion.rb +19 -10
  133. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  134. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +3 -0
  135. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  136. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  137. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  138. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  139. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -10
  140. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +6 -12
  141. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  142. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  143. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +36 -12
  144. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +12 -2
  145. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +10 -3
  146. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  147. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  148. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  149. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  150. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  151. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  152. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  153. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  154. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  155. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -0
  156. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  157. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  158. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  159. data/lib/rubocop/cop/lint/self_assignment.rb +39 -7
  160. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  161. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  162. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  163. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  164. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  165. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  166. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  167. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  168. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  169. data/lib/rubocop/cop/lint/to_enum_arguments.rb +28 -1
  170. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  171. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  172. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  173. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +4 -2
  174. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  175. data/lib/rubocop/cop/lint/unreachable_code.rb +7 -5
  176. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  177. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  178. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  179. data/lib/rubocop/cop/lint/useless_assignment.rb +53 -25
  180. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  181. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  182. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  183. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +8 -4
  184. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  185. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  186. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +37 -11
  187. data/lib/rubocop/cop/lint/void.rb +39 -12
  188. data/lib/rubocop/cop/message_annotator.rb +1 -1
  189. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  190. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  191. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  192. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  193. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  194. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  195. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  196. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  197. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
  198. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  199. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  200. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  201. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
  202. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  203. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  204. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  205. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  206. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  207. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  208. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  209. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  210. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  211. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  212. data/lib/rubocop/cop/mixin.rb +86 -0
  213. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  214. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  215. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  216. data/lib/rubocop/cop/naming/method_name.rb +5 -3
  217. data/lib/rubocop/cop/naming/predicate_method.rb +32 -8
  218. data/lib/rubocop/cop/naming/predicate_prefix.rb +12 -12
  219. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  220. data/lib/rubocop/cop/offense.rb +17 -1
  221. data/lib/rubocop/cop/registry.rb +62 -38
  222. data/lib/rubocop/cop/security/eval.rb +15 -2
  223. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  224. data/lib/rubocop/cop/security/json_load.rb +33 -11
  225. data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
  226. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  227. data/lib/rubocop/cop/style/alias.rb +15 -3
  228. data/lib/rubocop/cop/style/and_or.rb +2 -1
  229. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  230. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  231. data/lib/rubocop/cop/style/array_intersect.rb +50 -12
  232. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +50 -0
  233. data/lib/rubocop/cop/style/array_join.rb +4 -2
  234. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  235. data/lib/rubocop/cop/style/attr.rb +5 -2
  236. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  237. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  238. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  239. data/lib/rubocop/cop/style/block_delimiters.rb +39 -32
  240. data/lib/rubocop/cop/style/case_equality.rb +29 -15
  241. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  242. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -2
  243. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  244. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  245. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  246. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  247. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  248. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  249. data/lib/rubocop/cop/style/concat_array_literals.rb +7 -1
  250. data/lib/rubocop/cop/style/conditional_assignment.rb +14 -19
  251. data/lib/rubocop/cop/style/constant_visibility.rb +20 -12
  252. data/lib/rubocop/cop/style/copyright.rb +22 -11
  253. data/lib/rubocop/cop/style/date_time.rb +4 -4
  254. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  255. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  256. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  257. data/lib/rubocop/cop/style/documentation.rb +6 -6
  258. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  259. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  260. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  261. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  262. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  263. data/lib/rubocop/cop/style/empty_class_definition.rb +119 -0
  264. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  265. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  266. data/lib/rubocop/cop/style/encoding.rb +7 -1
  267. data/lib/rubocop/cop/style/end_block.rb +3 -1
  268. data/lib/rubocop/cop/style/endless_method.rb +23 -5
  269. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  270. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  271. data/lib/rubocop/cop/style/file_open.rb +84 -0
  272. data/lib/rubocop/cop/style/file_write.rb +21 -16
  273. data/lib/rubocop/cop/style/float_division.rb +15 -1
  274. data/lib/rubocop/cop/style/for.rb +3 -0
  275. data/lib/rubocop/cop/style/format_string.rb +4 -3
  276. data/lib/rubocop/cop/style/format_string_token.rb +49 -5
  277. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  278. data/lib/rubocop/cop/style/guard_clause.rb +27 -22
  279. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
  280. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  281. data/lib/rubocop/cop/style/hash_lookup_method.rb +106 -0
  282. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  283. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  284. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  285. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  286. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  287. data/lib/rubocop/cop/style/if_unless_modifier.rb +58 -18
  288. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  289. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  290. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  291. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  292. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  293. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  294. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  295. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  296. data/lib/rubocop/cop/style/map_join.rb +123 -0
  297. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  298. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  299. data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
  300. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  301. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  302. data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
  303. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  304. data/lib/rubocop/cop/style/mutable_constant.rb +106 -12
  305. data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
  306. data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
  307. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  308. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  309. data/lib/rubocop/cop/style/not.rb +2 -0
  310. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  311. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  312. data/lib/rubocop/cop/style/one_line_conditional.rb +21 -12
  313. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  314. data/lib/rubocop/cop/style/parallel_assignment.rb +14 -3
  315. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  316. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  317. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  318. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  319. data/lib/rubocop/cop/style/proc.rb +3 -2
  320. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  321. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  322. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  323. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  324. data/lib/rubocop/cop/style/redundant_begin.rb +37 -3
  325. data/lib/rubocop/cop/style/redundant_condition.rb +6 -3
  326. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  327. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  328. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  329. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  330. data/lib/rubocop/cop/style/redundant_format.rb +27 -5
  331. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  332. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  333. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  334. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  335. data/lib/rubocop/cop/style/redundant_parentheses.rb +36 -30
  336. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  337. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  338. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  339. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  340. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  341. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  342. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  343. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  344. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  345. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  346. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  347. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  348. data/lib/rubocop/cop/style/safe_navigation.rb +25 -8
  349. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  350. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  351. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  352. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  353. data/lib/rubocop/cop/style/semicolon.rb +41 -8
  354. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  355. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  356. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  357. data/lib/rubocop/cop/style/sole_nested_conditional.rb +12 -3
  358. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  359. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  360. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  361. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  362. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  363. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  364. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  365. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  366. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  367. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  368. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  369. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  370. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  371. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  372. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  373. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  374. data/lib/rubocop/cop/style/word_array.rb +1 -0
  375. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  376. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  377. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  378. data/lib/rubocop/cop/team.rb +87 -36
  379. data/lib/rubocop/cop/util.rb +2 -3
  380. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  381. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  382. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  383. data/lib/rubocop/cop/variable_force.rb +9 -7
  384. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  385. data/lib/rubocop/directive_comment.rb +48 -4
  386. data/lib/rubocop/file_patterns.rb +9 -1
  387. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  388. data/lib/rubocop/formatter/disabled_config_formatter.rb +38 -14
  389. data/lib/rubocop/formatter/formatter_set.rb +2 -2
  390. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  391. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  392. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  393. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  394. data/lib/rubocop/formatter.rb +22 -21
  395. data/lib/rubocop/lsp/diagnostic.rb +18 -33
  396. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  397. data/lib/rubocop/lsp/routes.rb +43 -7
  398. data/lib/rubocop/lsp/runtime.rb +13 -4
  399. data/lib/rubocop/lsp/stdin_runner.rb +8 -17
  400. data/lib/rubocop/magic_comment.rb +20 -0
  401. data/lib/rubocop/mcp/server.rb +200 -0
  402. data/lib/rubocop/options.rb +35 -4
  403. data/lib/rubocop/path_util.rb +14 -2
  404. data/lib/rubocop/plugin/loader.rb +1 -1
  405. data/lib/rubocop/project_index_loader.rb +66 -0
  406. data/lib/rubocop/rake_task.rb +1 -1
  407. data/lib/rubocop/remote_config.rb +10 -8
  408. data/lib/rubocop/result_cache.rb +61 -38
  409. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  410. data/lib/rubocop/rspec/shared_contexts.rb +39 -5
  411. data/lib/rubocop/rspec/support.rb +2 -1
  412. data/lib/rubocop/runner.rb +134 -57
  413. data/lib/rubocop/server/cache.rb +6 -29
  414. data/lib/rubocop/server/core.rb +8 -0
  415. data/lib/rubocop/target_finder.rb +17 -10
  416. data/lib/rubocop/target_ruby.rb +31 -14
  417. data/lib/rubocop/version.rb +21 -3
  418. data/lib/rubocop.rb +28 -96
  419. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  420. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  421. metadata +38 -9
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for `each_with_object`, `inject`, and `reduce` calls that build
7
+ # a hash from an enumerable, where `to_h` with a block could be used instead.
8
+ #
9
+ # This cop complements `Style/HashTransformKeys` and `Style/HashTransformValues`,
10
+ # which handle hash-to-hash transformations with destructured key-value pairs.
11
+ # This cop targets the case where a hash is built from individual elements
12
+ # (non-destructured block parameter).
13
+ #
14
+ # @safety
15
+ # This cop is unsafe because it cannot guarantee that the receiver
16
+ # is an `Enumerable` by static analysis, so the correction may
17
+ # not be actually equivalent. Additionally, `each_with_object` returns
18
+ # the hash object while `to_h` returns a new hash, which could matter
19
+ # if the hash object identity is important.
20
+ #
21
+ # @example
22
+ # # bad
23
+ # array.each_with_object({}) { |elem, hash| hash[elem.id] = elem.name }
24
+ #
25
+ # # bad
26
+ # array.inject({}) { |hash, elem| hash[elem.id] = elem.name; hash }
27
+ #
28
+ # # bad
29
+ # array.reduce({}) { |hash, elem| hash[elem.id] = elem.name; hash }
30
+ #
31
+ # # bad
32
+ # array.each_with_object({}) { |elem, hash| hash[elem] = elem.to_s }
33
+ #
34
+ # # good
35
+ # array.to_h { |elem| [elem.id, elem.name] }
36
+ #
37
+ # # good
38
+ # array.to_h { |elem| [elem, elem.to_s] }
39
+ #
40
+ class ReduceToHash < Base
41
+ extend AutoCorrector
42
+ extend TargetRubyVersion
43
+ include RangeHelp
44
+
45
+ minimum_target_ruby_version 2.6
46
+
47
+ MSG = 'Use `to_h { ... }` instead of `%<method>s`.'
48
+ RESTRICT_ON_SEND = %i[each_with_object inject reduce].freeze
49
+
50
+ # each_with_object({}) { |elem, hash| hash[key] = value }
51
+ # @!method each_with_object_to_hash?(node)
52
+ def_node_matcher :each_with_object_to_hash?, <<~PATTERN
53
+ {
54
+ (block
55
+ (call _ :each_with_object (hash))
56
+ (args (arg _elem) (arg _hash))
57
+ (send (lvar _hash) :[]= $_key $_value))
58
+ (numblock
59
+ (call _ :each_with_object (hash))
60
+ 2
61
+ (send (lvar :_2) :[]= $_key $_value))
62
+ }
63
+ PATTERN
64
+
65
+ # inject/reduce({}) { |hash, elem| hash[key] = value; hash }
66
+ # @!method inject_to_hash?(node)
67
+ def_node_matcher :inject_to_hash?, <<~PATTERN
68
+ {
69
+ (block
70
+ (call _ {:inject :reduce} (hash))
71
+ (args (arg _hash) (arg _elem))
72
+ (begin
73
+ (send (lvar _hash) :[]= $_key $_value)
74
+ (lvar _hash)))
75
+ (numblock
76
+ (call _ {:inject :reduce} (hash))
77
+ 2
78
+ (begin
79
+ (send (lvar :_1) :[]= $_key $_value)
80
+ (lvar :_1)))
81
+ }
82
+ PATTERN
83
+
84
+ def on_send(node)
85
+ block_node = node.block_node
86
+ return unless block_node
87
+
88
+ check_offense(node, block_node)
89
+ end
90
+ alias on_csend on_send
91
+
92
+ private
93
+
94
+ def check_offense(node, block_node)
95
+ key, value = if node.method?(:each_with_object)
96
+ each_with_object_to_hash?(block_node)
97
+ else
98
+ inject_to_hash?(block_node)
99
+ end
100
+ return unless key
101
+ return if accumulator_used_in_expressions?(block_node, key, value)
102
+ return if nested_match?(key) || nested_match?(value)
103
+
104
+ register_offense(node, block_node, key, value)
105
+ end
106
+
107
+ def accumulator_used_in_expressions?(block_node, key, value)
108
+ acc_name = accumulator_name(block_node)
109
+ references_variable?(key, acc_name) || references_variable?(value, acc_name)
110
+ end
111
+
112
+ def nested_match?(node)
113
+ node.each_node(:call).any? do |send_node|
114
+ next false unless RESTRICT_ON_SEND.include?(send_node.method_name)
115
+
116
+ inner_block = send_node.block_node
117
+ next false unless inner_block
118
+
119
+ if send_node.method?(:each_with_object)
120
+ each_with_object_to_hash?(inner_block)
121
+ else
122
+ inject_to_hash?(inner_block)
123
+ end
124
+ end
125
+ end
126
+
127
+ def accumulator_name(block_node)
128
+ index = block_node.method?(:each_with_object) ? 1 : 0
129
+ block_node.argument_list[index].name
130
+ end
131
+
132
+ def references_variable?(node, name)
133
+ node.each_node(:lvar).any? { |lvar| lvar.children.first == name }
134
+ end
135
+
136
+ def register_offense(send_node, block_node, key_expr, value_expr)
137
+ message = format(MSG, method: send_node.method_name)
138
+
139
+ add_offense(send_node.loc.selector, message: message) do |corrector|
140
+ corrector.replace(
141
+ replacement_range(send_node, block_node),
142
+ replacement(block_node, key_expr, value_expr)
143
+ )
144
+ end
145
+ end
146
+
147
+ def replacement(block_node, key_expr, value_expr)
148
+ key_source = adjusted_source(key_expr, block_node)
149
+ value_source = adjusted_source(value_expr, block_node)
150
+ body = "[#{key_source}, #{value_source}]"
151
+
152
+ if block_node.numblock_type?
153
+ block_node.braces? ? "to_h { #{body} }" : do_end_replacement(block_node, body)
154
+ else
155
+ named_block_replacement(block_node, body)
156
+ end
157
+ end
158
+
159
+ def named_block_replacement(block_node, body)
160
+ arg = element_arg_source(block_node)
161
+ if block_node.braces?
162
+ "to_h { |#{arg}| #{body} }"
163
+ else
164
+ do_end_replacement(block_node, body, arg)
165
+ end
166
+ end
167
+
168
+ def do_end_replacement(block_node, body, arg = nil)
169
+ args = arg ? " |#{arg}|" : ''
170
+ "to_h do#{args}\n#{indent(block_node)} #{body}\n#{indent(block_node)}end"
171
+ end
172
+
173
+ def replacement_range(send_node, block_node)
174
+ range_between(send_node.loc.selector.begin_pos, block_node.source_range.end_pos)
175
+ end
176
+
177
+ def element_arg_source(block_node)
178
+ if block_node.method?(:each_with_object)
179
+ block_node.first_argument.source
180
+ else
181
+ block_node.arguments[1].source
182
+ end
183
+ end
184
+
185
+ def adjusted_source(expr_node, block_node)
186
+ source = expr_node.source
187
+ return source unless block_node.numblock_type?
188
+ return source if block_node.method?(:each_with_object)
189
+
190
+ # For inject/reduce numblocks, _2 is the element (becomes _1)
191
+ source.gsub('_2', '_1')
192
+ end
193
+
194
+ def indent(node)
195
+ ' ' * node.source_range.column
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -37,6 +37,7 @@ module RuboCop
37
37
  # array.sum(0)
38
38
  # exit(true)
39
39
  # exit!(false)
40
+ # string.to_i(10)
40
41
  # string.split(" ")
41
42
  # "first\nsecond".split(" ")
42
43
  # string.chomp("\n")
@@ -49,6 +50,7 @@ module RuboCop
49
50
  # array.sum
50
51
  # exit
51
52
  # exit!
53
+ # string.to_i
52
54
  # string.split
53
55
  # "first second".split
54
56
  # string.chomp
@@ -3,8 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for the instantiation of array using redundant `Array` constructor.
7
- # Autocorrect replaces to array literal which is the simplest and fastest.
6
+ # Checks for the instantiation of an array using a redundant `Array` constructor.
7
+ # Autocorrect replaces it with an array literal which is the simplest and fastest.
8
8
  #
9
9
  # @example
10
10
  #
@@ -3,9 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for redundant `begin` blocks.
7
- #
8
- # Currently it checks for code like this:
6
+ # Checks for redundant `begin` blocks. A `begin` block is redundant
7
+ # when the `rescue`/`ensure` can be handled by the enclosing method
8
+ # or block definition directly, avoiding unnecessary indentation.
9
9
  #
10
10
  # @example
11
11
  #
@@ -85,6 +85,29 @@ module RuboCop
85
85
  end
86
86
  alias on_defs on_def
87
87
 
88
+ def on_if(node)
89
+ return if node.modifier_form?
90
+
91
+ inspect_branches(node)
92
+ end
93
+
94
+ def on_case(node)
95
+ inspect_branches(node)
96
+ end
97
+ alias on_case_match on_case
98
+
99
+ def on_while(node)
100
+ return if node.modifier_form?
101
+
102
+ body = node.body
103
+
104
+ return unless body&.kwbegin_type?
105
+ return if body.rescue_node || body.ensure_node
106
+
107
+ register_offense(body)
108
+ end
109
+ alias on_until on_while
110
+
88
111
  def on_block(node)
89
112
  return if target_ruby_version < 2.5
90
113
  return if node.send_node.lambda_literal?
@@ -180,6 +203,8 @@ module RuboCop
180
203
  end
181
204
 
182
205
  def begin_block_has_multiline_statements?(node)
206
+ return false unless node.parent
207
+
183
208
  node.children.count >= 2
184
209
  end
185
210
 
@@ -199,6 +224,15 @@ module RuboCop
199
224
  def valid_begin_assignment?(node)
200
225
  node.parent&.assignment? && !node.children.one?
201
226
  end
227
+
228
+ def inspect_branches(node)
229
+ node.branches.each do |branch|
230
+ next unless branch&.kwbegin_type?
231
+ next if branch.rescue_node || branch.ensure_node
232
+
233
+ register_offense(branch)
234
+ end
235
+ end
202
236
  end
203
237
  end
204
238
  end
@@ -58,7 +58,10 @@ module RuboCop
58
58
  # # good
59
59
  # a.nil? || a
60
60
  #
61
- # @example AllowedMethods: ['nonzero?'] (default)
61
+ # @example AllowedMethods: ['infinite?', 'nonzero?'] (default)
62
+ # # good
63
+ # num.infinite? ? true : false
64
+ #
62
65
  # # good
63
66
  # num.nonzero? ? true : false
64
67
  #
@@ -200,7 +203,7 @@ module RuboCop
200
203
  end
201
204
 
202
205
  def asgn_type?(node)
203
- node.type?(:lvasgn, :ivasgn, :cvasgn, :gvasgn)
206
+ node.type?(:lvasgn, :ivasgn, :cvasgn, :gvasgn, :casgn)
204
207
  end
205
208
 
206
209
  def branches_have_method?(node)
@@ -247,7 +250,7 @@ module RuboCop
247
250
  "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
248
251
  elsif if_branch.true_type?
249
252
  condition = if_branch.parent.condition
250
- return condition.source if condition.arguments.empty?
253
+ return condition.source if condition.arguments.empty? || condition.parenthesized?
251
254
 
252
255
  wrap_arguments_with_parens(condition)
253
256
  else
@@ -3,16 +3,16 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Avoid redundant `::` prefix on constant.
6
+ # Avoid redundant `::` prefix on a constant.
7
7
  #
8
- # How Ruby searches constant is a bit complicated, and it can often be difficult to
8
+ # How Ruby searches constants is a bit complicated, and it can often be difficult to
9
9
  # understand from the code whether the `::` is intended or not. Where `Module.nesting`
10
10
  # is empty, there is no need to prepend `::`, so it would be nice to consistently
11
11
  # avoid such meaningless `::` prefix to avoid confusion.
12
12
  #
13
- # NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled to prevent
14
- # conflicting rules. Because it respects user configurations that want to enable
15
- # `Lint/ConstantResolution` cop which is disabled by default.
13
+ # NOTE: This cop is disabled if `Lint/ConstantResolution` cop is enabled,
14
+ # to prevent conflicting rules. This is because it respects user configurations
15
+ # that want to enable `Lint/ConstantResolution` cop which is disabled by default.
16
16
  #
17
17
  # @example
18
18
  # # bad
@@ -64,7 +64,7 @@ module RuboCop
64
64
  def redundant_each_method(node)
65
65
  return if node.last_argument&.block_pass_type?
66
66
 
67
- if node.method?(:each) && !node.parent&.block_type?
67
+ if node.method?(:each) && !node.parent&.any_block_type?
68
68
  ancestor_node = node.each_ancestor(:call).detect do |ancestor|
69
69
  ancestor.receiver == node &&
70
70
  (RESTRICT_ON_SEND.include?(ancestor.method_name) || ancestor.method?(:reverse_each))
@@ -74,8 +74,8 @@ module RuboCop
74
74
  end
75
75
 
76
76
  return unless (prev_method = node.children.first)
77
- return if !prev_method.send_type? ||
78
- prev_method.parent.block_type? || prev_method.last_argument&.block_pass_type?
77
+ return if !prev_method.call_type? || prev_method.parent.any_block_type? ||
78
+ prev_method.last_argument&.block_pass_type?
79
79
 
80
80
  detected = prev_method.method_name.to_s.start_with?('each_') unless node.method?(:each)
81
81
 
@@ -51,7 +51,7 @@ module RuboCop
51
51
  end
52
52
 
53
53
  def string_message?(message)
54
- message.type?(:str, :dstr, :xstr)
54
+ message.any_str_type?
55
55
  end
56
56
 
57
57
  def fix_compact(node)
@@ -52,7 +52,7 @@ module RuboCop
52
52
  ${nil? basic_literal? const_type?})
53
53
  PATTERN
54
54
 
55
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
55
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
56
56
  redundant_fetch_block_candidate?(node) do |send, body|
57
57
  return if should_not_check?(send, body)
58
58
 
@@ -89,7 +89,7 @@ module RuboCop
89
89
 
90
90
  def on_send(node)
91
91
  format_without_additional_args?(node) do |value|
92
- replacement = value.source
92
+ replacement = escape_control_chars(value.source)
93
93
 
94
94
  add_offense(node, message: message(node, replacement)) do |corrector|
95
95
  corrector.replace(node, replacement)
@@ -108,6 +108,7 @@ module RuboCop
108
108
 
109
109
  def detect_unnecessary_fields(node)
110
110
  return unless node.first_argument&.str_type?
111
+ return if node.first_argument.heredoc?
111
112
 
112
113
  string = node.first_argument.value
113
114
  arguments = node.arguments[1..]
@@ -134,6 +135,7 @@ module RuboCop
134
135
  end
135
136
  end
136
137
 
138
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
137
139
  def all_fields_literal?(string, arguments)
138
140
  count = 0
139
141
  sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
@@ -141,29 +143,44 @@ module RuboCop
141
143
 
142
144
  sequences.each do |sequence|
143
145
  next if sequence.percent?
146
+ next if unknown_variable_width?(sequence, arguments)
144
147
 
145
148
  hash = arguments.detect(&:hash_type?)
146
149
  next unless (argument = find_argument(sequence, arguments, hash))
147
150
  next unless matching_argument?(sequence, argument)
151
+ next if (sequence.width || sequence.precision) && argument.dstr_type?
148
152
 
149
153
  count += 1
150
154
  end
151
155
 
152
156
  sequences.size == count
153
157
  end
158
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
154
159
 
160
+ # If the sequence has a variable (`*`) width, it cannot be autocorrected
161
+ # if the width is not given as a numeric literal argument
162
+ def unknown_variable_width?(sequence, arguments)
163
+ return false unless sequence.variable_width?
164
+
165
+ argument = arguments[sequence.variable_width_argument_number - 1]
166
+ !numeric?(argument)
167
+ end
168
+
169
+ # rubocop:disable Metrics/AbcSize
155
170
  def find_argument(sequence, arguments, hash)
156
171
  if hash && (sequence.annotated? || sequence.template?)
157
172
  find_hash_value_node(hash, sequence.name.to_sym).first
173
+ elsif sequence.variable_width?
174
+ # If the specifier contains `*`, the argument for the width can be ignored.
175
+ arguments.delete_at(sequence.variable_width_argument_number - 1)
176
+ arguments.shift
158
177
  elsif sequence.arg_number
159
178
  arguments[sequence.arg_number.to_i - 1]
160
179
  else
161
- # If the specifier contains `*`, the following arguments will be used
162
- # to specify the width and can be ignored.
163
- (sequence.arity - 1).times { arguments.shift }
164
180
  arguments.shift
165
181
  end
166
182
  end
183
+ # rubocop:enable Metrics/AbcSize
167
184
 
168
185
  def matching_argument?(sequence, argument)
169
186
  # Template specifiers don't give a type, any acceptable literal type is ok.
@@ -214,7 +231,12 @@ module RuboCop
214
231
  end
215
232
  end
216
233
 
217
- "#{start_delimiter}#{string}#{end_delimiter}"
234
+ "#{start_delimiter}#{escape_control_chars(string)}#{end_delimiter}"
235
+ end
236
+
237
+ # Escape any control characters in the string (eg. `\t` or `\n` become `\\t` or `\\n`)
238
+ def escape_control_chars(string)
239
+ string.gsub(/\p{Cc}/) { |s| s.dump[1..-2] }
218
240
  end
219
241
 
220
242
  def argument_values(arguments)
@@ -49,9 +49,10 @@ module RuboCop
49
49
  def on_dstr(node)
50
50
  return unless single_interpolation?(node)
51
51
 
52
- add_offense(node) do |corrector|
53
- embedded_node = node.children.first
52
+ embedded_node = node.children.first
53
+ return if use_match_pattern?(embedded_node)
54
54
 
55
+ add_offense(node) do |corrector|
55
56
  if variable_interpolation?(embedded_node)
56
57
  autocorrect_variable_interpolation(corrector, embedded_node, node)
57
58
  elsif single_variable_interpolation?(embedded_node)
@@ -71,6 +72,14 @@ module RuboCop
71
72
  !embedded_in_percent_array?(node)
72
73
  end
73
74
 
75
+ def use_match_pattern?(node)
76
+ return false if target_ruby_version <= 2.7
77
+
78
+ node.children.any? do |child|
79
+ child.respond_to?(:match_pattern_type?) && child.match_pattern_type?
80
+ end
81
+ end
82
+
74
83
  def single_variable_interpolation?(node)
75
84
  return false unless node.children.one?
76
85
 
@@ -15,6 +15,9 @@ module RuboCop
15
15
  # # bad
16
16
  # "#{foo} bar".dup
17
17
  #
18
+ # # bad
19
+ # String.new("#{foo} bar")
20
+ #
18
21
  # # good
19
22
  # "#{foo} bar"
20
23
  #
@@ -25,19 +28,32 @@ module RuboCop
25
28
 
26
29
  MSG = "Don't unfreeze interpolated strings as they are already unfrozen."
27
30
 
28
- RESTRICT_ON_SEND = %i[+@ dup].freeze
29
-
30
31
  minimum_target_ruby_version 3.0
31
32
 
32
- def on_send(node)
33
- return if node.arguments?
34
- return unless (receiver = node.receiver)
35
- return unless receiver.dstr_type?
36
- return if uninterpolated_string?(receiver) || uninterpolated_heredoc?(receiver)
33
+ # @!method redundant_unfreeze?(node)
34
+ def_node_matcher :redundant_unfreeze?, <<~PATTERN
35
+ {
36
+ (send dstr_type? {:+@ :dup})
37
+ (send (const nil? :String) :new dstr_type?)
38
+ }
39
+ PATTERN
40
+
41
+ def on_dstr(node)
42
+ return if uninterpolated_string?(node) || uninterpolated_heredoc?(node)
43
+ return unless redundant_unfreeze?(node.parent)
44
+
45
+ add_offense(offense_range(node.parent)) do |corrector|
46
+ corrector.replace(node.parent, node.source)
47
+ end
48
+ end
49
+
50
+ private
37
51
 
38
- add_offense(node.loc.selector) do |corrector|
39
- corrector.remove(node.loc.selector)
40
- corrector.remove(node.loc.dot) unless node.unary_operation?
52
+ def offense_range(node)
53
+ if node.method?(:new)
54
+ node.source_range.begin.join(node.loc.selector)
55
+ else
56
+ node.loc.selector
41
57
  end
42
58
  end
43
59
  end
@@ -82,6 +82,7 @@ module RuboCop
82
82
  tIDENTIFIER kBREAK kNEXT kRETURN kSUPER kYIELD
83
83
  ].freeze
84
84
  ARITHMETIC_OPERATOR_TOKENS = %i[tDIVIDE tDSTAR tMINUS tPERCENT tPLUS tSTAR2].freeze
85
+ STRING_LITERAL_BEGIN_TOKENS = %i[tSTRING_BEG tXSTRING_BEG tREGEXP_BEG tSYMBEG].freeze
85
86
 
86
87
  def on_new_investigation
87
88
  return unless processed_source.ast
@@ -105,6 +106,7 @@ module RuboCop
105
106
  string_concatenation?(range.source_line) ||
106
107
  start_with_arithmetic_operator?(range) ||
107
108
  inside_string_literal_or_method_with_argument?(range) ||
109
+ inside_string_literal_with_interpolation?(range) ||
108
110
  leading_dot_method_chain_with_blank_line?(range)
109
111
  end
110
112
 
@@ -132,6 +134,20 @@ module RuboCop
132
134
  end
133
135
  end
134
136
 
137
+ def inside_string_literal_with_interpolation?(range)
138
+ string_depth = 0
139
+ processed_source.tokens.each do |token|
140
+ break if token.pos.begin_pos >= range.begin_pos
141
+
142
+ if STRING_LITERAL_BEGIN_TOKENS.include?(token.type)
143
+ string_depth += 1
144
+ elsif token.type == :tSTRING_END
145
+ string_depth -= 1
146
+ end
147
+ end
148
+ string_depth.positive?
149
+ end
150
+
135
151
  def leading_dot_method_chain_with_blank_line?(range)
136
152
  return false unless range.source_line.strip.start_with?('.', '&.')
137
153
 
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies places where `max_by { ... }`, `min_by { ... }`, or
7
+ # `minmax_by { ... }` can be replaced by `max`, `min`, or `minmax`.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # array.max_by { |x| x }
12
+ # array.min_by { |x| x }
13
+ # array.minmax_by { |x| x }
14
+ #
15
+ # # good
16
+ # array.max
17
+ # array.min
18
+ # array.minmax
19
+ class RedundantMinMaxBy < Base
20
+ include RangeHelp
21
+ extend AutoCorrector
22
+
23
+ MSG_BLOCK = 'Use `%<replacement>s` instead of `%<original>s { |%<var>s| %<var>s }`.'
24
+ MSG_NUMBLOCK = 'Use `%<replacement>s` instead of `%<original>s { _1 }`.'
25
+ MSG_ITBLOCK = 'Use `%<replacement>s` instead of `%<original>s { it }`.'
26
+
27
+ REPLACEMENTS = { max_by: 'max', min_by: 'min', minmax_by: 'minmax' }.freeze
28
+
29
+ def on_block(node)
30
+ redundant_minmax_by_block(node) do |send, var_name|
31
+ register_offense(send, node, message_block(send, var_name))
32
+ end
33
+ end
34
+
35
+ def on_numblock(node)
36
+ redundant_minmax_by_numblock(node) do |send|
37
+ register_offense(send, node, message_numblock(send))
38
+ end
39
+ end
40
+
41
+ def on_itblock(node)
42
+ redundant_minmax_by_itblock(node) do |send|
43
+ register_offense(send, node, message_itblock(send))
44
+ end
45
+ end
46
+
47
+ private
48
+
49
+ # @!method redundant_minmax_by_block(node)
50
+ def_node_matcher :redundant_minmax_by_block, <<~PATTERN
51
+ (block $(call _ {:max_by :min_by :minmax_by}) (args (arg $_x)) (lvar _x))
52
+ PATTERN
53
+
54
+ # @!method redundant_minmax_by_numblock(node)
55
+ def_node_matcher :redundant_minmax_by_numblock, <<~PATTERN
56
+ (numblock $(call _ {:max_by :min_by :minmax_by}) 1 (lvar :_1))
57
+ PATTERN
58
+
59
+ # @!method redundant_minmax_by_itblock(node)
60
+ def_node_matcher :redundant_minmax_by_itblock, <<~PATTERN
61
+ (itblock $(call _ {:max_by :min_by :minmax_by}) _ (lvar :it))
62
+ PATTERN
63
+
64
+ def register_offense(send, node, message)
65
+ range = offense_range(send, node)
66
+
67
+ add_offense(range, message: message) do |corrector|
68
+ corrector.replace(range, REPLACEMENTS[send.method_name])
69
+ end
70
+ end
71
+
72
+ def offense_range(send, node)
73
+ range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
74
+ end
75
+
76
+ def message_block(send, var_name)
77
+ method = send.method_name
78
+ format(MSG_BLOCK, replacement: REPLACEMENTS[method], original: method, var: var_name)
79
+ end
80
+
81
+ def message_numblock(send)
82
+ method = send.method_name
83
+ format(MSG_NUMBLOCK, replacement: REPLACEMENTS[method], original: method)
84
+ end
85
+
86
+ def message_itblock(send)
87
+ method = send.method_name
88
+ format(MSG_ITBLOCK, replacement: REPLACEMENTS[method], original: method)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end