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.
- checksums.yaml +4 -4
- data/README.md +1 -3
- data/config/default.yml +36 -20
- data/lib/rubocop/cli.rb +12 -1
- data/lib/rubocop/config_loader.rb +1 -38
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
- data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -1
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/naming/method_name.rb +127 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +27 -2
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
- data/lib/rubocop/cop/style/array_intersect.rb +53 -23
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +18 -7
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +4 -4
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/server/cache.rb +4 -2
- data/lib/rubocop/server/client_command/base.rb +10 -0
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/server/client_command/start.rb +11 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
222
|
-
|
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
|
-
|
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
|
9
|
-
# or `(array1.intersection(array2)).any?` can be replaced by
|
10
|
-
# `array1.intersect?(array2)`.
|
8
|
+
# This cop identifies places where:
|
11
9
|
#
|
12
|
-
#
|
13
|
-
# `(array1
|
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
|
-
|
81
|
-
|
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
|
-
|
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
|
-
|
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
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
@@ -111,7 +111,7 @@ module RuboCop
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
-
#
|
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
|
-
#
|
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
|
@@ -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
|
-
|
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
|
-
#
|
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
|
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
|
-
#
|
17
|
-
# foo { |bar| it = bar }
|
18
|
-
# foo { it = _2 }
|
15
|
+
# it = 5
|
19
16
|
#
|
20
|
-
# # good
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
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.
|
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
|
-
|
226
|
-
return node.source.match?(/\A[+-]/) if node.complex_type?
|
225
|
+
return true if node.numeric_type? && node.sign?
|
227
226
|
|
228
|
-
|
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
|
-
|
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
|
-
#
|
118
|
-
#
|
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
|
137
|
-
@assignments.
|
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
|
-
|
141
|
-
|
142
|
-
|
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.
|
146
|
-
|
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
|
-
|
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?
|
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?) ||
|
@@ -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
|
-
|
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 #{
|
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
|
-
|
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?
|