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
@@ -181,11 +181,11 @@ module RuboCop
181
181
  def autocorrect_modifier(corrector, node)
182
182
  body = node.if_branch || node.else_branch
183
183
 
184
- replacement =
185
- "next #{node.inverse_keyword} #{node.condition.source}\n" \
186
- "#{' ' * node.source_range.column}#{body.source}"
184
+ next_code = "next #{node.inverse_keyword} #{node.condition.source}\n" \
185
+ "#{' ' * node.source_range.column}"
187
186
 
188
- corrector.replace(node, replacement)
187
+ corrector.insert_before(node, next_code)
188
+ corrector.remove(range_between(body.source_range.end_pos, node.source_range.end_pos))
189
189
  end
190
190
 
191
191
  def autocorrect_block(corrector, node)
@@ -231,7 +231,7 @@ module RuboCop
231
231
 
232
232
  # end_range starts with the final newline of the if body
233
233
  lines = (node.source_range.line + 1)...node.loc.end.line
234
- lines = lines.to_a - heredoc_lines(node)
234
+ lines = lines.to_a - string_content_lines(node)
235
235
  # Skip blank lines
236
236
  lines.reject { |lineno| /\A\s*\z/.match?(buffer.source_line(lineno)) }
237
237
  end
@@ -250,11 +250,25 @@ module RuboCop
250
250
  lines.map { |lineno| buffer.source_line(lineno) =~ /\S/ }.min
251
251
  end
252
252
 
253
- def heredoc_lines(node)
254
- node.each_node(:dstr)
255
- .select(&:heredoc?)
256
- .map { |n| n.loc.heredoc_body }
257
- .flat_map { |b| (b.line...b.last_line).to_a }
253
+ # Lines that start inside a string literal or a heredoc body, whose
254
+ # leading whitespace is part of the string value.
255
+ def string_content_lines(node)
256
+ buffer = node.source_range.source_buffer
257
+ ranges = node.each_node(:any_str).filter_map { |str| string_content_range(str) }
258
+
259
+ ranges.flat_map do |range|
260
+ (range.line..range.last_line).select do |lineno|
261
+ (range.begin_pos...range.end_pos).cover?(buffer.line_range(lineno).begin_pos)
262
+ end
263
+ end
264
+ end
265
+
266
+ def string_content_range(node)
267
+ if node.heredoc?
268
+ node.loc.heredoc_body
269
+ elsif node.loc?(:begin) && node.loc?(:end)
270
+ node.loc.begin.end.join(node.loc.end.begin)
271
+ end
258
272
  end
259
273
 
260
274
  def reindent_line(corrector, lineno, delta, buffer)
@@ -42,28 +42,68 @@ module RuboCop
42
42
  # @!method nil_check?(node)
43
43
  def_node_matcher :nil_check?, '(send _ :nil?)'
44
44
 
45
- # rubocop:disable-next Metrics/AbcSize
46
45
  def on_send(node)
47
46
  return unless node.receiver
47
+ return if prefer_comparison? && comment_before_dot?(node)
48
48
 
49
49
  style_check?(node) do
50
50
  add_offense(node.loc.selector) do |corrector|
51
51
  if prefer_comparison?
52
- range = node.loc.dot.join(node.loc.selector.end)
53
- corrector.replace(range, ' == nil')
52
+ autocorrect_to_comparison(corrector, node)
54
53
  else
55
- range = node.receiver.source_range.end.join(node.source_range.end)
56
- corrector.replace(range, '.nil?')
54
+ autocorrect_to_predicate(corrector, node)
57
55
  end
58
-
59
- parent = node.parent
60
- corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
61
56
  end
62
57
  end
63
58
  end
64
59
 
65
60
  private
66
61
 
62
+ def comment_before_dot?(node)
63
+ return false unless node.loc.dot
64
+
65
+ processed_source.contains_comment?(node.receiver.source_range.end.join(node.loc.dot))
66
+ end
67
+
68
+ def autocorrect_to_comparison(corrector, node)
69
+ range = node.receiver.source_range.end.join(node.loc.selector.end)
70
+ corrector.replace(range, ' == nil')
71
+
72
+ return if !operator_expression?(node.parent) && !receiver_of_parent?(node)
73
+
74
+ corrector.wrap(node, '(', ')')
75
+ end
76
+
77
+ def autocorrect_to_predicate(corrector, node)
78
+ receiver = node.receiver
79
+ if operator_expression?(receiver)
80
+ # A looser-binding receiver (e.g. `!x`, `a + b`) must be parenthesized
81
+ # so the appended `.nil?` applies to the whole expression.
82
+ corrector.replace(node, "(#{receiver.source}).nil?")
83
+ else
84
+ range = receiver.source_range.end.join(node.source_range.end)
85
+ corrector.replace(range, '.nil?')
86
+ end
87
+ end
88
+
89
+ def operator_expression?(node)
90
+ return false unless node
91
+
92
+ operator_send?(node) ||
93
+ node.operator_keyword? ||
94
+ (node.if_type? && node.ternary?) ||
95
+ node.type?(:range, :iflipflop, :eflipflop) ||
96
+ node.assignment?
97
+ end
98
+
99
+ def receiver_of_parent?(node)
100
+ node.parent&.call_type? && node.parent.receiver.equal?(node)
101
+ end
102
+
103
+ def operator_send?(node)
104
+ node.send_type? && node.operator_method? && !node.method?(:[]) && !node.method?(:[]=)
105
+ end
106
+
67
107
  def message(_node)
68
108
  prefer_comparison? ? EXPLICIT_MSG : PREDICATE_MSG
69
109
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  { ({return next break} nil) (nil) }
49
49
  PATTERN
50
50
 
51
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
51
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- a lambda whose body is `nil` uses no parameters
52
52
  return unless node.lambda_or_proc?
53
53
  return unless nil_return?(node.body)
54
54
  # `return` inside a non-lambda proc returns from the enclosing method,
@@ -109,7 +109,7 @@ module RuboCop
109
109
 
110
110
  def message(node)
111
111
  if node.method?(:!=) && !include_semantic_changes?
112
- prefer = "!#{node.receiver.source}.nil?"
112
+ prefer = non_nil_check_replacement(node)
113
113
  format(MSG_FOR_REPLACEMENT, prefer: prefer, current: node.source)
114
114
  else
115
115
  MSG_FOR_REDUNDANCY
@@ -120,11 +120,27 @@ module RuboCop
120
120
  cop_config['IncludeSemanticChanges']
121
121
  end
122
122
 
123
+ # An operator-expression receiver (e.g. `a + b`) binds looser than the
124
+ # appended `.nil?`, so it must be parenthesized: `!(a + b).nil?`.
125
+ def non_nil_check_replacement(node)
126
+ receiver = node.receiver
127
+ source = operator_expression?(receiver) ? "(#{receiver.source})" : receiver.source
128
+ "!#{source}.nil?"
129
+ end
130
+
131
+ def operator_expression?(node)
132
+ node.operator_keyword? ||
133
+ (node.send_type? && node.operator_method?) ||
134
+ (node.if_type? && node.ternary?) ||
135
+ node.type?(:range, :iflipflop, :eflipflop) ||
136
+ node.assignment?
137
+ end
138
+
123
139
  def autocorrect_comparison(corrector, node)
124
140
  if include_semantic_changes?
125
141
  corrector.replace(node, node.receiver.source)
126
142
  else
127
- corrector.replace(node, "!#{node.receiver.source}.nil?")
143
+ corrector.replace(node, non_nil_check_replacement(node))
128
144
  end
129
145
  end
130
146
 
@@ -54,9 +54,10 @@ module RuboCop
54
54
  end
55
55
 
56
56
  def requires_parens?(child)
57
- child.operator_keyword? ||
58
- (child.send_type? && child.binary_operation?) ||
59
- (child.if_type? && child.ternary?)
57
+ return true if child.operator_keyword? || operator_method_requiring_parens?(child)
58
+ return true if child.if_type? && child.ternary?
59
+
60
+ child.type?(:range, :iflipflop, :eflipflop) || child.assignment?
60
61
  end
61
62
 
62
63
  def correct_opposite_method(corrector, range, child)
@@ -72,6 +73,12 @@ module RuboCop
72
73
  def correct_without_parens(corrector, range)
73
74
  corrector.replace(range, '!')
74
75
  end
76
+
77
+ def operator_method_requiring_parens?(child)
78
+ return false unless child.send_type?
79
+
80
+ child.binary_operation? || (child.unary_operation? && child.method?(:~))
81
+ end
75
82
  end
76
83
  end
77
84
  end
@@ -55,7 +55,11 @@ module RuboCop
55
55
  return unless type
56
56
 
57
57
  add_offense(node) do |corrector|
58
- corrector.replace(node, send(:"format_#{type}", node.source))
58
+ # The prefix patterns below are anchored, so correct only the digits
59
+ # and leave any sign the literal's source carries in place.
60
+ range = unsigned_range(node)
61
+
62
+ corrector.replace(range, send(:"format_#{type}", range.source))
59
63
  end
60
64
  end
61
65
 
@@ -71,6 +75,12 @@ module RuboCop
71
75
  octal_literal_type(literal) || hex_bin_dec_literal_type(literal)
72
76
  end
73
77
 
78
+ def unsigned_range(node)
79
+ range = node.source_range
80
+
81
+ node.source.start_with?('+', '-') ? range.adjust(begin_pos: 1) : range
82
+ end
83
+
74
84
  def octal_literal_type(literal)
75
85
  if OCTAL_ZERO_ONLY_REGEX.match?(literal) && octal_zero_only?
76
86
  :octal_zero_only
@@ -90,6 +90,7 @@ module RuboCop
90
90
  def on_send(node)
91
91
  numeric, replacement = check(node)
92
92
  return unless numeric
93
+ return if handled_by_bitwise_predicate?(node)
93
94
 
94
95
  return if allowed_method_name?(node.method_name) ||
95
96
  node.each_ancestor(:send, :any_block).any? do |ancestor|
@@ -108,6 +109,12 @@ module RuboCop
108
109
  allowed_method?(name) || matches_allowed_pattern?(name)
109
110
  end
110
111
 
112
+ def handled_by_bitwise_predicate?(node)
113
+ bitwise_predicate_candidate?(node) &&
114
+ config.for_cop('Style/BitwisePredicate').fetch('Enabled') &&
115
+ BitwisePredicate.support_target_ruby_version?(target_ruby_version)
116
+ end
117
+
111
118
  def check(node)
112
119
  numeric, operator =
113
120
  if style == :predicate
@@ -124,7 +131,7 @@ module RuboCop
124
131
  def replacement(node, numeric, operation)
125
132
  if style == :predicate
126
133
  [parenthesized_source(numeric), REPLACEMENTS.invert[operation.to_s]].join('.')
127
- elsif negated?(node)
134
+ elsif comparison_requires_parentheses?(node)
128
135
  "(#{numeric.source} #{REPLACEMENTS[operation.to_s]} 0)"
129
136
  else
130
137
  [numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ')
@@ -140,7 +147,7 @@ module RuboCop
140
147
  end
141
148
 
142
149
  def require_parentheses?(node)
143
- node.send_type? && node.binary_operation? && !node.parenthesized?
150
+ node.send_type? && node.operator_method? && !node.parenthesized?
144
151
  end
145
152
 
146
153
  def replacement_supported?(operator)
@@ -159,10 +166,11 @@ module RuboCop
159
166
  end
160
167
  end
161
168
 
162
- def negated?(node)
163
- return false unless (parent = node.parent)
169
+ def comparison_requires_parentheses?(node)
170
+ return false unless (parent = node.parent)&.call_type?
171
+ return true if parent.receiver.equal?(node)
164
172
 
165
- parent.send_type? && parent.method?(:!)
173
+ parent.operator_method? && !parent.method?(:[]) && !parent.method?(:[]=)
166
174
  end
167
175
 
168
176
  # @!method predicate(node)
@@ -179,6 +187,14 @@ module RuboCop
179
187
  def_node_matcher :inverted_comparison, <<~PATTERN
180
188
  (send (int 0) ${:== :> :<} [$(...) !gvar_type?])
181
189
  PATTERN
190
+
191
+ # @!method bitwise_predicate_candidate?(node)
192
+ def_node_matcher :bitwise_predicate_candidate?, <<~PATTERN
193
+ {
194
+ (send {(begin (send _ :& _)) (send _ :& _)} {:positive? :zero?})
195
+ (send {(begin (send _ :& _)) (send _ :& _)} {:> :==} (int 0))
196
+ }
197
+ PATTERN
182
198
  end
183
199
  end
184
200
  end
@@ -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
@@ -117,13 +118,21 @@ module RuboCop
117
118
  end
118
119
 
119
120
  def ternary_replacement(node)
120
- condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
121
+ condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring -- takes all three branches in one step
121
122
 
122
123
  "#{expr_replacement(condition)} ? " \
123
124
  "#{expr_replacement(if_branch)} : " \
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?
@@ -184,7 +184,7 @@ module RuboCop
184
184
  def accesses?(rhs, lhs)
185
185
  if lhs.method?(:[]=)
186
186
  # FIXME: Workaround `rubocop:disable` comment for JRuby.
187
- # rubocop:disable-next Performance/RedundantEqualityComparisonBlock
187
+ # rubocop:disable-next Performance/RedundantEqualityComparisonBlock -- a JRuby workaround, as the comment above says
188
188
  matching_calls(rhs, lhs.receiver, :[]).any? { |args| args == lhs.arguments }
189
189
  else
190
190
  access_method = lhs.method_name.to_s.chop.to_sym
@@ -123,10 +123,15 @@ module RuboCop
123
123
 
124
124
  def parens_allowed?(node)
125
125
  parens_required?(node) ||
126
- (safe_assignment?(node) && safe_assignment_allowed?) ||
126
+ (safe_assignment?(node) && safe_assignment_kept?) ||
127
127
  (node.multiline? && allow_multiline_conditions?)
128
128
  end
129
129
 
130
+ def safe_assignment_kept?
131
+ safe_assignment_allowed? ||
132
+ config.for_enabled_cop('Lint/AssignmentInCondition')['AllowSafeAssignment']
133
+ end
134
+
130
135
  def allow_multiline_conditions?
131
136
  cop_config['AllowInMultilineConditions']
132
137
  end
@@ -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)
@@ -183,7 +183,7 @@ module RuboCop
183
183
  end
184
184
 
185
185
  def adjusted_source(expr_node, block_node)
186
- source = expr_node.source
186
+ source = element_source(expr_node)
187
187
  return source unless block_node.numblock_type?
188
188
  return source if block_node.method?(:each_with_object)
189
189
 
@@ -194,6 +194,22 @@ module RuboCop
194
194
  def indent(node)
195
195
  ' ' * node.source_range.column
196
196
  end
197
+
198
+ def element_source(expr_node)
199
+ if command_call?(expr_node) || expr_node.assignment? || expr_node.rescue_type?
200
+ return "(#{expr_node.source})"
201
+ end
202
+ return "[#{expr_node.source}]" if expr_node.array_type? && !expr_node.bracketed?
203
+
204
+ expr_node.source
205
+ end
206
+
207
+ def command_call?(node)
208
+ node = node.send_node if node.any_block_type?
209
+ return false unless node.type?(:call, :super, :yield)
210
+
211
+ node.arguments? && !node.parenthesized? && !node.operator_method? && !node.setter_method?
212
+ end
197
213
  end
198
214
  end
199
215
  end
@@ -139,7 +139,7 @@ module RuboCop
139
139
  offense_range = node.loc.begin
140
140
 
141
141
  add_offense(offense_range) do |corrector|
142
- if node.parent&.assignment?
142
+ if node.parent&.assignment? || node.parent&.type?(:return, :next, :break)
143
143
  replace_begin_with_statement(corrector, offense_range, node)
144
144
  else
145
145
  remove_begin(corrector, offense_range, node)
@@ -156,7 +156,7 @@ module RuboCop
156
156
  first_child = node.children.first
157
157
 
158
158
  source = first_child.source
159
- source = "(#{source})" if first_child.if_type? && first_child.modifier_form?
159
+ source = "(#{source})" if low_precedence_statement?(first_child)
160
160
 
161
161
  corrector.replace(offense_range, source)
162
162
  corrector.remove(range_between(offense_range.end_pos, first_child.source_range.end_pos))
@@ -164,6 +164,13 @@ module RuboCop
164
164
  restore_removed_comments(corrector, offense_range, node, first_child)
165
165
  end
166
166
 
167
+ def low_precedence_statement?(node)
168
+ return node.modifier_form? if node.basic_conditional?
169
+ return node.prefix_not? if node.send_type?
170
+
171
+ node.operator_keyword? && node.semantic_operator?
172
+ end
173
+
167
174
  def remove_begin(corrector, offense_range, node)
168
175
  if node.parent.respond_to?(:endless?) && node.parent.endless?
169
176
  offense_range = range_with_surrounding_space(offense_range, newlines: true)
@@ -222,7 +229,17 @@ module RuboCop
222
229
  end
223
230
 
224
231
  def valid_begin_assignment?(node)
225
- node.parent&.assignment? && !node.children.one?
232
+ node.parent&.assignment? && (!node.children.one? || too_long_when_joined?(node))
233
+ end
234
+
235
+ def too_long_when_joined?(node)
236
+ statement = node.children.first
237
+ max = config.for_enabled_cop('Layout/LineLength')['Max']
238
+ return false unless max && statement.single_line?
239
+
240
+ length = statement.source.length
241
+ length += 2 if low_precedence_statement?(statement)
242
+ node.loc.column + length > max
226
243
  end
227
244
 
228
245
  def inspect_branches(node)
@@ -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
@@ -119,7 +126,7 @@ module RuboCop
119
126
  end
120
127
 
121
128
  def offense?(node)
122
- _condition, _if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
129
+ _condition, _if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring -- takes all three branches in one step
123
130
  return false if use_if_branch?(else_branch) || use_hash_key_assignment?(else_branch)
124
131
 
125
132
  synonymous_condition_and_branch?(node) && !node.elsif? &&
@@ -143,7 +150,7 @@ module RuboCop
143
150
  end
144
151
 
145
152
  def synonymous_condition_and_branch?(node)
146
- condition, if_branch, _else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
153
+ condition, if_branch, _else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring -- takes all three branches in one step
147
154
  # e.g.
148
155
  # if var
149
156
  # var
@@ -192,7 +199,7 @@ module RuboCop
192
199
  end
193
200
 
194
201
  def branches_have_assignment?(node)
195
- _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
202
+ _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring -- takes all three branches in one step
196
203
 
197
204
  return false unless if_branch && else_branch
198
205
 
@@ -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)
@@ -294,12 +301,12 @@ module RuboCop
294
301
  end
295
302
 
296
303
  def make_ternary_form(node)
297
- _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
298
- arithmetic_operation = use_arithmetic_operation?(if_branch)
304
+ _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring -- takes all three branches in one step
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?)
@@ -108,7 +114,7 @@ module RuboCop
108
114
 
109
115
  node.arguments.map do |arg|
110
116
  if arg.hash_type?
111
- arg.source
117
+ hash_argument_source(arg)
112
118
  else
113
119
  "**#{arg.source}"
114
120
  end
@@ -122,6 +128,15 @@ module RuboCop
122
128
 
123
129
  mergeable?(parent)
124
130
  end
131
+
132
+ # A braced hash literal passed to `merge` must have its braces stripped so
133
+ # its pairs join the surrounding keyword arguments. Keeping the braces would
134
+ # produce a positional hash after keyword arguments, which is invalid Ruby.
135
+ def hash_argument_source(hash)
136
+ return hash.source unless hash.braces?
137
+
138
+ hash.pairs.map(&:source).join(', ')
139
+ end
125
140
  end
126
141
  end
127
142
  end