rubocop 1.79.2 → 1.87.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 (376) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +185 -20
  5. data/config/obsoletion.yml +9 -0
  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 +30 -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 +8 -2
  26. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  27. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  28. data/lib/rubocop/cop/correctors/alignment_corrector.rb +26 -7
  29. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  30. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  31. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  32. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  33. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  34. data/lib/rubocop/cop/correctors.rb +28 -0
  35. data/lib/rubocop/cop/documentation.rb +2 -3
  36. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  37. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  38. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  39. data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
  40. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  41. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  42. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  43. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  44. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  45. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  46. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  47. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  48. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  49. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  50. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  51. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  52. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  53. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  54. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  55. data/lib/rubocop/cop/layout/class_structure.rb +14 -7
  56. data/lib/rubocop/cop/layout/dot_position.rb +2 -2
  57. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +26 -7
  58. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +31 -13
  59. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
  60. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  61. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  62. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  63. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  64. data/lib/rubocop/cop/layout/end_alignment.rb +10 -3
  65. data/lib/rubocop/cop/layout/first_argument_indentation.rb +34 -1
  66. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  67. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  68. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  69. data/lib/rubocop/cop/layout/hash_alignment.rb +3 -6
  70. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  71. data/lib/rubocop/cop/layout/heredoc_indentation.rb +33 -3
  72. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  73. data/lib/rubocop/cop/layout/indentation_width.rb +123 -7
  74. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  75. data/lib/rubocop/cop/layout/line_length.rb +26 -9
  76. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  77. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  78. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  79. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  80. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  81. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +229 -39
  82. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  83. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  84. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  85. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +13 -3
  86. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  87. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  88. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  89. data/lib/rubocop/cop/layout/space_around_keyword.rb +4 -2
  90. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  91. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  92. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  93. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  94. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  95. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +4 -3
  96. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  97. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  98. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  99. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  100. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  101. data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -1
  102. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  103. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  104. data/lib/rubocop/cop/lint/duplicate_methods.rb +111 -12
  105. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  106. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  107. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  108. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  109. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  110. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  111. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  112. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  113. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  114. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  115. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -1
  116. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  117. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -9
  118. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  119. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  120. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  121. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  122. data/lib/rubocop/cop/lint/number_conversion.rb +6 -6
  123. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  124. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  125. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  126. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +2 -11
  127. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  128. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
  129. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +8 -2
  130. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +3 -3
  131. data/lib/rubocop/cop/lint/require_relative_self_path.rb +3 -1
  132. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  133. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
  134. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  135. data/lib/rubocop/cop/lint/self_assignment.rb +15 -6
  136. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  137. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  138. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  139. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  140. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  141. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
  142. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -1
  143. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  144. data/lib/rubocop/cop/lint/unreachable_code.rb +7 -5
  145. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  146. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  147. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  148. data/lib/rubocop/cop/lint/useless_assignment.rb +48 -25
  149. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  150. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  151. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  152. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  153. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +37 -11
  154. data/lib/rubocop/cop/lint/void.rb +39 -12
  155. data/lib/rubocop/cop/message_annotator.rb +1 -1
  156. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  157. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  158. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  159. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  160. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  161. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  162. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  163. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +4 -6
  164. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  165. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  166. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  167. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +5 -5
  168. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  169. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  170. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  171. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  172. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  173. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  174. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  175. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  176. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  177. data/lib/rubocop/cop/mixin/trailing_comma.rb +8 -5
  178. data/lib/rubocop/cop/mixin.rb +86 -0
  179. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  180. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  181. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  182. data/lib/rubocop/cop/naming/method_name.rb +5 -3
  183. data/lib/rubocop/cop/naming/predicate_method.rb +32 -8
  184. data/lib/rubocop/cop/naming/predicate_prefix.rb +12 -12
  185. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  186. data/lib/rubocop/cop/offense.rb +17 -1
  187. data/lib/rubocop/cop/registry.rb +62 -38
  188. data/lib/rubocop/cop/security/eval.rb +15 -2
  189. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  190. data/lib/rubocop/cop/security/json_load.rb +33 -11
  191. data/lib/rubocop/cop/style/access_modifier_declarations.rb +15 -4
  192. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  193. data/lib/rubocop/cop/style/alias.rb +14 -2
  194. data/lib/rubocop/cop/style/and_or.rb +1 -0
  195. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  196. data/lib/rubocop/cop/style/array_intersect.rb +46 -12
  197. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  198. data/lib/rubocop/cop/style/array_join.rb +4 -2
  199. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  200. data/lib/rubocop/cop/style/attr.rb +5 -2
  201. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  202. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  203. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  204. data/lib/rubocop/cop/style/block_delimiters.rb +27 -34
  205. data/lib/rubocop/cop/style/case_equality.rb +15 -13
  206. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  207. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -2
  208. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  209. data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
  210. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
  211. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -18
  212. data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
  213. data/lib/rubocop/cop/style/copyright.rb +22 -11
  214. data/lib/rubocop/cop/style/date_time.rb +2 -2
  215. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  216. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  217. data/lib/rubocop/cop/style/documentation.rb +6 -6
  218. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  219. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  220. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  221. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  222. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  223. data/lib/rubocop/cop/style/empty_class_definition.rb +119 -0
  224. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  225. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  226. data/lib/rubocop/cop/style/encoding.rb +7 -1
  227. data/lib/rubocop/cop/style/end_block.rb +3 -1
  228. data/lib/rubocop/cop/style/endless_method.rb +23 -5
  229. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  230. data/lib/rubocop/cop/style/file_open.rb +84 -0
  231. data/lib/rubocop/cop/style/file_write.rb +18 -16
  232. data/lib/rubocop/cop/style/float_division.rb +15 -1
  233. data/lib/rubocop/cop/style/for.rb +3 -0
  234. data/lib/rubocop/cop/style/format_string.rb +4 -3
  235. data/lib/rubocop/cop/style/format_string_token.rb +49 -5
  236. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  237. data/lib/rubocop/cop/style/guard_clause.rb +27 -22
  238. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +27 -9
  239. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  240. data/lib/rubocop/cop/style/hash_lookup_method.rb +106 -0
  241. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  242. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  243. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  244. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  245. data/lib/rubocop/cop/style/if_unless_modifier.rb +57 -17
  246. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  247. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  248. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  249. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  250. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  251. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  252. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  253. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  254. data/lib/rubocop/cop/style/map_join.rb +123 -0
  255. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  256. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  257. data/lib/rubocop/cop/style/method_def_parentheses.rb +2 -4
  258. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  259. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  260. data/lib/rubocop/cop/style/multiline_if_then.rb +4 -4
  261. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  262. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  263. data/lib/rubocop/cop/style/negative_array_index.rb +220 -0
  264. data/lib/rubocop/cop/style/nil_comparison.rb +11 -10
  265. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  266. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  267. data/lib/rubocop/cop/style/not.rb +2 -0
  268. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  269. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  270. data/lib/rubocop/cop/style/one_line_conditional.rb +21 -12
  271. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  272. data/lib/rubocop/cop/style/parallel_assignment.rb +6 -2
  273. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  274. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  275. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  276. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  277. data/lib/rubocop/cop/style/proc.rb +3 -2
  278. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  279. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  280. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  281. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  282. data/lib/rubocop/cop/style/redundant_begin.rb +37 -3
  283. data/lib/rubocop/cop/style/redundant_condition.rb +6 -3
  284. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  285. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  286. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  287. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  288. data/lib/rubocop/cop/style/redundant_format.rb +26 -5
  289. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  290. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  291. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  292. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  293. data/lib/rubocop/cop/style/redundant_parentheses.rb +36 -30
  294. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  295. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +9 -0
  296. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  297. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  298. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  299. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  300. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  301. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  302. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  303. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  304. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  305. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  306. data/lib/rubocop/cop/style/safe_navigation.rb +25 -8
  307. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  308. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  309. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  310. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  311. data/lib/rubocop/cop/style/semicolon.rb +25 -7
  312. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  313. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  314. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  315. data/lib/rubocop/cop/style/sole_nested_conditional.rb +12 -3
  316. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  317. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  318. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  319. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  320. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  321. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  322. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  323. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  324. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  325. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  326. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  327. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  328. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  329. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  330. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  331. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  332. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  333. data/lib/rubocop/cop/team.rb +87 -36
  334. data/lib/rubocop/cop/util.rb +2 -3
  335. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  336. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  337. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  338. data/lib/rubocop/cop/variable_force.rb +9 -7
  339. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  340. data/lib/rubocop/directive_comment.rb +48 -4
  341. data/lib/rubocop/file_patterns.rb +9 -1
  342. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  343. data/lib/rubocop/formatter/disabled_config_formatter.rb +24 -7
  344. data/lib/rubocop/formatter/formatter_set.rb +2 -2
  345. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  346. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  347. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  348. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  349. data/lib/rubocop/formatter.rb +22 -21
  350. data/lib/rubocop/lsp/diagnostic.rb +18 -33
  351. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  352. data/lib/rubocop/lsp/routes.rb +43 -7
  353. data/lib/rubocop/lsp/runtime.rb +13 -4
  354. data/lib/rubocop/lsp/stdin_runner.rb +8 -17
  355. data/lib/rubocop/magic_comment.rb +20 -0
  356. data/lib/rubocop/mcp/server.rb +200 -0
  357. data/lib/rubocop/options.rb +35 -4
  358. data/lib/rubocop/path_util.rb +14 -2
  359. data/lib/rubocop/plugin/loader.rb +1 -1
  360. data/lib/rubocop/project_index_loader.rb +66 -0
  361. data/lib/rubocop/rake_task.rb +1 -1
  362. data/lib/rubocop/remote_config.rb +10 -8
  363. data/lib/rubocop/result_cache.rb +61 -38
  364. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  365. data/lib/rubocop/rspec/shared_contexts.rb +39 -5
  366. data/lib/rubocop/rspec/support.rb +2 -1
  367. data/lib/rubocop/runner.rb +134 -57
  368. data/lib/rubocop/server/cache.rb +6 -29
  369. data/lib/rubocop/server/core.rb +2 -0
  370. data/lib/rubocop/target_finder.rb +17 -10
  371. data/lib/rubocop/target_ruby.rb +31 -14
  372. data/lib/rubocop/version.rb +21 -3
  373. data/lib/rubocop.rb +28 -96
  374. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  375. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  376. metadata +38 -9
@@ -0,0 +1,197 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Looks for places where a subset of an Enumerable (array,
7
+ # range, set, etc.; see note below) is calculated based on a range
8
+ # check, and suggests `grep` or `grep_v` instead.
9
+ #
10
+ # NOTE: Hashes do not behave as you may expect with `grep`, which
11
+ # means that `hash.grep` is not equivalent to `hash.select`. Although
12
+ # RuboCop is limited by static analysis, this cop attempts to avoid
13
+ # registering an offense when the receiver is a hash (hash literal,
14
+ # `Hash.new`, `Hash#[]`, or `to_h`/`to_hash`).
15
+ #
16
+ # @safety
17
+ # Autocorrection is marked as unsafe because the cop cannot guarantee
18
+ # that the receiver is actually an array by static analysis, so the
19
+ # correction may not be actually equivalent.
20
+ #
21
+ # @example
22
+ # # bad (select or find_all)
23
+ # array.select { |x| x.between?(1, 10) }
24
+ # array.select { |x| (1..10).cover?(x) }
25
+ # array.select { |x| (1..10).include?(x) }
26
+ #
27
+ # # bad (reject)
28
+ # array.reject { |x| x.between?(1, 10) }
29
+ #
30
+ # # bad (find or detect)
31
+ # array.find { |x| x.between?(1, 10) }
32
+ # array.detect { |x| (1..10).cover?(x) }
33
+ #
34
+ # # bad (negative form)
35
+ # array.reject { |x| !x.between?(1, 10) }
36
+ # array.find { |x| !(1..10).cover?(x) }
37
+ #
38
+ # # good
39
+ # array.grep(1..10)
40
+ # array.grep_v(1..10)
41
+ # array.grep(1..10).first
42
+ # array.grep_v(1..10).first
43
+ class SelectByRange < Base
44
+ extend AutoCorrector
45
+ include RangeHelp
46
+
47
+ MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a range check.'
48
+ RESTRICT_ON_SEND = %i[select filter find_all reject find detect].freeze
49
+ SELECT_METHODS = %i[select filter find_all].freeze
50
+ FIND_METHODS = %i[find detect].freeze
51
+
52
+ # @!method range_check?(node)
53
+ # Matches: x.between?(min, max) or (min..max).cover?(x) or (min..max).include?(x)
54
+ def_node_matcher :range_check?, <<~PATTERN
55
+ {
56
+ (block call (args (arg $_)) ${(send (lvar _) :between? _ _)})
57
+ (block call (args (arg $_)) ${(send {range (begin range)} {:cover? :include?} (lvar _))})
58
+ (block call (args (arg $_)) ${(send (send (lvar _) :between? _ _) :!)})
59
+ (block call (args (arg $_)) ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
60
+ (block call (args (arg $_)) ${(send (begin (send (lvar _) :between? _ _)) :!)})
61
+ (block call (args (arg $_)) ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
62
+ (numblock call $1 ${(send (lvar _) :between? _ _)})
63
+ (numblock call $1 ${(send {range (begin range)} {:cover? :include?} (lvar _))})
64
+ (numblock call $1 ${(send (send (lvar _) :between? _ _) :!)})
65
+ (numblock call $1 ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
66
+ (numblock call $1 ${(send (begin (send (lvar _) :between? _ _)) :!)})
67
+ (numblock call $1 ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
68
+ (itblock call $_ ${(send (lvar _) :between? _ _)})
69
+ (itblock call $_ ${(send {range (begin range)} {:cover? :include?} (lvar _))})
70
+ (itblock call $_ ${(send (send (lvar _) :between? _ _) :!)})
71
+ (itblock call $_ ${(send (send {range (begin range)} {:cover? :include?} (lvar _)) :!)})
72
+ (itblock call $_ ${(send (begin (send (lvar _) :between? _ _)) :!)})
73
+ (itblock call $_ ${(send (begin (send {range (begin range)} {:cover? :include?} (lvar _))) :!)})
74
+ }
75
+ PATTERN
76
+
77
+ # Returns true if a node appears to return a hash
78
+ # @!method creates_hash?(node)
79
+ def_node_matcher :creates_hash?, <<~PATTERN
80
+ {
81
+ (call (const _ :Hash) {:new :[]} ...)
82
+ (block (call (const _ :Hash) :new ...) ...)
83
+ (call _ { :to_h :to_hash } ...)
84
+ }
85
+ PATTERN
86
+
87
+ # @!method env_const?(node)
88
+ def_node_matcher :env_const?, <<~PATTERN
89
+ (const {nil? cbase} :ENV)
90
+ PATTERN
91
+
92
+ # @!method between_call?(node, name)
93
+ def_node_matcher :between_call?, <<~PATTERN
94
+ (send (lvar %1) :between? _ _)
95
+ PATTERN
96
+
97
+ # @!method range_cover_call?(node, name)
98
+ def_node_matcher :range_cover_call?, <<~PATTERN
99
+ (send {range (begin range)} {:cover? :include?} (lvar %1))
100
+ PATTERN
101
+
102
+ def on_send(node)
103
+ return unless (block_node = node.block_node)
104
+ return if block_node.body&.begin_type?
105
+ return if receiver_allowed?(block_node.receiver)
106
+ return unless (range_check_send_node = extract_send_node(block_node))
107
+
108
+ replacement = replacement(range_check_send_node, node)
109
+ range_literal = find_range(range_check_send_node)
110
+
111
+ register_offense(node, block_node, range_literal, replacement)
112
+ end
113
+ alias on_csend on_send
114
+
115
+ private
116
+
117
+ def receiver_allowed?(node)
118
+ return false unless node
119
+
120
+ node.hash_type? || creates_hash?(node) || env_const?(node)
121
+ end
122
+
123
+ def replacement(range_check_send_node, node)
124
+ negated = negated?(range_check_send_node)
125
+ method_name = node.method_name
126
+
127
+ if SELECT_METHODS.include?(method_name)
128
+ negated ? 'grep_v' : 'grep'
129
+ elsif FIND_METHODS.include?(method_name)
130
+ negated ? 'grep_v(...).first' : 'grep(...).first'
131
+ else # reject
132
+ negated ? 'grep' : 'grep_v'
133
+ end
134
+ end
135
+
136
+ def register_offense(node, block_node, range_literal, replacement)
137
+ message = format(MSG, replacement: replacement, original_method: node.method_name)
138
+
139
+ add_offense(block_node, message: message) do |corrector|
140
+ if range_literal
141
+ range = range_between(node.loc.selector.begin_pos, block_node.loc.end.end_pos)
142
+ grep_method = replacement.include?('grep_v') ? 'grep_v' : 'grep'
143
+ suffix = replacement.include?('.first') ? '.first' : ''
144
+ corrector.replace(range, "#{grep_method}(#{range_literal})#{suffix}")
145
+ end
146
+ end
147
+ end
148
+
149
+ def extract_send_node(block_node)
150
+ return unless (block_arg_name, range_check_send_node = range_check?(block_node))
151
+
152
+ block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
153
+ block_arg_name = :it if block_node.type?(:itblock)
154
+
155
+ inner_node = unwrap_negation(range_check_send_node)
156
+
157
+ range_check_send_node if calls_lvar_in_range_check?(inner_node, block_arg_name)
158
+ end
159
+
160
+ def calls_lvar_in_range_check?(node, block_arg_name)
161
+ between_call?(node, block_arg_name) || range_cover_call?(node, block_arg_name)
162
+ end
163
+
164
+ def negated?(range_check_send_node)
165
+ range_check_send_node.send_type? && range_check_send_node.method?(:!)
166
+ end
167
+
168
+ def unwrap_negation(node)
169
+ if node.send_type? && node.method?(:!)
170
+ receiver = node.receiver
171
+ receiver = receiver.children.first if receiver.begin_type?
172
+ receiver
173
+ else
174
+ node
175
+ end
176
+ end
177
+
178
+ def find_range(node)
179
+ inner = unwrap_negation(node)
180
+
181
+ if inner.method?(:between?)
182
+ # x.between?(min, max) -> min..max
183
+ min = inner.first_argument.source
184
+ max = inner.arguments[1].source
185
+ "#{min}..#{max}"
186
+ else
187
+ # (min..max).cover?(x) or (min..max).include?(x)
188
+ receiver = inner.receiver
189
+ # Unwrap begin node from parentheses
190
+ receiver = receiver.children.first if receiver.begin_type?
191
+ receiver.source
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+ end
@@ -39,6 +39,9 @@ module RuboCop
39
39
  # array.reject { |x| x =~ /regexp/ }
40
40
  # array.reject { |x| /regexp/ =~ x }
41
41
  #
42
+ # # bad (negative form)
43
+ # array.reject { |x| !x.match? /regexp/ }
44
+ #
42
45
  # # good
43
46
  # array.grep(regexp)
44
47
  # array.grep_v(regexp)
@@ -48,18 +51,19 @@ module RuboCop
48
51
 
49
52
  MSG = 'Prefer `%<replacement>s` to `%<original_method>s` with a regexp match.'
50
53
  RESTRICT_ON_SEND = %i[select filter find_all reject].freeze
51
- REPLACEMENTS = { select: 'grep', filter: 'grep', find_all: 'grep', reject: 'grep_v' }.freeze
52
- OPPOSITE_REPLACEMENTS = {
53
- select: 'grep_v', filter: 'grep_v', find_all: 'grep_v', reject: 'grep'
54
- }.freeze
55
- REGEXP_METHODS = %i[match? =~ !~].to_set.freeze
54
+ SELECT_METHODS = %i[select filter find_all].freeze
55
+ REGEXP_METHODS = %i[match? =~].to_set.freeze
56
+ REGEXP_METHODS_NEGATED = %i[!~].to_set.freeze
56
57
 
57
58
  # @!method regexp_match?(node)
58
59
  def_node_matcher :regexp_match?, <<~PATTERN
59
60
  {
60
- (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
61
- (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
62
- (itblock call $_ ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
61
+ (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) (send _ %REGEXP_METHODS_NEGATED _) match-with-lvasgn})
62
+ (block call (args (arg $_)) ${(send (send _ %REGEXP_METHODS _) :!) (send (begin (send _ %REGEXP_METHODS _)) :!) (send match-with-lvasgn :!) (send (begin match-with-lvasgn) :!)})
63
+ (numblock call $1 ${(send _ %REGEXP_METHODS _) (send _ %REGEXP_METHODS_NEGATED _) match-with-lvasgn})
64
+ (numblock call $1 ${(send (send _ %REGEXP_METHODS _) :!) (send (begin (send _ %REGEXP_METHODS _)) :!) (send match-with-lvasgn :!) (send (begin match-with-lvasgn) :!)})
65
+ (itblock call $_ ${(send _ %REGEXP_METHODS _) (send _ %REGEXP_METHODS_NEGATED _) match-with-lvasgn})
66
+ (itblock call $_ ${(send (send _ %REGEXP_METHODS _) :!) (send (begin (send _ %REGEXP_METHODS _)) :!) (send match-with-lvasgn :!) (send (begin match-with-lvasgn) :!)})
63
67
  }
64
68
  PATTERN
65
69
 
@@ -84,6 +88,12 @@ module RuboCop
84
88
  (send (lvar %1) ...)
85
89
  (send ... (lvar %1))
86
90
  (match-with-lvasgn regexp (lvar %1))
91
+ (send (send (lvar %1) ...) :!)
92
+ (send (send ... (lvar %1)) :!)
93
+ (send (match-with-lvasgn regexp (lvar %1)) :!)
94
+ (send (begin (send (lvar %1) ...)) :!)
95
+ (send (begin (send ... (lvar %1))) :!)
96
+ (send (begin (match-with-lvasgn regexp (lvar %1))) :!)
87
97
  }
88
98
  PATTERN
89
99
 
@@ -97,7 +107,7 @@ module RuboCop
97
107
  return if match_predicate_without_receiver?(regexp_method_send_node)
98
108
 
99
109
  replacement = replacement(regexp_method_send_node, node)
100
- return if target_ruby_version <= 2.2 && replacement == 'grep_v'
110
+ return if target_ruby_version <= 2.2 && replacement.include?('grep_v')
101
111
 
102
112
  regexp = find_regexp(regexp_method_send_node, block_node)
103
113
 
@@ -115,11 +125,14 @@ module RuboCop
115
125
  end
116
126
 
117
127
  def replacement(regexp_method_send_node, node)
118
- opposite = opposite?(regexp_method_send_node)
119
-
128
+ negated = negated?(regexp_method_send_node)
120
129
  method_name = node.method_name
121
130
 
122
- opposite ? OPPOSITE_REPLACEMENTS[method_name] : REPLACEMENTS[method_name]
131
+ if SELECT_METHODS.include?(method_name)
132
+ negated ? 'grep_v' : 'grep'
133
+ else # reject
134
+ negated ? 'grep' : 'grep_v'
135
+ end
123
136
  end
124
137
 
125
138
  def register_offense(node, block_node, regexp, replacement)
@@ -138,30 +151,47 @@ module RuboCop
138
151
  return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
139
152
 
140
153
  block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
154
+ block_arg_name = :it if block_node.type?(:itblock)
141
155
 
142
156
  return unless calls_lvar?(regexp_method_send_node, block_arg_name)
143
157
 
144
158
  regexp_method_send_node
145
159
  end
146
160
 
147
- def opposite?(regexp_method_send_node)
148
- regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!~)
161
+ def negated?(regexp_method_send_node)
162
+ return true if regexp_method_send_node.send_type? && regexp_method_send_node.method?(:!)
163
+
164
+ inner = unwrap_negation(regexp_method_send_node)
165
+ inner.send_type? && inner.method?(:!~)
166
+ end
167
+
168
+ def unwrap_negation(node)
169
+ if node.send_type? && node.method?(:!)
170
+ receiver = node.receiver
171
+ receiver = receiver.children.first if receiver.begin_type?
172
+ receiver
173
+ else
174
+ node
175
+ end
149
176
  end
150
177
 
151
178
  def find_regexp(node, block)
152
- return node.child_nodes.first if node.match_with_lvasgn_type?
179
+ inner = unwrap_negation(node)
180
+
181
+ return inner.child_nodes.first if inner.match_with_lvasgn_type?
153
182
 
154
- if node.receiver.lvar_type? &&
183
+ if inner.receiver.lvar_type? &&
155
184
  (block.type?(:numblock, :itblock) ||
156
- node.receiver.source == block.first_argument.source)
157
- node.first_argument
158
- elsif node.first_argument.lvar_type?
159
- node.receiver
185
+ inner.receiver.source == block.first_argument.source)
186
+ inner.first_argument
187
+ elsif inner.first_argument&.lvar_type?
188
+ inner.receiver
160
189
  end
161
190
  end
162
191
 
163
192
  def match_predicate_without_receiver?(node)
164
- node.send_type? && node.method?(:match?) && node.receiver.nil?
193
+ inner = unwrap_negation(node)
194
+ inner.send_type? && inner.method?(:match?) && inner.receiver.nil?
165
195
  end
166
196
  end
167
197
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Enforces the use the shorthand for self-assignment.
6
+ # Enforces the use of the shorthand for self-assignment.
7
7
  #
8
8
  # @example
9
9
  #
@@ -5,6 +5,8 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for multiple expressions placed on the same line.
7
7
  # It also checks for lines terminated with a semicolon.
8
+ # In idiomatic Ruby, each expression should be on its own line
9
+ # for readability.
8
10
  #
9
11
  # This cop has `AllowAsExpressionSeparator` configuration option.
10
12
  # It allows `;` to separate several expressions on the same line.
@@ -69,10 +71,11 @@ module RuboCop
69
71
 
70
72
  def each_semicolon
71
73
  tokens_for_lines.each do |line, tokens|
72
- semicolon_pos = semicolon_position(tokens)
74
+ next unless (semicolon_pos = semicolon_position(tokens))
75
+
73
76
  after_expr_pos = semicolon_pos == -1 ? -2 : semicolon_pos
74
77
 
75
- yield line, tokens[semicolon_pos].column, tokens[after_expr_pos] if semicolon_pos
78
+ yield line, tokens[semicolon_pos].column, tokens[after_expr_pos]
76
79
  end
77
80
  end
78
81
 
@@ -119,6 +122,7 @@ module RuboCop
119
122
  tokens[1]&.type == :tSTRING_DBEG && tokens[2]&.semicolon?
120
123
  end
121
124
 
125
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
122
126
  def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
123
127
  range = source_range(processed_source.buffer, line, column)
124
128
 
@@ -130,14 +134,19 @@ module RuboCop
130
134
  # without parentheses.
131
135
  # See: https://github.com/rubocop/rubocop/issues/10791
132
136
  if token_before_semicolon&.regexp_dots?
133
- range_node = find_range_node(token_before_semicolon)
134
- corrector.wrap(range_node, '(', ')') if range_node
137
+ node = find_node(range_nodes, token_before_semicolon)
138
+ elsif token_before_semicolon&.type == :tLABEL
139
+ node = find_node(value_omission_pair_nodes, token_before_semicolon).parent
140
+ space = node.parent.loc.selector.end.join(node.source_range.begin)
141
+ corrector.remove(space)
135
142
  end
136
143
 
144
+ corrector.wrap(node, '(', ')') if node
137
145
  corrector.remove(range)
138
146
  end
139
147
  end
140
148
  end
149
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
141
150
 
142
151
  def expressions_per_line(exprs)
143
152
  # create a map matching lines to the number of expressions on them
@@ -153,9 +162,9 @@ module RuboCop
153
162
  end
154
163
  end
155
164
 
156
- def find_range_node(token_before_semicolon)
157
- range_nodes.detect do |range_node|
158
- range_node.source_range.contains?(token_before_semicolon.pos)
165
+ def find_node(nodes, token_before_semicolon)
166
+ nodes.detect do |node|
167
+ node.source_range.overlaps?(token_before_semicolon.pos)
159
168
  end
160
169
  end
161
170
 
@@ -166,6 +175,15 @@ module RuboCop
166
175
  @range_nodes = ast.range_type? ? [ast] : []
167
176
  @range_nodes.concat(ast.each_descendant(:range).to_a)
168
177
  end
178
+
179
+ def value_omission_pair_nodes
180
+ if instance_variable_defined?(:@value_omission_pair_nodes)
181
+ return @value_omission_pair_nodes
182
+ end
183
+
184
+ ast = processed_source.ast
185
+ @value_omission_pair_nodes = ast.each_descendant(:pair).to_a.select(&:value_omission?)
186
+ end
169
187
  end
170
188
  end
171
189
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
 
34
34
  MSG = 'Name `%<method>s` block params `|%<params>s|`.'
35
35
 
36
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
36
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
37
37
  return unless node.single_line?
38
38
 
39
39
  return unless eligible_method?(node)
@@ -89,7 +89,7 @@ module RuboCop
89
89
  end
90
90
 
91
91
  def method_names
92
- methods.map { |method| method_name(method).to_sym }
92
+ @method_names ||= methods.map { |method| method_name(method).to_sym }.freeze
93
93
  end
94
94
 
95
95
  def method_name(method)
@@ -38,7 +38,7 @@ module RuboCop
38
38
 
39
39
  # rubocop:disable Metrics/AbcSize
40
40
  def on_block(node)
41
- return if !node.single_line? || node.braces?
41
+ return if node.multiline? || node.braces?
42
42
  return if single_line_blocks_preferred? && suitable_as_single_line?(node)
43
43
 
44
44
  add_offense(node) do |corrector|
@@ -4,7 +4,9 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for single-line method definitions that contain a body.
7
- # It will accept single-line methods with no body.
7
+ # Single-line methods with a body are harder to read and debug
8
+ # than their multi-line equivalents. It will accept single-line
9
+ # methods with no body.
8
10
  #
9
11
  # Endless methods added in Ruby 3.0 are also accepted by this cop.
10
12
  #
@@ -65,7 +65,10 @@ module RuboCop
65
65
 
66
66
  message = format(MSG, conditional_type: node.keyword)
67
67
  add_offense(if_branch.loc.keyword, message: message) do |corrector|
68
+ next if ignored_node?(node)
69
+
68
70
  autocorrect(corrector, node, if_branch)
71
+ ignore_node(if_branch)
69
72
  end
70
73
  end
71
74
 
@@ -115,9 +118,8 @@ module RuboCop
115
118
  end
116
119
 
117
120
  def correct_node(corrector, node)
118
- corrector.replace(node.loc.keyword, 'if') if node.unless? && !part_of_ignored_node?(node)
121
+ corrector.replace(node.loc.keyword, 'if') if node.unless?
119
122
  corrector.replace(node.condition, chainable_condition(node))
120
- ignore_node(node)
121
123
  end
122
124
 
123
125
  def correct_for_guard_condition_style(corrector, node, if_branch)
@@ -129,6 +131,7 @@ module RuboCop
129
131
  corrector.remove(range_with_surrounding_space(range, newlines: false))
130
132
  end
131
133
 
134
+ # rubocop:disable Metrics/AbcSize
132
135
  def correct_for_basic_condition_style(corrector, node, if_branch)
133
136
  range = range_between(
134
137
  node.condition.source_range.end_pos, if_branch.condition.source_range.begin_pos
@@ -137,8 +140,14 @@ module RuboCop
137
140
 
138
141
  corrector.replace(if_branch.condition, chainable_condition(if_branch))
139
142
 
140
- corrector.remove(range_by_whole_lines(node.loc.end, include_final_newline: true))
143
+ end_range = if same_line?(node.loc.end, node.if_branch.loc.end)
144
+ node.loc.end
145
+ else
146
+ range_by_whole_lines(node.loc.end, include_final_newline: true)
147
+ end
148
+ corrector.remove(end_range)
141
149
  end
150
+ # rubocop:enable Metrics/AbcSize
142
151
 
143
152
  def autocorrect_outer_condition_modify_form(corrector, node, if_branch)
144
153
  correct_node(corrector, if_branch)
@@ -4,7 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Looks for uses of Perl-style global variables.
7
- # Correcting to global variables in the 'English' library
7
+ # Perl-style global variables like `$;` or `$/` are cryptic
8
+ # and hard to understand without consulting documentation.
9
+ # The `English` library provides descriptive aliases like
10
+ # `$FIELD_SEPARATOR` and `$INPUT_RECORD_SEPARATOR`.
11
+ #
12
+ # Correcting to global variables in the `English` library
8
13
  # will add a require statement to the top of the file if
9
14
  # enabled by RequireEnglish config.
10
15
  #
@@ -100,7 +100,7 @@ module RuboCop
100
100
  node.receiver.str_type? &&
101
101
  node.first_argument.str_type? &&
102
102
  node.multiline? &&
103
- node.source =~ /\+\s*\n/
103
+ node.source.match?(/\+\s*\n/)
104
104
  end
105
105
 
106
106
  def find_topmost_plus_node(node)
@@ -141,22 +141,26 @@ module RuboCop
141
141
  end
142
142
 
143
143
  def replacement(parts)
144
- interpolated_parts = parts.map do |part|
145
- case part.type
146
- when :str
147
- adjust_str(part)
148
- when :dstr
149
- part.children.all?(&:str_type?) ? adjust_str(part) : part.value
150
- else
151
- "\#{#{part.source}}"
152
- end
153
- end
144
+ interpolated_parts = parts.map { |part| adjust_str(part) }
154
145
 
155
146
  "\"#{handle_quotes(interpolated_parts).join}\""
156
147
  end
157
148
 
158
- def adjust_str(node)
159
- single_quoted?(node) ? node.value.gsub(/(\\|")/, '\\\\\&') : node.value.inspect[1..-2]
149
+ def adjust_str(part)
150
+ case part.type
151
+ when :str
152
+ if single_quoted?(part)
153
+ part.value.gsub(/(\\|"|#\{|#@|#\$)/, '\\\\\&')
154
+ else
155
+ part.value.inspect[1..-2]
156
+ end
157
+ when :dstr, :begin
158
+ part.children.map do |child|
159
+ adjust_str(child)
160
+ end.join
161
+ else
162
+ "\#{#{part.source}}"
163
+ end
160
164
  end
161
165
 
162
166
  def handle_quotes(parts)
@@ -61,6 +61,8 @@ module RuboCop
61
61
  corrector.remove(range_with_surrounding_space(parent.loc.end, newlines: false))
62
62
  elsif (class_node = parent.parent).body.nil?
63
63
  corrector.remove(range_for_empty_class_body(class_node, parent))
64
+ elsif unparenthesized_struct_new?(parent)
65
+ wrap_unparenthesized_call_with_do(corrector, parent)
64
66
  else
65
67
  corrector.insert_after(parent, ' do')
66
68
  end
@@ -73,6 +75,17 @@ module RuboCop
73
75
  range_by_whole_lines(class_node.loc.end, include_final_newline: true)
74
76
  end
75
77
  end
78
+
79
+ def unparenthesized_struct_new?(parent)
80
+ parent.send_type? && parent.arguments.any? && !parent.parenthesized?
81
+ end
82
+
83
+ def wrap_unparenthesized_call_with_do(corrector, parent)
84
+ args_source = parent.arguments.map(&:source).join(', ')
85
+ range = parent.loc.selector.end.join(parent.source_range.end)
86
+
87
+ corrector.replace(range, "(#{args_source}) do")
88
+ end
76
89
  end
77
90
  end
78
91
  end
@@ -109,7 +109,7 @@ module RuboCop
109
109
 
110
110
  def_args.zip(super_args).each do |def_arg, super_arg|
111
111
  next if positional_arg_same?(def_arg, super_arg)
112
- next if positional_rest_arg_same(def_arg, super_arg)
112
+ next if positional_rest_arg_same?(def_arg, super_arg)
113
113
  next if keyword_arg_same?(def_arg, super_arg)
114
114
  next if keyword_rest_arg_same?(def_arg, super_arg)
115
115
  next if block_arg_same?(def_node, super_node, def_arg, super_arg)
@@ -147,7 +147,7 @@ module RuboCop
147
147
  def_arg.name == super_arg.children.first
148
148
  end
149
149
 
150
- def positional_rest_arg_same(def_arg, super_arg)
150
+ def positional_rest_arg_same?(def_arg, super_arg)
151
151
  return false unless def_arg.restarg_type?
152
152
  # anonymous forwarding
153
153
  return true if def_arg.name.nil? && super_arg.forwarded_restarg_type?
@@ -81,7 +81,7 @@ module RuboCop
81
81
 
82
82
  content = *sym
83
83
  content = content.map { |c| c.is_a?(AST::Node) ? c.source : c }.join
84
- content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s\(\)]*\))/, '')
84
+ content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s()]*\))/, '')
85
85
 
86
86
  content.include?(' ') || DELIMITERS.any? do |delimiter|
87
87
  content_without_delimiter_pairs.include?(delimiter)
@@ -179,7 +179,7 @@ module RuboCop
179
179
  return if allowed_method_name?(dispatch_node.method_name)
180
180
  return if allow_if_method_has_argument?(node.send_node)
181
181
  return if node.block_type? && destructuring_block_argument?(arguments_node)
182
- return if allow_comments? && contains_comments?(node)
182
+ return if allow_comments?(node)
183
183
 
184
184
  register_offense(node, method_name, dispatch_node.method_name)
185
185
  end
@@ -260,10 +260,10 @@ module RuboCop
260
260
  end
261
261
 
262
262
  def begin_pos_for_replacement(node)
263
- expr = node.send_node.source_range
263
+ send_node = node.send_node
264
264
 
265
- if (paren_pos = (expr.source =~ /\(\s*\)$/))
266
- expr.begin_pos + paren_pos
265
+ if send_node.parenthesized? && send_node.arguments.empty?
266
+ send_node.loc.begin.begin_pos
267
267
  else
268
268
  node.loc.begin.begin_pos
269
269
  end
@@ -273,8 +273,9 @@ module RuboCop
273
273
  !!cop_config.fetch('AllowMethodsWithArguments', false) && send_node.arguments.any?
274
274
  end
275
275
 
276
- def allow_comments?
277
- cop_config.fetch('AllowComments', false)
276
+ def allow_comments?(node)
277
+ cop_config.fetch('AllowComments', false) && contains_comments?(node) &&
278
+ !comments_contain_disables?(node, name)
278
279
  end
279
280
  end
280
281
  end