rubocop 1.92.0 → 1.92.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/config/default.yml +1 -0
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +16 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/class_structure.rb +21 -7
- data/lib/rubocop/cop/layout/dot_position.rb +25 -15
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +11 -7
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -7
- data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +26 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +11 -5
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +5 -3
- data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
- data/lib/rubocop/cop/layout/line_length.rb +104 -6
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -4
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -5
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +3 -3
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +2 -0
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
- data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -6
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +3 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +36 -0
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +9 -4
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +30 -2
- data/lib/rubocop/cop/style/and_or.rb +3 -3
- data/lib/rubocop/cop/style/arguments_forwarding.rb +28 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +6 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +2 -0
- data/lib/rubocop/cop/style/class_methods_definitions.rb +7 -0
- data/lib/rubocop/cop/style/collection_querying.rb +79 -46
- data/lib/rubocop/cop/style/combinable_loops.rb +4 -0
- data/lib/rubocop/cop/style/concat_array_literals.rb +13 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
- data/lib/rubocop/cop/style/endless_method.rb +5 -1
- data/lib/rubocop/cop/style/for.rb +14 -1
- data/lib/rubocop/cop/style/guard_clause.rb +22 -21
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +63 -2
- data/lib/rubocop/cop/style/lambda.rb +16 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +36 -4
- data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
- data/lib/rubocop/cop/style/mutable_constant.rb +25 -7
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +1 -1
- data/lib/rubocop/cop/style/raise_args.rb +19 -9
- data/lib/rubocop/cop/style/redundant_begin.rb +2 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -2
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
- data/lib/rubocop/cop/style/require_order.rb +16 -8
- data/lib/rubocop/cop/style/rescue_modifier.rb +6 -0
- data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +5 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +23 -23
- data/lib/rubocop/server/cache.rb +4 -4
- data/lib/rubocop/target_ruby.rb +21 -9
- data/lib/rubocop/version.rb +1 -1
- metadata +2 -2
|
@@ -132,10 +132,11 @@ module RuboCop
|
|
|
132
132
|
# private alias_method :qux, :foo
|
|
133
133
|
#
|
|
134
134
|
# end
|
|
135
|
-
class AccessModifierDeclarations < Base
|
|
135
|
+
class AccessModifierDeclarations < Base # rubocop:disable Metrics/ClassLength
|
|
136
136
|
extend AutoCorrector
|
|
137
137
|
|
|
138
138
|
include ConfigurableEnforcedStyle
|
|
139
|
+
include EndlessMethodBodyHelp
|
|
139
140
|
include RangeHelp
|
|
140
141
|
|
|
141
142
|
GROUP_STYLE_MESSAGE = [
|
|
@@ -169,6 +170,10 @@ module RuboCop
|
|
|
169
170
|
(send nil? :alias_method _ _))
|
|
170
171
|
PATTERN
|
|
171
172
|
|
|
173
|
+
def self.autocorrect_incompatible_with
|
|
174
|
+
[Style::EndlessMethod, Style::SingleLineMethods]
|
|
175
|
+
end
|
|
176
|
+
|
|
172
177
|
def on_send(node)
|
|
173
178
|
return if allowed?(node)
|
|
174
179
|
|
|
@@ -248,10 +253,33 @@ module RuboCop
|
|
|
248
253
|
|
|
249
254
|
access_modifier_is_inlined?(node) && !right_siblings_same_inline_method?(node)
|
|
250
255
|
else
|
|
251
|
-
access_modifier_is_not_inlined?(node) && select_grouped_def_nodes(node)
|
|
256
|
+
access_modifier_is_not_inlined?(node) && inlinable?(select_grouped_def_nodes(node))
|
|
252
257
|
end
|
|
253
258
|
end
|
|
254
259
|
|
|
260
|
+
def inlinable?(def_nodes)
|
|
261
|
+
return false if def_nodes.empty?
|
|
262
|
+
|
|
263
|
+
def_nodes.none? { |def_node| def_node.endless? && body_unsafe_to_inline?(def_node.body) }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def body_unsafe_to_inline?(body)
|
|
267
|
+
return true if body.each_node(:any_block).any? { |block| do_end_block?(block) }
|
|
268
|
+
|
|
269
|
+
target_ruby_version < 4.0 && command_body?(body)
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def do_end_block?(block)
|
|
273
|
+
block.keywords? && !block.send_node.lambda_literal?
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def command_body?(body)
|
|
277
|
+
body = body.body while body.rescue_type?
|
|
278
|
+
return true if body.send_type? && body.prefix_not? && !body.parenthesized?
|
|
279
|
+
|
|
280
|
+
command_call?(body)
|
|
281
|
+
end
|
|
282
|
+
|
|
255
283
|
def correctable_group_offense?(node)
|
|
256
284
|
return false unless group_style?
|
|
257
285
|
return false if allowed?(node)
|
|
@@ -49,7 +49,7 @@ module RuboCop
|
|
|
49
49
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
|
50
50
|
|
|
51
51
|
def self.autocorrect_incompatible_with
|
|
52
|
-
[Style::MethodCallWithArgsParentheses, Style::ReturnNilInPredicateMethodDefinition]
|
|
52
|
+
[Style::MethodCallWithArgsParentheses, Style::ReturnNil, Style::ReturnNilInPredicateMethodDefinition]
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def on_and(node)
|
|
@@ -73,7 +73,7 @@ module RuboCop
|
|
|
73
73
|
message = message(node)
|
|
74
74
|
add_offense(node.loc.operator, message: message) do |corrector|
|
|
75
75
|
node.each_child_node do |expr|
|
|
76
|
-
if expr.
|
|
76
|
+
if expr.call_type?
|
|
77
77
|
correct_send(expr, corrector)
|
|
78
78
|
elsif expr.type?(:return, :next, :break, :yield) || expr.assignment?
|
|
79
79
|
correct_other(expr, corrector)
|
|
@@ -120,7 +120,7 @@ module RuboCop
|
|
|
120
120
|
# however, 'not x' also parses as (send x :!)
|
|
121
121
|
def correct_not(node, receiver, corrector)
|
|
122
122
|
if node.prefix_bang?
|
|
123
|
-
return unless receiver.
|
|
123
|
+
return unless receiver.call_type?
|
|
124
124
|
|
|
125
125
|
correct_send(receiver, corrector)
|
|
126
126
|
elsif node.prefix_not?
|
|
@@ -169,7 +169,7 @@ module RuboCop
|
|
|
169
169
|
|
|
170
170
|
return if send_classifications.empty?
|
|
171
171
|
|
|
172
|
-
if
|
|
172
|
+
if forward_all?(send_nodes, send_classifications)
|
|
173
173
|
add_forward_all_offenses(node, send_classifications, forwardable_args)
|
|
174
174
|
elsif target_ruby_version >= 3.2
|
|
175
175
|
add_post_ruby_32_offenses(node, send_classifications, forwardable_args)
|
|
@@ -192,11 +192,33 @@ module RuboCop
|
|
|
192
192
|
[restarg_node, kwrestarg_node, blockarg_node]
|
|
193
193
|
end
|
|
194
194
|
|
|
195
|
+
def forward_all?(send_nodes, send_classifications)
|
|
196
|
+
only_forwards_all?(send_classifications) &&
|
|
197
|
+
!anonymous_forwarding_elsewhere?(send_nodes, send_classifications)
|
|
198
|
+
end
|
|
199
|
+
|
|
195
200
|
def only_forwards_all?(send_classifications)
|
|
196
201
|
all_classifications = %i[all all_anonymous].freeze
|
|
197
202
|
send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
|
|
198
203
|
end
|
|
199
204
|
|
|
205
|
+
def anonymous_forwarding_elsewhere?(send_nodes, send_classifications)
|
|
206
|
+
classified = send_classifications.map(&:first)
|
|
207
|
+
|
|
208
|
+
send_nodes.any? do |send_node|
|
|
209
|
+
classified.none? { |node| node.equal?(send_node) } &&
|
|
210
|
+
forwards_anonymous_args?(send_node)
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def forwards_anonymous_args?(send_node)
|
|
215
|
+
send_node.arguments.any? do |arg|
|
|
216
|
+
next arg.children.any?(&:forwarded_kwrestarg_type?) if arg.hash_type?
|
|
217
|
+
|
|
218
|
+
arg.forwarded_restarg_type?
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
|
|
200
222
|
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
201
223
|
def add_forward_all_offenses(node, send_classifications, forwardable_args)
|
|
202
224
|
rest_arg, kwrest_arg, block_arg = *forwardable_args
|
|
@@ -376,12 +398,16 @@ module RuboCop
|
|
|
376
398
|
|
|
377
399
|
def add_parens_if_missing(node, corrector)
|
|
378
400
|
return if parentheses?(node)
|
|
379
|
-
return if node.
|
|
401
|
+
return if node.call_type? && operator_or_setter_call?(node)
|
|
380
402
|
return unless (@parenthesized_nodes ||= Set.new.compare_by_identity).add?(node)
|
|
381
403
|
|
|
382
404
|
add_parentheses(node, corrector)
|
|
383
405
|
end
|
|
384
406
|
|
|
407
|
+
def operator_or_setter_call?(node)
|
|
408
|
+
node.setter_method? || (node.operator_method? && !node.loc.dot)
|
|
409
|
+
end
|
|
410
|
+
|
|
385
411
|
# Classifies send nodes for possible rest/kwrest/all (including block) forwarding.
|
|
386
412
|
class SendNodeClassifier
|
|
387
413
|
extend NodePattern::Macros
|
|
@@ -306,6 +306,8 @@ module RuboCop
|
|
|
306
306
|
end
|
|
307
307
|
|
|
308
308
|
def move_comment_before_block(corrector, comment, block_node, closing_brace)
|
|
309
|
+
return if directive_comment?(comment)
|
|
310
|
+
|
|
309
311
|
range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
|
|
310
312
|
|
|
311
313
|
# It is possible that there is code between the block and the comment
|
|
@@ -319,6 +321,10 @@ module RuboCop
|
|
|
319
321
|
corrector.insert_before(block_node, "#{comment.text}\n")
|
|
320
322
|
end
|
|
321
323
|
|
|
324
|
+
def directive_comment?(comment)
|
|
325
|
+
comment.text.match?(DirectiveComment::DIRECTIVE_COMMENT_REGEXP)
|
|
326
|
+
end
|
|
327
|
+
|
|
322
328
|
def source_range_before_comment(range, comment)
|
|
323
329
|
range = range.end.join(comment.source_range.begin)
|
|
324
330
|
|
|
@@ -152,7 +152,9 @@ module RuboCop
|
|
|
152
152
|
# making autocorrection ping-pong between the two forms.
|
|
153
153
|
return if style_for_kind(node.body.type) == :nested
|
|
154
154
|
return unless compactible_namespace?(node)
|
|
155
|
+
return if node.each_ancestor(:class, :module).any? { |ancestor| ignored_node?(ancestor) }
|
|
155
156
|
|
|
157
|
+
ignore_node(node)
|
|
156
158
|
compact_node(corrector, node)
|
|
157
159
|
remove_end(corrector, node.body)
|
|
158
160
|
unindent(corrector, node)
|
|
@@ -75,7 +75,10 @@ module RuboCop
|
|
|
75
75
|
return unless def_nodes(node).all? { |def_node| no_code_after?(def_node.source_range) }
|
|
76
76
|
|
|
77
77
|
add_offense(node, message: MSG_SCLASS) do |corrector|
|
|
78
|
+
next if inside_corrected_sclass?(node)
|
|
79
|
+
|
|
78
80
|
autocorrect_sclass(node, corrector)
|
|
81
|
+
ignore_node(node)
|
|
79
82
|
end
|
|
80
83
|
end
|
|
81
84
|
|
|
@@ -120,6 +123,10 @@ module RuboCop
|
|
|
120
123
|
trailing.empty? || trailing.start_with?('#')
|
|
121
124
|
end
|
|
122
125
|
|
|
126
|
+
def inside_corrected_sclass?(node)
|
|
127
|
+
node.each_ancestor(:sclass).any? { |ancestor| ignored_node?(ancestor) }
|
|
128
|
+
end
|
|
129
|
+
|
|
123
130
|
def autocorrect_sclass(node, corrector)
|
|
124
131
|
rewritten_defs = []
|
|
125
132
|
|
|
@@ -15,31 +15,24 @@ module RuboCop
|
|
|
15
15
|
# would yield false positives. For example, `String` implements `length`
|
|
16
16
|
# and `size`, but it doesn't include `Enumerable`.
|
|
17
17
|
#
|
|
18
|
+
# When `count` is used without a block, it measures the collection's
|
|
19
|
+
# size, so the offense is corrected to `empty?` or `!empty?`. The
|
|
20
|
+
# predicate methods `any?`, `none?` and `one?` test element truthiness
|
|
21
|
+
# instead and would return different results for collections with falsey
|
|
22
|
+
# elements (e.g. `[false].count.zero?` is `false`, but `[false].none?`
|
|
23
|
+
# is `true`). For the same reason, `count == 1` and `count > 1` without
|
|
24
|
+
# a block are not flagged.
|
|
25
|
+
#
|
|
18
26
|
# @safety
|
|
19
27
|
# The cop is unsafe because receiver might not include `Enumerable`, or
|
|
20
28
|
# it has nonstandard implementation of `count` or any replacement
|
|
21
29
|
# methods.
|
|
22
30
|
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
# `
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
# ----
|
|
29
|
-
# [nil, false].count.positive?
|
|
30
|
-
# [nil].count == 1
|
|
31
|
-
# # => true
|
|
32
|
-
#
|
|
33
|
-
# [nil, false].any?
|
|
34
|
-
# [nil].one?
|
|
35
|
-
# # => false
|
|
36
|
-
#
|
|
37
|
-
# [nil].count == 0
|
|
38
|
-
# # => false
|
|
39
|
-
#
|
|
40
|
-
# [nil].none?
|
|
41
|
-
# # => true
|
|
42
|
-
# ----
|
|
31
|
+
# Blockless `count` is corrected to `empty?`, which `Enumerable` doesn't
|
|
32
|
+
# define. `Range`, `Enumerator` (including lazy ones), `Struct`, `StringIO`
|
|
33
|
+
# and any class that just includes `Enumerable` respond to `count` but not
|
|
34
|
+
# to `empty?`, so `(1...1).count.zero?` is `true` while `(1...1).empty?`
|
|
35
|
+
# raises `NoMethodError`.
|
|
43
36
|
#
|
|
44
37
|
# Autocorrection is unsafe when replacement methods don't iterate over
|
|
45
38
|
# every element in collection and the given block runs side effects:
|
|
@@ -60,12 +53,14 @@ module RuboCop
|
|
|
60
53
|
# x.count > 0
|
|
61
54
|
# x.count != 0
|
|
62
55
|
#
|
|
56
|
+
# # good
|
|
57
|
+
# !x.empty?
|
|
58
|
+
#
|
|
59
|
+
# # bad
|
|
63
60
|
# x.count(&:foo?).positive?
|
|
64
61
|
# x.count { |item| item.foo? }.positive?
|
|
65
62
|
#
|
|
66
63
|
# # good
|
|
67
|
-
# x.any?
|
|
68
|
-
#
|
|
69
64
|
# x.any?(&:foo?)
|
|
70
65
|
# x.any? { |item| item.foo? }
|
|
71
66
|
#
|
|
@@ -74,24 +69,33 @@ module RuboCop
|
|
|
74
69
|
# x.count == 0
|
|
75
70
|
#
|
|
76
71
|
# # good
|
|
77
|
-
# x.
|
|
72
|
+
# x.empty?
|
|
78
73
|
#
|
|
79
74
|
# # bad
|
|
80
|
-
# x.count
|
|
81
|
-
# x.
|
|
75
|
+
# x.count(&:foo?).zero?
|
|
76
|
+
# x.count(&:foo?) == 0
|
|
77
|
+
#
|
|
78
|
+
# # good
|
|
79
|
+
# x.none?(&:foo?)
|
|
80
|
+
#
|
|
81
|
+
# # bad
|
|
82
|
+
# x.count(&:foo?) == 1
|
|
83
|
+
#
|
|
84
|
+
# # good
|
|
85
|
+
# x.one?(&:foo?)
|
|
82
86
|
#
|
|
83
87
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
|
84
88
|
#
|
|
85
89
|
# # good
|
|
86
|
-
# x.count > 1
|
|
90
|
+
# x.count(&:foo?) > 1
|
|
87
91
|
#
|
|
88
92
|
# @example AllCops:ActiveSupportExtensionsEnabled: true
|
|
89
93
|
#
|
|
90
94
|
# # bad
|
|
91
|
-
# x.count > 1
|
|
95
|
+
# x.count(&:foo?) > 1
|
|
92
96
|
#
|
|
93
97
|
# # good
|
|
94
|
-
# x.many?
|
|
98
|
+
# x.many?(&:foo?)
|
|
95
99
|
#
|
|
96
100
|
class CollectionQuerying < Base
|
|
97
101
|
include RangeHelp
|
|
@@ -102,13 +106,21 @@ module RuboCop
|
|
|
102
106
|
RESTRICT_ON_SEND = %i[positive? > != zero? ==].freeze
|
|
103
107
|
|
|
104
108
|
REPLACEMENTS = {
|
|
105
|
-
[:positive?, nil] =>
|
|
106
|
-
[:>, 0] =>
|
|
107
|
-
[:!=, 0] =>
|
|
108
|
-
[:zero?, nil] =>
|
|
109
|
-
[:==, 0] =>
|
|
110
|
-
[:==, 1] =>
|
|
111
|
-
[:>, 1] =>
|
|
109
|
+
[:positive?, nil] => 'any?',
|
|
110
|
+
[:>, 0] => 'any?',
|
|
111
|
+
[:!=, 0] => 'any?',
|
|
112
|
+
[:zero?, nil] => 'none?',
|
|
113
|
+
[:==, 0] => 'none?',
|
|
114
|
+
[:==, 1] => 'one?',
|
|
115
|
+
[:>, 1] => 'many?'
|
|
116
|
+
}.freeze
|
|
117
|
+
|
|
118
|
+
NO_BLOCK_REPLACEMENTS = {
|
|
119
|
+
[:positive?, nil] => '!empty?',
|
|
120
|
+
[:>, 0] => '!empty?',
|
|
121
|
+
[:!=, 0] => '!empty?',
|
|
122
|
+
[:zero?, nil] => 'empty?',
|
|
123
|
+
[:==, 0] => 'empty?'
|
|
112
124
|
}.freeze
|
|
113
125
|
|
|
114
126
|
# @!method count_predicate(node)
|
|
@@ -132,28 +144,49 @@ module RuboCop
|
|
|
132
144
|
def on_send(node)
|
|
133
145
|
return unless (count_node = count_predicate(node))
|
|
134
146
|
|
|
135
|
-
|
|
147
|
+
replacement = replacement_method(node, count_node)
|
|
136
148
|
|
|
137
|
-
return unless replacement_supported?(
|
|
149
|
+
return unless replacement_supported?(node, replacement)
|
|
138
150
|
|
|
139
151
|
offense_range = count_node.loc.selector.join(node.source_range.end)
|
|
140
|
-
add_offense(offense_range,
|
|
141
|
-
|
|
142
|
-
corrector.replace(count_node.loc.selector, replacement_method)
|
|
143
|
-
corrector.remove(removal_range(node))
|
|
152
|
+
add_offense(offense_range, message: format(MSG, prefer: replacement)) do |corrector|
|
|
153
|
+
autocorrect(corrector, node, count_node, replacement)
|
|
144
154
|
end
|
|
145
155
|
end
|
|
146
156
|
|
|
147
157
|
private
|
|
148
158
|
|
|
149
|
-
def replacement_method(node)
|
|
150
|
-
|
|
159
|
+
def replacement_method(node, count_node)
|
|
160
|
+
key = [node.method_name, node.first_argument&.value]
|
|
161
|
+
return REPLACEMENTS.fetch(key) if block_given_to_count?(count_node)
|
|
162
|
+
|
|
163
|
+
NO_BLOCK_REPLACEMENTS[key]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def block_given_to_count?(count_node)
|
|
167
|
+
count_node.parent&.any_block_type? || count_node.last_argument&.block_pass_type?
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def replacement_supported?(node, replacement)
|
|
171
|
+
return false unless replacement
|
|
172
|
+
return false if replacement == 'many?' && !active_support_extensions_enabled?
|
|
173
|
+
|
|
174
|
+
!(replacement.start_with?('!') && receiver_of_call?(node))
|
|
151
175
|
end
|
|
152
176
|
|
|
153
|
-
def
|
|
154
|
-
|
|
177
|
+
def receiver_of_call?(node)
|
|
178
|
+
node.parent&.call_type? && node.parent.receiver == node && node.parent.loc.dot
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def autocorrect(corrector, node, count_node, replacement)
|
|
182
|
+
if replacement.start_with?('!')
|
|
183
|
+
corrector.insert_before(count_node, '!')
|
|
184
|
+
corrector.replace(count_node.loc.selector, 'empty?')
|
|
185
|
+
else
|
|
186
|
+
corrector.replace(count_node.loc.selector, replacement)
|
|
187
|
+
end
|
|
155
188
|
|
|
156
|
-
|
|
189
|
+
corrector.remove(removal_range(node))
|
|
157
190
|
end
|
|
158
191
|
|
|
159
192
|
def removal_range(node)
|
|
@@ -65,6 +65,10 @@ module RuboCop
|
|
|
65
65
|
|
|
66
66
|
MSG = 'Combine this loop with the previous loop.'
|
|
67
67
|
|
|
68
|
+
def self.autocorrect_incompatible_with
|
|
69
|
+
[Layout::BlockEndNewline, Layout::RedundantLineBreak]
|
|
70
|
+
end
|
|
71
|
+
|
|
68
72
|
def on_block(node)
|
|
69
73
|
return unless node.parent&.begin_type?
|
|
70
74
|
return unless collection_looping_method?(node)
|
|
@@ -118,7 +118,8 @@ module RuboCop
|
|
|
118
118
|
# A comment or a heredoc body there can't be moved out of the way.
|
|
119
119
|
return unless closing_ranges.all? { |range| range.source.match?(/\A[\s,]*\]\z/) }
|
|
120
120
|
|
|
121
|
-
corrector
|
|
121
|
+
replace_selector(corrector, node)
|
|
122
|
+
|
|
122
123
|
node.arguments.zip(closing_ranges) do |argument, closing_range|
|
|
123
124
|
corrector.remove(argument.loc.begin)
|
|
124
125
|
corrector.remove(closing_range)
|
|
@@ -133,6 +134,17 @@ module RuboCop
|
|
|
133
134
|
|
|
134
135
|
range_between(argument.children.last.source_range.end_pos, argument.loc.end.end_pos)
|
|
135
136
|
end
|
|
137
|
+
|
|
138
|
+
def replace_selector(corrector, node)
|
|
139
|
+
if node.parenthesized? || node.single_line?
|
|
140
|
+
corrector.replace(node.loc.selector, 'push')
|
|
141
|
+
else
|
|
142
|
+
range = node.loc.selector.join(node.first_argument.source_range.begin)
|
|
143
|
+
|
|
144
|
+
corrector.replace(range, 'push(')
|
|
145
|
+
corrector.insert_after(node.last_argument.loc.end, ')')
|
|
146
|
+
end
|
|
147
|
+
end
|
|
136
148
|
end
|
|
137
149
|
end
|
|
138
150
|
end
|
|
@@ -45,6 +45,8 @@ module RuboCop
|
|
|
45
45
|
MSG_TRAILING_CONDITIONAL = 'Do not use trailing conditionals in string interpolation.'
|
|
46
46
|
MSG_TERNARY = 'Do not return empty strings in string interpolation.'
|
|
47
47
|
|
|
48
|
+
INTERPOLATED_TYPES = %i[dstr dsym regexp xstr].freeze
|
|
49
|
+
|
|
48
50
|
def on_interpolation(node)
|
|
49
51
|
node.each_child_node(:if) do |child_node|
|
|
50
52
|
if style == :trailing_conditional
|
|
@@ -63,28 +65,54 @@ module RuboCop
|
|
|
63
65
|
return if child_node.modifier_form?
|
|
64
66
|
|
|
65
67
|
if empty_if_outcome?(child_node)
|
|
66
|
-
ternary_style_autocorrect(child_node, child_node.else_branch
|
|
68
|
+
ternary_style_autocorrect(child_node, child_node.else_branch, 'unless')
|
|
67
69
|
end
|
|
68
70
|
|
|
69
71
|
return unless empty_else_outcome?(child_node)
|
|
70
72
|
|
|
71
|
-
ternary_style_autocorrect(child_node, child_node.if_branch
|
|
73
|
+
ternary_style_autocorrect(child_node, child_node.if_branch, 'if')
|
|
72
74
|
end
|
|
73
75
|
|
|
74
76
|
def ternary_correction(node, child_node)
|
|
75
77
|
return unless child_node.modifier_form?
|
|
76
78
|
|
|
77
|
-
ternary_component = if child_node.unless?
|
|
78
|
-
"'' : #{child_node.if_branch.source}"
|
|
79
|
-
else
|
|
80
|
-
"#{child_node.if_branch.source} : ''"
|
|
81
|
-
end
|
|
82
|
-
|
|
83
79
|
add_offense(node, message: MSG_TRAILING_CONDITIONAL) do |corrector|
|
|
84
|
-
|
|
80
|
+
next if nested_trailing_conditional?(child_node)
|
|
81
|
+
|
|
82
|
+
autocorrect_to_ternary(corrector, child_node)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def nested_trailing_conditional?(if_node)
|
|
87
|
+
if_node.each_descendant(:if).any? do |node|
|
|
88
|
+
node.modifier_form? && node.parent.parent&.type?(*INTERPOLATED_TYPES)
|
|
85
89
|
end
|
|
86
90
|
end
|
|
87
91
|
|
|
92
|
+
def autocorrect_to_ternary(corrector, if_node)
|
|
93
|
+
condition = if_node.condition.source
|
|
94
|
+
branch = ternary_branch_source(if_node.if_branch)
|
|
95
|
+
outcomes = if_node.unless? ? "'' : #{branch}" : "#{branch} : ''"
|
|
96
|
+
|
|
97
|
+
corrector.replace(if_node, "#{condition} ? #{outcomes}")
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def ternary_branch_source(branch)
|
|
101
|
+
return branch.source unless command_call?(branch)
|
|
102
|
+
|
|
103
|
+
arguments = branch.first_argument.source_range.join(branch.last_argument.source_range)
|
|
104
|
+
method = branch.source_range.begin.join(arguments.begin).source.rstrip
|
|
105
|
+
|
|
106
|
+
"#{method}(#{arguments.source})"
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def command_call?(node)
|
|
110
|
+
return false unless node.type?(:call, :super, :yield) && node.arguments?
|
|
111
|
+
return false if node.call_type? && (node.operator_method? || node.setter_method?)
|
|
112
|
+
|
|
113
|
+
!node.parenthesized?
|
|
114
|
+
end
|
|
115
|
+
|
|
88
116
|
def empty_if_outcome?(node)
|
|
89
117
|
empty_branch_outcome?(node.if_branch)
|
|
90
118
|
end
|
|
@@ -99,9 +127,23 @@ module RuboCop
|
|
|
99
127
|
branch.nil_type? || (branch.str_type? && branch.value.empty?)
|
|
100
128
|
end
|
|
101
129
|
|
|
102
|
-
def ternary_style_autocorrect(node, outcome,
|
|
130
|
+
def ternary_style_autocorrect(node, outcome, keyword)
|
|
103
131
|
add_offense(node, message: MSG_TERNARY) do |corrector|
|
|
104
|
-
|
|
132
|
+
next if empty_string_conditional_in_condition?(node)
|
|
133
|
+
|
|
134
|
+
head = node.source_range.begin.join(outcome.source_range.begin)
|
|
135
|
+
tail = outcome.source_range.end.join(node.source_range.end)
|
|
136
|
+
|
|
137
|
+
corrector.remove(head)
|
|
138
|
+
corrector.replace(tail, " #{keyword} #{node.condition.source}")
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def empty_string_conditional_in_condition?(if_node)
|
|
143
|
+
if_node.condition.each_descendant(:if).any? do |node|
|
|
144
|
+
next false if node.modifier_form? || !node.parent.parent&.type?(*INTERPOLATED_TYPES)
|
|
145
|
+
|
|
146
|
+
empty_if_outcome?(node) || empty_else_outcome?(node)
|
|
105
147
|
end
|
|
106
148
|
end
|
|
107
149
|
end
|
|
@@ -147,8 +147,12 @@ module RuboCop
|
|
|
147
147
|
|
|
148
148
|
# `Style/MethodDefParentheses` adds the same parentheses a parameter list needs in
|
|
149
149
|
# an endless definition, so letting both correct in the same pass would double them.
|
|
150
|
+
# `Style/GuardClause` turns a single `if` body into several statements,
|
|
151
|
+
# which an endless definition cannot hold.
|
|
152
|
+
# `Style/RaiseArgs` would drop the parentheses of a `raise` that becomes
|
|
153
|
+
# the body in the same pass, which a method passed as an argument needs.
|
|
150
154
|
def self.autocorrect_incompatible_with
|
|
151
|
-
[Style::MethodCallWithArgsParentheses, Style::MethodDefParentheses]
|
|
155
|
+
[Style::GuardClause, Style::MethodCallWithArgsParentheses, Style::MethodDefParentheses, Style::RaiseArgs]
|
|
152
156
|
end
|
|
153
157
|
|
|
154
158
|
def on_def(node)
|
|
@@ -56,8 +56,10 @@ module RuboCop
|
|
|
56
56
|
|
|
57
57
|
# `Style/RedundantBegin` removes the `begin` of a block body in the same pass,
|
|
58
58
|
# leaving a `rescue` or `ensure` clause directly in the `for` body.
|
|
59
|
+
# `Style/ItBlockParameter` replaces block parameter references in the body
|
|
60
|
+
# with `it` in the same pass, while they become `for` loop variables.
|
|
59
61
|
def self.autocorrect_incompatible_with
|
|
60
|
-
[Style::RedundantBegin]
|
|
62
|
+
[Style::RedundantBegin, Style::ItBlockParameter]
|
|
61
63
|
end
|
|
62
64
|
|
|
63
65
|
def on_for(node)
|
|
@@ -78,6 +80,7 @@ module RuboCop
|
|
|
78
80
|
|
|
79
81
|
if style == :for
|
|
80
82
|
return unless node.receiver
|
|
83
|
+
return unless convertible_to_for?(node)
|
|
81
84
|
return if rescue_or_ensure_body?(node) || do_end_block_in_collection?(node)
|
|
82
85
|
|
|
83
86
|
add_offense(node, message: PREFER_FOR) do |corrector|
|
|
@@ -123,6 +126,16 @@ module RuboCop
|
|
|
123
126
|
node.multiline? && node.method?(:each) && !node.send_node.arguments?
|
|
124
127
|
end
|
|
125
128
|
|
|
129
|
+
def convertible_to_for?(node)
|
|
130
|
+
return false if node.type?(:numblock, :itblock)
|
|
131
|
+
return true unless node.arguments?
|
|
132
|
+
|
|
133
|
+
arguments = node.arguments
|
|
134
|
+
return false if arguments.children.one? && arguments.children.first.restarg_type?
|
|
135
|
+
|
|
136
|
+
arguments.each_descendant.all? { |arg| arg.type?(:arg, :restarg, :mlhs) }
|
|
137
|
+
end
|
|
138
|
+
|
|
126
139
|
def rescue_or_ensure_body?(node)
|
|
127
140
|
node.body&.type?(:rescue, :ensure)
|
|
128
141
|
end
|
|
@@ -186,40 +186,37 @@ module RuboCop
|
|
|
186
186
|
return if trivial?(node)
|
|
187
187
|
|
|
188
188
|
example = "#{conditional_keyword} #{condition.source}; #{scope_exiting_keyword}; end"
|
|
189
|
-
replacement =
|
|
190
|
-
#{conditional_keyword} #{condition.source}
|
|
191
|
-
#{scope_exiting_keyword}
|
|
192
|
-
end
|
|
193
|
-
RUBY
|
|
189
|
+
replacement = "#{conditional_keyword} #{condition.source}\n #{scope_exiting_keyword}"
|
|
194
190
|
end
|
|
195
191
|
|
|
196
192
|
add_offense(node.loc.keyword, message: format(MSG, example: example)) do |corrector|
|
|
197
193
|
next if node.else? && guard.nil?
|
|
198
194
|
|
|
199
|
-
autocorrect(corrector, node,
|
|
195
|
+
autocorrect(corrector, node, example, replacement, guard)
|
|
200
196
|
end
|
|
201
197
|
end
|
|
202
198
|
|
|
203
199
|
# rubocop:disable-next Metrics/AbcSize
|
|
204
|
-
def autocorrect(corrector, node,
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if_branch = node.if_branch
|
|
208
|
-
else_branch = node.else_branch
|
|
209
|
-
|
|
200
|
+
def autocorrect(corrector, node, example, multiline_replacement, guard)
|
|
201
|
+
range = node.loc.keyword.join(node.condition.source_range)
|
|
202
|
+
corrector.replace(range, multiline_replacement || example)
|
|
210
203
|
corrector.replace(node.loc.begin, "\n") if node.then?
|
|
211
204
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
elsif (else_heredoc = find_heredoc_argument(else_branch))
|
|
215
|
-
autocorrect_heredoc_argument(corrector, node, else_heredoc, if_branch, guard)
|
|
216
|
-
else
|
|
217
|
-
corrector.remove(node.loc.end)
|
|
218
|
-
return unless node.else?
|
|
205
|
+
branches = [node.if_branch, node.else_branch]
|
|
206
|
+
guard_branch, leave_branch = guard == :else ? branches.reverse : branches
|
|
219
207
|
|
|
220
|
-
|
|
221
|
-
|
|
208
|
+
heredoc = find_heredoc_argument(guard_branch)
|
|
209
|
+
close_multiline_guard_clause(corrector, range, heredoc, guard) if multiline_replacement
|
|
210
|
+
|
|
211
|
+
if heredoc
|
|
212
|
+
return autocorrect_heredoc_argument(corrector, node, heredoc, leave_branch, guard)
|
|
222
213
|
end
|
|
214
|
+
|
|
215
|
+
corrector.remove(node.loc.end)
|
|
216
|
+
return unless node.else?
|
|
217
|
+
|
|
218
|
+
corrector.remove(node.loc.else)
|
|
219
|
+
corrector.remove(range_of_branch_to_remove(node, guard))
|
|
223
220
|
end
|
|
224
221
|
|
|
225
222
|
def heredoc?(argument)
|
|
@@ -241,6 +238,10 @@ module RuboCop
|
|
|
241
238
|
find_heredoc_argument(node.receiver)
|
|
242
239
|
end
|
|
243
240
|
|
|
241
|
+
def close_multiline_guard_clause(corrector, range, heredoc, guard)
|
|
242
|
+
corrector.insert_after(guard && heredoc ? heredoc.loc.heredoc_end : range, "\nend")
|
|
243
|
+
end
|
|
244
|
+
|
|
244
245
|
def autocorrect_heredoc_argument(corrector, node, heredoc_node, leave_branch, guard)
|
|
245
246
|
remove_whole_lines(corrector, node.loc.end)
|
|
246
247
|
return unless node.else?
|
|
@@ -44,6 +44,10 @@ module RuboCop
|
|
|
44
44
|
include ConfigurableEnforcedStyle
|
|
45
45
|
extend AutoCorrector
|
|
46
46
|
|
|
47
|
+
def self.autocorrect_incompatible_with
|
|
48
|
+
[Style::TrailingCommaInArrayLiteral]
|
|
49
|
+
end
|
|
50
|
+
|
|
47
51
|
def on_hash(node)
|
|
48
52
|
return if node.children.first&.kwsplat_type?
|
|
49
53
|
return unless (array = containing_array(node))
|