rubocop 1.59.0 → 1.68.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (352) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +69 -70
  4. data/assets/output.css.erb +159 -0
  5. data/assets/output.html.erb +1 -160
  6. data/config/default.yml +165 -24
  7. data/config/internal_affairs.yml +11 -0
  8. data/exe/rubocop +4 -3
  9. data/lib/rubocop/cached_data.rb +21 -5
  10. data/lib/rubocop/cli/command/auto_generate_config.rb +18 -10
  11. data/lib/rubocop/cli/command/execute_runner.rb +1 -1
  12. data/lib/rubocop/cli/command/lsp.rb +4 -4
  13. data/lib/rubocop/cli/command/show_docs_url.rb +2 -2
  14. data/lib/rubocop/cli/command/version.rb +2 -2
  15. data/lib/rubocop/cli.rb +10 -1
  16. data/lib/rubocop/comment_config.rb +1 -1
  17. data/lib/rubocop/config.rb +41 -13
  18. data/lib/rubocop/config_finder.rb +12 -2
  19. data/lib/rubocop/config_loader.rb +15 -10
  20. data/lib/rubocop/config_loader_resolver.rb +13 -8
  21. data/lib/rubocop/config_obsoletion.rb +1 -1
  22. data/lib/rubocop/config_validator.rb +17 -9
  23. data/lib/rubocop/cop/autocorrect_logic.rb +28 -3
  24. data/lib/rubocop/cop/base.rb +73 -18
  25. data/lib/rubocop/cop/bundler/gem_version.rb +4 -5
  26. data/lib/rubocop/cop/cop.rb +30 -4
  27. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -12
  28. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  29. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  30. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -0
  31. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  32. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +10 -0
  33. data/lib/rubocop/cop/documentation.rb +32 -5
  34. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  35. data/lib/rubocop/cop/force.rb +12 -0
  36. data/lib/rubocop/cop/gemspec/add_runtime_dependency.rb +38 -0
  37. data/lib/rubocop/cop/gemspec/dependency_version.rb +3 -5
  38. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  39. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +5 -1
  40. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  41. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  42. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +2 -1
  43. data/lib/rubocop/cop/internal_affairs/example_description.rb +6 -5
  44. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  45. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +123 -29
  46. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  47. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  48. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  49. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +11 -1
  50. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  51. data/lib/rubocop/cop/internal_affairs.rb +17 -0
  52. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  53. data/lib/rubocop/cop/layout/assignment_indentation.rb +3 -2
  54. data/lib/rubocop/cop/layout/block_alignment.rb +30 -12
  55. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  56. data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
  57. data/lib/rubocop/cop/layout/condition_position.rb +0 -4
  58. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  59. data/lib/rubocop/cop/layout/empty_comment.rb +3 -1
  60. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  61. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +14 -7
  62. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +1 -1
  63. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  64. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +8 -3
  65. data/lib/rubocop/cop/layout/end_alignment.rb +8 -2
  66. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  67. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +18 -4
  68. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  69. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  70. data/lib/rubocop/cop/layout/indentation_width.rb +5 -6
  71. data/lib/rubocop/cop/layout/leading_comment_space.rb +56 -1
  72. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  73. data/lib/rubocop/cop/layout/line_length.rb +20 -20
  74. data/lib/rubocop/cop/layout/redundant_line_break.rb +14 -2
  75. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -0
  76. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  77. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -5
  78. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +4 -0
  79. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  80. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +3 -4
  81. data/lib/rubocop/cop/legacy/corrector.rb +12 -2
  82. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +0 -2
  83. data/lib/rubocop/cop/lint/ambiguous_operator.rb +0 -2
  84. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  85. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +0 -2
  86. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -2
  87. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  88. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -3
  89. data/lib/rubocop/cop/lint/circular_argument_reference.rb +0 -13
  90. data/lib/rubocop/cop/lint/debugger.rb +27 -6
  91. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  92. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +0 -10
  93. data/lib/rubocop/cop/lint/duplicate_branch.rb +39 -4
  94. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +1 -5
  95. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +0 -4
  96. data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -10
  97. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  98. data/lib/rubocop/cop/lint/each_with_object_argument.rb +0 -4
  99. data/lib/rubocop/cop/lint/else_layout.rb +0 -2
  100. data/lib/rubocop/cop/lint/empty_conditional_body.rb +29 -8
  101. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -11
  102. data/lib/rubocop/cop/lint/empty_interpolation.rb +0 -4
  103. data/lib/rubocop/cop/lint/empty_when.rb +1 -3
  104. data/lib/rubocop/cop/lint/ensure_return.rb +1 -9
  105. data/lib/rubocop/cop/lint/erb_new_arguments.rb +21 -14
  106. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  107. data/lib/rubocop/cop/lint/float_out_of_range.rb +0 -4
  108. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +0 -10
  109. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +23 -12
  110. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +0 -7
  111. data/lib/rubocop/cop/lint/interpolation_check.rb +0 -4
  112. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  113. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  114. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +13 -6
  115. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -6
  116. data/lib/rubocop/cop/lint/loop.rb +6 -12
  117. data/lib/rubocop/cop/lint/mixed_case_range.rb +9 -4
  118. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -7
  119. data/lib/rubocop/cop/lint/next_without_accumulator.rb +0 -4
  120. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +0 -5
  121. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +7 -0
  122. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  123. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  124. data/lib/rubocop/cop/lint/percent_string_array.rb +0 -4
  125. data/lib/rubocop/cop/lint/percent_symbol_array.rb +0 -4
  126. data/lib/rubocop/cop/lint/rand_one.rb +0 -4
  127. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +3 -1
  128. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -9
  129. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  130. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +0 -4
  131. data/lib/rubocop/cop/lint/redundant_with_index.rb +4 -0
  132. data/lib/rubocop/cop/lint/require_parentheses.rb +0 -4
  133. data/lib/rubocop/cop/lint/rescue_exception.rb +0 -4
  134. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  135. data/lib/rubocop/cop/lint/return_in_void_context.rb +0 -2
  136. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +9 -4
  137. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +107 -41
  138. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  139. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  140. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +6 -10
  141. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  142. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  143. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -3
  144. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +88 -0
  145. data/lib/rubocop/cop/lint/unified_integer.rb +0 -4
  146. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  147. data/lib/rubocop/cop/lint/unreachable_code.rb +4 -7
  148. data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
  149. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  150. data/lib/rubocop/cop/lint/useless_assignment.rb +19 -16
  151. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +0 -4
  152. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +77 -0
  153. data/lib/rubocop/cop/lint/useless_setter_call.rb +0 -4
  154. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  155. data/lib/rubocop/cop/lint/void.rb +41 -9
  156. data/lib/rubocop/cop/metrics/block_length.rb +6 -5
  157. data/lib/rubocop/cop/metrics/block_nesting.rb +19 -7
  158. data/lib/rubocop/cop/metrics/class_length.rb +6 -5
  159. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +4 -1
  160. data/lib/rubocop/cop/metrics/method_length.rb +6 -5
  161. data/lib/rubocop/cop/metrics/module_length.rb +6 -5
  162. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -5
  163. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  164. data/lib/rubocop/cop/mixin/allowed_methods.rb +7 -1
  165. data/lib/rubocop/cop/mixin/allowed_pattern.rb +15 -3
  166. data/lib/rubocop/cop/mixin/annotation_comment.rb +0 -2
  167. data/lib/rubocop/cop/mixin/check_line_breakable.rb +10 -0
  168. data/lib/rubocop/cop/mixin/code_length.rb +12 -1
  169. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  170. data/lib/rubocop/cop/mixin/configurable_max.rb +5 -1
  171. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +24 -0
  172. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +22 -10
  173. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +9 -2
  174. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -2
  175. data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
  176. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  177. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  178. data/lib/rubocop/cop/mixin/rescue_node.rb +4 -0
  179. data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
  180. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  181. data/lib/rubocop/cop/mixin/string_literals_help.rb +12 -0
  182. data/lib/rubocop/cop/naming/accessor_method_name.rb +5 -0
  183. data/lib/rubocop/cop/naming/block_forwarding.rb +33 -6
  184. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  185. data/lib/rubocop/cop/naming/inclusive_language.rb +13 -5
  186. data/lib/rubocop/cop/naming/predicate_name.rb +55 -29
  187. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +10 -1
  188. data/lib/rubocop/cop/offense.rb +4 -5
  189. data/lib/rubocop/cop/registry.rb +1 -1
  190. data/lib/rubocop/cop/security/compound_hash.rb +2 -2
  191. data/lib/rubocop/cop/security/open.rb +2 -2
  192. data/lib/rubocop/cop/style/access_modifier_declarations.rb +62 -2
  193. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  194. data/lib/rubocop/cop/style/alias.rb +2 -1
  195. data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +79 -0
  196. data/lib/rubocop/cop/style/arguments_forwarding.rb +141 -24
  197. data/lib/rubocop/cop/style/bitwise_predicate.rb +100 -0
  198. data/lib/rubocop/cop/style/block_delimiters.rb +31 -3
  199. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  200. data/lib/rubocop/cop/style/class_vars.rb +3 -3
  201. data/lib/rubocop/cop/style/collection_compact.rb +19 -10
  202. data/lib/rubocop/cop/style/combinable_defined.rb +115 -0
  203. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  204. data/lib/rubocop/cop/style/commented_keyword.rb +12 -3
  205. data/lib/rubocop/cop/style/conditional_assignment.rb +7 -8
  206. data/lib/rubocop/cop/style/copyright.rb +31 -21
  207. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  208. data/lib/rubocop/cop/style/def_with_parentheses.rb +0 -2
  209. data/lib/rubocop/cop/style/documentation.rb +24 -24
  210. data/lib/rubocop/cop/style/documentation_method.rb +20 -0
  211. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -8
  212. data/lib/rubocop/cop/style/empty_else.rb +6 -5
  213. data/lib/rubocop/cop/style/empty_heredoc.rb +1 -14
  214. data/lib/rubocop/cop/style/empty_literal.rb +31 -22
  215. data/lib/rubocop/cop/style/endless_method.rb +1 -14
  216. data/lib/rubocop/cop/style/eval_with_location.rb +16 -24
  217. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  218. data/lib/rubocop/cop/style/file_read.rb +2 -5
  219. data/lib/rubocop/cop/style/file_write.rb +2 -5
  220. data/lib/rubocop/cop/style/for.rb +2 -0
  221. data/lib/rubocop/cop/style/format_string.rb +9 -9
  222. data/lib/rubocop/cop/style/format_string_token.rb +2 -2
  223. data/lib/rubocop/cop/style/global_std_stream.rb +7 -1
  224. data/lib/rubocop/cop/style/guard_clause.rb +17 -2
  225. data/lib/rubocop/cop/style/hash_each_methods.rb +35 -8
  226. data/lib/rubocop/cop/style/hash_except.rb +8 -5
  227. data/lib/rubocop/cop/style/hash_syntax.rb +26 -4
  228. data/lib/rubocop/cop/style/identical_conditional_branches.rb +5 -2
  229. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  230. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +5 -4
  231. data/lib/rubocop/cop/style/if_with_semicolon.rb +49 -6
  232. data/lib/rubocop/cop/style/in_pattern_then.rb +6 -2
  233. data/lib/rubocop/cop/style/inverse_methods.rb +8 -8
  234. data/lib/rubocop/cop/style/invertible_unless_condition.rb +46 -4
  235. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +67 -0
  236. data/lib/rubocop/cop/style/lambda.rb +1 -1
  237. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  238. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +81 -50
  239. data/lib/rubocop/cop/style/map_into_array.rb +233 -0
  240. data/lib/rubocop/cop/style/map_to_hash.rb +10 -6
  241. data/lib/rubocop/cop/style/map_to_set.rb +1 -1
  242. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +29 -11
  243. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  244. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -2
  245. data/lib/rubocop/cop/style/missing_else.rb +0 -4
  246. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  247. data/lib/rubocop/cop/style/multiline_method_signature.rb +10 -1
  248. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +5 -3
  249. data/lib/rubocop/cop/style/multiline_when_then.rb +0 -4
  250. data/lib/rubocop/cop/style/multiple_comparison.rb +28 -47
  251. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  252. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  253. data/lib/rubocop/cop/style/nil_comparison.rb +2 -0
  254. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  255. data/lib/rubocop/cop/style/numeric_predicate.rb +12 -4
  256. data/lib/rubocop/cop/style/object_then.rb +5 -3
  257. data/lib/rubocop/cop/style/one_line_conditional.rb +6 -2
  258. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  259. data/lib/rubocop/cop/style/parallel_assignment.rb +8 -9
  260. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  261. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -3
  262. data/lib/rubocop/cop/style/raise_args.rb +4 -1
  263. data/lib/rubocop/cop/style/redundant_argument.rb +25 -2
  264. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  265. data/lib/rubocop/cop/style/redundant_begin.rb +5 -1
  266. data/lib/rubocop/cop/style/redundant_condition.rb +4 -4
  267. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +5 -4
  268. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  269. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -1
  270. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  271. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +46 -0
  272. data/lib/rubocop/cop/style/redundant_line_continuation.rb +40 -7
  273. data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -13
  274. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  275. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  276. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -24
  277. data/lib/rubocop/cop/style/redundant_return.rb +6 -0
  278. data/lib/rubocop/cop/style/require_order.rb +2 -2
  279. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  280. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  281. data/lib/rubocop/cop/style/safe_navigation.rb +106 -52
  282. data/lib/rubocop/cop/style/safe_navigation_chain_length.rb +52 -0
  283. data/lib/rubocop/cop/style/sample.rb +1 -3
  284. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  285. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  286. data/lib/rubocop/cop/style/send.rb +4 -4
  287. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +104 -0
  288. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  289. data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -2
  290. data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
  291. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  292. data/lib/rubocop/cop/style/super_arguments.rb +174 -0
  293. data/lib/rubocop/cop/style/symbol_proc.rb +75 -5
  294. data/lib/rubocop/cop/style/ternary_parentheses.rb +26 -5
  295. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  296. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  297. data/lib/rubocop/cop/style/while_until_do.rb +0 -2
  298. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  299. data/lib/rubocop/cop/style/zero_length_predicate.rb +32 -24
  300. data/lib/rubocop/cop/team.rb +27 -3
  301. data/lib/rubocop/cop/util.rb +8 -2
  302. data/lib/rubocop/cop/utils/regexp_ranges.rb +1 -1
  303. data/lib/rubocop/cop/variable_force/assignment.rb +18 -3
  304. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  305. data/lib/rubocop/cop/variable_force/variable.rb +5 -1
  306. data/lib/rubocop/cop/variable_force/variable_table.rb +2 -2
  307. data/lib/rubocop/cop/variable_force.rb +13 -1
  308. data/lib/rubocop/cops_documentation_generator.rb +96 -43
  309. data/lib/rubocop/core_ext/string.rb +2 -6
  310. data/lib/rubocop/directive_comment.rb +10 -8
  311. data/lib/rubocop/ext/regexp_node.rb +18 -35
  312. data/lib/rubocop/ext/regexp_parser.rb +4 -21
  313. data/lib/rubocop/file_finder.rb +9 -4
  314. data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
  315. data/lib/rubocop/formatter/disabled_config_formatter.rb +24 -9
  316. data/lib/rubocop/formatter/formatter_set.rb +7 -1
  317. data/lib/rubocop/formatter/html_formatter.rb +32 -10
  318. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  319. data/lib/rubocop/formatter/junit_formatter.rb +70 -23
  320. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  321. data/lib/rubocop/formatter/tap_formatter.rb +3 -7
  322. data/lib/rubocop/formatter.rb +1 -1
  323. data/lib/rubocop/lockfile.rb +58 -7
  324. data/lib/rubocop/lsp/logger.rb +1 -1
  325. data/lib/rubocop/lsp/routes.rb +12 -15
  326. data/lib/rubocop/lsp/runtime.rb +3 -1
  327. data/lib/rubocop/lsp/server.rb +6 -2
  328. data/lib/rubocop/lsp/severity.rb +1 -1
  329. data/lib/rubocop/lsp.rb +36 -0
  330. data/lib/rubocop/magic_comment.rb +1 -1
  331. data/lib/rubocop/options.rb +17 -12
  332. data/lib/rubocop/path_util.rb +6 -2
  333. data/lib/rubocop/rake_task.rb +1 -1
  334. data/lib/rubocop/remote_config.rb +5 -1
  335. data/lib/rubocop/result_cache.rb +2 -8
  336. data/lib/rubocop/rspec/cop_helper.rb +8 -2
  337. data/lib/rubocop/rspec/expect_offense.rb +17 -8
  338. data/lib/rubocop/rspec/shared_contexts.rb +75 -18
  339. data/lib/rubocop/rspec/support.rb +3 -0
  340. data/lib/rubocop/runner.rb +31 -9
  341. data/lib/rubocop/server/cache.rb +16 -2
  342. data/lib/rubocop/server/client_command/exec.rb +2 -3
  343. data/lib/rubocop/server/client_command/start.rb +1 -1
  344. data/lib/rubocop/server/core.rb +5 -0
  345. data/lib/rubocop/server/server_command/exec.rb +0 -1
  346. data/lib/rubocop/target_finder.rb +84 -78
  347. data/lib/rubocop/target_ruby.rb +87 -81
  348. data/lib/rubocop/version.rb +45 -9
  349. data/lib/rubocop/yaml_duplication_checker.rb +20 -26
  350. data/lib/rubocop.rb +21 -1
  351. metadata +33 -35
  352. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -40,6 +40,7 @@ module RuboCop
40
40
 
41
41
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
42
42
  UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
43
+ ARRAY_CONVERTER_METHODS = %i[assoc chunk flatten rassoc sort sort_by to_a].freeze
43
44
 
44
45
  # @!method kv_each(node)
45
46
  def_node_matcher :kv_each, <<~PATTERN
@@ -56,7 +57,11 @@ module RuboCop
56
57
  (call $(call _ ${:keys :values}) :each (block_pass (sym _)))
57
58
  PATTERN
58
59
 
59
- # rubocop:disable Metrics/AbcSize
60
+ # @!method hash_mutated?(node, receiver)
61
+ def_node_matcher :hash_mutated?, <<~PATTERN
62
+ `(send %1 :[]= ...)
63
+ PATTERN
64
+
60
65
  def on_block(node)
61
66
  return unless handleable?(node)
62
67
 
@@ -66,12 +71,24 @@ module RuboCop
66
71
 
67
72
  return unless (key, value = each_arguments(node))
68
73
 
69
- if unused_block_arg_exist?(node, value)
74
+ check_unused_block_args(node, key, value)
75
+ end
76
+ alias on_numblock on_block
77
+
78
+ # rubocop:disable Metrics/AbcSize
79
+ def check_unused_block_args(node, key, value)
80
+ return if node.body.nil?
81
+
82
+ value_unused = unused_block_arg_exist?(node, value)
83
+ key_unused = unused_block_arg_exist?(node, key)
84
+ return if value_unused && key_unused
85
+
86
+ if value_unused
70
87
  message = message('each_key', node.method_name, value.source)
71
88
  unused_range = key.source_range.end.join(value.source_range.end)
72
89
 
73
90
  register_each_args_offense(node, message, 'each_key', unused_range)
74
- elsif unused_block_arg_exist?(node, key)
91
+ elsif key_unused
75
92
  message = message('each_value', node.method_name, key.source)
76
93
  unused_range = key.source_range.begin.join(value.source_range.begin)
77
94
 
@@ -80,8 +97,6 @@ module RuboCop
80
97
  end
81
98
  # rubocop:enable Metrics/AbcSize
82
99
 
83
- alias on_numblock on_block
84
-
85
100
  def on_block_pass(node)
86
101
  kv_each_with_block_pass(node.parent) do |target, method|
87
102
  register_kv_with_block_pass_offense(node, target, method)
@@ -91,7 +106,9 @@ module RuboCop
91
106
  private
92
107
 
93
108
  def handleable?(node)
109
+ return false if use_array_converter_method_as_preceding?(node)
94
110
  return false unless (root_receiver = root_receiver(node))
111
+ return false if hash_mutated?(node, root_receiver)
95
112
 
96
113
  !root_receiver.literal? || root_receiver.hash_type?
97
114
  end
@@ -111,11 +128,11 @@ module RuboCop
111
128
  lvar_sources = node.body.each_descendant(:lvar).map(&:source)
112
129
 
113
130
  if block_arg.mlhs_type?
114
- block_arg.each_descendant(:arg).all? do |block_arg|
115
- lvar_sources.none?(block_arg.source)
131
+ block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
132
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
116
133
  end
117
134
  else
118
- lvar_sources.none?(block_arg.source)
135
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
119
136
  end
120
137
  end
121
138
 
@@ -143,6 +160,16 @@ module RuboCop
143
160
  end
144
161
  end
145
162
 
163
+ def use_array_converter_method_as_preceding?(node)
164
+ return false unless (preceding_method = node.children.first.children.first)
165
+ unless preceding_method.call_type? ||
166
+ preceding_method.block_type? || preceding_method.numblock_type?
167
+ return false
168
+ end
169
+
170
+ ARRAY_CONVERTER_METHODS.include?(preceding_method.method_name)
171
+ end
172
+
146
173
  def root_receiver(node)
147
174
  receiver = node.receiver
148
175
  if receiver&.receiver
@@ -23,9 +23,9 @@ module RuboCop
23
23
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
24
24
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
25
25
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
26
- # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[foo bar].include?(k) }
27
- # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[foo bar].include?(k) }
28
- # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[foo bar].include?(k) }
26
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].include?(k) }
27
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].include?(k) }
28
+ # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[bar].include?(k) }
29
29
  #
30
30
  # # good
31
31
  # {foo: 1, bar: 2, baz: 3}.except(:bar)
@@ -73,8 +73,9 @@ module RuboCop
73
73
  PATTERN
74
74
 
75
75
  def on_send(node)
76
+ method_name = node.method_name
76
77
  block = node.parent
77
- return unless bad_method?(block) && semantically_except_method?(node, block)
78
+ return unless bad_method?(method_name, block) && semantically_except_method?(node, block)
78
79
 
79
80
  except_key = except_key(block)
80
81
  return if except_key.nil? || !safe_to_register_offense?(block, except_key)
@@ -91,7 +92,7 @@ module RuboCop
91
92
  private
92
93
 
93
94
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
94
- def bad_method?(block)
95
+ def bad_method?(method_name, block)
95
96
  if active_support_extensions_enabled?
96
97
  bad_method_with_active_support?(block) do |key_arg, send_node|
97
98
  if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
@@ -103,6 +104,8 @@ module RuboCop
103
104
  end
104
105
  else
105
106
  bad_method_with_poro?(block) do |key_arg, send_node|
107
+ return false if method_name == :reject && block.body.method?(:!)
108
+
106
109
  !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
107
110
  end
108
111
  end
@@ -29,6 +29,8 @@ module RuboCop
29
29
  # * never - forces use of explicit hash literal value
30
30
  # * either - accepts both shorthand and explicit use of hash literal value
31
31
  # * consistent - forces use of the 3.1 syntax only if all values can be omitted in the hash
32
+ # * either_consistent - accepts both shorthand and explicit use of hash literal value,
33
+ # but they must be consistent
32
34
  #
33
35
  # @example EnforcedStyle: ruby19 (default)
34
36
  # # bad
@@ -66,7 +68,7 @@ module RuboCop
66
68
  # {a: 1, b: 2}
67
69
  # {:c => 3, 'd' => 4}
68
70
  #
69
- # @example EnforcedShorthandSyntax: always (default)
71
+ # @example EnforcedShorthandSyntax: always
70
72
  #
71
73
  # # bad
72
74
  # {foo: foo, bar: bar}
@@ -82,7 +84,7 @@ module RuboCop
82
84
  # # good
83
85
  # {foo: foo, bar: bar}
84
86
  #
85
- # @example EnforcedShorthandSyntax: either
87
+ # @example EnforcedShorthandSyntax: either (default)
86
88
  #
87
89
  # # good
88
90
  # {foo: foo, bar: bar}
@@ -110,6 +112,22 @@ module RuboCop
110
112
  # # good - can't omit `baz`
111
113
  # {foo: foo, bar: baz}
112
114
  #
115
+ # @example EnforcedShorthandSyntax: either_consistent
116
+ #
117
+ # # good - `foo` and `bar` values can be omitted, but they are consistent, so it's accepted
118
+ # {foo: foo, bar: bar}
119
+ #
120
+ # # bad - `bar` value can be omitted
121
+ # {foo:, bar: bar}
122
+ #
123
+ # # bad - mixed syntaxes
124
+ # {foo:, bar: baz}
125
+ #
126
+ # # good
127
+ # {foo:, bar:}
128
+ #
129
+ # # good - can't omit `baz`
130
+ # {foo: foo, bar: baz}
113
131
  class HashSyntax < Base
114
132
  include ConfigurableEnforcedStyle
115
133
  include HashShorthandSyntax
@@ -195,6 +213,7 @@ module RuboCop
195
213
  acceptable_19_syntax_symbol?(pair.key.source)
196
214
  end
197
215
 
216
+ # rubocop:disable Metrics/CyclomaticComplexity
198
217
  def acceptable_19_syntax_symbol?(sym_name)
199
218
  sym_name.delete_prefix!(':')
200
219
 
@@ -209,9 +228,12 @@ module RuboCop
209
228
  # Most hash keys can be matched against a simple regex.
210
229
  return true if /\A[_a-z]\w*[?!]?\z/i.match?(sym_name)
211
230
 
212
- # For more complicated hash keys, let the parser validate the syntax.
213
- parse("{ #{sym_name}: :foo }").valid_syntax?
231
+ return false if target_ruby_version <= 2.1
232
+
233
+ (sym_name.start_with?("'") && sym_name.end_with?("'")) ||
234
+ (sym_name.start_with?('"') && sym_name.end_with?('"'))
214
235
  end
236
+ # rubocop:enable Metrics/CyclomaticComplexity
215
237
 
216
238
  def check(pairs, delim, msg)
217
239
  pairs.each do |pair|
@@ -155,10 +155,13 @@ module RuboCop
155
155
  condition_variable = assignable_condition_value(node)
156
156
 
157
157
  head = heads.first
158
- if head.assignment?
158
+ if head.respond_to?(:assignment?) && head.assignment?
159
159
  # The `send` node is used instead of the `indexasgn` node, so `name` cannot be used.
160
160
  # https://github.com/rubocop/rubocop-ast/blob/v1.29.0/lib/rubocop/ast/node/indexasgn_node.rb
161
- assigned_value = head.send_type? ? head.receiver.source : head.name.to_s
161
+ #
162
+ # FIXME: It would be better to update `RuboCop::AST::OpAsgnNode` or its subclasses to
163
+ # handle `self.foo ||= value` as a solution, instead of using `head.node_parts[0].to_s`.
164
+ assigned_value = head.send_type? ? head.receiver.source : head.node_parts[0].to_s
162
165
 
163
166
  return if condition_variable == assigned_value
164
167
  end
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  else_branch = node.else_branch
73
73
 
74
- return unless else_branch&.if_type? && else_branch&.if?
74
+ return unless else_branch&.if_type? && else_branch.if?
75
75
  return if allow_if_modifier_in_else_branch?(else_branch)
76
76
 
77
77
  add_offense(else_branch.loc.keyword) do |corrector|
@@ -44,7 +44,6 @@ module RuboCop
44
44
  # # good
45
45
  # foo == bar
46
46
  #
47
- # @example
48
47
  # # bad
49
48
  # if foo.do_something?
50
49
  # true
@@ -112,9 +111,11 @@ module RuboCop
112
111
  end
113
112
 
114
113
  def message(node, keyword)
115
- message_template = node.elsif? ? MSG_FOR_ELSIF : MSG
116
-
117
- format(message_template, keyword: keyword)
114
+ if node.elsif?
115
+ MSG_FOR_ELSIF
116
+ else
117
+ format(MSG, keyword: keyword)
118
+ end
118
119
  end
119
120
 
120
121
  def return_boolean_value?(condition)
@@ -18,6 +18,7 @@ module RuboCop
18
18
  extend AutoCorrector
19
19
 
20
20
  MSG_IF_ELSE = 'Do not use `if %<expr>s;` - use `if/else` instead.'
21
+ MSG_NEWLINE = 'Do not use `if %<expr>s;` - use a newline instead.'
21
22
  MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
22
23
 
23
24
  def on_normal_if_unless(node)
@@ -26,20 +27,47 @@ module RuboCop
26
27
  beginning = node.loc.begin
27
28
  return unless beginning&.is?(';')
28
29
 
29
- message = node.else_branch&.if_type? ? MSG_IF_ELSE : MSG_TERNARY
30
+ message = message(node)
30
31
 
31
- add_offense(node, message: format(message, expr: node.condition.source)) do |corrector|
32
- corrector.replace(node, autocorrect(node))
32
+ add_offense(node, message: message) do |corrector|
33
+ autocorrect(corrector, node)
33
34
  end
34
35
  end
35
36
 
36
37
  private
37
38
 
38
- def autocorrect(node)
39
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
40
+ def message(node)
41
+ template = if node.if_branch&.begin_type?
42
+ MSG_NEWLINE
43
+ elsif node.else_branch&.if_type? || node.else_branch&.begin_type? ||
44
+ use_block_in_branches?(node)
45
+ MSG_IF_ELSE
46
+ else
47
+ MSG_TERNARY
48
+ end
49
+
50
+ format(template, expr: node.condition.source)
51
+ end
52
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
53
+
54
+ def autocorrect(corrector, node)
55
+ if node.branches.compact.any?(&:begin_type?) || use_block_in_branches?(node)
56
+ corrector.replace(node.loc.begin, "\n")
57
+ else
58
+ corrector.replace(node, replacement(node))
59
+ end
60
+ end
61
+
62
+ def use_block_in_branches?(node)
63
+ node.branches.compact.any? { |branch| branch.block_type? || branch.numblock_type? }
64
+ end
65
+
66
+ def replacement(node)
39
67
  return correct_elsif(node) if node.else_branch&.if_type?
40
68
 
41
- then_code = node.if_branch ? node.if_branch.source : 'nil'
42
- else_code = node.else_branch ? node.else_branch.source : 'nil'
69
+ then_code = node.if_branch ? build_expression(node.if_branch) : 'nil'
70
+ else_code = node.else_branch ? build_expression(node.else_branch) : 'nil'
43
71
 
44
72
  "#{node.condition.source} ? #{then_code} : #{else_code}"
45
73
  end
@@ -53,6 +81,15 @@ module RuboCop
53
81
  RUBY
54
82
  end
55
83
 
84
+ def build_expression(expr)
85
+ return expr.source unless require_argument_parentheses?(expr)
86
+
87
+ method = expr.source_range.begin.join(expr.loc.selector.end)
88
+ arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
89
+
90
+ "#{method.source}(#{arguments.source})"
91
+ end
92
+
56
93
  def build_else_branch(second_condition)
57
94
  result = <<~RUBY
58
95
  elsif #{second_condition.condition.source}
@@ -72,6 +109,12 @@ module RuboCop
72
109
 
73
110
  result
74
111
  end
112
+
113
+ def require_argument_parentheses?(node)
114
+ return false unless node.call_type?
115
+
116
+ !node.parenthesized? && node.arguments.any? && !node.method?(:[]) && !node.method?(:[]=)
117
+ end
75
118
  end
76
119
  end
77
120
  end
@@ -44,11 +44,15 @@ module RuboCop
44
44
  private
45
45
 
46
46
  def alternative_pattern_source(pattern)
47
+ collect_alternative_patterns(pattern).join(' | ')
48
+ end
49
+
50
+ def collect_alternative_patterns(pattern)
47
51
  return pattern.children.map(&:source) unless pattern.children.first.match_alt_type?
48
52
 
49
- pattern_sources = alternative_pattern_source(pattern.children.first)
53
+ pattern_sources = collect_alternative_patterns(pattern.children.first)
50
54
 
51
- (pattern_sources << pattern.children[1].source).join(' | ')
55
+ pattern_sources << pattern.children[1].source
52
56
  end
53
57
  end
54
58
  end
@@ -76,9 +76,9 @@ module RuboCop
76
76
  PATTERN
77
77
 
78
78
  def on_send(node)
79
- inverse_candidate?(node) do |_method_call, lhs, method, rhs|
79
+ inverse_candidate?(node) do |method_call, lhs, method, rhs|
80
80
  return unless inverse_methods.key?(method)
81
- return if negated?(node)
81
+ return if negated?(node) || relational_comparison_with_safe_navigation?(method_call)
82
82
  return if part_of_ignored_node?(node)
83
83
  return if possible_class_hierarchy_check?(lhs, rhs, method)
84
84
 
@@ -155,16 +155,16 @@ module RuboCop
155
155
  node.parent.respond_to?(:method?) && node.parent.method?(:!)
156
156
  end
157
157
 
158
+ def relational_comparison_with_safe_navigation?(node)
159
+ node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name)
160
+ end
161
+
158
162
  def not_to_receiver(node, method_call)
159
- Parser::Source::Range.new(node.source_range.source_buffer,
160
- node.loc.selector.begin_pos,
161
- method_call.source_range.begin_pos)
163
+ node.loc.selector.begin.join(method_call.source_range.begin)
162
164
  end
163
165
 
164
166
  def end_parentheses(node, method_call)
165
- Parser::Source::Range.new(node.source_range.source_buffer,
166
- method_call.source_range.end_pos,
167
- node.source_range.end_pos)
167
+ method_call.source_range.end.join(node.source_range.end)
168
168
  end
169
169
 
170
170
  # When comparing classes, `!(Integer < Numeric)` is not the same as
@@ -32,12 +32,14 @@ module RuboCop
32
32
  # foo unless x != y
33
33
  # foo unless x >= 10
34
34
  # foo unless x.even?
35
+ # foo unless odd?
35
36
  #
36
37
  # # good
37
38
  # foo if bar
38
39
  # foo if x == y
39
40
  # foo if x < 10
40
41
  # foo if x.odd?
42
+ # foo if even?
41
43
  #
42
44
  # # bad (complex condition)
43
45
  # foo unless x != y || x.even?
@@ -51,7 +53,7 @@ module RuboCop
51
53
  class InvertibleUnlessCondition < Base
52
54
  extend AutoCorrector
53
55
 
54
- MSG = 'Favor `if` with inverted condition over `unless`.'
56
+ MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
55
57
 
56
58
  def on_if(node)
57
59
  return unless node.unless?
@@ -59,7 +61,10 @@ module RuboCop
59
61
  condition = node.condition
60
62
  return unless invertible?(condition)
61
63
 
62
- add_offense(node) do |corrector|
64
+ message = format(MSG, prefer: "#{node.inverse_keyword} #{preferred_condition(condition)}",
65
+ current: "#{node.keyword} #{condition.source}")
66
+
67
+ add_offense(node, message: message) do |corrector|
63
68
  corrector.replace(node.loc.keyword, node.inverse_keyword)
64
69
  autocorrect(corrector, condition)
65
70
  end
@@ -67,8 +72,8 @@ module RuboCop
67
72
 
68
73
  private
69
74
 
70
- def invertible?(node)
71
- case node.type
75
+ def invertible?(node) # rubocop:disable Metrics/CyclomaticComplexity
76
+ case node&.type
72
77
  when :begin
73
78
  invertible?(node.children.first)
74
79
  when :send
@@ -88,6 +93,43 @@ module RuboCop
88
93
  (argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
89
94
  end
90
95
 
96
+ def preferred_condition(node)
97
+ case node.type
98
+ when :begin then "(#{preferred_condition(node.children.first)})"
99
+ when :send then preferred_send_condition(node)
100
+ when :or, :and then preferred_logical_condition(node)
101
+ end
102
+ end
103
+
104
+ def preferred_send_condition(node) # rubocop:disable Metrics/CyclomaticComplexity
105
+ receiver_source = node.receiver&.source
106
+ return receiver_source if node.method?(:!)
107
+
108
+ # receiver may be implicit (self)
109
+ dotted_receiver_source = receiver_source ? "#{receiver_source}." : ''
110
+
111
+ inverse_method_name = inverse_methods[node.method_name]
112
+ return "#{dotted_receiver_source}#{inverse_method_name}" unless node.arguments?
113
+
114
+ argument_list = node.arguments.map(&:source).join(', ')
115
+ if node.operator_method?
116
+ return "#{receiver_source} #{inverse_method_name} #{argument_list}"
117
+ end
118
+
119
+ if node.parenthesized?
120
+ return "#{dotted_receiver_source}#{inverse_method_name}(#{argument_list})"
121
+ end
122
+
123
+ "#{dotted_receiver_source}#{inverse_method_name} #{argument_list}"
124
+ end
125
+
126
+ def preferred_logical_condition(node)
127
+ preferred_lhs = preferred_condition(node.lhs)
128
+ preferred_rhs = preferred_condition(node.rhs)
129
+
130
+ "#{preferred_lhs} #{node.inverse_operator} #{preferred_rhs}"
131
+ end
132
+
91
133
  def autocorrect(corrector, node)
92
134
  case node.type
93
135
  when :begin
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # When passing an existing hash as keyword arguments, provide additional arguments
7
+ # directly rather than using `merge`.
8
+ #
9
+ # Providing arguments directly is more performant, than using `merge`, and
10
+ # also leads to a shorter and simpler code.
11
+ #
12
+ # @example
13
+ # # bad
14
+ # some_method(**opts.merge(foo: true))
15
+ # some_method(**opts.merge(other_opts))
16
+ #
17
+ # # good
18
+ # some_method(**opts, foo: true)
19
+ # some_method(**opts, **other_opts)
20
+ #
21
+ class KeywordArgumentsMerging < Base
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Provide additional arguments directly rather than using `merge`.'
25
+
26
+ # @!method merge_kwargs?(node)
27
+ def_node_matcher :merge_kwargs?, <<~PATTERN
28
+ (send _ _
29
+ ...
30
+ (hash
31
+ (kwsplat
32
+ $(send $_ :merge $...))
33
+ ...))
34
+ PATTERN
35
+
36
+ def on_kwsplat(node)
37
+ return unless (ancestor = node.parent&.parent)
38
+
39
+ merge_kwargs?(ancestor) do |merge_node, hash_node, other_hash_node|
40
+ add_offense(merge_node) do |corrector|
41
+ autocorrect(corrector, node, hash_node, other_hash_node)
42
+ end
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def autocorrect(corrector, kwsplat_node, hash_node, other_hash_node)
49
+ other_hash_node_replacement =
50
+ other_hash_node.map do |node|
51
+ if node.hash_type?
52
+ if node.braces?
53
+ node.source[1...-1]
54
+ else
55
+ node.source
56
+ end
57
+ else
58
+ "**#{node.source}"
59
+ end
60
+ end.join(', ')
61
+
62
+ corrector.replace(kwsplat_node, "**#{hash_node.source}, #{other_hash_node_replacement}")
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -68,7 +68,7 @@ module RuboCop
68
68
 
69
69
  return unless offending_selector?(node, selector)
70
70
 
71
- add_offense(node.send_node.source_range, message: message(node, selector)) do |corrector|
71
+ add_offense(node.send_node, message: message(node, selector)) do |corrector|
72
72
  if node.send_node.lambda_literal?
73
73
  LambdaLiteralToMethodCorrector.new(node).call(corrector)
74
74
  else
@@ -105,7 +105,7 @@ module RuboCop
105
105
 
106
106
  # Value object to extract source ranges for the different parts of a magic comment
107
107
  class CommentRange
108
- extend Forwardable
108
+ extend SimpleForwardable
109
109
 
110
110
  DIRECTIVE_REGEXP = Regexp.union(MagicComment::KEYWORDS.map do |_, v|
111
111
  Regexp.new(v, Regexp::IGNORECASE)