rubocop 1.91.0 → 1.92.2

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 (278) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +4 -1
  4. data/lib/rubocop/cached_data.rb +47 -7
  5. data/lib/rubocop/changed_files.rb +35 -13
  6. data/lib/rubocop/cli/command/cop_names.rb +57 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +2 -2
  8. data/lib/rubocop/cli/command/explain.rb +34 -0
  9. data/lib/rubocop/cli/command/show_cops.rb +6 -0
  10. data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
  11. data/lib/rubocop/cli.rb +3 -2
  12. data/lib/rubocop/comment_config.rb +17 -8
  13. data/lib/rubocop/config.rb +0 -4
  14. data/lib/rubocop/config_loader.rb +6 -3
  15. data/lib/rubocop/config_loader_resolver.rb +37 -12
  16. data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
  17. data/lib/rubocop/config_obsoletion.rb +9 -0
  18. data/lib/rubocop/config_validator.rb +11 -0
  19. data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
  20. data/lib/rubocop/cop/badge.rb +1 -1
  21. data/lib/rubocop/cop/base.rb +42 -21
  22. data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
  23. data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
  24. data/lib/rubocop/cop/cop_documentation.rb +81 -0
  25. data/lib/rubocop/cop/cop_explanation.rb +140 -0
  26. data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
  27. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
  28. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  29. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
  30. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
  31. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
  32. data/lib/rubocop/cop/documentation.rb +1 -1
  33. data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
  34. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
  35. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
  36. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
  37. data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
  38. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
  39. data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
  40. data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
  41. data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
  42. data/lib/rubocop/cop/layout/class_structure.rb +29 -7
  43. data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
  44. data/lib/rubocop/cop/layout/dot_position.rb +25 -15
  45. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
  46. data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
  47. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  48. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  49. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
  50. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
  51. data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
  52. data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
  53. data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
  54. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
  55. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
  56. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
  57. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
  58. data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
  59. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
  60. data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
  61. data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
  62. data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
  63. data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
  64. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
  65. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
  66. data/lib/rubocop/cop/layout/line_length.rb +176 -26
  67. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
  68. data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
  69. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
  70. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
  71. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
  72. data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
  73. data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
  74. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
  75. data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
  76. data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
  77. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
  78. data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
  79. data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
  80. data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
  81. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
  82. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
  83. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
  84. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
  85. data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
  86. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
  87. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
  88. data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
  89. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
  90. data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
  91. data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
  92. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
  93. data/lib/rubocop/cop/lint/loop.rb +24 -2
  94. data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
  95. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  96. data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
  97. data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
  98. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
  99. data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
  100. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
  101. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
  102. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
  103. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
  104. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
  105. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
  106. data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
  107. data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
  108. data/lib/rubocop/cop/lint/void.rb +8 -1
  109. data/lib/rubocop/cop/metrics/class_length.rb +4 -2
  110. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
  111. data/lib/rubocop/cop/metrics/module_length.rb +4 -2
  112. data/lib/rubocop/cop/mixin/alignment.rb +4 -0
  113. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
  114. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
  115. data/lib/rubocop/cop/mixin/code_length.rb +10 -2
  116. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  117. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
  118. data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
  119. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
  120. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
  121. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
  122. data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
  123. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
  124. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
  125. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  126. data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
  127. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
  128. data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
  129. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
  130. data/lib/rubocop/cop/mixin/statement_modifier.rb +39 -5
  131. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  132. data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
  133. data/lib/rubocop/cop/mixin.rb +2 -0
  134. data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
  135. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  136. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
  137. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
  138. data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
  139. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  140. data/lib/rubocop/cop/offense.rb +59 -3
  141. data/lib/rubocop/cop/registry.rb +1 -1
  142. data/lib/rubocop/cop/security/json_load.rb +16 -4
  143. data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
  144. data/lib/rubocop/cop/style/alias.rb +18 -5
  145. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
  146. data/lib/rubocop/cop/style/and_or.rb +37 -10
  147. data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
  148. data/lib/rubocop/cop/style/attr.rb +3 -4
  149. data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
  150. data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
  151. data/lib/rubocop/cop/style/case_equality.rb +14 -3
  152. data/lib/rubocop/cop/style/case_like_if.rb +6 -3
  153. data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
  154. data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
  155. data/lib/rubocop/cop/style/collection_querying.rb +79 -46
  156. data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
  157. data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
  158. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
  159. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
  160. data/lib/rubocop/cop/style/double_negation.rb +11 -0
  161. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  162. data/lib/rubocop/cop/style/empty_method.rb +3 -1
  163. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
  164. data/lib/rubocop/cop/style/endless_method.rb +104 -11
  165. data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
  166. data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
  167. data/lib/rubocop/cop/style/float_division.rb +37 -2
  168. data/lib/rubocop/cop/style/for.rb +53 -2
  169. data/lib/rubocop/cop/style/format_string.rb +28 -6
  170. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
  171. data/lib/rubocop/cop/style/guard_clause.rb +31 -22
  172. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
  173. data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
  174. data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -9
  175. data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
  176. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  177. data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
  178. data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
  179. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
  180. data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
  181. data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
  182. data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
  183. data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
  184. data/lib/rubocop/cop/style/lambda.rb +34 -1
  185. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  186. data/lib/rubocop/cop/style/map_into_array.rb +1 -2
  187. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
  188. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
  189. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
  190. data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
  191. data/lib/rubocop/cop/style/missing_else.rb +7 -1
  192. data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
  193. data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
  194. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
  195. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
  196. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
  197. data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
  198. data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
  199. data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
  200. data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
  201. data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
  202. data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
  203. data/lib/rubocop/cop/style/next.rb +24 -10
  204. data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
  205. data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
  206. data/lib/rubocop/cop/style/not.rb +10 -5
  207. data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
  208. data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
  209. data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
  210. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
  211. data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
  212. data/lib/rubocop/cop/style/raise_args.rb +80 -11
  213. data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
  214. data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
  215. data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
  216. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
  217. data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
  218. data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
  219. data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
  220. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
  221. data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
  222. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
  223. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
  224. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
  225. data/lib/rubocop/cop/style/redundant_self.rb +9 -1
  226. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
  227. data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
  228. data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
  229. data/lib/rubocop/cop/style/require_order.rb +25 -8
  230. data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
  231. data/lib/rubocop/cop/style/return_nil.rb +17 -2
  232. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
  233. data/lib/rubocop/cop/style/sample.rb +9 -8
  234. data/lib/rubocop/cop/style/self_assignment.rb +12 -15
  235. data/lib/rubocop/cop/style/semicolon.rb +16 -5
  236. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
  237. data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
  238. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
  239. data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
  240. data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
  241. data/lib/rubocop/cop/style/super_arguments.rb +3 -0
  242. data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
  243. data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
  244. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
  245. data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
  246. data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
  247. data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
  248. data/lib/rubocop/cop/team.rb +10 -5
  249. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  250. data/lib/rubocop/cop/variable_force/variable.rb +14 -1
  251. data/lib/rubocop/formatter/base_formatter.rb +22 -0
  252. data/lib/rubocop/formatter/formatter_set.rb +4 -0
  253. data/lib/rubocop/formatter/json_formatter.rb +71 -1
  254. data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
  255. data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
  256. data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
  257. data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
  258. data/lib/rubocop/lsp/routes.rb +23 -23
  259. data/lib/rubocop/lsp/runtime.rb +47 -11
  260. data/lib/rubocop/lsp/stdin_runner.rb +11 -5
  261. data/lib/rubocop/lsp.rb +5 -2
  262. data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
  263. data/lib/rubocop/mcp/explain_tool.rb +68 -0
  264. data/lib/rubocop/mcp/scope.rb +65 -0
  265. data/lib/rubocop/mcp/server.rb +184 -76
  266. data/lib/rubocop/mcp/source_files.rb +35 -0
  267. data/lib/rubocop/offense_limit.rb +30 -0
  268. data/lib/rubocop/options.rb +45 -10
  269. data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
  270. data/lib/rubocop/remote_config.rb +4 -1
  271. data/lib/rubocop/result_cache.rb +20 -13
  272. data/lib/rubocop/runner.rb +110 -42
  273. data/lib/rubocop/server/cache.rb +6 -6
  274. data/lib/rubocop/target_ruby.rb +21 -9
  275. data/lib/rubocop/util.rb +69 -0
  276. data/lib/rubocop/version.rb +1 -1
  277. data/lib/rubocop.rb +5 -0
  278. metadata +16 -5
@@ -102,6 +102,7 @@ module RuboCop
102
102
  return replaced_node unless node.parent
103
103
  return "(#{replaced_node})" if node.parent.operator_keyword?
104
104
  return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
105
+ return "(#{replaced_node})" if parent_binds_tighter_than_ternary?(node)
105
106
 
106
107
  replaced_node
107
108
  end
@@ -124,6 +125,14 @@ module RuboCop
124
125
  "#{expr_replacement(else_branch)}"
125
126
  end
126
127
 
128
+ def parent_binds_tighter_than_ternary?(node)
129
+ parent = node.parent
130
+ return parent.receiver.equal?(node) if parent.call_type?
131
+ return parent.namespace.equal?(node) if parent.type?(:const, :casgn)
132
+
133
+ parent.if_type? && parent.ternary? && parent.condition.equal?(node)
134
+ end
135
+
127
136
  def expr_replacement(node)
128
137
  return 'nil' if node.nil?
129
138
 
@@ -131,7 +140,9 @@ module RuboCop
131
140
  end
132
141
 
133
142
  def requires_parentheses?(node)
134
- return true if %i[and or if].include?(node.type)
143
+ return true if node.type?(
144
+ :and, :or, :if, :rescue, :while, :until, :while_post, :until_post, :any_match_pattern
145
+ )
135
146
  return true if node.assignment?
136
147
  return true if method_call_with_changed_precedence?(node)
137
148
 
@@ -139,7 +150,7 @@ module RuboCop
139
150
  end
140
151
 
141
152
  def method_call_with_changed_precedence?(node)
142
- return false unless node.send_type? && node.arguments?
153
+ return false unless node.call_type? && node.arguments?
143
154
  return false if node.parenthesized_call?
144
155
 
145
156
  !node.operator_method?
@@ -83,7 +83,8 @@ module RuboCop
83
83
  control_op_condition(cond) do |first_child, rest_children|
84
84
  return if require_parentheses?(node, first_child)
85
85
  return if semicolon_separated_expressions?(first_child, rest_children)
86
- return if modifier_op?(first_child)
86
+ return if modifier_op?(first_child) || first_child.masgn_type?
87
+ return if command_call_assignment?(first_child)
87
88
  return if parens_allowed?(cond)
88
89
 
89
90
  message = message(cond)
@@ -115,6 +116,23 @@ module RuboCop
115
116
  node.basic_conditional? && node.modifier_form?
116
117
  end
117
118
 
119
+ def command_call_assignment?(node)
120
+ return false if !node.assignment? && !setter_method?(node)
121
+
122
+ value = node.children.last
123
+ value = value.body if value.rescue_type?
124
+
125
+ command_call_assignment?(value) || command_call?(value)
126
+ end
127
+
128
+ def command_call?(node)
129
+ node = node.send_node if node.any_block_type?
130
+ return false if !node.type?(:call, :super, :yield) || !node.arguments?
131
+ return false if node.parenthesized? || node.operator_method? || node.setter_method?
132
+
133
+ !node.method?(:[])
134
+ end
135
+
118
136
  def message(node)
119
137
  kw = node.parent.keyword
120
138
  article = kw == 'while' ? 'a' : 'an'
@@ -86,10 +86,21 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def include_same_character_as_used_for_delimiter?(node, type)
89
- return false unless %w[%w %i].include?(type)
90
-
91
89
  used_delimiters = matchpairs(begin_source(node)[-1])
92
- contains_delimiter?(node, used_delimiters)
90
+ return contains_delimiter?(node, used_delimiters) if %w[%w %i].include?(type)
91
+ return false unless %w[%q %s %r].include?(type)
92
+
93
+ contains_escaped_delimiter?(node, used_delimiters)
94
+ end
95
+
96
+ # Without interpolation an escaped delimiter means something different
97
+ # once the delimiters change (`%q!it\!s!` is `"it!s"`, but `%q(it\!s)`
98
+ # is `"it\\!s"`). For a regexp the match stays the same, but its source
99
+ # changes. Interpolating literals read `\!` as `!` with any delimiter.
100
+ def contains_escaped_delimiter?(node, delimiters)
101
+ body = node.loc.begin.end.join(node.loc.end.begin).source
102
+
103
+ body.scan(/\\./m).any? { |escape| delimiters.include?(escape[1]) }
93
104
  end
94
105
 
95
106
  def contains_delimiter?(node, delimiters)
@@ -86,14 +86,12 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def correct_quotes(str)
89
- correction = if style == :single_quotes
90
- to_string_literal(str)
91
- else
92
- str.gsub("\\'", "'").inspect
93
- end
94
-
95
89
  # The conversion process doubles escaped slashes, so they have to be reverted
96
- correction.gsub('\\\\', '\\').gsub('\"', '"')
90
+ if style == :single_quotes
91
+ to_string_literal(str).gsub('\\\\', '\\').gsub('\"', '"')
92
+ else
93
+ str.gsub("\\'", "'").inspect.gsub('\\\\', '\\')
94
+ end
97
95
  end
98
96
 
99
97
  def style
@@ -74,7 +74,7 @@ module RuboCop
74
74
 
75
75
  arguments = [exception_node, message_node].compact.map(&:source).join(', ')
76
76
 
77
- if node.parent && requires_parens?(node.parent)
77
+ if parenthesize?(node)
78
78
  "#{node.method_name}(#{arguments})"
79
79
  else
80
80
  "#{node.method_name} #{arguments}"
@@ -86,31 +86,60 @@ module RuboCop
86
86
  return if message_nodes.size > 1
87
87
 
88
88
  argument = message_nodes.first.source
89
- exception_class = exception_node.receiver&.source || exception_node.source
89
+ exception_class = exception_class_node(exception_node).source
90
90
 
91
- if node.parent && requires_parens?(node.parent)
91
+ if parenthesize?(node)
92
92
  "#{node.method_name}(#{exception_class}.new(#{argument}))"
93
93
  else
94
94
  "#{node.method_name} #{exception_class}.new(#{argument})"
95
95
  end
96
96
  end
97
97
 
98
+ def exception_class_node(exception_node)
99
+ use_new_method?(exception_node) ? exception_node.receiver : exception_node
100
+ end
101
+
98
102
  def check_compact(node)
99
103
  if node.arguments.size > 1
100
104
  exception = node.first_argument
101
105
  return if exception.send_type? && exception.first_argument&.hash_type?
106
+ return if use_new_method?(exception) && exception.arguments?
102
107
 
103
108
  add_offense(node, message: format(COMPACT_MSG, method: node.method_name)) do |corrector|
104
- replacement = correction_exploded_to_compact(node)
105
-
106
- corrector.replace(node, replacement)
107
- opposite_style_detected
109
+ correct_raise(node) { correct_to_compact(corrector, node) }
108
110
  end
109
111
  else
110
112
  correct_style_detected
111
113
  end
112
114
  end
113
115
 
116
+ def correct_to_compact(corrector, node)
117
+ if heredoc_message?(node)
118
+ correct_heredoc_exploded_to_compact(corrector, node)
119
+ else
120
+ corrector.replace(node, correction_exploded_to_compact(node))
121
+ end
122
+ end
123
+
124
+ def heredoc_message?(node)
125
+ node.parenthesized? && node.arguments.size == 2 && heredoc?(node.last_argument)
126
+ end
127
+
128
+ def heredoc?(node)
129
+ node.each_node(:any_str).any?(&:heredoc?)
130
+ end
131
+
132
+ def correct_heredoc_exploded_to_compact(corrector, node)
133
+ exception_node, message_node = *node.arguments
134
+ exception_class = exception_class_node(exception_node)
135
+ corrector.replace(range_between(exception_class, message_node), '.new(')
136
+ corrector.insert_after(message_node, ')')
137
+ end
138
+
139
+ def range_between(left_node, right_node)
140
+ left_node.source_range.end.join(right_node.source_range.begin)
141
+ end
142
+
114
143
  def check_exploded(node)
115
144
  return correct_style_detected unless node.arguments.one?
116
145
 
@@ -121,13 +150,43 @@ module RuboCop
121
150
  return if allowed_non_exploded_type?(first_arg)
122
151
 
123
152
  add_offense(node, message: format(EXPLODED_MSG, method: node.method_name)) do |corrector|
124
- replacement = correction_compact_to_exploded(node)
153
+ correct_raise(node) { correct_to_exploded(corrector, node) }
154
+ end
155
+ end
125
156
 
126
- corrector.replace(node, replacement)
127
- opposite_style_detected
157
+ def correct_to_exploded(corrector, node)
158
+ if heredoc_new_argument?(node)
159
+ correct_heredoc_compact_to_exploded(corrector, node)
160
+ else
161
+ corrector.replace(node, correction_compact_to_exploded(node))
128
162
  end
129
163
  end
130
164
 
165
+ def heredoc_new_argument?(node)
166
+ new_call = node.first_argument
167
+ return false unless new_call.arguments.one? && heredoc?(new_call.first_argument)
168
+
169
+ node.parenthesized? || !parenthesize?(node)
170
+ end
171
+
172
+ def correct_heredoc_compact_to_exploded(corrector, node)
173
+ new_call = node.first_argument
174
+ corrector.replace(range_between(new_call.receiver, new_call.first_argument), ', ')
175
+ corrector.remove(new_call.loc.end) if new_call.loc.end
176
+ end
177
+
178
+ def correct_raise(node)
179
+ opposite_style_detected
180
+ return if inside_corrected_raise?(node)
181
+
182
+ yield
183
+ ignore_node(node)
184
+ end
185
+
186
+ def inside_corrected_raise?(node)
187
+ node.each_ancestor(:send).any? { |ancestor| ignored_node?(ancestor) }
188
+ end
189
+
131
190
  def use_new_method?(first_arg)
132
191
  first_arg.send_type? && first_arg.receiver && first_arg.method?(:new)
133
192
  end
@@ -151,8 +210,18 @@ module RuboCop
151
210
  Array(cop_config['AllowedCompactTypes']).include?(type)
152
211
  end
153
212
 
213
+ def parenthesize?(node)
214
+ return true if node.parenthesized?
215
+
216
+ node.parent && requires_parens?(node.parent)
217
+ end
218
+
154
219
  def requires_parens?(parent)
155
- parent.operator_keyword? || (parent.if_type? && parent.ternary?)
220
+ return true if parent.operator_keyword?
221
+ return true if parent.type?(:call, :super, :yield, :array, :pair)
222
+ return true if parent.any_def_type? && parent.endless?
223
+
224
+ parent.if_type? && parent.ternary?
156
225
  end
157
226
  end
158
227
  end
@@ -100,6 +100,7 @@ module RuboCop
100
100
  return unless key
101
101
  return if accumulator_used_in_expressions?(block_node, key, value)
102
102
  return if nested_match?(key) || nested_match?(value)
103
+ return if heredoc_in_inject_numblock?(block_node, key, value)
103
104
 
104
105
  register_offense(node, block_node, key, value)
105
106
  end
@@ -124,6 +125,12 @@ module RuboCop
124
125
  end
125
126
  end
126
127
 
128
+ def heredoc_in_inject_numblock?(block_node, key, value)
129
+ return false if !block_node.numblock_type? || block_node.method?(:each_with_object)
130
+
131
+ [key, value].any? { |node| node.each_node(:any_str).any?(&:heredoc?) }
132
+ end
133
+
127
134
  def accumulator_name(block_node)
128
135
  index = block_node.method?(:each_with_object) ? 1 : 0
129
136
  block_node.argument_list[index].name
@@ -136,42 +143,20 @@ module RuboCop
136
143
  def register_offense(send_node, block_node, key_expr, value_expr)
137
144
  message = format(MSG, method: send_node.method_name)
138
145
 
146
+ # Each part is replaced separately so that heredoc bodies between them are kept.
139
147
  add_offense(send_node.loc.selector, message: message) do |corrector|
140
- corrector.replace(
141
- replacement_range(send_node, block_node),
142
- replacement(block_node, key_expr, value_expr)
143
- )
144
- end
145
- end
146
-
147
- def replacement(block_node, key_expr, value_expr)
148
- key_source = adjusted_source(key_expr, block_node)
149
- value_source = adjusted_source(value_expr, block_node)
150
- body = "[#{key_source}, #{value_source}]"
148
+ corrector.replace(method_call_range(send_node), 'to_h')
151
149
 
152
- if block_node.numblock_type?
153
- block_node.braces? ? "to_h { #{body} }" : do_end_replacement(block_node, body)
154
- else
155
- named_block_replacement(block_node, body)
156
- end
157
- end
150
+ unless block_node.numblock_type?
151
+ corrector.replace(block_node.arguments, "|#{element_arg_source(block_node)}|")
152
+ end
158
153
 
159
- def named_block_replacement(block_node, body)
160
- arg = element_arg_source(block_node)
161
- if block_node.braces?
162
- "to_h { |#{arg}| #{body} }"
163
- else
164
- do_end_replacement(block_node, body, arg)
154
+ correct_body(corrector, block_node, key_expr, value_expr)
165
155
  end
166
156
  end
167
157
 
168
- def do_end_replacement(block_node, body, arg = nil)
169
- args = arg ? " |#{arg}|" : ''
170
- "to_h do#{args}\n#{indent(block_node)} #{body}\n#{indent(block_node)}end"
171
- end
172
-
173
- def replacement_range(send_node, block_node)
174
- range_between(send_node.loc.selector.begin_pos, block_node.source_range.end_pos)
158
+ def method_call_range(send_node)
159
+ range_between(send_node.loc.selector.begin_pos, send_node.source_range.end_pos)
175
160
  end
176
161
 
177
162
  def element_arg_source(block_node)
@@ -182,8 +167,18 @@ module RuboCop
182
167
  end
183
168
  end
184
169
 
170
+ def correct_body(corrector, block_node, key_expr, value_expr)
171
+ key_source = adjusted_source(key_expr, block_node)
172
+ value_source = adjusted_source(value_expr, block_node)
173
+ body = block_node.body
174
+ assignment = body.begin_type? ? body.children.first : body
175
+
176
+ corrector.replace(assignment, "[#{key_source}, #{value_source}]")
177
+ corrector.remove(accumulator_range(body.children.last)) if body.begin_type?
178
+ end
179
+
185
180
  def adjusted_source(expr_node, block_node)
186
- source = expr_node.source
181
+ source = element_source(expr_node)
187
182
  return source unless block_node.numblock_type?
188
183
  return source if block_node.method?(:each_with_object)
189
184
 
@@ -191,8 +186,27 @@ module RuboCop
191
186
  source.gsub('_2', '_1')
192
187
  end
193
188
 
194
- def indent(node)
195
- ' ' * node.source_range.column
189
+ def element_source(expr_node)
190
+ if command_call?(expr_node) || expr_node.assignment? || expr_node.rescue_type?
191
+ return "(#{expr_node.source})"
192
+ end
193
+ return "[#{expr_node.source}]" if expr_node.array_type? && !expr_node.bracketed?
194
+
195
+ expr_node.source
196
+ end
197
+
198
+ def command_call?(node)
199
+ node = node.send_node if node.any_block_type?
200
+ return false unless node.type?(:call, :super, :yield)
201
+
202
+ node.arguments? && !node.parenthesized? && !node.operator_method? && !node.setter_method?
203
+ end
204
+
205
+ def accumulator_range(accumulator)
206
+ range = range_with_surrounding_space(accumulator.source_range, side: :left)
207
+ return range unless range.source_buffer.source[range.begin_pos - 1] == ';'
208
+
209
+ range_with_surrounding_space(range.adjust(begin_pos: -1), side: :left, newlines: false)
196
210
  end
197
211
  end
198
212
  end
@@ -67,6 +67,7 @@ module RuboCop
67
67
  extend AutoCorrector
68
68
 
69
69
  MSG = 'Redundant `begin` block detected.'
70
+ SPACE_INSIDE_PARENS_STYLES = %w[space compact].freeze
70
71
 
71
72
  def self.autocorrect_incompatible_with
72
73
  [Style::BlockDelimiters]
@@ -132,14 +133,15 @@ module RuboCop
132
133
  empty_begin?(node) ||
133
134
  begin_block_has_multiline_statements?(node) ||
134
135
  contain_rescue_or_ensure?(node) ||
135
- valid_context_using_only_begin?(node)
136
+ valid_context_using_only_begin?(node) ||
137
+ ternary_operand_requiring_begin?(node)
136
138
  end
137
139
 
138
140
  def register_offense(node)
139
141
  offense_range = node.loc.begin
140
142
 
141
143
  add_offense(offense_range) do |corrector|
142
- if node.parent&.assignment?
144
+ if node.parent&.assignment? || node.parent&.type?(:return, :next, :break)
143
145
  replace_begin_with_statement(corrector, offense_range, node)
144
146
  else
145
147
  remove_begin(corrector, offense_range, node)
@@ -156,7 +158,7 @@ module RuboCop
156
158
  first_child = node.children.first
157
159
 
158
160
  source = first_child.source
159
- source = "(#{source})" if first_child.if_type? && first_child.modifier_form?
161
+ source = "(#{source})" if low_precedence_statement?(first_child)
160
162
 
161
163
  corrector.replace(offense_range, source)
162
164
  corrector.remove(range_between(offense_range.end_pos, first_child.source_range.end_pos))
@@ -164,6 +166,13 @@ module RuboCop
164
166
  restore_removed_comments(corrector, offense_range, node, first_child)
165
167
  end
166
168
 
169
+ def low_precedence_statement?(node)
170
+ return node.modifier_form? if node.basic_conditional?
171
+ return node.prefix_not? if node.send_type?
172
+
173
+ node.operator_keyword? && node.semantic_operator?
174
+ end
175
+
167
176
  def remove_begin(corrector, offense_range, node)
168
177
  if node.parent.respond_to?(:endless?) && node.parent.endless?
169
178
  offense_range = range_with_surrounding_space(offense_range, newlines: true)
@@ -184,7 +193,8 @@ module RuboCop
184
193
  def use_modifier_form_after_multiline_begin_block?(node)
185
194
  return false unless (parent = node.parent)
186
195
 
187
- node.multiline? && parent.if_type? && parent.modifier_form?
196
+ node.multiline? && parent.if_type? && parent.modifier_form? &&
197
+ !parent.condition.equal?(node)
188
198
  end
189
199
 
190
200
  def correct_modifier_form_after_multiline_begin_block(corrector, node)
@@ -221,14 +231,44 @@ module RuboCop
221
231
  parent&.send_type? || parent&.operator_keyword?
222
232
  end
223
233
 
234
+ def ternary_operand_requiring_begin?(node)
235
+ return false unless node.parent&.if_type? && node.parent.ternary?
236
+
237
+ !node.children.one? || low_precedence_statement?(node.children.first)
238
+ end
239
+
224
240
  def valid_begin_assignment?(node)
225
- node.parent&.assignment? && !node.children.one?
241
+ node.parent&.assignment? && (!node.children.one? || too_long_when_joined?(node))
242
+ end
243
+
244
+ def too_long_when_joined?(node)
245
+ statement = node.children.first
246
+ max = config.for_enabled_cop('Layout/LineLength')['Max']
247
+ return false unless max && statement.single_line?
248
+
249
+ length = statement.source.length
250
+ length += parentheses_width if low_precedence_statement?(statement)
251
+
252
+ node.loc.column + length > max
253
+ end
254
+
255
+ def parentheses_width
256
+ space_inside_parens? ? 4 : 2
257
+ end
258
+
259
+ def space_inside_parens?
260
+ return false unless config.cop_enabled?('Layout/SpaceInsideParens')
261
+
262
+ style = config.for_cop('Layout/SpaceInsideParens')['EnforcedStyle']
263
+
264
+ SPACE_INSIDE_PARENS_STYLES.include?(style)
226
265
  end
227
266
 
228
267
  def inspect_branches(node)
229
268
  node.branches.each do |branch|
230
269
  next unless branch&.kwbegin_type?
231
270
  next if branch.rescue_node || branch.ensure_node
271
+ next if ternary_operand_requiring_begin?(branch)
232
272
 
233
273
  register_offense(branch)
234
274
  end
@@ -78,7 +78,10 @@ module RuboCop
78
78
  ].freeze
79
79
 
80
80
  def on_if(node)
81
- return if node.modifier_form? || node.elsif_conditional? || !offense?(node)
81
+ return unless redundant_condition_offense?(node)
82
+ return if node.each_ancestor(:if).any? do |ancestor|
83
+ redundant_condition_offense?(ancestor)
84
+ end
82
85
 
83
86
  message = message(node)
84
87
 
@@ -89,6 +92,10 @@ module RuboCop
89
92
 
90
93
  private
91
94
 
95
+ def redundant_condition_offense?(node)
96
+ !node.modifier_form? && !node.elsif_conditional? && offense?(node)
97
+ end
98
+
92
99
  def message(node)
93
100
  if redundant_condition?(node)
94
101
  REDUNDANT_CONDITION
@@ -240,10 +247,10 @@ module RuboCop
240
247
  end
241
248
 
242
249
  # rubocop:disable-next Metrics/AbcSize
243
- def if_source(if_branch, arithmetic_operation)
250
+ def if_source(if_branch, binary_operator)
244
251
  if branches_have_method?(if_branch.parent) && if_branch.parenthesized?
245
252
  if_branch.source.delete_suffix(')')
246
- elsif arithmetic_operation
253
+ elsif binary_operator
247
254
  argument_source = if_branch.first_argument.source
248
255
 
249
256
  "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
@@ -257,8 +264,8 @@ module RuboCop
257
264
  end
258
265
  end
259
266
 
260
- def else_source(else_branch, arithmetic_operation) # rubocop:disable Metrics/AbcSize
261
- if arithmetic_operation
267
+ def else_source(else_branch, binary_operator) # rubocop:disable Metrics/AbcSize
268
+ if binary_operator
262
269
  "#{else_branch.first_argument.source})"
263
270
  elsif branches_have_method?(else_branch.parent)
264
271
  else_source_if_has_method(else_branch)
@@ -295,11 +302,11 @@ module RuboCop
295
302
 
296
303
  def make_ternary_form(node)
297
304
  _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring -- takes all three branches in one step
298
- arithmetic_operation = use_arithmetic_operation?(if_branch)
305
+ binary_operator = branches_have_method?(node) && binary_operator?(if_branch)
299
306
 
300
307
  ternary_form = [
301
- if_source(if_branch, arithmetic_operation),
302
- else_source(else_branch, arithmetic_operation)
308
+ if_source(if_branch, binary_operator),
309
+ else_source(else_branch, binary_operator)
303
310
  ].join(' || ')
304
311
  ternary_form += ')' if branches_have_method?(node) && if_branch.parenthesized?
305
312
 
@@ -320,17 +327,27 @@ module RuboCop
320
327
 
321
328
  def require_parentheses?(node)
322
329
  (node.basic_conditional? && node.modifier_form?) ||
323
- node.range_type? ||
324
- node.rescue_type? ||
325
- (node.respond_to?(:semantic_operator?) && node.semantic_operator?)
330
+ node.range_type? || node.rescue_type? ||
331
+ (node.respond_to?(:semantic_operator?) && node.semantic_operator?) ||
332
+ low_precedence_keyword?(node)
333
+ end
334
+
335
+ # e.g. `not foo`, `return foo`, and `yield foo`
336
+ def low_precedence_keyword?(node)
337
+ return node.prefix_not? if node.send_type?
338
+
339
+ node.type?(:return, :next, :break, :yield, :super) &&
340
+ node.arguments? && !node.parenthesized?
326
341
  end
327
342
 
328
343
  def require_braces?(node)
329
344
  node.hash_type? && !node.braces?
330
345
  end
331
346
 
332
- def use_arithmetic_operation?(node)
333
- node.respond_to?(:arithmetic_operation?) && node.arithmetic_operation?
347
+ # Any binary operator binds tighter than `||`, so the fallback must be
348
+ # parenthesized as its argument, e.g. `stack << (foo || :new)`.
349
+ def binary_operator?(node)
350
+ node.binary_operation? && !node.dot?
334
351
  end
335
352
 
336
353
  def without_argument_parentheses_method?(node)
@@ -25,6 +25,12 @@ module RuboCop
25
25
  MSG = 'Remove the redundant double splat and braces, use keyword arguments directly.'
26
26
  MERGE_METHODS = %i[merge merge!].freeze
27
27
 
28
+ # `Style/TrailingCommaInHashLiteral` adds a comma to the hash literal whose braces
29
+ # this cop removes in the same pass, which would leave a dangling comma.
30
+ def self.autocorrect_incompatible_with
31
+ [Style::TrailingCommaInHashLiteral]
32
+ end
33
+
28
34
  # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
29
35
  def on_hash(node)
30
36
  return if node.pairs.empty? || node.pairs.any?(&:hash_rocket?)
@@ -37,23 +37,18 @@ module RuboCop
37
37
  private
38
38
 
39
39
  def fix_exploded(node)
40
- exploded?(node) do |command, message|
40
+ exploded?(node) do |exception, message|
41
41
  # `raise RuntimeError, nil` uses the class name as the message, so
42
42
  # rewriting it to `raise nil.to_s` (an empty message) would change it.
43
43
  next if message.nil_type?
44
44
 
45
45
  add_offense(node, message: MSG_1) do |corrector|
46
- corrector.replace(node, replaced_exploded(node, command, message))
46
+ corrector.remove(exception.source_range.join(message.source_range.begin))
47
+ corrector.replace(message, message_source(message))
47
48
  end
48
49
  end
49
50
  end
50
51
 
51
- def replaced_exploded(node, command, message)
52
- arg = string_message?(message) ? message.source : "#{message.source}.to_s"
53
- arg = node.parenthesized? ? "(#{arg})" : " #{arg}"
54
- "#{command}#{arg}"
55
- end
56
-
57
52
  def string_message?(message)
58
53
  message.any_str_type?
59
54
  end
@@ -63,22 +58,43 @@ module RuboCop
63
58
  next if message.nil_type?
64
59
 
65
60
  add_offense(node, message: MSG_2) do |corrector|
66
- corrector.replace(new_call, replaced_compact(message))
61
+ # Replacing the call would drop a heredoc body that sits inside its parentheses.
62
+ next if heredoc_body_inside?(new_call, message)
63
+
64
+ corrector.replace(new_call, message_source(message))
67
65
  end
68
66
  end
69
67
  end
70
68
 
71
- def replaced_compact(message)
69
+ def heredoc_body_inside?(new_call, message)
70
+ message.each_node(:any_str).any? do |str|
71
+ str.heredoc? && str.loc.heredoc_end.end_pos < new_call.source_range.end_pos
72
+ end
73
+ end
74
+
75
+ def message_source(message)
72
76
  if string_message?(message)
73
77
  message.source
78
+ elsif require_parentheses?(message)
79
+ "(#{message.source}).to_s"
74
80
  else
75
81
  "#{message.source}.to_s"
76
82
  end
77
83
  end
78
84
 
85
+ def require_parentheses?(message)
86
+ if message.call_type?
87
+ !message.method?(:[]) &&
88
+ (message.operator_method? || (message.arguments? && !message.parenthesized?))
89
+ else
90
+ !(message.basic_literal? || message.variable? ||
91
+ message.type?(:const, :self, :begin, :array))
92
+ end
93
+ end
94
+
79
95
  # @!method exploded?(node)
80
96
  def_node_matcher :exploded?, <<~PATTERN
81
- (send nil? ${:raise :fail} (const {nil? cbase} :RuntimeError) $_)
97
+ (send nil? {:raise :fail} $(const {nil? cbase} :RuntimeError) $_)
82
98
  PATTERN
83
99
 
84
100
  # @!method compact?(node)