rubocop 1.90.0 → 1.92.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 (303) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +241 -11
  4. data/config/obsoletion.yml +9 -0
  5. data/lib/rubocop/cached_data.rb +47 -7
  6. data/lib/rubocop/changed_files.rb +134 -0
  7. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  8. data/lib/rubocop/cli/command/execute_runner.rb +23 -3
  9. data/lib/rubocop/cli/command/explain.rb +34 -0
  10. data/lib/rubocop/cli/command/show_cops.rb +6 -0
  11. data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
  12. data/lib/rubocop/cli.rb +11 -4
  13. data/lib/rubocop/comment_config/directive_range.rb +3 -1
  14. data/lib/rubocop/comment_config/disable_next.rb +49 -3
  15. data/lib/rubocop/comment_config/push_pop.rb +55 -0
  16. data/lib/rubocop/comment_config.rb +32 -53
  17. data/lib/rubocop/config.rb +10 -0
  18. data/lib/rubocop/config_loader.rb +18 -7
  19. data/lib/rubocop/config_loader_resolver.rb +79 -12
  20. data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
  21. data/lib/rubocop/config_obsoletion/removed_cop.rb +9 -1
  22. data/lib/rubocop/config_obsoletion.rb +1 -1
  23. data/lib/rubocop/config_validator.rb +29 -3
  24. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  25. data/lib/rubocop/cop/badge.rb +1 -1
  26. data/lib/rubocop/cop/base.rb +57 -24
  27. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  28. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  29. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  30. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  31. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  32. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +22 -7
  33. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  34. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +23 -2
  35. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  36. data/lib/rubocop/cop/correctors.rb +2 -2
  37. data/lib/rubocop/cop/documentation.rb +1 -1
  38. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  39. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  40. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  41. data/lib/rubocop/cop/generator.rb +1 -1
  42. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  43. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  44. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  45. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  46. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  47. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
  48. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  49. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  50. data/lib/rubocop/cop/layout/class_structure.rb +9 -1
  51. data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
  52. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  53. data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
  54. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +14 -5
  55. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  56. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  57. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  58. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -1
  59. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  60. data/lib/rubocop/cop/layout/end_alignment.rb +16 -10
  61. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -0
  62. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  63. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
  64. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +5 -3
  65. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  66. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  67. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
  68. data/lib/rubocop/cop/layout/hash_alignment.rb +15 -10
  69. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -0
  70. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  71. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  72. data/lib/rubocop/cop/layout/indentation_width.rb +4 -4
  73. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  74. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  75. data/lib/rubocop/cop/layout/line_length.rb +71 -19
  76. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  77. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  78. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +7 -0
  79. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  80. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +14 -0
  81. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  82. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  83. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +15 -2
  84. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  85. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  86. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +10 -4
  87. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  88. data/lib/rubocop/cop/layout/space_around_operators.rb +5 -1
  89. data/lib/rubocop/cop/layout/space_before_block_braces.rb +4 -1
  90. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  91. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +31 -8
  92. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  93. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  94. data/lib/rubocop/cop/lint/argument_mismatch.rb +14 -4
  95. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
  96. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +88 -14
  97. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  98. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  99. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  100. data/lib/rubocop/cop/lint/literal_as_condition.rb +23 -2
  101. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  102. data/lib/rubocop/cop/lint/loop.rb +24 -2
  103. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +245 -0
  104. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +19 -4
  105. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  106. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  107. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  108. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
  109. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +42 -17
  110. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  111. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  112. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  113. data/lib/rubocop/cop/lint/void.rb +8 -1
  114. data/lib/rubocop/cop/lint.rb +1 -0
  115. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  116. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  117. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  118. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  119. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  120. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  121. data/lib/rubocop/cop/mixin/check_line_breakable.rb +17 -14
  122. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +5 -1
  123. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  124. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  125. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +69 -0
  126. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  127. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +26 -2
  128. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +31 -3
  129. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  130. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  131. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  132. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  133. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  134. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  135. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +29 -3
  136. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  137. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  138. data/lib/rubocop/cop/mixin/statement_modifier.rb +22 -2
  139. data/lib/rubocop/cop/mixin.rb +4 -2
  140. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  141. data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
  142. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  143. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  144. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  145. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  146. data/lib/rubocop/cop/offense.rb +59 -3
  147. data/lib/rubocop/cop/registry.rb +10 -2
  148. data/lib/rubocop/cop/security/json_load.rb +16 -4
  149. data/lib/rubocop/cop/severity.rb +12 -0
  150. data/lib/rubocop/cop/style/access_modifier_declarations.rb +10 -2
  151. data/lib/rubocop/cop/style/alias.rb +18 -5
  152. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  153. data/lib/rubocop/cop/style/and_or.rb +4 -0
  154. data/lib/rubocop/cop/style/arguments_forwarding.rb +6 -3
  155. data/lib/rubocop/cop/style/attr.rb +3 -4
  156. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  157. data/lib/rubocop/cop/style/block_delimiters.rb +29 -3
  158. data/lib/rubocop/cop/style/case_like_if.rb +9 -6
  159. data/lib/rubocop/cop/style/class_and_module_children.rb +54 -16
  160. data/lib/rubocop/cop/style/class_methods_definitions.rb +8 -0
  161. data/lib/rubocop/cop/style/combinable_loops.rb +85 -11
  162. data/lib/rubocop/cop/style/concat_array_literals.rb +46 -7
  163. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  164. data/lib/rubocop/cop/style/constant_visibility.rb +6 -2
  165. data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
  166. data/lib/rubocop/cop/style/directive_scope.rb +154 -21
  167. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +118 -18
  168. data/lib/rubocop/cop/style/documentation_method.rb +1 -9
  169. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  170. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  171. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  172. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  173. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  174. data/lib/rubocop/cop/style/empty_method.rb +15 -3
  175. data/lib/rubocop/cop/style/endless_method.rb +91 -11
  176. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  177. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  178. data/lib/rubocop/cop/style/file_null.rb +2 -2
  179. data/lib/rubocop/cop/style/float_division.rb +28 -14
  180. data/lib/rubocop/cop/style/for.rb +44 -1
  181. data/lib/rubocop/cop/style/format_string.rb +28 -6
  182. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  183. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  184. data/lib/rubocop/cop/style/guard_clause.rb +13 -1
  185. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  186. data/lib/rubocop/cop/style/hash_lookup_method.rb +35 -9
  187. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  188. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  189. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  190. data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -20
  191. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  192. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  193. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  194. data/lib/rubocop/cop/style/it_block_parameter.rb +32 -3
  195. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  196. data/lib/rubocop/cop/style/lambda.rb +18 -0
  197. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  198. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  199. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  200. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +45 -10
  201. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +25 -2
  202. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  203. data/lib/rubocop/cop/style/method_def_parentheses.rb +29 -3
  204. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  205. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  206. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  207. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  208. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  209. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  210. data/lib/rubocop/cop/style/mutable_constant.rb +28 -1
  211. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  212. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  213. data/lib/rubocop/cop/style/nested_modifier.rb +36 -12
  214. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  215. data/lib/rubocop/cop/style/next.rb +24 -10
  216. data/lib/rubocop/cop/style/nil_comparison.rb +48 -8
  217. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  218. data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
  219. data/lib/rubocop/cop/style/not.rb +10 -3
  220. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
  221. data/lib/rubocop/cop/style/numeric_predicate.rb +21 -5
  222. data/lib/rubocop/cop/style/one_line_conditional.rb +14 -3
  223. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  224. data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
  225. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  226. data/lib/rubocop/cop/style/reduce_to_hash.rb +17 -1
  227. data/lib/rubocop/cop/style/redundant_begin.rb +20 -3
  228. data/lib/rubocop/cop/style/redundant_condition.rb +34 -17
  229. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +16 -1
  230. data/lib/rubocop/cop/style/redundant_exception.rb +15 -3
  231. data/lib/rubocop/cop/style/redundant_fetch_block.rb +10 -2
  232. data/lib/rubocop/cop/style/redundant_freeze.rb +10 -1
  233. data/lib/rubocop/cop/style/redundant_parentheses.rb +23 -2
  234. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  235. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
  236. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +25 -5
  237. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  238. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  239. data/lib/rubocop/cop/style/require_order.rb +9 -0
  240. data/lib/rubocop/cop/style/rescue_modifier.rb +5 -9
  241. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  242. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  243. data/lib/rubocop/cop/style/sample.rb +9 -8
  244. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  245. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  246. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  247. data/lib/rubocop/cop/style/single_line_methods.rb +16 -4
  248. data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
  249. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  250. data/lib/rubocop/cop/style/string_concatenation.rb +9 -3
  251. data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
  252. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  253. data/lib/rubocop/cop/style/symbol_proc.rb +23 -5
  254. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  255. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  256. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  257. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  258. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  259. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  260. data/lib/rubocop/cop/style.rb +0 -1
  261. data/lib/rubocop/cop/team.rb +10 -5
  262. data/lib/rubocop/cop/util.rb +1 -1
  263. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  264. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  265. data/lib/rubocop/cops_documentation_generator.rb +23 -2
  266. data/lib/rubocop/directive_comment.rb +37 -8
  267. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  268. data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
  269. data/lib/rubocop/formatter/formatter_set.rb +6 -1
  270. data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
  271. data/lib/rubocop/formatter/html_formatter.rb +2 -2
  272. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  273. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  274. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  275. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  276. data/lib/rubocop/formatter/sarif_formatter.rb +186 -0
  277. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  278. data/lib/rubocop/formatter.rb +1 -0
  279. data/lib/rubocop/lsp/runtime.rb +47 -11
  280. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  281. data/lib/rubocop/lsp.rb +5 -2
  282. data/lib/rubocop/magic_comment.rb +27 -0
  283. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  284. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  285. data/lib/rubocop/mcp/scope.rb +65 -0
  286. data/lib/rubocop/mcp/server.rb +184 -76
  287. data/lib/rubocop/mcp/source_files.rb +35 -0
  288. data/lib/rubocop/offense_limit.rb +30 -0
  289. data/lib/rubocop/options.rb +97 -4
  290. data/lib/rubocop/path_util.rb +1 -1
  291. data/lib/rubocop/plugin/configuration_integrator.rb +12 -5
  292. data/lib/rubocop/plugin/loader.rb +1 -1
  293. data/lib/rubocop/rake_task.rb +1 -1
  294. data/lib/rubocop/remote_config.rb +8 -5
  295. data/lib/rubocop/result_cache.rb +36 -18
  296. data/lib/rubocop/runner.rb +160 -49
  297. data/lib/rubocop/server/cache.rb +2 -2
  298. data/lib/rubocop/unified_diff.rb +257 -0
  299. data/lib/rubocop/util.rb +69 -0
  300. data/lib/rubocop/version.rb +1 -1
  301. data/lib/rubocop.rb +9 -1
  302. metadata +21 -6
  303. data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
@@ -53,6 +53,12 @@ module RuboCop
53
53
  # @!method hash_from_array?(node)
54
54
  def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
55
55
 
56
+ # A hash literal as the first argument needs the parentheses of the call,
57
+ # which `Style/MethodCallWithArgsParentheses` removes in the same pass.
58
+ def self.autocorrect_incompatible_with
59
+ [Style::MethodCallWithArgsParentheses]
60
+ end
61
+
56
62
  def on_send(node)
57
63
  return if part_of_ignored_node?(node) || !hash_from_array?(node)
58
64
 
@@ -95,12 +101,30 @@ module RuboCop
95
101
  def register_offense_for_hash(node, hash_argument)
96
102
  add_offense(node, message: MSG_LITERAL_HASH_ARG) do |corrector|
97
103
  corrector.replace(node, "{#{hash_argument.source}}")
98
-
99
- parent = node.parent
100
- add_parentheses(parent, corrector) if parent&.send_type? && !parent.parenthesized?
104
+ add_parentheses_to_parent_call(node, corrector)
101
105
  end
102
106
  end
103
107
 
108
+ # Without parentheses, a hash literal at the start of the first argument would be
109
+ # parsed as a block. That includes an argument like `Hash[a, b].merge(c)`.
110
+ def add_parentheses_to_parent_call(node, corrector)
111
+ node = node.parent while starts_parent?(node)
112
+ parent = node.parent
113
+ return unless parent&.type?(:call, :super, :yield)
114
+ return if parent.parenthesized? || operator_syntax?(parent)
115
+
116
+ add_parentheses(parent, corrector)
117
+ end
118
+
119
+ def starts_parent?(node)
120
+ node.parent && node.parent.source_range.begin_pos == node.source_range.begin_pos
121
+ end
122
+
123
+ # `foo[x]`, `foo.bar = x` and `foo == x` take a hash literal as it is.
124
+ def operator_syntax?(call)
125
+ call.setter_method? || (call.operator_method? && !call.loc?(:dot))
126
+ end
127
+
104
128
  def register_offense_for_zip_method(node, zip_method)
105
129
  add_offense(node, message: MSG_TO_H) do |corrector|
106
130
  if zip_method.parenthesized?
@@ -135,11 +159,7 @@ module RuboCop
135
159
  def correct_multi_argument(node)
136
160
  add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
137
161
  corrector.replace(node, args_to_hash(node.arguments))
138
-
139
- parent = node.parent
140
- if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
141
- add_parentheses(parent, corrector)
142
- end
162
+ add_parentheses_to_parent_call(node, corrector)
143
163
  end
144
164
  end
145
165
 
@@ -34,6 +34,9 @@ module RuboCop
34
34
  # # good - fetch with block is allowed
35
35
  # hash.fetch(key) { default }
36
36
  #
37
+ # # good - `ENV.fetch` is left to `Style/FetchEnvVar` when it is enabled
38
+ # ENV.fetch(key)
39
+ #
37
40
  # @example EnforcedStyle: fetch
38
41
  # # bad
39
42
  # hash[key]
@@ -55,14 +58,20 @@ module RuboCop
55
58
 
56
59
  RESTRICT_ON_SEND = %i[[] fetch].freeze
57
60
 
61
+ # @!method env_const?(node)
62
+ def_node_matcher :env_const?, '(const {nil? cbase} :ENV)'
63
+
58
64
  def on_send(node)
59
65
  return if (receiver = node.receiver) && allowed_receiver?(receiver)
66
+ return if part_of_ignored_node?(node)
60
67
 
61
68
  if offense_for_brackets?(node)
69
+ ignore_node(node.first_argument)
62
70
  add_offense(node.loc.selector, message: BRACKET_MSG) do |corrector|
63
71
  correct_fetch_to_brackets(corrector, node)
64
72
  end
65
73
  elsif offense_for_fetch?(node)
74
+ ignore_node(node.first_argument)
66
75
  add_offense(node, message: FETCH_MSG) do |corrector|
67
76
  correct_brackets_to_fetch(corrector, node)
68
77
  end
@@ -73,28 +82,45 @@ module RuboCop
73
82
  private
74
83
 
75
84
  def offense_for_brackets?(node)
76
- style == :brackets && node.receiver && node.method?(:fetch) && node.arguments.one? &&
77
- !node.block_literal? && !node.csend_type?
85
+ return false if style != :brackets || !node.receiver
86
+ return false if !node.method?(:fetch) || !node.arguments.one?
87
+ return false if node.block_literal? || node.csend_type?
88
+
89
+ !exempt_from_brackets?(node)
90
+ end
91
+
92
+ def exempt_from_brackets?(node)
93
+ comment_before_dot?(node) || env_fetch_preferred?(node.receiver)
94
+ end
95
+
96
+ def comment_before_dot?(node)
97
+ processed_source.contains_comment?(node.receiver.source_range.end.join(node.loc.dot))
98
+ end
99
+
100
+ def env_fetch_preferred?(receiver)
101
+ env_const?(receiver) && processed_source.registry.enabled?(Style::FetchEnvVar, config)
78
102
  end
79
103
 
80
104
  def offense_for_fetch?(node)
81
- style == :fetch && node.method?(:[]) && node.arguments.one?
105
+ style == :fetch && node.method?(:[]) && node.arguments.one? &&
106
+ !compound_assignment_target?(node)
107
+ end
108
+
109
+ def compound_assignment_target?(node)
110
+ node.parent&.type?(:op_asgn, :or_asgn, :and_asgn) && node.parent.children.first == node
82
111
  end
83
112
 
84
113
  def correct_fetch_to_brackets(corrector, node)
85
114
  key = node.first_argument.source
86
115
 
87
- corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
116
+ corrector.replace(node.receiver.source_range.end.join(node.source_range.end), "[#{key}]")
88
117
  end
89
118
 
90
119
  def correct_brackets_to_fetch(corrector, node)
91
120
  key = node.first_argument.source
121
+ replacement = node.loc.dot ? "fetch(#{key})" : ".fetch(#{key})"
92
122
 
93
- if node.csend_type?
94
- corrector.replace(node.loc.dot.join(node.source_range.end), "&.fetch(#{key})")
95
- else
96
- corrector.replace(node.loc.selector.join(node.source_range.end), ".fetch(#{key})")
97
- end
123
+ corrector.replace(node.loc.selector.join(node.source_range.end), replacement)
98
124
  end
99
125
  end
100
126
  end
@@ -142,6 +142,13 @@ module RuboCop
142
142
  MSG_HASH_ROCKETS = 'Use hash rockets syntax.'
143
143
  NO_MIXED_KEYS_STYLES = %i[ruby19_no_mixed_keys no_mixed_keys].freeze
144
144
 
145
+ # Omitting the last hash value of a call without parentheses adds the parentheses,
146
+ # which `Style/MethodCallWithArgsParentheses` and `Style/SuperWithArgsParentheses`
147
+ # would add again in the same pass.
148
+ def self.autocorrect_incompatible_with
149
+ [Style::MethodCallWithArgsParentheses, Style::SuperWithArgsParentheses]
150
+ end
151
+
145
152
  def on_hash(node)
146
153
  pairs = node.pairs
147
154
 
@@ -241,6 +248,8 @@ module RuboCop
241
248
  def check(pairs, delim, msg)
242
249
  pairs.each do |pair|
243
250
  if pair.delimiter == delim
251
+ next if pair.value_omission? && keyword_value?(pair)
252
+
244
253
  location = pair.source_range.begin.join(pair.loc.operator)
245
254
  add_offense(location, message: msg) do |corrector|
246
255
  autocorrect(corrector, pair)
@@ -283,12 +292,16 @@ module RuboCop
283
292
  end
284
293
 
285
294
  def autocorrect_hash_rockets(corrector, pair_node)
286
- op = pair_node.loc.operator
287
-
288
295
  key_with_hash_rocket = ":#{pair_node.key.source}#{pair_node.inverse_delimiter(true)}"
289
296
  key_with_hash_rocket += pair_node.key.source if pair_node.value_omission?
290
297
  corrector.replace(pair_node.key, key_with_hash_rocket)
291
- corrector.remove(range_with_surrounding_space(op))
298
+ corrector.remove(operator_range(pair_node))
299
+ end
300
+
301
+ def operator_range(pair_node)
302
+ op = pair_node.loc.operator
303
+
304
+ range_with_surrounding_space(op, newlines: !pair_node.value_omission?)
292
305
  end
293
306
 
294
307
  def autocorrect_no_mixed_keys(corrector, pair_node)
@@ -188,7 +188,7 @@ module RuboCop
188
188
  end
189
189
  end
190
190
 
191
- # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
191
+ # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
192
192
  def check_expressions(node, expressions, insert_position)
193
193
  return if expressions.any?(&:nil?)
194
194
 
@@ -198,8 +198,7 @@ module RuboCop
198
198
  add_offense(expression) do |corrector|
199
199
  next if node.if_type? && (node.ternary? || node.then?)
200
200
 
201
- range = range_by_whole_lines(expression.source_range, include_final_newline: true)
202
- corrector.remove(range)
201
+ corrector.remove(removal_range(expression))
203
202
  next if inserted_expression
204
203
 
205
204
  if node.parent&.assignment?
@@ -213,6 +212,26 @@ module RuboCop
213
212
  end
214
213
  end
215
214
 
215
+ def removal_range(expression)
216
+ range = expression.source_range
217
+ whole_lines = range_by_whole_lines(range, include_final_newline: true)
218
+ return whole_lines if whole_lines.source.strip == expression.source
219
+
220
+ range_with_separator(range)
221
+ end
222
+
223
+ def range_with_separator(range)
224
+ source = range.source_buffer.source
225
+
226
+ if (following = source[range.end_pos..][/\A[ \t]*;[ \t]*/])
227
+ range.adjust(end_pos: following.length)
228
+ elsif (preceding = source[0...range.begin_pos][/[ \t]*;[ \t]*\z/])
229
+ range.adjust(begin_pos: -preceding.length)
230
+ else
231
+ range_with_surrounding_space(range, side: :right, newlines: false)
232
+ end
233
+ end
234
+
216
235
  def correct_assignment(corrector, node, expression, insert_position)
217
236
  indentation = indentation_of(node.parent)
218
237
 
@@ -75,7 +75,7 @@ module RuboCop
75
75
  return if comments_between_else_and_if?(node, else_branch)
76
76
 
77
77
  add_offense(else_branch.loc.keyword) do |corrector|
78
- next if part_of_ignored_node?(node)
78
+ next if part_of_ignored_node?(node) || shares_lines_with_other_code?(else_branch)
79
79
 
80
80
  autocorrect(corrector, else_branch)
81
81
  ignore_node(node)
@@ -84,6 +84,26 @@ module RuboCop
84
84
 
85
85
  private
86
86
 
87
+ # The `elsif` correction moves and removes whole lines, so it can't be applied when
88
+ # the nested `if` condition, its `if` branch or its `end` shares a line with other code.
89
+ # A modifier `if` on the `else` line would leave its body on the `elsif` line.
90
+ def shares_lines_with_other_code?(node)
91
+ return false if node.then?
92
+ return !begins_its_line?(node.source_range) if node.modifier_form?
93
+
94
+ moved_ranges(node).any? { |range| !begins_its_line?(range) || !ends_its_line?(range) }
95
+ end
96
+
97
+ def moved_ranges(node)
98
+ ranges = [if_condition_range(node, node.condition), find_end_range(node)]
99
+ ranges << range_with_comments(node.if_branch) if node.if_branch
100
+ ranges
101
+ end
102
+
103
+ def ends_its_line?(range)
104
+ processed_source.lines[range.last_line - 1][range.last_column..].match?(/\A\s*(#|\z)/)
105
+ end
106
+
87
107
  def autocorrect(corrector, node)
88
108
  if node.then?
89
109
  # If the nested `if` is a then node, correct it first,
@@ -159,7 +159,7 @@ module RuboCop
159
159
  corrector.replace(node, replacement)
160
160
  end
161
161
 
162
- def replacement_for_modifier_form(corrector, node) # rubocop:disable Metrics/AbcSize
162
+ def replacement_for_modifier_form(corrector, node)
163
163
  comment = comment_on_node_line(node)
164
164
  if comment && too_long_due_to_comment_after_modifier?(node, comment)
165
165
  remove_comment(corrector, node, comment)
@@ -167,12 +167,11 @@ module RuboCop
167
167
  return to_modifier_form_with_move_comment(node, indent(node), comment)
168
168
  end
169
169
 
170
- last_argument = node.if_branch.last_argument if node.if_branch.send_type?
171
- if last_argument.respond_to?(:heredoc?) && last_argument.heredoc?
172
- heredoc = extract_heredoc_from(last_argument)
173
- remove_heredoc(corrector, heredoc)
170
+ heredoc_ranges = heredoc_ranges_in(node.if_branch)
171
+ unless heredoc_ranges.empty?
172
+ remove_heredoc(corrector, heredoc_ranges)
174
173
 
175
- return to_normal_form_with_heredoc(node, indent(node), heredoc)
174
+ return to_normal_form_with_heredoc(node, indent(node), heredoc_ranges)
176
175
  end
177
176
 
178
177
  to_normal_form(node, indent(node))
@@ -275,14 +274,19 @@ module RuboCop
275
274
  RUBY
276
275
  end
277
276
 
278
- def to_normal_form_with_heredoc(node, indentation, heredoc)
279
- heredoc_body, heredoc_end = heredoc
277
+ def remove_heredoc(corrector, heredoc_ranges)
278
+ heredoc_ranges.each do |range|
279
+ corrector.remove(range_by_whole_lines(range, include_final_newline: true))
280
+ end
281
+ end
282
+
283
+ def to_normal_form_with_heredoc(node, indentation, heredoc_ranges)
284
+ heredoc_lines = heredoc_ranges.map(&:source)
280
285
 
281
286
  <<~RUBY.chomp
282
287
  #{node.keyword} #{node.condition.source}
283
288
  #{indentation} #{node.body.source}
284
- #{indentation} #{heredoc_body.source.chomp}
285
- #{indentation} #{heredoc_end.source.chomp}
289
+ #{heredoc_lines.join("\n")}
286
290
  #{indentation}end
287
291
  RUBY
288
292
  end
@@ -294,17 +298,12 @@ module RuboCop
294
298
  RUBY
295
299
  end
296
300
 
297
- def extract_heredoc_from(last_argument)
298
- heredoc_body = last_argument.loc.heredoc_body
299
- heredoc_end = last_argument.loc.heredoc_end
300
-
301
- [heredoc_body, heredoc_end]
302
- end
303
-
304
- def remove_heredoc(corrector, heredoc)
305
- heredoc.each do |range|
306
- corrector.remove(range_by_whole_lines(range, include_final_newline: true))
301
+ def heredoc_ranges_in(body)
302
+ ranges = body.each_node(:any_str).select(&:heredoc?).map do |heredoc|
303
+ range_by_whole_lines(heredoc.loc.heredoc_body.join(heredoc.loc.heredoc_end))
307
304
  end
305
+ # A heredoc opened in another heredoc's interpolation is moved along with it.
306
+ ranges.reject { |range| ranges.any? { |other| other.contains?(range) } }
308
307
  end
309
308
 
310
309
  def comment_on_node_line(node)
@@ -28,6 +28,10 @@ module RuboCop
28
28
 
29
29
  MSG = 'Avoid modifier `%<keyword>s` after another conditional.'
30
30
 
31
+ def self.autocorrect_incompatible_with
32
+ [Style::SoleNestedConditional]
33
+ end
34
+
31
35
  # rubocop:disable-next Metrics/AbcSize
32
36
  def on_if(node)
33
37
  return unless node.modifier_form? && node.body.if_type?
@@ -21,6 +21,10 @@ module RuboCop
21
21
  MSG_NEWLINE = 'Do not use `%<keyword>s %<expr>s;` - use a newline instead.'
22
22
  MSG_TERNARY = 'Do not use `%<keyword>s %<expr>s;` - use a ternary operator instead.'
23
23
 
24
+ LOWER_PRECEDENCE_THAN_TERNARY_TYPES = %i[
25
+ and or if rescue any_match_pattern while until while_post until_post
26
+ ].freeze
27
+
24
28
  def on_normal_if_unless(node)
25
29
  return if node.parent&.if_type?
26
30
  return if part_of_ignored_node?(node)
@@ -77,12 +81,14 @@ module RuboCop
77
81
  def replacement(node)
78
82
  return correct_elsif(node) if node.else_branch&.if_type?
79
83
 
80
- then_code = node.if_branch ? build_expression(node.if_branch) : 'nil'
81
- else_code = node.else_branch ? build_expression(node.else_branch) : 'nil'
84
+ then_code = node.if_branch ? ternary_operand(node.if_branch) : 'nil'
85
+ else_code = node.else_branch ? ternary_operand(node.else_branch) : 'nil'
82
86
 
83
87
  then_code, else_code = else_code, then_code if node.unless?
84
88
 
85
- "#{ternary_condition(node)} ? #{then_code} : #{else_code}"
89
+ ternary = "#{ternary_condition(node)} ? #{then_code} : #{else_code}"
90
+
91
+ operand_of_parent?(node) ? "(#{ternary})" : ternary
86
92
  end
87
93
 
88
94
  def correct_elsif(node)
@@ -108,7 +114,34 @@ module RuboCop
108
114
  # `(a = b) ? c : d`), changing what gets assigned.
109
115
  def ternary_condition(node)
110
116
  condition = node.condition
111
- condition.assignment? ? "(#{condition.source})" : condition.source
117
+ condition.assignment? ? "(#{condition.source})" : ternary_operand(condition)
118
+ end
119
+
120
+ def ternary_operand(expr)
121
+ lower_precedence_than_ternary?(expr) ? "(#{expr.source})" : build_expression(expr)
122
+ end
123
+
124
+ # Expressions that bind looser than `?:` (or cannot appear unparenthesized
125
+ # inside it) would otherwise absorb or break the ternary.
126
+ def lower_precedence_than_ternary?(node)
127
+ return true if node.type?(*LOWER_PRECEDENCE_THAN_TERNARY_TYPES)
128
+ return true if node.send_type? && (node.prefix_not? || prefix_bang_on_command?(node))
129
+
130
+ node.type?(:yield, :super, :defined?) && node.arguments.any? && !node.parenthesized?
131
+ end
132
+
133
+ def operand_of_parent?(node)
134
+ return false unless (parent = node.parent)
135
+ return true if parent.operator_keyword?
136
+
137
+ parent.call_type? && call_operand?(parent, node)
138
+ end
139
+
140
+ def call_operand?(call, node)
141
+ return false if call.prefix_not?
142
+ return true if call.receiver.equal?(node)
143
+
144
+ call.operator_method? && !call.method?(:[]) && !call.method?(:[]=)
112
145
  end
113
146
 
114
147
  def build_else_branch(second_condition)
@@ -131,6 +164,16 @@ module RuboCop
131
164
  result
132
165
  end
133
166
 
167
+ # `!foo a` leaves the arguments of `foo` unparenthesized at the end of the operand,
168
+ # where they would absorb the rest of the ternary.
169
+ def prefix_bang_on_command?(node)
170
+ return false unless node.prefix_bang?
171
+
172
+ receiver = node.receiver
173
+
174
+ require_argument_parentheses?(receiver) || lower_precedence_than_ternary?(receiver)
175
+ end
176
+
134
177
  def require_argument_parentheses?(node)
135
178
  return false if !node.call_type? || node.arithmetic_operation?
136
179
 
@@ -114,9 +114,31 @@ module RuboCop
114
114
  method_call, _lhs, method, _rhs = inverse_candidate?(node)
115
115
  return unless method_call && method
116
116
 
117
- corrector.remove(not_to_receiver(node, method_call))
117
+ remove_negation(corrector, node, method, method_call)
118
118
  corrector.replace(method_call.loc.selector, inverse_methods[method].to_s)
119
- remove_end_parenthesis(corrector, node, method, method_call)
119
+ end
120
+
121
+ def remove_negation(corrector, node, method, method_call)
122
+ if !keep_parentheses?(node, method_call)
123
+ corrector.remove(not_to_receiver(node, method_call))
124
+ remove_end_parenthesis(corrector, node, method, method_call)
125
+ elsif node.parenthesized?
126
+ corrector.remove(node.loc.selector)
127
+ else
128
+ corrector.remove(node.loc.selector.join(node.receiver.source_range.begin))
129
+ end
130
+ end
131
+
132
+ def keep_parentheses?(node, method_call)
133
+ return false unless node.receiver.begin_type? || node.parenthesized?
134
+ return false unless method_call.operator_method?
135
+ return false unless (parent = node.parent)&.call_type?
136
+
137
+ binary_operator?(parent) || parent.receiver.equal?(node)
138
+ end
139
+
140
+ def binary_operator?(node)
141
+ node.operator_method? && !node.loc.dot && !node.method?(:[]) && !node.method?(:[]=)
120
142
  end
121
143
 
122
144
  def correct_inverse_block(corrector, node)
@@ -61,6 +61,12 @@ module RuboCop
61
61
 
62
62
  minimum_target_ruby_version 3.4
63
63
 
64
+ # `Style/StabbyLambdaParentheses` wraps the parameter this cop removes in the same pass,
65
+ # which would leave an empty parameter list that forbids `it`.
66
+ def self.autocorrect_incompatible_with
67
+ [Style::Lambda, Style::StabbyLambdaParentheses]
68
+ end
69
+
64
70
  def on_block(node)
65
71
  return unless style == :always
66
72
  return unless node.arguments.one?
@@ -68,9 +74,10 @@ module RuboCop
68
74
  # `restarg`, `kwrestarg`, `blockarg` nodes can return early.
69
75
  return unless node.first_argument.arg_type?
70
76
 
71
- variables = find_block_variables(node, node.first_argument.source)
77
+ block_argument_name = node.first_argument.source
78
+ return if used_within_inner_block?(node.body, block_argument_name)
72
79
 
73
- variables.each do |variable|
80
+ find_block_variables(node, block_argument_name).each do |variable|
74
81
  add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
75
82
  corrector.remove(node.arguments)
76
83
  corrector.replace(variable, 'it')
@@ -111,7 +118,29 @@ module RuboCop
111
118
  def find_block_variables(node, block_argument_name)
112
119
  return [] unless node.body
113
120
 
114
- node.body.each_descendant(:lvar).select do |descendant|
121
+ ranges = inner_block_ranges(node.body)
122
+
123
+ block_variables(node.body, block_argument_name).reject do |variable|
124
+ ranges.any? { |range| range.contains?(variable.source_range) }
125
+ end
126
+ end
127
+
128
+ def inner_block_ranges(body)
129
+ body.each_node(:any_block).map { |block| block.loc.begin.join(block.loc.end) }
130
+ end
131
+
132
+ def used_within_inner_block?(body, block_argument_name)
133
+ return false unless body
134
+
135
+ ranges = inner_block_ranges(body)
136
+
137
+ block_variables(body, block_argument_name).any? do |variable|
138
+ ranges.any? { |range| range.contains?(variable.source_range) }
139
+ end
140
+ end
141
+
142
+ def block_variables(body, block_argument_name)
143
+ body.each_descendant(:lvar).select do |descendant|
115
144
  descendant.source == block_argument_name
116
145
  end
117
146
  end
@@ -75,7 +75,9 @@ module RuboCop
75
75
 
76
76
  def remove_kwargs(kwarg_nodes, corrector)
77
77
  kwarg_nodes.each do |kwarg|
78
- with_space = range_with_surrounding_space(kwarg.source_range)
78
+ range = range_with_surrounding_space(kwarg.source_range, side: :left, whitespace: true)
79
+ with_space = range_with_surrounding_space(range, side: :right)
80
+
79
81
  corrector.remove(range_with_surrounding_comma(with_space, :left))
80
82
  end
81
83
  end
@@ -48,6 +48,8 @@ module RuboCop
48
48
  # end
49
49
  class Lambda < Base
50
50
  include ConfigurableEnforcedStyle
51
+ include PatternMatchingHelp
52
+ include RescueNode
51
53
  extend AutoCorrector
52
54
 
53
55
  LITERAL_MESSAGE = 'Use the `-> { ... }` lambda literal syntax for %<modifier>s lambdas.'
@@ -71,6 +73,7 @@ module RuboCop
71
73
  selector = node.send_node.source
72
74
 
73
75
  return unless offending_selector?(node, selector)
76
+ return if uncorrectable_lambda_literal?(node)
74
77
 
75
78
  add_offense(node.send_node, message: message(node, selector)) do |corrector|
76
79
  if node.send_node.lambda_literal?
@@ -91,6 +94,21 @@ module RuboCop
91
94
  selector == OFFENDING_SELECTORS[:style][style][lines]
92
95
  end
93
96
 
97
+ def uncorrectable_lambda_literal?(node)
98
+ return false unless node.send_node.lambda_literal?
99
+ return true if in_pattern?(node)
100
+ return false unless rescue_or_ensure_clause?(node.body)
101
+
102
+ LambdaLiteralToMethodCorrector.new(node).replace_delimiters?
103
+ end
104
+
105
+ def rescue_or_ensure_clause?(body)
106
+ return false unless body
107
+ return true if body.ensure_type?
108
+
109
+ body.rescue_type? && !rescue_modifier?(body.resbody_branches.first)
110
+ end
111
+
94
112
  def message(node, selector)
95
113
  message = selector == '->' ? METHOD_MESSAGE : LITERAL_MESSAGE
96
114
 
@@ -28,8 +28,9 @@ module RuboCop
28
28
  def on_send(node)
29
29
  return unless node.receiver
30
30
  # Rewriting the call rebuilds it as a single expression, which would drop
31
- # any comments inside the argument list, so leave it to be fixed manually.
32
- return if comments_in_node?(node)
31
+ # any comments or heredoc bodies inside the argument list, so leave it to be
32
+ # fixed manually.
33
+ return if comments_in_node?(node) || heredoc_argument?(node)
33
34
 
34
35
  if offense?(node)
35
36
  prefer = prefer(node)
@@ -59,6 +60,10 @@ module RuboCop
59
60
  end
60
61
  end
61
62
 
63
+ def heredoc_argument?(node)
64
+ node.arguments.any? { |argument| argument.each_node(:any_str).any?(&:heredoc?) }
65
+ end
66
+
62
67
  def offense?(node)
63
68
  (explicit_style? && node.implicit_call?) || (implicit_style? && !node.implicit_call?)
64
69
  end
@@ -187,7 +187,7 @@ module RuboCop
187
187
  issues[:directives] << directive if directive_offends?(directive)
188
188
  end
189
189
 
190
- comment.values.each do |value| # rubocop:disable Style/HashEachMethods
190
+ comment.values.each do |value| # rubocop:disable Style/HashEachMethods -- the receiver is a magic comment, not a Hash
191
191
  issues[:values] << value if wrong_capitalization?(value.source, value_capitalization)
192
192
  end
193
193
 
@@ -112,8 +112,7 @@ module RuboCop
112
112
 
113
113
  def on_block(node)
114
114
  return unless each_block_with_push?(node)
115
-
116
- dest_var = find_dest_var(node)
115
+ return unless (dest_var = find_dest_var(node))
117
116
 
118
117
  if offending_empty_array_tap?(node, dest_var)
119
118
  asgn = dest_var.declaration_node