rubocop 1.77.0 → 1.79.1

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -3
  3. data/config/default.yml +36 -20
  4. data/lib/rubocop/cli.rb +12 -1
  5. data/lib/rubocop/config_loader.rb +1 -38
  6. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  7. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  8. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  9. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
  10. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  11. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  12. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  13. data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -1
  14. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  15. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  16. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  17. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  18. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  19. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  20. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  21. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  22. data/lib/rubocop/cop/naming/predicate_method.rb +27 -2
  23. data/lib/rubocop/cop/security/eval.rb +2 -1
  24. data/lib/rubocop/cop/security/open.rb +1 -0
  25. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  26. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  27. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  28. data/lib/rubocop/cop/style/array_intersect.rb +53 -23
  29. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  30. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  31. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  32. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  33. data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
  34. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  35. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  36. data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
  37. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  38. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  39. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  40. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  41. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  42. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  43. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -0
  44. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  45. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  46. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  47. data/lib/rubocop/cop/variable_force.rb +18 -7
  48. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  49. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  50. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  51. data/lib/rubocop/lsp/routes.rb +4 -4
  52. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  53. data/lib/rubocop/server/cache.rb +4 -2
  54. data/lib/rubocop/server/client_command/base.rb +10 -0
  55. data/lib/rubocop/server/client_command/exec.rb +2 -1
  56. data/lib/rubocop/server/client_command/start.rb +11 -1
  57. data/lib/rubocop/version.rb +1 -1
  58. data/lib/rubocop.rb +3 -0
  59. metadata +13 -7
@@ -146,7 +146,6 @@ module RuboCop
146
146
  minimum_target_ruby_version 2.7
147
147
 
148
148
  FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
149
- ADDITIONAL_ARG_TYPES = %i[lvar arg optarg].freeze
150
149
 
151
150
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
152
151
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
@@ -198,9 +197,9 @@ module RuboCop
198
197
  send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
199
198
  end
200
199
 
201
- # rubocop:disable Metrics/MethodLength
200
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
202
201
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
203
- _rest_arg, _kwrest_arg, block_arg = *forwardable_args
202
+ rest_arg, kwrest_arg, block_arg = *forwardable_args
204
203
  registered_block_arg_offense = false
205
204
 
206
205
  send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
@@ -212,16 +211,20 @@ module RuboCop
212
211
  registered_block_arg_offense = true
213
212
  break
214
213
  else
215
- register_forward_all_offense(send_node, send_node, forward_rest)
214
+ first_arg = forward_rest || forward_kwrest || forward_all_first_argument(send_node)
215
+ register_forward_all_offense(send_node, send_node, first_arg)
216
216
  end
217
217
  end
218
218
 
219
219
  return if registered_block_arg_offense
220
220
 
221
- rest_arg, _kwrest_arg, _block_arg = *forwardable_args
222
- register_forward_all_offense(node, node.arguments, rest_arg)
221
+ register_forward_all_offense(node, node.arguments, rest_arg || kwrest_arg)
222
+ end
223
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
224
+
225
+ def forward_all_first_argument(node)
226
+ node.arguments.reverse_each.find(&:forwarded_restarg_type?)
223
227
  end
224
- # rubocop:enable Metrics/MethodLength
225
228
 
226
229
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
227
230
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
@@ -361,18 +364,9 @@ module RuboCop
361
364
  end
362
365
  end
363
366
 
364
- # rubocop:disable Metrics/AbcSize
365
367
  def arguments_range(node, first_node)
366
- arguments = node.arguments.reject do |arg|
367
- next true if ADDITIONAL_ARG_TYPES.include?(arg.type) || arg.variable? || arg.call_type?
368
-
369
- arg.literal? && arg.each_descendant(:kwsplat).none?
370
- end
371
-
372
- start_node = first_node || arguments.first
373
- start_node.source_range.begin.join(arguments.last.source_range.end)
368
+ first_node.source_range.begin.join(node.last_argument.source_range.end)
374
369
  end
375
- # rubocop:enable Metrics/AbcSize
376
370
 
377
371
  def allow_only_rest_arguments?
378
372
  cop_config.fetch('AllowOnlyRestArgument', true)
@@ -5,12 +5,15 @@ module RuboCop
5
5
  module Style
6
6
  # In Ruby 3.1, `Array#intersect?` has been added.
7
7
  #
8
- # This cop identifies places where `(array1 & array2).any?`
9
- # or `(array1.intersection(array2)).any?` can be replaced by
10
- # `array1.intersect?(array2)`.
8
+ # This cop identifies places where:
11
9
  #
12
- # The `array1.intersect?(array2)` method is faster than
13
- # `(array1 & array2).any?` and is more readable.
10
+ # * `(array1 & array2).any?`
11
+ # * `(array1.intersection(array2)).any?`
12
+ # * `array1.any? { |elem| array2.member?(elem) }`
13
+ #
14
+ # can be replaced with `array1.intersect?(array2)`.
15
+ #
16
+ # `array1.intersect?(array2)` is faster and more readable.
14
17
  #
15
18
  # In cases like the following, compatibility is not ensured,
16
19
  # so it will not be detected when using block argument.
@@ -40,6 +43,10 @@ module RuboCop
40
43
  # array1.intersection(array2).empty?
41
44
  # array1.intersection(array2).none?
42
45
  #
46
+ # # bad
47
+ # array1.any? { |elem| array2.member?(elem) }
48
+ # array1.none? { |elem| array2.member?(elem) }
49
+ #
43
50
  # # good
44
51
  # array1.intersect?(array2)
45
52
  # !array1.intersect?(array2)
@@ -77,8 +84,26 @@ module RuboCop
77
84
  )
78
85
  PATTERN
79
86
 
80
- MSG = 'Use `%<negated>s%<receiver>s%<dot>sintersect?(%<argument>s)` ' \
81
- 'instead of `%<existing>s`.'
87
+ # @!method any_none_block_intersection(node)
88
+ def_node_matcher :any_none_block_intersection, <<~PATTERN
89
+ {
90
+ (block
91
+ (call $_receiver ${:any? :none?})
92
+ (args (arg _key))
93
+ (send $_argument :member? (lvar _key))
94
+ )
95
+ (numblock
96
+ (call $_receiver ${:any? :none?}) 1
97
+ (send $_argument :member? (lvar :_1))
98
+ )
99
+ (itblock
100
+ (call $_receiver ${:any? :none?}) :it
101
+ (send $_argument :member? (lvar :it))
102
+ )
103
+ }
104
+ PATTERN
105
+
106
+ MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
82
107
  STRAIGHT_METHODS = %i[present? any?].freeze
83
108
  NEGATED_METHODS = %i[blank? empty? none?].freeze
84
109
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
@@ -88,16 +113,25 @@ module RuboCop
88
113
  return unless (receiver, argument, method_name = bad_intersection?(node))
89
114
 
90
115
  dot = node.loc.dot.source
91
- message = message(receiver.source, argument.source, method_name, dot, node.source)
92
-
93
- add_offense(node, message: message) do |corrector|
94
- bang = straight?(method_name) ? '' : '!'
116
+ bang = straight?(method_name) ? '' : '!'
117
+ replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
95
118
 
96
- corrector.replace(node, "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})")
97
- end
119
+ register_offense(node, replacement)
98
120
  end
99
121
  alias on_csend on_send
100
122
 
123
+ def on_block(node)
124
+ return unless (receiver, method_name, argument = any_none_block_intersection(node))
125
+
126
+ dot = node.send_node.loc.dot.source
127
+ bang = method_name == :any? ? '' : '!'
128
+ replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
129
+
130
+ register_offense(node, replacement)
131
+ end
132
+ alias on_numblock on_block
133
+ alias on_itblock on_block
134
+
101
135
  private
102
136
 
103
137
  def bad_intersection?(node)
@@ -114,16 +148,12 @@ module RuboCop
114
148
  STRAIGHT_METHODS.include?(method_name.to_sym)
115
149
  end
116
150
 
117
- def message(receiver, argument, method_name, dot, existing)
118
- negated = straight?(method_name) ? '' : '!'
119
- format(
120
- MSG,
121
- negated: negated,
122
- receiver: receiver,
123
- argument: argument,
124
- dot: dot,
125
- existing: existing
126
- )
151
+ def register_offense(node, replacement)
152
+ message = format(MSG, replacement: replacement, existing: node.source)
153
+
154
+ add_offense(node, message: message) do |corrector|
155
+ corrector.replace(node, replacement)
156
+ end
127
157
  end
128
158
  end
129
159
  end
@@ -4,7 +4,7 @@
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Style
7
- # Check for uses of braces or do/end around single line or
7
+ # Checks for uses of braces or do/end around single line or
8
8
  # multi-line blocks.
9
9
  #
10
10
  # Methods that can be either procedural or functional and cannot be
@@ -111,7 +111,7 @@ module RuboCop
111
111
  end
112
112
  end
113
113
 
114
- # Check for `if` and `case` statements where each branch is used for
114
+ # Checks for `if` and `case` statements where each branch is used for
115
115
  # both the assignment and comparison of the same variable
116
116
  # when using the return of the condition can be used instead.
117
117
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for chained `dig` calls that can be collapsed into a single `dig`.
6
+ # Checks for chained `dig` calls that can be collapsed into a single `dig`.
7
7
  #
8
8
  # @safety
9
9
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -59,6 +59,7 @@ module RuboCop
59
59
  #
60
60
  class ExponentialNotation < Base
61
61
  include ConfigurableEnforcedStyle
62
+
62
63
  MESSAGES = {
63
64
  scientific: 'Use a mantissa >= 1 and < 10.',
64
65
  engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',
@@ -44,10 +44,10 @@ module RuboCop
44
44
  class HashConversion < Base
45
45
  extend AutoCorrector
46
46
 
47
- MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
48
- MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to Hash[arg1, arg2, ...].'
49
- MSG_LITERAL_HASH_ARG = 'Prefer literal hash to Hash[key: value, ...].'
50
- MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
47
+ MSG_TO_H = 'Prefer `ary.to_h` to `Hash[ary]`.'
48
+ MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to `Hash[arg1, arg2, ...]`.'
49
+ MSG_LITERAL_HASH_ARG = 'Prefer literal hash to `Hash[key: value, ...]`.'
50
+ MSG_SPLAT = 'Prefer `array_of_pairs.to_h` to `Hash[*array]`.'
51
51
  RESTRICT_ON_SEND = %i[[]].freeze
52
52
 
53
53
  # @!method hash_from_array?(node)
@@ -64,11 +64,11 @@ module RuboCop
64
64
  # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
65
65
  # ...but don't suggest correction if there is odd number of them (it is a bug)
66
66
  node.arguments.one? ? single_argument(node) : multi_argument(node)
67
+ ignore_node(node)
67
68
  end
68
69
 
69
70
  private
70
71
 
71
- # rubocop:disable Metrics/MethodLength
72
72
  def single_argument(node)
73
73
  first_argument = node.first_argument
74
74
  if first_argument.hash_type?
@@ -83,11 +83,8 @@ module RuboCop
83
83
  replacement = "(#{replacement})" if requires_parens?(first_argument)
84
84
  corrector.replace(node, "#{replacement}.to_h")
85
85
  end
86
-
87
- ignore_node(node)
88
86
  end
89
87
  end
90
- # rubocop:enable Metrics/MethodLength
91
88
 
92
89
  def use_zip_method_without_argument?(first_argument)
93
90
  return false unless first_argument&.send_type?
@@ -131,7 +128,9 @@ module RuboCop
131
128
  corrector.replace(node, args_to_hash(node.arguments))
132
129
 
133
130
  parent = node.parent
134
- add_parentheses(parent, corrector) if parent&.send_type? && !parent.parenthesized?
131
+ if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
132
+ add_parentheses(parent, corrector)
133
+ end
135
134
  end
136
135
  end
137
136
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for usages of not (`not` or `!`) called on a method
6
+ # Checks for usages of not (`not` or `!`) called on a method
7
7
  # when an inverse of that method can be used instead.
8
8
  #
9
9
  # Methods that can be inverted by a not (`not` or `!`) should be defined
@@ -3,33 +3,90 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for assignments to a local `it` variable inside a block
6
+ # Checks for local variables and method parameters named `it`,
7
7
  # where `it` can refer to the first anonymous parameter as of Ruby 3.4.
8
+ # Use a meaningful variable name instead.
8
9
  #
9
- # Although Ruby allows reassigning `it` in these cases, it could
10
+ # NOTE: Although Ruby allows reassigning `it` in these cases, it could
10
11
  # cause confusion if `it` is used as a block parameter elsewhere.
11
- # For consistency, this also applies to numblocks and blocks with
12
- # parameters, even though `it` cannot be used in those cases.
13
12
  #
14
13
  # @example
15
14
  # # bad
16
- # foo { it = 5 }
17
- # foo { |bar| it = bar }
18
- # foo { it = _2 }
15
+ # it = 5
19
16
  #
20
- # # good - use a different variable name
21
- # foo { var = 5 }
22
- # foo { |bar| var = bar }
23
- # foo { bar = _2 }
17
+ # # good
18
+ # var = 5
19
+ #
20
+ # # bad
21
+ # def foo(it)
22
+ # end
23
+ #
24
+ # # good
25
+ # def foo(arg)
26
+ # end
27
+ #
28
+ # # bad
29
+ # def foo(it = 5)
30
+ # end
31
+ #
32
+ # # good
33
+ # def foo(arg = 5)
34
+ # end
35
+ #
36
+ # # bad
37
+ # def foo(*it)
38
+ # end
39
+ #
40
+ # # good
41
+ # def foo(*args)
42
+ # end
43
+ #
44
+ # # bad
45
+ # def foo(it:)
46
+ # end
47
+ #
48
+ # # good
49
+ # def foo(arg:)
50
+ # end
51
+ #
52
+ # # bad
53
+ # def foo(it: 5)
54
+ # end
55
+ #
56
+ # # good
57
+ # def foo(arg: 5)
58
+ # end
59
+ #
60
+ # # bad
61
+ # def foo(**it)
62
+ # end
63
+ #
64
+ # # good
65
+ # def foo(**kwargs)
66
+ # end
67
+ #
68
+ # # bad
69
+ # def foo(&it)
70
+ # end
71
+ #
72
+ # # good
73
+ # def foo(&block)
74
+ # end
24
75
  class ItAssignment < Base
25
76
  MSG = '`it` is the default block parameter; consider another name.'
26
77
 
27
78
  def on_lvasgn(node)
28
79
  return unless node.name == :it
29
- return unless node.each_ancestor(:any_block).any?
30
80
 
31
81
  add_offense(node.loc.name)
32
82
  end
83
+ alias on_arg on_lvasgn
84
+ alias on_optarg on_lvasgn
85
+ alias on_restarg on_lvasgn
86
+ alias on_blockarg on_lvasgn
87
+ alias on_kwarg on_lvasgn
88
+ alias on_kwoptarg on_lvasgn
89
+ alias on_kwrestarg on_lvasgn
33
90
  end
34
91
  end
35
92
  end
@@ -109,7 +109,9 @@ module RuboCop
109
109
  private
110
110
 
111
111
  def find_block_variables(node, block_argument_name)
112
- node.each_descendant(:lvar).select do |descendant|
112
+ return [] unless node.body
113
+
114
+ node.body.each_descendant(:lvar).select do |descendant|
113
115
  descendant.source == block_argument_name
114
116
  end
115
117
  end
@@ -222,11 +222,9 @@ module RuboCop
222
222
  end
223
223
 
224
224
  def unary_literal?(node)
225
- # NOTE: should be removed after releasing https://github.com/rubocop/rubocop-ast/pull/379
226
- return node.source.match?(/\A[+-]/) if node.complex_type?
225
+ return true if node.numeric_type? && node.sign?
227
226
 
228
- (node.numeric_type? && node.sign?) ||
229
- (node.parent&.send_type? && node.parent.unary_operation?)
227
+ node.parent&.send_type? && node.parent.unary_operation?
230
228
  end
231
229
 
232
230
  def assigned_before?(node, target)
@@ -132,6 +132,22 @@ module RuboCop
132
132
  # bar :baz
133
133
  # end
134
134
  #
135
+ # @example AllowedMethods: ["puts", "print"]
136
+ #
137
+ # # good
138
+ # puts "Hello world"
139
+ # print "Hello world"
140
+ # # still enforces parentheses on other methods
141
+ # array.delete(e)
142
+ #
143
+ # @example AllowedPatterns: ["^assert"]
144
+ #
145
+ # # good
146
+ # assert_equal 'test', x
147
+ # assert_match(/foo/, bar)
148
+ # # still enforces parentheses on other methods
149
+ # array.delete(e)
150
+ #
135
151
  # @example AllowParenthesesInMultilineCall: false (default)
136
152
  #
137
153
  # # bad
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tsort'
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -29,6 +27,8 @@ module RuboCop
29
27
  MSG = 'Do not use parallel assignment.'
30
28
 
31
29
  def on_masgn(node) # rubocop:disable Metrics/AbcSize
30
+ return if part_of_ignored_node?(node)
31
+
32
32
  rhs = node.rhs
33
33
  rhs = rhs.body if rhs.rescue_type?
34
34
  rhs_elements = Array(rhs).compact # edge case for one constant
@@ -41,6 +41,7 @@ module RuboCop
41
41
  add_offense(range) do |corrector|
42
42
  autocorrect(corrector, node, rhs)
43
43
  end
44
+ ignore_node(node)
44
45
  end
45
46
 
46
47
  private
@@ -91,15 +92,9 @@ module RuboCop
91
92
  def find_valid_order(left_elements, right_elements)
92
93
  # arrange left_elements in an order such that no corresponding right
93
94
  # element refers to a left element earlier in the sequence
94
- # this can be done using an algorithm called a "topological sort"
95
- # fortunately for us, Ruby's stdlib contains an implementation
96
95
  assignments = left_elements.zip(right_elements)
97
96
 
98
- begin
99
- AssignmentSorter.new(assignments).tsort
100
- rescue TSort::Cyclic
101
- nil
102
- end
97
+ AssignmentSorter.new(assignments).tsort
103
98
  end
104
99
 
105
100
  # Converts (send nil :something) nodes to (send (:self) :something).
@@ -114,10 +109,9 @@ module RuboCop
114
109
  # @!method implicit_self_getter?(node)
115
110
  def_node_matcher :implicit_self_getter?, '(send nil? $_)'
116
111
 
117
- # Helper class necessitated by silly design of TSort prior to Ruby 2.1
118
- # Newer versions have a better API, but that doesn't help us
112
+ # Topologically sorts the assignments with Kahn's algorithm.
113
+ # https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
119
114
  class AssignmentSorter
120
- include TSort
121
115
  extend RuboCop::NodePattern::Macros
122
116
 
123
117
  # @!method var_name(node)
@@ -133,21 +127,39 @@ module RuboCop
133
127
  @assignments = assignments
134
128
  end
135
129
 
136
- def tsort_each_node(...)
137
- @assignments.each(...)
130
+ def tsort
131
+ dependencies = @assignments.to_h do |assignment|
132
+ [assignment, dependencies_for_assignment(assignment)]
133
+ end
134
+ result = []
135
+
136
+ while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
137
+ dependencies.delete(matched_node)
138
+ result.push(matched_node)
139
+
140
+ dependencies.each do |node, edges|
141
+ dependencies[node].delete(matched_node) if edges.include?(matched_node)
142
+ end
143
+ end
144
+ # Cyclic dependency
145
+ return nil if dependencies.any?
146
+
147
+ result
138
148
  end
139
149
 
140
- def tsort_each_child(assignment)
141
- # yield all the assignments which must come after `assignment`
142
- # (due to dependencies on the previous value of the assigned var)
150
+ # Returns all the assignments which must come after `assignment`
151
+ # (due to dependencies on the previous value of the assigned var)
152
+ def dependencies_for_assignment(assignment)
143
153
  my_lhs, _my_rhs = *assignment
144
154
 
145
- @assignments.each do |other|
146
- _other_lhs, other_rhs = *other
155
+ @assignments.filter_map do |other|
156
+ # Exclude self, there are no dependencies in cases such as `a, b = a, b`.
157
+ next if other == assignment
147
158
 
159
+ _other_lhs, other_rhs = *other
148
160
  next unless dependency?(my_lhs, other_rhs)
149
161
 
150
- yield other
162
+ other
151
163
  end
152
164
  end
153
165
 
@@ -49,7 +49,7 @@ module RuboCop
49
49
  (block
50
50
  $(call _ :fetch _)
51
51
  (args)
52
- ${nil? #basic_literal? #const_type?})
52
+ ${nil? basic_literal? const_type?})
53
53
  PATTERN
54
54
 
55
55
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
@@ -71,14 +71,6 @@ module RuboCop
71
71
 
72
72
  private
73
73
 
74
- def basic_literal?(node)
75
- node&.basic_literal?
76
- end
77
-
78
- def const_type?(node)
79
- node&.const_type?
80
- end
81
-
82
74
  def should_not_check?(send, body)
83
75
  (body&.const_type? && !check_for_constant?) ||
84
76
  (body&.str_type? && !check_for_string?) ||
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for uses of `Object#freeze` on immutable objects.
6
+ # Checks for uses of `Object#freeze` on immutable objects.
7
7
  #
8
8
  # NOTE: `Regexp` and `Range` literals are frozen objects since Ruby 3.0.
9
9
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for redundant line continuation.
6
+ # Checks for redundant line continuation.
7
7
  #
8
8
  # This cop marks a line continuation as redundant if removing the backslash
9
9
  # does not result in a syntax error.
@@ -169,6 +169,7 @@ module RuboCop
169
169
  end
170
170
  return 'an interpolated expression' if interpolation?(begin_node)
171
171
  return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
172
+ return 'a one-line rescue' if oneline_rescue_parentheses_required?(begin_node, node)
172
173
 
173
174
  return if begin_node.chained?
174
175
 
@@ -200,6 +201,14 @@ module RuboCop
200
201
  parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
201
202
  end
202
203
 
204
+ def oneline_rescue_parentheses_required?(begin_node, node)
205
+ return false unless node.rescue_type?
206
+ return false unless (parent = begin_node.parent)
207
+ return false if parent.if_type? && parent.ternary?
208
+
209
+ !parent.type?(:call, :array, :pair)
210
+ end
211
+
203
212
  def method_call_parentheses_required?(node)
204
213
  return false unless node.call_type?
205
214
 
@@ -65,7 +65,7 @@ module RuboCop
65
65
  return false if target_ruby_version < 3.0
66
66
  return false if disallow_endless_method_style?
67
67
  return false unless body_node
68
- return false if body_node.parent.assignment_method? ||
68
+ return false if body_node.basic_conditional? || body_node.parent.assignment_method? ||
69
69
  NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES.include?(body_node.type)
70
70
 
71
71
  !body_node.type?(:begin, :kwbegin)
@@ -86,10 +86,10 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def correct_to_endless(corrector, node)
89
- self_receiver = node.self_receiver? ? 'self.' : ''
89
+ receiver = "#{node.receiver.source}." if node.receiver
90
90
  arguments = node.arguments.any? ? node.arguments.source : '()'
91
91
  body_source = method_body_source(node.body)
92
- replacement = "def #{self_receiver}#{node.method_name}#{arguments} = #{body_source}"
92
+ replacement = "def #{receiver}#{node.method_name}#{arguments} = #{body_source}"
93
93
 
94
94
  corrector.replace(node, replacement)
95
95
  end
@@ -130,7 +130,10 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def require_parentheses?(method_body)
133
- method_body.send_type? && !method_body.arguments.empty? && !method_body.comparison_method?
133
+ return false unless method_body.send_type?
134
+ return false if method_body.arithmetic_operation?
135
+
136
+ !method_body.arguments.empty? && !method_body.comparison_method?
134
137
  end
135
138
 
136
139
  def disallow_endless_method_style?