rubocop 1.57.1 → 1.65.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 (266) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -5
  4. data/assets/output.css.erb +159 -0
  5. data/assets/output.html.erb +1 -160
  6. data/config/default.yml +136 -19
  7. data/lib/rubocop/cached_data.rb +11 -3
  8. data/lib/rubocop/cli/command/auto_generate_config.rb +22 -8
  9. data/lib/rubocop/cli/command/lsp.rb +2 -2
  10. data/lib/rubocop/cli/command/show_docs_url.rb +2 -2
  11. data/lib/rubocop/cli.rb +10 -1
  12. data/lib/rubocop/config.rb +36 -12
  13. data/lib/rubocop/config_finder.rb +12 -2
  14. data/lib/rubocop/config_loader.rb +1 -2
  15. data/lib/rubocop/config_loader_resolver.rb +9 -3
  16. data/lib/rubocop/config_obsoletion.rb +11 -8
  17. data/lib/rubocop/config_validator.rb +14 -7
  18. data/lib/rubocop/cop/autocorrect_logic.rb +6 -1
  19. data/lib/rubocop/cop/base.rb +63 -16
  20. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  21. data/lib/rubocop/cop/bundler/gem_version.rb +3 -5
  22. data/lib/rubocop/cop/cop.rb +20 -2
  23. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  24. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  25. data/lib/rubocop/cop/documentation.rb +16 -6
  26. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  27. data/lib/rubocop/cop/force.rb +12 -0
  28. data/lib/rubocop/cop/gemspec/add_runtime_dependency.rb +38 -0
  29. data/lib/rubocop/cop/gemspec/dependency_version.rb +3 -5
  30. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  31. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  32. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +5 -1
  33. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  34. data/lib/rubocop/cop/internal_affairs/example_description.rb +6 -5
  35. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  36. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  37. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  38. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +123 -29
  39. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  40. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  41. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  42. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  43. data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
  44. data/lib/rubocop/cop/layout/empty_comment.rb +3 -1
  45. data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +14 -7
  46. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +1 -1
  47. data/lib/rubocop/cop/layout/end_alignment.rb +15 -3
  48. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  49. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  50. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +24 -7
  51. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  52. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  53. data/lib/rubocop/cop/layout/heredoc_indentation.rb +2 -2
  54. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  55. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  56. data/lib/rubocop/cop/layout/line_length.rb +20 -20
  57. data/lib/rubocop/cop/layout/redundant_line_break.rb +16 -3
  58. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  59. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  60. data/lib/rubocop/cop/layout/space_around_operators.rb +53 -20
  61. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  62. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +1 -1
  63. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +3 -4
  64. data/lib/rubocop/cop/legacy/corrector.rb +12 -2
  65. data/lib/rubocop/cop/lint/assignment_in_condition.rb +6 -6
  66. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  67. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  68. data/lib/rubocop/cop/lint/debugger.rb +29 -3
  69. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  70. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +1 -1
  71. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  72. data/lib/rubocop/cop/lint/empty_conditional_body.rb +2 -2
  73. data/lib/rubocop/cop/lint/empty_when.rb +1 -1
  74. data/lib/rubocop/cop/lint/erb_new_arguments.rb +24 -17
  75. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  76. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  77. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +14 -7
  78. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  79. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  80. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  81. data/lib/rubocop/cop/lint/mixed_case_range.rb +9 -4
  82. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  83. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  84. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  85. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  86. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +54 -6
  87. data/lib/rubocop/cop/lint/redundant_with_index.rb +6 -2
  88. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  89. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  90. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  91. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  92. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  93. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  94. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  95. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  96. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -3
  97. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  98. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +3 -2
  99. data/lib/rubocop/cop/lint/unreachable_code.rb +4 -2
  100. data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
  101. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  102. data/lib/rubocop/cop/lint/useless_times.rb +2 -2
  103. data/lib/rubocop/cop/lint/void.rb +53 -12
  104. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  105. data/lib/rubocop/cop/metrics/block_nesting.rb +19 -7
  106. data/lib/rubocop/cop/metrics/class_length.rb +6 -1
  107. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -5
  108. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  109. data/lib/rubocop/cop/mixin/allowed_methods.rb +7 -1
  110. data/lib/rubocop/cop/mixin/allowed_pattern.rb +15 -3
  111. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  112. data/lib/rubocop/cop/mixin/code_length.rb +12 -1
  113. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  114. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  115. data/lib/rubocop/cop/mixin/configurable_max.rb +5 -1
  116. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +23 -13
  117. data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
  118. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  119. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  120. data/lib/rubocop/cop/mixin/rescue_node.rb +4 -0
  121. data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
  122. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  123. data/lib/rubocop/cop/naming/block_forwarding.rb +34 -7
  124. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  125. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  126. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -2
  127. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  128. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  129. data/lib/rubocop/cop/registry.rb +1 -1
  130. data/lib/rubocop/cop/security/compound_hash.rb +2 -2
  131. data/lib/rubocop/cop/security/open.rb +2 -2
  132. data/lib/rubocop/cop/style/access_modifier_declarations.rb +52 -2
  133. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  134. data/lib/rubocop/cop/style/alias.rb +1 -0
  135. data/lib/rubocop/cop/style/arguments_forwarding.rb +155 -21
  136. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  137. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  138. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  139. data/lib/rubocop/cop/style/case_like_if.rb +5 -5
  140. data/lib/rubocop/cop/style/class_check.rb +1 -0
  141. data/lib/rubocop/cop/style/class_vars.rb +3 -3
  142. data/lib/rubocop/cop/style/collection_compact.rb +21 -11
  143. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  144. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  145. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  146. data/lib/rubocop/cop/style/conditional_assignment.rb +7 -8
  147. data/lib/rubocop/cop/style/copyright.rb +31 -21
  148. data/lib/rubocop/cop/style/date_time.rb +5 -4
  149. data/lib/rubocop/cop/style/documentation.rb +24 -24
  150. data/lib/rubocop/cop/style/documentation_method.rb +20 -0
  151. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  152. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  153. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  154. data/lib/rubocop/cop/style/eval_with_location.rb +6 -15
  155. data/lib/rubocop/cop/style/exact_regexp_match.rb +4 -2
  156. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  157. data/lib/rubocop/cop/style/for.rb +2 -0
  158. data/lib/rubocop/cop/style/format_string.rb +9 -9
  159. data/lib/rubocop/cop/style/hash_each_methods.rb +105 -11
  160. data/lib/rubocop/cop/style/hash_except.rb +10 -6
  161. data/lib/rubocop/cop/style/hash_syntax.rb +24 -2
  162. data/lib/rubocop/cop/style/identical_conditional_branches.rb +12 -1
  163. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +5 -3
  164. data/lib/rubocop/cop/style/inverse_methods.rb +14 -13
  165. data/lib/rubocop/cop/style/invertible_unless_condition.rb +44 -2
  166. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +82 -50
  167. data/lib/rubocop/cop/style/map_into_array.rb +175 -0
  168. data/lib/rubocop/cop/style/map_to_hash.rb +18 -8
  169. data/lib/rubocop/cop/style/map_to_set.rb +1 -1
  170. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +19 -5
  171. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  172. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  173. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  174. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  175. data/lib/rubocop/cop/style/multiline_method_signature.rb +10 -1
  176. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +5 -3
  177. data/lib/rubocop/cop/style/next.rb +1 -1
  178. data/lib/rubocop/cop/style/nil_comparison.rb +2 -0
  179. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  180. data/lib/rubocop/cop/style/numeric_predicate.rb +10 -2
  181. data/lib/rubocop/cop/style/object_then.rb +5 -3
  182. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  183. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  184. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -5
  185. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  186. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  187. data/lib/rubocop/cop/style/raise_args.rb +4 -1
  188. data/lib/rubocop/cop/style/redundant_argument.rb +27 -3
  189. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  190. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  191. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +5 -4
  192. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +17 -10
  193. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  194. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  195. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -1
  196. data/lib/rubocop/cop/style/redundant_filter_chain.rb +5 -4
  197. data/lib/rubocop/cop/style/redundant_line_continuation.rb +19 -2
  198. data/lib/rubocop/cop/style/redundant_parentheses.rb +50 -19
  199. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  200. data/lib/rubocop/cop/style/redundant_return.rb +7 -1
  201. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  202. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  203. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  204. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  205. data/lib/rubocop/cop/style/require_order.rb +1 -1
  206. data/lib/rubocop/cop/style/sample.rb +3 -4
  207. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  208. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  209. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  210. data/lib/rubocop/cop/style/send.rb +4 -4
  211. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +104 -0
  212. data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
  213. data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
  214. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  215. data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
  216. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  217. data/lib/rubocop/cop/style/strip.rb +7 -4
  218. data/lib/rubocop/cop/style/super_arguments.rb +174 -0
  219. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  220. data/lib/rubocop/cop/style/symbol_proc.rb +75 -5
  221. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  222. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  223. data/lib/rubocop/cop/style/zero_length_predicate.rb +28 -24
  224. data/lib/rubocop/cop/team.rb +13 -0
  225. data/lib/rubocop/cop/util.rb +7 -1
  226. data/lib/rubocop/cop/utils/regexp_ranges.rb +1 -1
  227. data/lib/rubocop/cops_documentation_generator.rb +16 -4
  228. data/lib/rubocop/directive_comment.rb +10 -8
  229. data/lib/rubocop/ext/regexp_node.rb +9 -4
  230. data/lib/rubocop/ext/regexp_parser.rb +4 -21
  231. data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
  232. data/lib/rubocop/formatter/disabled_config_formatter.rb +23 -8
  233. data/lib/rubocop/formatter/formatter_set.rb +7 -1
  234. data/lib/rubocop/formatter/html_formatter.rb +37 -14
  235. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  236. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  237. data/lib/rubocop/formatter/tap_formatter.rb +3 -7
  238. data/lib/rubocop/formatter.rb +1 -1
  239. data/lib/rubocop/lockfile.rb +56 -7
  240. data/lib/rubocop/lsp/logger.rb +1 -1
  241. data/lib/rubocop/lsp/routes.rb +12 -15
  242. data/lib/rubocop/lsp/runtime.rb +1 -1
  243. data/lib/rubocop/lsp/server.rb +7 -2
  244. data/lib/rubocop/lsp/severity.rb +1 -1
  245. data/lib/rubocop/lsp.rb +36 -0
  246. data/lib/rubocop/magic_comment.rb +1 -1
  247. data/lib/rubocop/options.rb +14 -11
  248. data/lib/rubocop/path_util.rb +6 -2
  249. data/lib/rubocop/rake_task.rb +1 -1
  250. data/lib/rubocop/result_cache.rb +0 -1
  251. data/lib/rubocop/rspec/cop_helper.rb +8 -2
  252. data/lib/rubocop/rspec/expect_offense.rb +16 -8
  253. data/lib/rubocop/rspec/shared_contexts.rb +73 -16
  254. data/lib/rubocop/rspec/support.rb +3 -0
  255. data/lib/rubocop/runner.rb +14 -3
  256. data/lib/rubocop/server/cache.rb +11 -2
  257. data/lib/rubocop/server/client_command/exec.rb +2 -3
  258. data/lib/rubocop/server/client_command/start.rb +1 -1
  259. data/lib/rubocop/server/core.rb +4 -0
  260. data/lib/rubocop/server/server_command/exec.rb +0 -1
  261. data/lib/rubocop/target_finder.rb +84 -78
  262. data/lib/rubocop/target_ruby.rb +82 -80
  263. data/lib/rubocop/version.rb +19 -4
  264. data/lib/rubocop.rb +8 -0
  265. metadata +27 -29
  266. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for usages of `each` with `<<`, `push`, or `append` which
7
+ # can be replaced by `map`.
8
+ #
9
+ # If `PreferredMethods` is configured for `map` in `Style/CollectionMethods`,
10
+ # this cop uses the specified method for replacement.
11
+ #
12
+ # NOTE: The return value of `Enumerable#each` is `self`, whereas the
13
+ # return value of `Enumerable#map` is an `Array`. They are not autocorrected
14
+ # when a return value could be used because these types differ.
15
+ #
16
+ # NOTE: It only detects when the mapping destination is a local variable
17
+ # initialized as an empty array and referred to only by the pushing operation.
18
+ # This is because, if not, it's challenging to statically guarantee that the
19
+ # mapping destination variable remains an empty array:
20
+ #
21
+ # [source,ruby]
22
+ # ----
23
+ # ret = []
24
+ # src.each { |e| ret << e * 2 } # `<<` method may mutate `ret`
25
+ #
26
+ # dest = []
27
+ # src.each { |e| dest << transform(e, dest) } # `transform` method may mutate `dest`
28
+ # ----
29
+ #
30
+ # @safety
31
+ # This cop is unsafe because not all objects that have an `each`
32
+ # method also have a `map` method (e.g. `ENV`). Additionally, for calls
33
+ # with a block, not all objects that have a `map` method return an array
34
+ # (e.g. `Enumerator::Lazy`).
35
+ #
36
+ # @example
37
+ # # bad
38
+ # dest = []
39
+ # src.each { |e| dest << e * 2 }
40
+ # dest
41
+ #
42
+ # # good
43
+ # dest = src.map { |e| e * 2 }
44
+ #
45
+ # # good - contains another operation
46
+ # dest = []
47
+ # src.each { |e| dest << e * 2; puts e }
48
+ # dest
49
+ #
50
+ class MapIntoArray < Base
51
+ include RangeHelp
52
+ extend AutoCorrector
53
+
54
+ MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
55
+
56
+ # @!method each_block_with_push?(node)
57
+ def_node_matcher :each_block_with_push?, <<-PATTERN
58
+ [
59
+ ^({begin kwbegin} ...)
60
+ ({block numblock} (send !{nil? self} :each) _
61
+ (send (lvar _) {:<< :push :append} _))
62
+ ]
63
+ PATTERN
64
+
65
+ # @!method empty_array_asgn?(node)
66
+ def_node_matcher :empty_array_asgn?, '(lvasgn _ (array))'
67
+
68
+ # @!method lvar_ref?(node, name)
69
+ def_node_matcher :lvar_ref?, '(lvar %1)'
70
+
71
+ def self.joining_forces
72
+ VariableForce
73
+ end
74
+
75
+ def after_leaving_scope(scope, _variable_table)
76
+ (@scopes ||= []) << scope
77
+ end
78
+
79
+ def on_block(node)
80
+ return unless each_block_with_push?(node)
81
+
82
+ dest_var = find_dest_var(node)
83
+ return unless (asgn = find_closest_assignment(node, dest_var))
84
+ return unless empty_array_asgn?(asgn)
85
+ return unless dest_used_only_for_mapping?(node, dest_var, asgn)
86
+
87
+ register_offense(node, dest_var, asgn)
88
+ end
89
+
90
+ alias on_numblock on_block
91
+
92
+ private
93
+
94
+ def find_dest_var(block)
95
+ node = block.body.receiver
96
+ name = node.children.first
97
+
98
+ candidates = @scopes.lazy.filter_map { |s| s.variables[name] }
99
+ candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
100
+ end
101
+
102
+ def find_closest_assignment(block, dest_var)
103
+ dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
104
+ node.source_range.end_pos < block.source_range.begin_pos
105
+ end
106
+ end
107
+
108
+ def dest_used_only_for_mapping?(block, dest_var, asgn)
109
+ range = asgn.source_range.join(block.source_range)
110
+
111
+ asgn.parent.equal?(block.parent) &&
112
+ dest_var.references.one? { |r| range.contains?(r.node.source_range) } &&
113
+ dest_var.assignments.one? { |a| range.contains?(a.node.source_range) }
114
+ end
115
+
116
+ def register_offense(block, dest_var, asgn)
117
+ add_offense(block, message: format(MSG, new_method_name: new_method_name)) do |corrector|
118
+ next if return_value_used?(block)
119
+
120
+ corrector.replace(block.send_node.selector, new_method_name)
121
+ remove_assignment(corrector, asgn)
122
+ correct_push_node(corrector, block.body)
123
+ correct_return_value_handling(corrector, block, dest_var)
124
+ end
125
+ end
126
+
127
+ def new_method_name
128
+ default = 'map'
129
+ alternative = config.for_cop('Style/CollectionMethods').dig('PreferredMethods', default)
130
+ alternative || default
131
+ end
132
+
133
+ def return_value_used?(node)
134
+ parent = node.parent
135
+
136
+ case parent&.type
137
+ when nil
138
+ false
139
+ when :begin, :kwbegin
140
+ !node.right_sibling && return_value_used?(parent)
141
+ when :block, :numblock
142
+ !parent.void_context?
143
+ else
144
+ true
145
+ end
146
+ end
147
+
148
+ def remove_assignment(corrector, asgn)
149
+ range = range_with_surrounding_space(asgn.source_range, side: :right)
150
+ range = range_with_surrounding_space(range, side: :right, newlines: false)
151
+
152
+ corrector.remove(range)
153
+ end
154
+
155
+ def correct_push_node(corrector, push_node)
156
+ range = push_node.source_range
157
+ arg_range = push_node.first_argument.source_range
158
+
159
+ corrector.remove(range_between(range.begin_pos, arg_range.begin_pos))
160
+ corrector.remove(range_between(arg_range.end_pos, range.end_pos))
161
+ end
162
+
163
+ def correct_return_value_handling(corrector, block, dest_var)
164
+ next_node = block.right_sibling
165
+
166
+ if lvar_ref?(next_node, dest_var.name)
167
+ corrector.remove(range_with_surrounding_space(next_node.source_range, side: :left))
168
+ end
169
+
170
+ corrector.insert_before(block, "#{dest_var.name} = ")
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -34,37 +34,47 @@ module RuboCop
34
34
 
35
35
  minimum_target_ruby_version 2.6
36
36
 
37
- MSG = 'Pass a block to `to_h` instead of calling `%<method>s.to_h`.'
37
+ MSG = 'Pass a block to `to_h` instead of calling `%<method>s%<dot>sto_h`.'
38
38
  RESTRICT_ON_SEND = %i[to_h].freeze
39
39
 
40
- # @!method map_to_h?(node)
41
- def_node_matcher :map_to_h?, <<~PATTERN
40
+ # @!method map_to_h(node)
41
+ def_node_matcher :map_to_h, <<~PATTERN
42
42
  {
43
- $(send ({block numblock} $(send _ {:map :collect}) ...) :to_h)
44
- $(send $(send _ {:map :collect} (block_pass sym)) :to_h)
43
+ $(call ({block numblock} $(call _ {:map :collect}) ...) :to_h)
44
+ $(call $(call _ {:map :collect} (block_pass sym)) :to_h)
45
45
  }
46
46
  PATTERN
47
47
 
48
+ def self.autocorrect_incompatible_with
49
+ [Layout::SingleLineBlockChain]
50
+ end
51
+
48
52
  def on_send(node)
49
- return unless (to_h_node, map_node = map_to_h?(node))
53
+ return unless (to_h_node, map_node = map_to_h(node))
50
54
 
51
- message = format(MSG, method: map_node.loc.selector.source)
55
+ message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
52
56
  add_offense(map_node.loc.selector, message: message) do |corrector|
53
57
  # If the `to_h` call already has a block, do not autocorrect.
54
- next if to_h_node.block_node
58
+ next if to_h_node.block_literal?
55
59
 
56
60
  autocorrect(corrector, to_h_node, map_node)
57
61
  end
58
62
  end
63
+ alias on_csend on_send
59
64
 
60
65
  private
61
66
 
67
+ # rubocop:disable Metrics/AbcSize
62
68
  def autocorrect(corrector, to_h, map)
63
69
  removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
64
70
 
65
71
  corrector.remove(range_with_surrounding_space(removal_range, side: :left))
72
+ if (map_dot = map.loc.dot)
73
+ corrector.replace(map_dot, to_h.loc.dot.source)
74
+ end
66
75
  corrector.replace(map.loc.selector, 'to_h')
67
76
  end
77
+ # rubocop:enable Metrics/AbcSize
68
78
  end
69
79
  end
70
80
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
  message = format(MSG, method: map_node.loc.selector.source)
45
45
  add_offense(map_node.loc.selector, message: message) do |corrector|
46
46
  # If the `to_set` call already has a block, do not autocorrect.
47
- next if to_set_node.block_node
47
+ next if to_set_node.block_literal?
48
48
 
49
49
  autocorrect(corrector, to_set_node, map_node)
50
50
  end
@@ -18,6 +18,7 @@ module RuboCop
18
18
  return if inside_endless_method_def?(node)
19
19
  return if require_parentheses_for_hash_value_omission?(node)
20
20
  return if syntax_like_method_call?(node)
21
+ return if method_call_before_constant_resolution?(node)
21
22
  return if super_call_without_arguments?(node)
22
23
  return if legitimate_call_with_parentheses?(node)
23
24
  return if allowed_camel_case_method_call?(node)
@@ -63,6 +64,10 @@ module RuboCop
63
64
  node.implicit_call? || node.operator_method?
64
65
  end
65
66
 
67
+ def method_call_before_constant_resolution?(node)
68
+ node.parent&.const_type?
69
+ end
70
+
66
71
  def super_call_without_arguments?(node)
67
72
  node.super_type? && node.arguments.none?
68
73
  end
@@ -86,6 +91,7 @@ module RuboCop
86
91
 
87
92
  def legitimate_call_with_parentheses?(node) # rubocop:disable Metrics/PerceivedComplexity
88
93
  call_in_literals?(node) ||
94
+ node.parent&.when_type? ||
89
95
  call_with_ambiguous_arguments?(node) ||
90
96
  call_in_logical_operators?(node) ||
91
97
  call_in_optional_arguments?(node) ||
@@ -126,23 +132,31 @@ module RuboCop
126
132
 
127
133
  def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
128
134
  call_with_braced_block?(node) ||
135
+ call_in_argument_with_block?(node) ||
129
136
  call_as_argument_or_chain?(node) ||
130
137
  call_in_match_pattern?(node) ||
131
138
  hash_literal_in_arguments?(node) ||
132
139
  node.descendants.any? do |n|
133
- n.forwarded_args_type? || ambiguous_literal?(n) || logical_operator?(n) ||
134
- call_with_braced_block?(n)
140
+ n.forwarded_args_type? || n.block_type? || n.numblock_type? ||
141
+ ambiguous_literal?(n) || logical_operator?(n)
135
142
  end
136
143
  end
137
144
 
138
145
  def call_with_braced_block?(node)
139
- (node.send_type? || node.super_type?) && node.block_node&.braces?
146
+ (node.call_type? || node.super_type?) && node.block_node&.braces?
147
+ end
148
+
149
+ def call_in_argument_with_block?(node)
150
+ parent = node.parent&.block_type? && node.parent&.parent
151
+ return false unless parent
152
+
153
+ parent.call_type? || parent.super_type? || parent.yield_type?
140
154
  end
141
155
 
142
156
  def call_as_argument_or_chain?(node)
143
157
  node.parent &&
144
- ((node.parent.send_type? && !assigned_before?(node.parent, node)) ||
145
- node.parent.csend_type? || node.parent.super_type? || node.parent.yield_type?)
158
+ (node.parent.call_type? || node.parent.super_type? || node.parent.yield_type?) &&
159
+ !assigned_before?(node.parent, node)
146
160
  end
147
161
 
148
162
  def call_in_match_pattern?(node)
@@ -218,15 +218,13 @@ module RuboCop
218
218
  send(style, node) # call require_parentheses or omit_parentheses
219
219
  end
220
220
  alias on_csend on_send
221
- alias on_super on_send
222
221
  alias on_yield on_send
223
222
 
224
223
  private
225
224
 
226
225
  def args_begin(node)
227
226
  loc = node.loc
228
- selector =
229
- node.super_type? || node.yield_type? ? loc.keyword : loc.selector
227
+ selector = node.yield_type? ? loc.keyword : loc.selector
230
228
 
231
229
  resize_by = args_parenthesized?(node) ? 2 : 1
232
230
  selector.end.resize(resize_by)
@@ -239,7 +237,7 @@ module RuboCop
239
237
  def args_parenthesized?(node)
240
238
  return false unless node.arguments.one?
241
239
 
242
- first_node = node.arguments.first
240
+ first_node = node.first_argument
243
241
  first_node.begin_type? && first_node.parenthesized_call?
244
242
  end
245
243
  end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # This cop can be customized allowed methods with `AllowedMethods`.
9
9
  # By default, there are no methods to allowed.
10
10
  #
11
+ # NOTE: This cop allows the use of `it()` without arguments in blocks,
12
+ # as in `0.times { it() }`, following `Lint/ItWithoutArgumentsInBlock` cop.
13
+ #
11
14
  # @example
12
15
  # # bad
13
16
  # object.some_method()
@@ -30,15 +33,18 @@ module RuboCop
30
33
 
31
34
  MSG = 'Do not use parentheses for method calls with no arguments.'
32
35
 
36
+ # rubocop:disable Metrics/CyclomaticComplexity
33
37
  def on_send(node)
34
38
  return unless !node.arguments? && node.parenthesized?
35
39
  return if ineligible_node?(node)
36
40
  return if default_argument?(node)
37
41
  return if allowed_method_name?(node.method_name)
38
42
  return if same_name_assignment?(node)
43
+ return if parenthesized_it_method_in_block?(node)
39
44
 
40
45
  register_offense(node)
41
46
  end
47
+ # rubocop:enable Metrics/CyclomaticComplexity
42
48
 
43
49
  private
44
50
 
@@ -71,6 +77,20 @@ module RuboCop
71
77
  end
72
78
  end
73
79
 
80
+ # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
81
+ #
82
+ # $ ruby -e '0.times { begin; it; end }'
83
+ # -e:1: warning: `it` calls without arguments will refer to the first block param in
84
+ # Ruby 3.4; use it() or self.it
85
+ #
86
+ def parenthesized_it_method_in_block?(node)
87
+ return false unless node.method?(:it)
88
+ return false unless (block_node = node.each_ancestor(:block).first)
89
+ return false unless block_node.arguments.empty_and_without_delimiters?
90
+
91
+ !node.receiver && node.arguments.empty? && !node.block_literal?
92
+ end
93
+
74
94
  def any_assignment?(node)
75
95
  node.each_ancestor(*AST::Node::ASSIGNMENTS).any? do |asgn_node|
76
96
  # `obj.method = value` parses as (send ... :method= ...), and will
@@ -170,7 +170,7 @@ module RuboCop
170
170
  return true if node.arguments.any? do |arg|
171
171
  arg.forward_arg_type? || arg.restarg_type? || arg.kwrestarg_type?
172
172
  end
173
- return false unless (last_argument = node.arguments.last)
173
+ return false unless (last_argument = node.last_argument)
174
174
 
175
175
  last_argument.blockarg_type? && last_argument.name.nil?
176
176
  end
@@ -7,12 +7,12 @@ module RuboCop
7
7
  # defining `respond_to_missing?`.
8
8
  #
9
9
  # @example
10
- # #bad
10
+ # # bad
11
11
  # def method_missing(name, *args)
12
12
  # # ...
13
13
  # end
14
14
  #
15
- # #good
15
+ # # good
16
16
  # def respond_to_missing?(name, include_private)
17
17
  # # ...
18
18
  # end
@@ -38,6 +38,7 @@ module RuboCop
38
38
 
39
39
  private
40
40
 
41
+ # rubocop:disable Metrics/AbcSize
41
42
  def autocorrect(corrector, node, begin_of_arguments)
42
43
  arguments = node.arguments
43
44
  joined_arguments = arguments.map(&:source).join(', ')
@@ -49,9 +50,17 @@ module RuboCop
49
50
  corrector.remove(range_by_whole_lines(arguments.loc.end, include_final_newline: true))
50
51
  end
51
52
 
52
- corrector.remove(arguments_range(node))
53
+ arguments_range = arguments_range(node)
54
+ # If the method name isn't on the same line as def, move it directly after def
55
+ if arguments_range.first_line != opening_line(node)
56
+ corrector.remove(node.loc.name)
57
+ corrector.insert_after(node.loc.keyword, " #{node.loc.name.source}")
58
+ end
59
+
60
+ corrector.remove(arguments_range)
53
61
  corrector.insert_after(begin_of_arguments, joined_arguments)
54
62
  end
63
+ # rubocop:enable Metrics/AbcSize
55
64
 
56
65
  def last_line_source_of_arguments(arguments)
57
66
  processed_source[arguments.last_line - 1].strip
@@ -47,19 +47,21 @@ module RuboCop
47
47
  message = enforce_single_line_ternary_operator?(node) ? MSG_SINGLE_LINE : MSG_IF
48
48
 
49
49
  add_offense(node, message: message) do |corrector|
50
+ next if part_of_ignored_node?(node)
51
+
50
52
  autocorrect(corrector, node)
53
+
54
+ ignore_node(node)
51
55
  end
52
56
  end
53
57
 
54
58
  private
55
59
 
56
60
  def offense?(node)
57
- node.ternary? && node.multiline?
61
+ node.ternary? && node.multiline? && node.source != replacement(node)
58
62
  end
59
63
 
60
64
  def autocorrect(corrector, node)
61
- return unless offense?(node)
62
-
63
65
  corrector.replace(node, replacement(node))
64
66
  return unless (parent = node.parent)
65
67
  return unless (comments_in_condition = comments_in_condition(node))
@@ -66,7 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def on_block(node)
69
- return unless node.send_node.send_type? && node.send_node.enumerator_method?
69
+ return unless node.send_node.call_type? && node.send_node.enumerator_method?
70
70
 
71
71
  check(node)
72
72
  end
@@ -44,6 +44,8 @@ module RuboCop
44
44
  def_node_matcher :nil_check?, '(send _ :nil?)'
45
45
 
46
46
  def on_send(node)
47
+ return unless node.receiver
48
+
47
49
  style_check?(node) do
48
50
  add_offense(node.loc.selector) do |corrector|
49
51
  new_code = if prefer_comparison?
@@ -62,7 +62,7 @@ module RuboCop
62
62
  private
63
63
 
64
64
  def message(node)
65
- self.class.const_get("#{literal_type(node).upcase}_MSG")
65
+ self.class.const_get(:"#{literal_type(node).upcase}_MSG")
66
66
  end
67
67
 
68
68
  def literal_type(node)
@@ -118,12 +118,14 @@ module RuboCop
118
118
 
119
119
  return unless numeric && operator && replacement_supported?(operator)
120
120
 
121
- [numeric, replacement(numeric, operator)]
121
+ [numeric, replacement(node, numeric, operator)]
122
122
  end
123
123
 
124
- def replacement(numeric, operation)
124
+ def replacement(node, numeric, operation)
125
125
  if style == :predicate
126
126
  [parenthesized_source(numeric), REPLACEMENTS.invert[operation.to_s]].join('.')
127
+ elsif negated?(node)
128
+ "(#{numeric.source} #{REPLACEMENTS[operation.to_s]} 0)"
127
129
  else
128
130
  [numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ')
129
131
  end
@@ -157,6 +159,12 @@ module RuboCop
157
159
  end
158
160
  end
159
161
 
162
+ def negated?(node)
163
+ return false unless (parent = node.parent)
164
+
165
+ parent.send_type? && parent.method?(:!)
166
+ end
167
+
160
168
  # @!method predicate(node)
161
169
  def_node_matcher :predicate, <<~PATTERN
162
170
  (send $(...) ${:zero? :positive? :negative?})
@@ -46,15 +46,17 @@ module RuboCop
46
46
  private
47
47
 
48
48
  def check_method_node(node)
49
- return unless preferred_method(node)
49
+ return unless preferred_method?(node)
50
50
 
51
51
  message = message(node)
52
52
  add_offense(node.loc.selector, message: message) do |corrector|
53
- corrector.replace(node.loc.selector, style.to_s)
53
+ prefer = style == :then && node.receiver.nil? ? 'self.then' : style
54
+
55
+ corrector.replace(node.loc.selector, prefer)
54
56
  end
55
57
  end
56
58
 
57
- def preferred_method(node)
59
+ def preferred_method?(node)
58
60
  case style
59
61
  when :then
60
62
  node.method?(:yield_self)
@@ -75,7 +75,7 @@ module RuboCop
75
75
  end
76
76
 
77
77
  def always_multiline?
78
- @config.for_cop('Style/OneLineConditional')['AlwaysCorrectToMultiline']
78
+ cop_config['AlwaysCorrectToMultiline']
79
79
  end
80
80
 
81
81
  def cannot_replace_to_ternary?(node)
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for redundant dot before operator method call.
7
- # The target operator methods are `|`, `^`, `&`, `<=>`, `==`, `===`, `=~`, `>`, `>=`, `<`,
8
- # `<=`, `<<`, `>>`, `+`, `-`, `*`, `/`, `%`, `**`, `~`, `!`, `!=`, and `!~`.
7
+ # The target operator methods are `|`, `^`, `&`, ``<=>``, `==`, `===`, `=~`, `>`, `>=`, `<`,
8
+ # ``<=``, `<<`, `>>`, `+`, `-`, `*`, `/`, `%`, `**`, `~`, `!`, `!=`, and `!~`.
9
9
  #
10
10
  # @example
11
11
  #
@@ -142,8 +142,8 @@ module RuboCop
142
142
  @assignments = assignments
143
143
  end
144
144
 
145
- def tsort_each_node(&block)
146
- @assignments.each(&block)
145
+ def tsort_each_node(...)
146
+ @assignments.each(...)
147
147
  end
148
148
 
149
149
  def tsort_each_child(assignment)
@@ -289,9 +289,7 @@ module RuboCop
289
289
  private
290
290
 
291
291
  def modifier_range(node)
292
- Parser::Source::Range.new(node.source_range.source_buffer,
293
- node.loc.keyword.begin_pos,
294
- node.source_range.end_pos)
292
+ node.loc.keyword.join(node.source_range.end)
295
293
  end
296
294
  end
297
295
  end
@@ -81,6 +81,7 @@ module RuboCop
81
81
  cond = node.condition
82
82
 
83
83
  control_op_condition(cond) do |first_child, rest_children|
84
+ return if require_parentheses?(node, first_child)
84
85
  return if semicolon_separated_expressions?(first_child, rest_children)
85
86
  return if modifier_op?(first_child)
86
87
  return if parens_allowed?(cond)
@@ -92,6 +93,13 @@ module RuboCop
92
93
  end
93
94
  end
94
95
 
96
+ def require_parentheses?(node, condition_body)
97
+ return false if !node.while_type? && !node.until_type?
98
+ return false if !condition_body.block_type? && !condition_body.numblock_type?
99
+
100
+ condition_body.send_node.block_literal? && condition_body.keywords?
101
+ end
102
+
95
103
  def semicolon_separated_expressions?(first_exp, rest_exps)
96
104
  return false unless (second_exp = rest_exps.first)
97
105
 
@@ -9,7 +9,7 @@ module RuboCop
9
9
  #
10
10
  # String interpolation is always kept in double quotes.
11
11
  #
12
- # Note: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
12
+ # NOTE: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
13
13
  # are not quoted that don't need to be. This cop is for configuring the quoting
14
14
  # style to use for symbols that require quotes.
15
15
  #
@@ -16,6 +16,9 @@ module RuboCop
16
16
  # The exploded style has an `AllowedCompactTypes` configuration
17
17
  # option that takes an Array of exception name Strings.
18
18
  #
19
+ # @safety
20
+ # This cop is unsafe because `raise Foo` calls `Foo.exception`, not `Foo.new`.
21
+ #
19
22
  # @example EnforcedStyle: exploded (default)
20
23
  # # bad
21
24
  # raise StandardError.new('message')
@@ -77,7 +80,7 @@ module RuboCop
77
80
 
78
81
  def correction_exploded_to_compact(node)
79
82
  exception_node, *message_nodes = *node.arguments
80
- return node.source if message_nodes.size > 1
83
+ return if message_nodes.size > 1
81
84
 
82
85
  argument = message_nodes.first.source
83
86
  exception_class = exception_node.receiver&.source || exception_node.source