rubocop 1.81.7 → 1.92.1

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 (621) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +626 -111
  5. data/config/obsoletion.yml +39 -1
  6. data/exe/rubocop +1 -1
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cached_data.rb +50 -5
  9. data/lib/rubocop/changed_files.rb +134 -0
  10. data/lib/rubocop/cli/command/auto_generate_config.rb +34 -2
  11. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  12. data/lib/rubocop/cli/command/execute_runner.rb +54 -3
  13. data/lib/rubocop/cli/command/explain.rb +34 -0
  14. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  15. data/lib/rubocop/cli/command/lsp.rb +1 -1
  16. data/lib/rubocop/cli/command/mcp.rb +19 -0
  17. data/lib/rubocop/cli/command/show_cops.rb +8 -2
  18. data/lib/rubocop/cli/command/show_docs_url.rb +8 -10
  19. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  20. data/lib/rubocop/cli.rb +41 -14
  21. data/lib/rubocop/comment_config/directive_range.rb +34 -0
  22. data/lib/rubocop/comment_config/disable_next.rb +143 -0
  23. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  24. data/lib/rubocop/comment_config.rb +97 -44
  25. data/lib/rubocop/config.rb +100 -15
  26. data/lib/rubocop/config_finder.rb +1 -1
  27. data/lib/rubocop/config_loader.rb +49 -27
  28. data/lib/rubocop/config_loader_resolver.rb +95 -20
  29. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  30. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  31. data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
  32. data/lib/rubocop/config_obsoletion.rb +1 -1
  33. data/lib/rubocop/config_store.rb +2 -2
  34. data/lib/rubocop/config_validator.rb +69 -7
  35. data/lib/rubocop/cop/autocorrect_logic.rb +24 -6
  36. data/lib/rubocop/cop/badge.rb +10 -2
  37. data/lib/rubocop/cop/base.rb +128 -35
  38. data/lib/rubocop/cop/bundler/gem_comment.rb +5 -4
  39. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  40. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -10
  41. data/lib/rubocop/cop/bundler.rb +19 -0
  42. data/lib/rubocop/cop/commissioner.rb +13 -11
  43. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  44. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  45. data/lib/rubocop/cop/corrector.rb +4 -4
  46. data/lib/rubocop/cop/correctors/alignment_corrector.rb +87 -9
  47. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  48. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
  49. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  50. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +31 -4
  51. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +35 -5
  52. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
  53. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +70 -2
  54. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  55. data/lib/rubocop/cop/correctors.rb +28 -0
  56. data/lib/rubocop/cop/documentation.rb +3 -4
  57. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  58. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  59. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  60. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +14 -4
  61. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -8
  62. data/lib/rubocop/cop/gemspec/require_mfa.rb +9 -6
  63. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +12 -7
  64. data/lib/rubocop/cop/gemspec.rb +22 -0
  65. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  66. data/lib/rubocop/cop/generator.rb +5 -1
  67. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  68. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  69. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  70. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  71. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +12 -16
  72. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  73. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
  74. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  75. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  76. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  77. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  78. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  79. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  80. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  81. data/lib/rubocop/cop/layout/argument_alignment.rb +9 -3
  82. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  83. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  84. data/lib/rubocop/cop/layout/block_alignment.rb +84 -4
  85. data/lib/rubocop/cop/layout/case_indentation.rb +28 -10
  86. data/lib/rubocop/cop/layout/class_structure.rb +156 -41
  87. data/lib/rubocop/cop/layout/comment_indentation.rb +17 -7
  88. data/lib/rubocop/cop/layout/condition_position.rb +13 -3
  89. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  90. data/lib/rubocop/cop/layout/dot_position.rb +26 -16
  91. data/lib/rubocop/cop/layout/else_alignment.rb +2 -14
  92. data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
  93. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +25 -7
  94. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
  95. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
  96. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +15 -2
  97. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  98. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +2 -2
  99. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -0
  100. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +21 -5
  101. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  102. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  103. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  104. data/lib/rubocop/cop/layout/end_alignment.rb +23 -15
  105. data/lib/rubocop/cop/layout/extra_spacing.rb +109 -3
  106. data/lib/rubocop/cop/layout/first_argument_indentation.rb +81 -2
  107. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +18 -5
  108. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +41 -0
  109. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +36 -23
  110. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  111. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  112. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  113. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  114. data/lib/rubocop/cop/layout/hash_alignment.rb +16 -11
  115. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +8 -4
  116. data/lib/rubocop/cop/layout/heredoc_indentation.rb +48 -3
  117. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  118. data/lib/rubocop/cop/layout/indentation_style.rb +5 -2
  119. data/lib/rubocop/cop/layout/indentation_width.rb +158 -12
  120. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  121. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +10 -4
  122. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  123. data/lib/rubocop/cop/layout/line_length.rb +265 -29
  124. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +69 -57
  125. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +8 -2
  126. data/lib/rubocop/cop/layout/multiline_block_layout.rb +10 -1
  127. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +65 -56
  128. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +7 -1
  129. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +268 -38
  130. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  131. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  132. data/lib/rubocop/cop/layout/redundant_line_break.rb +102 -2
  133. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +19 -5
  134. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  135. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  136. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  137. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  138. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
  139. data/lib/rubocop/cop/layout/space_around_keyword.rb +14 -2
  140. data/lib/rubocop/cop/layout/space_around_operators.rb +18 -3
  141. data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
  142. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  143. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +9 -8
  144. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +44 -34
  145. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
  146. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  147. data/lib/rubocop/cop/layout.rb +114 -0
  148. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  149. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -17
  150. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +72 -1
  151. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  152. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +3 -11
  153. data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
  154. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  155. data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
  156. data/lib/rubocop/cop/lint/assignment_in_condition.rb +15 -1
  157. data/lib/rubocop/cop/lint/circular_argument_reference.rb +45 -3
  158. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  159. data/lib/rubocop/cop/lint/constant_reassignment.rb +94 -15
  160. data/lib/rubocop/cop/lint/constant_resolution.rb +31 -6
  161. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +165 -15
  162. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  163. data/lib/rubocop/cop/lint/debugger.rb +13 -2
  164. data/lib/rubocop/cop/lint/deprecated_constants.rb +2 -8
  165. data/lib/rubocop/cop/lint/deprecated_reference.rb +194 -0
  166. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  167. data/lib/rubocop/cop/lint/duplicate_methods.rb +405 -28
  168. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  169. data/lib/rubocop/cop/lint/empty_block.rb +4 -4
  170. data/lib/rubocop/cop/lint/empty_conditional_body.rb +9 -2
  171. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  172. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  173. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  174. data/lib/rubocop/cop/lint/erb_new_arguments.rb +4 -2
  175. data/lib/rubocop/cop/lint/float_comparison.rb +2 -1
  176. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  177. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
  178. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  179. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
  180. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  181. data/lib/rubocop/cop/lint/interpolation_check.rb +25 -6
  182. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  183. data/lib/rubocop/cop/lint/literal_as_condition.rb +36 -9
  184. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  185. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +75 -37
  186. data/lib/rubocop/cop/lint/loop.rb +24 -2
  187. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
  188. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +46 -18
  189. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  190. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  191. data/lib/rubocop/cop/lint/name_typo.rb +270 -0
  192. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  193. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  194. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +20 -0
  195. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  196. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  197. data/lib/rubocop/cop/lint/number_conversion.rb +22 -11
  198. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  199. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +10 -0
  200. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  201. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -4
  202. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +20 -14
  203. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  204. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  205. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +160 -39
  206. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +53 -19
  207. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +28 -6
  208. data/lib/rubocop/cop/lint/redundant_require_statement.rb +5 -4
  209. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +37 -14
  210. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +13 -4
  211. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +11 -5
  212. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -3
  213. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  214. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  215. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  216. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  217. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  218. data/lib/rubocop/cop/lint/require_relative_self_path.rb +7 -5
  219. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  220. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +24 -0
  221. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +56 -6
  222. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  223. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  224. data/lib/rubocop/cop/lint/self_assignment.rb +33 -2
  225. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  226. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  227. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  228. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  229. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  230. data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
  231. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +13 -2
  232. data/lib/rubocop/cop/lint/symbol_conversion.rb +22 -4
  233. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  234. data/lib/rubocop/cop/lint/to_enum_arguments.rb +72 -4
  235. data/lib/rubocop/cop/lint/to_json.rb +20 -17
  236. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  237. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +5 -1
  238. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +54 -11
  239. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  240. data/lib/rubocop/cop/lint/unreachable_code.rb +15 -9
  241. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  242. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  243. data/lib/rubocop/cop/lint/unused_private_method.rb +234 -0
  244. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
  245. data/lib/rubocop/cop/lint/useless_assignment.rb +61 -27
  246. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +40 -4
  247. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  248. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  249. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
  250. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  251. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
  252. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +10 -4
  253. data/lib/rubocop/cop/lint/useless_setter_call.rb +11 -5
  254. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  255. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +94 -14
  256. data/lib/rubocop/cop/lint/void.rb +59 -22
  257. data/lib/rubocop/cop/lint.rb +179 -0
  258. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  259. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  260. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  261. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  262. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  263. data/lib/rubocop/cop/metrics/method_length.rb +2 -2
  264. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  265. data/lib/rubocop/cop/metrics/perceived_complexity.rb +37 -7
  266. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  267. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  268. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  269. data/lib/rubocop/cop/metrics.rb +31 -0
  270. data/lib/rubocop/cop/migration/department_name.rb +11 -5
  271. data/lib/rubocop/cop/migration.rb +13 -0
  272. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  273. data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
  274. data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
  275. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  276. data/lib/rubocop/cop/mixin/check_line_breakable.rb +47 -25
  277. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +12 -8
  278. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  279. data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
  280. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
  281. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  282. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  283. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
  284. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  285. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  286. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
  287. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +58 -12
  288. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +37 -10
  289. data/lib/rubocop/cop/mixin/hash_subset.rb +9 -2
  290. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  291. data/lib/rubocop/cop/mixin/hash_transform_method.rb +14 -60
  292. data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
  293. data/lib/rubocop/cop/mixin/line_length_help.rb +28 -3
  294. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  295. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  296. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  297. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  298. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +10 -1
  299. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  300. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  301. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +79 -8
  302. data/lib/rubocop/cop/mixin/project_index_help.rb +203 -0
  303. data/lib/rubocop/cop/mixin/range_help.rb +1 -2
  304. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +181 -0
  305. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  306. data/lib/rubocop/cop/mixin/statement_modifier.rb +69 -12
  307. data/lib/rubocop/cop/mixin/trailing_comma.rb +7 -6
  308. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  309. data/lib/rubocop/cop/mixin.rb +93 -0
  310. data/lib/rubocop/cop/naming/accessor_method_name.rb +8 -0
  311. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +17 -1
  312. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  313. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  314. data/lib/rubocop/cop/naming/file_name.rb +4 -3
  315. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  316. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  317. data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
  318. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +12 -5
  319. data/lib/rubocop/cop/naming/method_name.rb +6 -3
  320. data/lib/rubocop/cop/naming/predicate_method.rb +13 -2
  321. data/lib/rubocop/cop/naming/predicate_prefix.rb +30 -17
  322. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +12 -7
  323. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  324. data/lib/rubocop/cop/naming.rb +31 -0
  325. data/lib/rubocop/cop/offense.rb +108 -9
  326. data/lib/rubocop/cop/registry.rb +232 -66
  327. data/lib/rubocop/cop/security/eval.rb +15 -2
  328. data/lib/rubocop/cop/security/io_methods.rb +2 -2
  329. data/lib/rubocop/cop/security/json_load.rb +16 -4
  330. data/lib/rubocop/cop/security/marshal_load.rb +1 -1
  331. data/lib/rubocop/cop/security.rb +19 -0
  332. data/lib/rubocop/cop/severity.rb +12 -0
  333. data/lib/rubocop/cop/style/access_modifier_declarations.rb +55 -8
  334. data/lib/rubocop/cop/style/accessor_grouping.rb +16 -5
  335. data/lib/rubocop/cop/style/alias.rb +33 -8
  336. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  337. data/lib/rubocop/cop/style/and_or.rb +8 -3
  338. data/lib/rubocop/cop/style/arguments_forwarding.rb +61 -17
  339. data/lib/rubocop/cop/style/array_first_last.rb +13 -3
  340. data/lib/rubocop/cop/style/array_intersect.rb +28 -5
  341. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
  342. data/lib/rubocop/cop/style/array_join.rb +4 -2
  343. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  344. data/lib/rubocop/cop/style/attr.rb +8 -6
  345. data/lib/rubocop/cop/style/bare_percent_literals.rb +4 -3
  346. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  347. data/lib/rubocop/cop/style/bitwise_predicate.rb +7 -6
  348. data/lib/rubocop/cop/style/block_delimiters.rb +74 -38
  349. data/lib/rubocop/cop/style/case_equality.rb +29 -15
  350. data/lib/rubocop/cop/style/case_like_if.rb +11 -10
  351. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  352. data/lib/rubocop/cop/style/class_and_module_children.rb +151 -35
  353. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  354. data/lib/rubocop/cop/style/class_methods_definitions.rb +26 -5
  355. data/lib/rubocop/cop/style/collection_compact.rb +37 -17
  356. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  357. data/lib/rubocop/cop/style/colon_method_call.rb +16 -7
  358. data/lib/rubocop/cop/style/combinable_loops.rb +91 -9
  359. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  360. data/lib/rubocop/cop/style/concat_array_literals.rb +65 -9
  361. data/lib/rubocop/cop/style/conditional_assignment.rb +34 -18
  362. data/lib/rubocop/cop/style/constant_visibility.rb +13 -7
  363. data/lib/rubocop/cop/style/copyright.rb +27 -12
  364. data/lib/rubocop/cop/style/data_inheritance.rb +18 -0
  365. data/lib/rubocop/cop/style/date_time.rb +4 -4
  366. data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
  367. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  368. data/lib/rubocop/cop/style/dir_empty.rb +4 -0
  369. data/lib/rubocop/cop/style/directive_scope.rb +304 -0
  370. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +184 -20
  371. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  372. data/lib/rubocop/cop/style/documentation.rb +53 -6
  373. data/lib/rubocop/cop/style/documentation_method.rb +8 -12
  374. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  375. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  376. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  377. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  378. data/lib/rubocop/cop/style/empty_case_condition.rb +13 -4
  379. data/lib/rubocop/cop/style/empty_class_definition.rb +126 -0
  380. data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
  381. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  382. data/lib/rubocop/cop/style/empty_literal.rb +8 -3
  383. data/lib/rubocop/cop/style/empty_method.rb +15 -9
  384. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +74 -22
  385. data/lib/rubocop/cop/style/encoding.rb +7 -1
  386. data/lib/rubocop/cop/style/end_block.rb +3 -1
  387. data/lib/rubocop/cop/style/endless_method.rb +100 -21
  388. data/lib/rubocop/cop/style/env_home.rb +4 -0
  389. data/lib/rubocop/cop/style/even_odd.rb +11 -1
  390. data/lib/rubocop/cop/style/exact_regexp_match.rb +52 -8
  391. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  392. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  393. data/lib/rubocop/cop/style/file_null.rb +6 -4
  394. data/lib/rubocop/cop/style/file_open.rb +84 -0
  395. data/lib/rubocop/cop/style/file_write.rb +21 -16
  396. data/lib/rubocop/cop/style/float_division.rb +28 -14
  397. data/lib/rubocop/cop/style/for.rb +60 -1
  398. data/lib/rubocop/cop/style/format_string.rb +44 -9
  399. data/lib/rubocop/cop/style/format_string_token.rb +50 -7
  400. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  401. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  402. data/lib/rubocop/cop/style/guard_clause.rb +57 -40
  403. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +31 -9
  404. data/lib/rubocop/cop/style/hash_conversion.rb +39 -11
  405. data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
  406. data/lib/rubocop/cop/style/hash_lookup_method.rb +128 -0
  407. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  408. data/lib/rubocop/cop/style/hash_syntax.rb +19 -5
  409. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  410. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  411. data/lib/rubocop/cop/style/identical_conditional_branches.rb +38 -10
  412. data/lib/rubocop/cop/style/if_inside_else.rb +36 -8
  413. data/lib/rubocop/cop/style/if_unless_modifier.rb +80 -76
  414. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +17 -14
  415. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +4 -1
  416. data/lib/rubocop/cop/style/if_with_semicolon.rb +61 -8
  417. data/lib/rubocop/cop/style/inline_comment.rb +5 -2
  418. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  419. data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
  420. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  421. data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
  422. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
  423. data/lib/rubocop/cop/style/keyword_parameters_order.rb +10 -4
  424. data/lib/rubocop/cop/style/lambda.rb +45 -2
  425. data/lib/rubocop/cop/style/lambda_call.rb +24 -8
  426. data/lib/rubocop/cop/style/magic_comment_format.rb +4 -4
  427. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
  428. data/lib/rubocop/cop/style/map_into_array.rb +2 -3
  429. data/lib/rubocop/cop/style/map_join.rb +123 -0
  430. data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
  431. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +102 -20
  432. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +15 -2
  433. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +42 -6
  434. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +36 -8
  435. data/lib/rubocop/cop/style/method_def_parentheses.rb +36 -8
  436. data/lib/rubocop/cop/style/min_max_comparison.rb +4 -1
  437. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  438. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +40 -7
  439. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  440. data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
  441. data/lib/rubocop/cop/style/module_function.rb +4 -2
  442. data/lib/rubocop/cop/style/module_member_existence_check.rb +110 -0
  443. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  444. data/lib/rubocop/cop/style/multiline_if_then.rb +10 -4
  445. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  446. data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
  447. data/lib/rubocop/cop/style/multiline_method_signature.rb +14 -13
  448. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  449. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  450. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  451. data/lib/rubocop/cop/style/mutable_constant.rb +158 -14
  452. data/lib/rubocop/cop/style/negated_if_else_condition.rb +3 -3
  453. data/lib/rubocop/cop/style/negative_array_index.rb +228 -0
  454. data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
  455. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  456. data/lib/rubocop/cop/style/next.rb +24 -10
  457. data/lib/rubocop/cop/style/nil_comparison.rb +50 -12
  458. data/lib/rubocop/cop/style/nil_lambda.rb +9 -1
  459. data/lib/rubocop/cop/style/non_nil_check.rb +21 -11
  460. data/lib/rubocop/cop/style/not.rb +12 -3
  461. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  462. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  463. data/lib/rubocop/cop/style/numeric_predicate.rb +22 -6
  464. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  465. data/lib/rubocop/cop/style/one_line_conditional.rb +18 -6
  466. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  467. data/lib/rubocop/cop/style/operator_method_call.rb +10 -2
  468. data/lib/rubocop/cop/style/option_hash.rb +1 -1
  469. data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
  470. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -7
  471. data/lib/rubocop/cop/style/parentheses_around_condition.rb +7 -2
  472. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  473. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +18 -3
  474. data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
  475. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  476. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  477. data/lib/rubocop/cop/style/proc.rb +3 -2
  478. data/lib/rubocop/cop/style/raise_args.rb +20 -10
  479. data/lib/rubocop/cop/style/reduce_to_hash.rb +216 -0
  480. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  481. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  482. data/lib/rubocop/cop/style/redundant_assignment.rb +12 -2
  483. data/lib/rubocop/cop/style/redundant_begin.rb +25 -7
  484. data/lib/rubocop/cop/style/redundant_condition.rb +41 -23
  485. data/lib/rubocop/cop/style/redundant_constant_base.rb +42 -6
  486. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
  487. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +17 -3
  488. data/lib/rubocop/cop/style/redundant_each.rb +4 -5
  489. data/lib/rubocop/cop/style/redundant_exception.rb +21 -3
  490. data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
  491. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  492. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  493. data/lib/rubocop/cop/style/redundant_format.rb +43 -7
  494. data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
  495. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
  496. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +49 -10
  497. data/lib/rubocop/cop/style/redundant_line_continuation.rb +82 -115
  498. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  499. data/lib/rubocop/cop/style/redundant_parentheses.rb +105 -83
  500. data/lib/rubocop/cop/style/redundant_percent_q.rb +5 -3
  501. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +13 -2
  502. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  503. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +27 -7
  504. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -4
  505. data/lib/rubocop/cop/style/redundant_return.rb +4 -3
  506. data/lib/rubocop/cop/style/redundant_self.rb +11 -2
  507. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
  508. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +27 -4
  509. data/lib/rubocop/cop/style/redundant_sort.rb +45 -11
  510. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
  511. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +133 -0
  512. data/lib/rubocop/cop/style/regexp_literal.rb +106 -28
  513. data/lib/rubocop/cop/style/require_order.rb +25 -8
  514. data/lib/rubocop/cop/style/rescue_modifier.rb +15 -14
  515. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  516. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  517. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  518. data/lib/rubocop/cop/style/safe_navigation.rb +18 -9
  519. data/lib/rubocop/cop/style/sample.rb +19 -13
  520. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  521. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  522. data/lib/rubocop/cop/style/select_by_regexp.rb +52 -23
  523. data/lib/rubocop/cop/style/self_assignment.rb +13 -16
  524. data/lib/rubocop/cop/style/semicolon.rb +39 -13
  525. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  526. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  527. data/lib/rubocop/cop/style/single_line_do_end_block.rb +19 -7
  528. data/lib/rubocop/cop/style/single_line_methods.rb +24 -5
  529. data/lib/rubocop/cop/style/sole_nested_conditional.rb +25 -7
  530. data/lib/rubocop/cop/style/special_global_vars.rb +7 -2
  531. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +31 -5
  532. data/lib/rubocop/cop/style/static_class.rb +21 -0
  533. data/lib/rubocop/cop/style/string_concatenation.rb +25 -6
  534. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  535. data/lib/rubocop/cop/style/struct_inheritance.rb +36 -1
  536. data/lib/rubocop/cop/style/super_arguments.rb +6 -4
  537. data/lib/rubocop/cop/style/symbol_proc.rb +32 -14
  538. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  539. data/lib/rubocop/cop/style/ternary_parentheses.rb +34 -1
  540. data/lib/rubocop/cop/style/time_now.rb +41 -0
  541. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  542. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  543. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  544. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +35 -32
  545. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  546. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  547. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  548. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  549. data/lib/rubocop/cop/style/word_array.rb +1 -0
  550. data/lib/rubocop/cop/style/yoda_condition.rb +26 -12
  551. data/lib/rubocop/cop/style/yoda_expression.rb +11 -2
  552. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  553. data/lib/rubocop/cop/style.rb +311 -0
  554. data/lib/rubocop/cop/team.rb +171 -48
  555. data/lib/rubocop/cop/util.rb +5 -8
  556. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  557. data/lib/rubocop/cop/variable_force/branch.rb +30 -6
  558. data/lib/rubocop/cop/variable_force/variable.rb +33 -9
  559. data/lib/rubocop/cops_documentation_generator.rb +27 -9
  560. data/lib/rubocop/directive_comment.rb +116 -7
  561. data/lib/rubocop/feature_loader.rb +0 -6
  562. data/lib/rubocop/file_patterns.rb +9 -1
  563. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  564. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  565. data/lib/rubocop/formatter/disabled_config_formatter.rb +39 -10
  566. data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
  567. data/lib/rubocop/formatter/formatter_set.rb +7 -2
  568. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  569. data/lib/rubocop/formatter/html_formatter.rb +2 -4
  570. data/lib/rubocop/formatter/json_formatter.rb +82 -7
  571. data/lib/rubocop/formatter/junit_formatter.rb +27 -10
  572. data/lib/rubocop/formatter/offense_count_formatter.rb +4 -3
  573. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  574. data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
  575. data/lib/rubocop/formatter/simple_text_formatter.rb +4 -5
  576. data/lib/rubocop/formatter/tap_formatter.rb +8 -3
  577. data/lib/rubocop/formatter/worst_offenders_formatter.rb +5 -4
  578. data/lib/rubocop/formatter.rb +23 -21
  579. data/lib/rubocop/lockfile.rb +18 -0
  580. data/lib/rubocop/lsp/diagnostic.rb +22 -42
  581. data/lib/rubocop/lsp/disable_comment_edits.rb +135 -0
  582. data/lib/rubocop/lsp/routes.rb +130 -83
  583. data/lib/rubocop/lsp/runtime.rb +64 -15
  584. data/lib/rubocop/lsp/server.rb +6 -2
  585. data/lib/rubocop/lsp/stdin_runner.rb +35 -2
  586. data/lib/rubocop/lsp.rb +5 -2
  587. data/lib/rubocop/magic_comment.rb +47 -0
  588. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  589. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  590. data/lib/rubocop/mcp/scope.rb +65 -0
  591. data/lib/rubocop/mcp/server.rb +307 -0
  592. data/lib/rubocop/mcp/source_files.rb +35 -0
  593. data/lib/rubocop/offense_limit.rb +30 -0
  594. data/lib/rubocop/options.rb +141 -13
  595. data/lib/rubocop/path_util.rb +16 -5
  596. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  597. data/lib/rubocop/plugin/configuration_integrator.rb +13 -3
  598. data/lib/rubocop/plugin/loader.rb +2 -2
  599. data/lib/rubocop/project_index_loader.rb +82 -0
  600. data/lib/rubocop/rake_task.rb +2 -3
  601. data/lib/rubocop/remote_config.rb +18 -13
  602. data/lib/rubocop/result_cache.rb +102 -59
  603. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  604. data/lib/rubocop/rspec/expect_offense.rb +2 -4
  605. data/lib/rubocop/rspec/shared_contexts.rb +53 -5
  606. data/lib/rubocop/rspec/support.rb +2 -1
  607. data/lib/rubocop/runner.rb +362 -106
  608. data/lib/rubocop/server/cache.rb +21 -40
  609. data/lib/rubocop/server/cli.rb +2 -4
  610. data/lib/rubocop/server/core.rb +8 -0
  611. data/lib/rubocop/server.rb +14 -0
  612. data/lib/rubocop/target_finder.rb +18 -8
  613. data/lib/rubocop/target_ruby.rb +42 -22
  614. data/lib/rubocop/todo_audit.rb +111 -0
  615. data/lib/rubocop/unified_diff.rb +257 -0
  616. data/lib/rubocop/util.rb +69 -0
  617. data/lib/rubocop/version.rb +23 -7
  618. data/lib/rubocop.rb +34 -701
  619. data/lib/ruby_lsp/rubocop/addon.rb +1 -2
  620. metadata +80 -14
  621. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -60,11 +60,11 @@ module RuboCop
60
60
 
61
61
  private
62
62
 
63
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
63
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
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,14 +74,13 @@ 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
 
82
82
  prev_method if detected || prev_method.method?(:reverse_each)
83
83
  end
84
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
85
84
 
86
85
  def range(node)
87
86
  return node.selector unless node.method?(:each)
@@ -38,6 +38,10 @@ module RuboCop
38
38
 
39
39
  def fix_exploded(node)
40
40
  exploded?(node) do |command, message|
41
+ # `raise RuntimeError, nil` uses the class name as the message, so
42
+ # rewriting it to `raise nil.to_s` (an empty message) would change it.
43
+ next if message.nil_type?
44
+
41
45
  add_offense(node, message: MSG_1) do |corrector|
42
46
  corrector.replace(node, replaced_exploded(node, command, message))
43
47
  end
@@ -45,7 +49,7 @@ module RuboCop
45
49
  end
46
50
 
47
51
  def replaced_exploded(node, command, message)
48
- arg = string_message?(message) ? message.source : "#{message.source}.to_s"
52
+ arg = message_source(message)
49
53
  arg = node.parenthesized? ? "(#{arg})" : " #{arg}"
50
54
  "#{command}#{arg}"
51
55
  end
@@ -56,20 +60,34 @@ module RuboCop
56
60
 
57
61
  def fix_compact(node)
58
62
  compact?(node) do |new_call, message|
63
+ next if message.nil_type?
64
+
59
65
  add_offense(node, message: MSG_2) do |corrector|
60
- corrector.replace(new_call, replaced_compact(message))
66
+ corrector.replace(new_call, message_source(message))
61
67
  end
62
68
  end
63
69
  end
64
70
 
65
- def replaced_compact(message)
71
+ def message_source(message)
66
72
  if string_message?(message)
67
73
  message.source
74
+ elsif require_parentheses?(message)
75
+ "(#{message.source}).to_s"
68
76
  else
69
77
  "#{message.source}.to_s"
70
78
  end
71
79
  end
72
80
 
81
+ def require_parentheses?(message)
82
+ if message.call_type?
83
+ !message.method?(:[]) &&
84
+ (message.operator_method? || (message.arguments? && !message.parenthesized?))
85
+ else
86
+ !(message.basic_literal? || message.variable? ||
87
+ message.type?(:const, :self, :begin, :array))
88
+ end
89
+ end
90
+
73
91
  # @!method exploded?(node)
74
92
  def_node_matcher :exploded?, <<~PATTERN
75
93
  (send nil? ${:raise :fail} (const {nil? cbase} :RuntimeError) $_)
@@ -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 -- the matched body is a literal or a constant
56
56
  redundant_fetch_block_candidate?(node) do |send, body|
57
57
  return if should_not_check?(send, body)
58
58
 
@@ -74,7 +74,8 @@ module RuboCop
74
74
  def should_not_check?(send, body)
75
75
  (body&.const_type? && !check_for_constant?) ||
76
76
  (body&.str_type? && !check_for_string?) ||
77
- rails_cache?(send.receiver)
77
+ rails_cache?(send.receiver) ||
78
+ unappendable_argument?(send)
78
79
  end
79
80
 
80
81
  # @!method rails_cache?(node)
@@ -107,6 +108,13 @@ module RuboCop
107
108
  def check_for_string?
108
109
  frozen_string_literals_enabled?
109
110
  end
111
+
112
+ def unappendable_argument?(send)
113
+ argument = send.first_argument
114
+
115
+ argument.type?(:splat, :forwarded_restarg) ||
116
+ (argument.hash_type? && !argument.braces?)
117
+ end
110
118
  end
111
119
  end
112
120
  end
@@ -41,6 +41,10 @@ module RuboCop
41
41
  return unless name_node.value.end_with?('.rb')
42
42
 
43
43
  extension_range = extension_range(name_node)
44
+ # A backslash right before the extension means the path is escaped;
45
+ # dropping the extension would either produce invalid Ruby (a trailing
46
+ # backslash escaping the closing quote) or a confusing path, so skip it.
47
+ return if escaped_extension?(name_node, extension_range)
44
48
 
45
49
  add_offense(extension_range) do |corrector|
46
50
  corrector.remove(extension_range)
@@ -55,6 +59,11 @@ module RuboCop
55
59
 
56
60
  range_between(end_of_path_string - 4, end_of_path_string - 1)
57
61
  end
62
+
63
+ def escaped_extension?(name_node, extension_range)
64
+ offset = extension_range.begin_pos - name_node.source_range.begin_pos
65
+ name_node.source[0...offset].end_with?('\\')
66
+ end
58
67
  end
59
68
  end
60
69
  end
@@ -62,7 +62,7 @@ module RuboCop
62
62
  def_node_matcher :select_predicate?, <<~PATTERN
63
63
  (call
64
64
  {
65
- (block $(call _ {:select :filter :find_all}) ...)
65
+ (any_block $(call _ {:select :filter :find_all}) ...)
66
66
  $(call _ {:select :filter :find_all} block_pass_type?)
67
67
  }
68
68
  ${:#{RESTRICT_ON_SEND.join(' :')}})
@@ -89,6 +89,8 @@ module RuboCop
89
89
 
90
90
  def on_send(node)
91
91
  format_without_additional_args?(node) do |value|
92
+ next if string_with_format_sequence?(value)
93
+
92
94
  replacement = escape_control_chars(value.source)
93
95
 
94
96
  add_offense(node, message: message(node, replacement)) do |corrector|
@@ -102,12 +104,31 @@ module RuboCop
102
104
 
103
105
  private
104
106
 
107
+ # A single-argument `format` whose string still contains a format sequence is
108
+ # not redundant: `format('%s')` raises at runtime, and `format('%%')` returns
109
+ # `'%'`, so replacing it with the literal would change behavior.
110
+ def string_with_format_sequence?(node)
111
+ string = static_string_value(node)
112
+ return false unless string
113
+
114
+ RuboCop::Cop::Utils::FormatString.new(string).format_sequences.any?
115
+ end
116
+
117
+ def static_string_value(node)
118
+ if node.str_type?
119
+ node.value
120
+ elsif node.dstr_type?
121
+ node.children.select(&:str_type?).map(&:value).join
122
+ end
123
+ end
124
+
105
125
  def message(node, prefer)
106
126
  format(MSG, prefer: prefer, method_name: node.method_name)
107
127
  end
108
128
 
109
129
  def detect_unnecessary_fields(node)
110
130
  return unless node.first_argument&.str_type?
131
+ return if node.first_argument.heredoc?
111
132
 
112
133
  string = node.first_argument.value
113
134
  arguments = node.arguments[1..]
@@ -134,7 +155,7 @@ module RuboCop
134
155
  end
135
156
  end
136
157
 
137
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
158
+ # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
138
159
  def all_fields_literal?(string, arguments)
139
160
  count = 0
140
161
  sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
@@ -154,18 +175,17 @@ module RuboCop
154
175
 
155
176
  sequences.size == count
156
177
  end
157
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
158
178
 
159
179
  # If the sequence has a variable (`*`) width, it cannot be autocorrected
160
180
  # if the width is not given as a numeric literal argument
161
181
  def unknown_variable_width?(sequence, arguments)
162
182
  return false unless sequence.variable_width?
163
183
 
164
- argument = arguments[sequence.variable_width_argument_number - 1]
165
- !numeric?(argument)
184
+ argument = positional_argument(arguments, sequence.variable_width_argument_number)
185
+ argument.nil? || !numeric?(argument)
166
186
  end
167
187
 
168
- # rubocop:disable Metrics/AbcSize
188
+ # rubocop:disable-next Metrics/AbcSize
169
189
  def find_argument(sequence, arguments, hash)
170
190
  if hash && (sequence.annotated? || sequence.template?)
171
191
  find_hash_value_node(hash, sequence.name.to_sym).first
@@ -174,12 +194,11 @@ module RuboCop
174
194
  arguments.delete_at(sequence.variable_width_argument_number - 1)
175
195
  arguments.shift
176
196
  elsif sequence.arg_number
177
- arguments[sequence.arg_number.to_i - 1]
197
+ positional_argument(arguments, sequence.arg_number.to_i)
178
198
  else
179
199
  arguments.shift
180
200
  end
181
201
  end
182
- # rubocop:enable Metrics/AbcSize
183
202
 
184
203
  def matching_argument?(sequence, argument)
185
204
  # Template specifiers don't give a type, any acceptable literal type is ok.
@@ -199,6 +218,15 @@ module RuboCop
199
218
  end
200
219
  end
201
220
 
221
+ # `Array#[]` raises `RangeError` for an index beyond the 64-bit range
222
+ # (e.g. `format('%*9999999999999999999999$d', 1)`) instead of returning `nil`,
223
+ # so the index needs to be checked before accessing the array.
224
+ def positional_argument(arguments, number)
225
+ index = number - 1
226
+
227
+ arguments[index] if index < arguments.size
228
+ end
229
+
202
230
  def numeric?(argument)
203
231
  argument.type?(:numeric, :str) ||
204
232
  rational_number?(argument) ||
@@ -245,6 +273,14 @@ module RuboCop
245
273
  def argument_value(argument)
246
274
  argument = argument.children.first if argument.begin_type?
247
275
 
276
+ # `nil` formats to an empty string (`format('%s', nil) == ''`), not the
277
+ # literal `'nil'` that `argument.source` would return.
278
+ return if argument.nil_type?
279
+
280
+ typed_argument_value(argument)
281
+ end
282
+
283
+ def typed_argument_value(argument)
248
284
  if argument.dsym_type?
249
285
  dsym_value(argument)
250
286
  elsif argument.hash_type?
@@ -29,8 +29,7 @@ module RuboCop
29
29
  operation_produces_immutable_object?(node.receiver))
30
30
 
31
31
  add_offense(node) do |corrector|
32
- corrector.remove(node.loc.dot)
33
- corrector.remove(node.loc.selector)
32
+ corrector.remove(node.loc.dot.join(node.loc.selector))
34
33
  end
35
34
  end
36
35
 
@@ -57,12 +56,21 @@ module RuboCop
57
56
  def_node_matcher :operation_produces_immutable_object?, <<~PATTERN
58
57
  {
59
58
  (begin (send {float int} {:+ :- :* :** :/ :% :<<} _))
60
- (begin (send !{(str _) array} {:+ :- :* :** :/ :%} {float int}))
59
+ (begin (send #numeric_operation? {:+ :- :* :** :/ :%} {float int}))
61
60
  (begin (send _ {:== :=== :!= :<= :>= :< :>} _))
62
61
  (send _ {:count :length :size} ...)
63
62
  (any_block (send _ {:count :length :size} ...) ...)
64
63
  }
65
64
  PATTERN
65
+
66
+ # @!method numeric_operation?(node)
67
+ def_node_matcher :numeric_operation?, <<~PATTERN
68
+ {
69
+ float int
70
+ (begin #numeric_operation?)
71
+ (send #numeric_operation? {:+ :- :* :** :/ :%} #numeric_operation?)
72
+ }
73
+ PATTERN
66
74
  end
67
75
  end
68
76
  end
@@ -48,8 +48,13 @@ module RuboCop
48
48
  def need_heredoc_delimiter_quotes?(node)
49
49
  heredoc_delimiter = node.source.delete(heredoc_type(node))
50
50
  return true unless heredoc_delimiter.start_with?("'", '"')
51
+ return true if node.loc.heredoc_end.source.strip.match?(/\W/)
51
52
 
52
- node.loc.heredoc_end.source.strip.match?(/\W/) ||
53
+ # A double-quoted delimiter interpolates exactly like an unquoted one,
54
+ # so its quotes are always redundant. A single-quoted delimiter is
55
+ # required when the body contains interpolation or escapes that would
56
+ # otherwise be evaluated.
57
+ heredoc_delimiter.start_with?("'") &&
53
58
  node.loc.heredoc_body.source.match?(STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN)
54
59
  end
55
60
  end
@@ -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,55 @@ 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
+ def self.autocorrect_incompatible_with
34
+ [Layout::MultilineMethodCallBraceLayout]
35
+ end
36
+
37
+ # @!method redundant_unfreeze?(node)
38
+ def_node_matcher :redundant_unfreeze?, <<~PATTERN
39
+ {
40
+ (send dstr_type? {:+@ :dup})
41
+ (send (const nil? :String) :new dstr_type?)
42
+ }
43
+ PATTERN
44
+
45
+ def on_dstr(node)
46
+ return if uninterpolated_string?(node) || uninterpolated_heredoc?(node)
47
+ return unless redundant_unfreeze?(node.parent)
48
+ return if comment_outside_string?(node)
49
+
50
+ add_offense(offense_range(node.parent)) do |corrector|
51
+ remove_unfreeze(corrector, node.parent, node)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def offense_range(node)
58
+ if node.method?(:new)
59
+ node.source_range.begin.join(node.loc.selector)
60
+ else
61
+ node.loc.selector
62
+ end
63
+ end
64
+
65
+ # Only the parts around the string are removed, so that an unfrozen string
66
+ # interpolated into it can be corrected in the same pass.
67
+ def remove_unfreeze(corrector, node, string)
68
+ corrector.remove(node.source_range.begin.join(string.source_range.begin))
69
+ corrector.remove(string.source_range.end.join(node.source_range.end))
70
+ end
71
+
72
+ def comment_outside_string?(node)
73
+ call_range = node.parent.source_range
74
+ string_range = node.source_range
75
+
76
+ processed_source.comments.any? do |comment|
77
+ range = comment.source_range
37
78
 
38
- add_offense(node.loc.selector) do |corrector|
39
- corrector.remove(node.loc.selector)
40
- corrector.remove(node.loc.dot) unless node.unary_operation?
79
+ call_range.contains?(range) && !string_range.contains?(range)
41
80
  end
42
81
  end
43
82
  end
@@ -5,10 +5,13 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for redundant line continuation.
7
7
  #
8
- # This cop marks a line continuation as redundant if removing the backslash
9
- # does not result in a syntax error.
10
- # However, a backslash at the end of a comment or
11
- # for string concatenation is not redundant and is not considered an offense.
8
+ # A line continuation is redundant when removing the backslash does not
9
+ # change how the program parses: the source is reparsed without the
10
+ # backslash and the resulting AST is compared to the original. Only
11
+ # backslashes that are pure noise are reported; backslashes that are
12
+ # significant — inside strings, for string concatenation, before an
13
+ # operator or argument that would otherwise start a new statement, and
14
+ # so on — are left alone, as are backslashes in comments.
12
15
  #
13
16
  # @example
14
17
  # # bad
@@ -66,30 +69,23 @@ module RuboCop
66
69
  #
67
70
  class RedundantLineContinuation < Base
68
71
  include MatchRange
72
+ include ReparsedEquivalence
69
73
  extend AutoCorrector
70
74
 
71
75
  MSG = 'Redundant line continuation.'
72
76
  LINE_CONTINUATION = '\\'
73
77
  LINE_CONTINUATION_PATTERN = /(\\\n)/.freeze
74
- ALLOWED_STRING_TOKENS = %i[tSTRING tSTRING_CONTENT].freeze
75
- ARGUMENT_TYPES = %i[
76
- kDEF kDEFINED kFALSE kNIL kSELF kTRUE tAMPER tBANG tCARET tCHARACTER tCOLON3 tCONSTANT
77
- tCVAR tDOT2 tDOT3 tFLOAT tGVAR tIDENTIFIER tINTEGER tIVAR tLAMBDA tLBRACK tLCURLY
78
- tLPAREN_ARG tPIPE tQSYMBOLS_BEG tQWORDS_BEG tREGEXP_BEG tSTAR tSTRING tSTRING_BEG tSYMBEG
79
- tSYMBOL tSYMBOLS_BEG tTILDE tUMINUS tUNARY_NUM tUPLUS tWORDS_BEG tXSTRING_BEG
80
- ].freeze
81
- ARGUMENT_TAKING_FLOW_TOKEN_TYPES = %i[
82
- tIDENTIFIER kBREAK kNEXT kRETURN kSUPER kYIELD
83
- ].freeze
84
- ARITHMETIC_OPERATOR_TOKENS = %i[tDIVIDE tDSTAR tMINUS tPERCENT tPLUS tSTAR2].freeze
78
+ STRING_TOKEN_TYPES = %i[tSTRING tSTRING_CONTENT].freeze
79
+ STRING_LITERAL_ENDING_TOKEN_TYPES = %i[tSTRING tSTRING_END].freeze
80
+ STRING_LITERAL_BEGINNING_TOKEN_TYPES = %i[tSTRING tSTRING_BEG].freeze
85
81
 
86
82
  def on_new_investigation
87
83
  return unless processed_source.ast
88
84
 
89
- each_match_range(processed_source.ast.source_range, LINE_CONTINUATION_PATTERN) do |range|
90
- next if require_line_continuation?(range)
91
- next unless redundant_line_continuation?(range)
92
-
85
+ # A backslash is redundant if the source parses to the same AST
86
+ # without it; verification is the offense logic itself, so oversized
87
+ # scopes are reparsed regardless of size.
88
+ verified_by_reparse(line_continuation_candidates, oversized: :verify).each do |range|
93
89
  add_offense(range) do |corrector|
94
90
  corrector.remove_leading(range, 1)
95
91
  end
@@ -100,135 +96,106 @@ module RuboCop
100
96
 
101
97
  private
102
98
 
103
- def require_line_continuation?(range)
104
- !ends_with_uncommented_backslash?(range) ||
105
- string_concatenation?(range.source_line) ||
106
- start_with_arithmetic_operator?(range) ||
107
- inside_string_literal_or_method_with_argument?(range) ||
108
- leading_dot_method_chain_with_blank_line?(range)
99
+ def apply_reparse_correction(corrector, range)
100
+ corrector.remove_leading(range, 1)
109
101
  end
110
102
 
111
- def ends_with_uncommented_backslash?(range)
112
- # A line continuation always needs to be the last character on the line, which
113
- # means that it is impossible to have a comment following a continuation.
114
- # Therefore, if the line contains a comment, it cannot end with a continuation.
115
- return false if processed_source.line_with_comment?(range.line)
116
-
117
- range.source_line.end_with?(LINE_CONTINUATION)
118
- end
103
+ def line_continuation_candidates
104
+ candidates = []
119
105
 
120
- def string_concatenation?(source_line)
121
- /["']\s*\\\z/.match?(source_line)
122
- end
106
+ each_match_range(processed_source.ast.source_range, LINE_CONTINUATION_PATTERN) do |range|
107
+ next if within_comment?(range) || implicit_string_concatenation?(range)
108
+ next if within_string_content?(range)
109
+ next if leading_dot_method_chain_with_blank_line?(range)
123
110
 
124
- def inside_string_literal_or_method_with_argument?(range)
125
- line_range = range_by_whole_lines(range)
111
+ candidates << range
112
+ end
126
113
 
127
- processed_source.tokens.each_cons(2).any? do |token, next_token|
128
- next if token.line == next_token.line
114
+ candidates
115
+ end
129
116
 
130
- inside_string_literal?(range, token) ||
131
- method_with_argument?(line_range, token, next_token)
117
+ def within_comment?(range)
118
+ processed_source.comments.any? do |comment|
119
+ comment.source_range.overlaps?(range)
132
120
  end
133
121
  end
134
122
 
135
- def leading_dot_method_chain_with_blank_line?(range)
136
- return false unless range.source_line.strip.start_with?('.', '&.')
137
-
138
- processed_source[range.line].strip.empty?
139
- end
123
+ # A backslash directly joining two string literals is never redundant:
124
+ # removing it moves the second literal onto its own logical line,
125
+ # severing the implicit string concatenation. Skipping these up front
126
+ # avoids reparse verification, which is prohibitively slow on generated
127
+ # files gluing thousands of string fragments with line continuations.
128
+ def implicit_string_concatenation?(range)
129
+ tokens = processed_source.sorted_tokens
140
130
 
141
- def redundant_line_continuation?(range)
142
- return true unless (node = find_node_for_line(range.last_line))
143
- return false if argument_newline?(node)
131
+ token_before = last_token_before(tokens, range)
132
+ return false unless string_literal_ending_on_line?(token_before, range.line)
144
133
 
145
- # Check if source is still valid without the continuation
146
- source = processed_source.raw_source.dup
147
- source[range.begin_pos, range.length] = "\n"
148
- parse(source).valid_syntax?
134
+ string_literal_beginning_on_line?(first_token_after(tokens, range), range.line + 1)
149
135
  end
150
136
 
151
- def inspect_end_of_ruby_code_line_continuation
152
- last_line = processed_source.lines[processed_source.ast.last_line - 1]
153
- return unless code_ends_with_continuation?(last_line)
154
-
155
- last_column = last_line.length
156
- line_continuation_range = range_between(last_column - 1, last_column)
157
-
158
- add_offense(line_continuation_range) do |corrector|
159
- corrector.remove_trailing(line_continuation_range, 1)
137
+ # A backslash in string content is never a redundant line continuation
138
+ # (removing it changes the string's value, which the reparse check would
139
+ # detect); skipping it up front just avoids a useless reparse.
140
+ def within_string_content?(range)
141
+ @string_content_ranges ||= processed_source.tokens.filter_map do |token|
142
+ token.pos if STRING_TOKEN_TYPES.include?(token.type)
160
143
  end
144
+
145
+ @string_content_ranges.any? { |pos| pos.overlaps?(range) }
161
146
  end
162
147
 
163
- def code_ends_with_continuation?(last_line)
164
- return false if processed_source.line_with_comment?(processed_source.ast.last_line)
148
+ # The `parser` gem, unlike Ruby itself, accepts a leading-dot method
149
+ # chain continued across a blank line, so the reparse check alone would
150
+ # deem the backslash redundant even though removing it breaks the code
151
+ # on MRI. Prism matches Ruby here and does not need this guard.
152
+ def leading_dot_method_chain_with_blank_line?(range)
153
+ return false unless range.source_line.strip.start_with?('.', '&.')
165
154
 
166
- last_line.end_with?(LINE_CONTINUATION)
155
+ processed_source[range.line].strip.empty?
167
156
  end
168
157
 
169
- def inside_string_literal?(range, token)
170
- ALLOWED_STRING_TOKENS.include?(token.type) && token.pos.overlaps?(range)
158
+ def last_token_before(tokens, range)
159
+ index = tokens.bsearch_index { |token| token.end_pos > range.begin_pos }
160
+ index&.positive? ? tokens[index - 1] : nil
171
161
  end
172
162
 
173
- # A method call without parentheses such as the following cannot remove `\`:
174
- #
175
- # do_something \
176
- # argument
177
- def method_with_argument?(line_range, current_token, next_token)
178
- return false unless ARGUMENT_TAKING_FLOW_TOKEN_TYPES.include?(current_token.type)
179
- return false unless current_token.pos.overlaps?(line_range)
180
-
181
- ARGUMENT_TYPES.include?(next_token.type)
163
+ def first_token_after(tokens, range)
164
+ tokens.bsearch { |token| token.begin_pos >= range.end_pos }
182
165
  end
183
166
 
184
- def argument_newline?(node)
185
- return false if node.parenthesized_call?
186
-
187
- node = node.children.first if node.root? && node.begin_type?
188
-
189
- if argument_is_method?(node)
190
- argument_newline?(node.first_argument)
191
- else
192
- return false unless method_call_with_arguments?(node)
193
-
194
- node.loc.selector.line != node.first_argument.loc.line
195
- end
196
- end
167
+ def string_literal_ending_on_line?(token, line)
168
+ return false unless token
197
169
 
198
- def find_node_for_line(last_line)
199
- processed_source.ast.each_node do |node|
200
- return node if same_line?(node, last_line)
201
- end
170
+ STRING_LITERAL_ENDING_TOKEN_TYPES.include?(token.type) && token.pos.last_line == line
202
171
  end
203
172
 
204
- def same_line?(node, line)
205
- return false unless (source_range = node.source_range)
173
+ def string_literal_beginning_on_line?(token, line)
174
+ return false unless token
206
175
 
207
- if node.is_a?(AST::StrNode)
208
- if node.heredoc?
209
- (node.loc.heredoc_body.line..node.loc.heredoc_body.last_line).cover?(line)
210
- else
211
- (source_range.line..source_range.last_line).cover?(line)
212
- end
213
- else
214
- source_range.line == line
215
- end
176
+ STRING_LITERAL_BEGINNING_TOKEN_TYPES.include?(token.type) && token.line == line
216
177
  end
217
178
 
218
- def argument_is_method?(node)
219
- return false unless node.send_type?
220
- return false unless (first_argument = node.first_argument)
179
+ def inspect_end_of_ruby_code_line_continuation
180
+ last_line_number = processed_source.ast.last_line
181
+ last_line = processed_source.lines[last_line_number - 1]
182
+ return unless last_line&.end_with?(LINE_CONTINUATION)
221
183
 
222
- method_call_with_arguments?(first_argument)
223
- end
184
+ range = trailing_line_continuation_range(last_line_number)
185
+ return if within_comment?(range)
186
+ return if verified_by_reparse([range], oversized: :verify).empty?
224
187
 
225
- def method_call_with_arguments?(node)
226
- node.call_type? && !node.arguments.empty?
188
+ add_offense(range) do |corrector|
189
+ corrector.remove_trailing(range, 1)
190
+ end
227
191
  end
228
192
 
229
- def start_with_arithmetic_operator?(range)
230
- line_range = processed_source.buffer.line_range(range.line + 1)
231
- ARITHMETIC_OPERATOR_TOKENS.include?(processed_source.first_token_of(line_range).type)
193
+ # The backslash is the last character of the line; locate it by the line's
194
+ # position in the buffer rather than treating the column as an absolute offset
195
+ # (which corrupts an earlier line in multi-line files).
196
+ def trailing_line_continuation_range(line_number)
197
+ line_range = processed_source.buffer.line_range(line_number)
198
+ range_between(line_range.end_pos - 1, line_range.end_pos)
232
199
  end
233
200
  end
234
201
  end