rubocop 1.66.1 → 1.67.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.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +15 -6
- data/config/internal_affairs.yml +11 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
- data/lib/rubocop/cli/command/lsp.rb +2 -2
- data/lib/rubocop/config_loader_resolver.rb +3 -3
- data/lib/rubocop/config_validator.rb +2 -1
- data/lib/rubocop/cop/base.rb +6 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
- data/lib/rubocop/cop/cop.rb +8 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
- data/lib/rubocop/cop/internal_affairs.rb +16 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
- data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
- data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
- data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
- data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
- data/lib/rubocop/cop/style/collection_compact.rb +10 -10
- data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +1 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -3
- data/lib/rubocop/cop/style/lambda.rb +1 -1
- data/lib/rubocop/cop/style/map_into_array.rb +53 -7
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
- data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +73 -34
- data/lib/rubocop/file_finder.rb +9 -4
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -0
- data/lib/rubocop/runner.rb +3 -0
- data/lib/rubocop/server/cache.rb +6 -1
- data/lib/rubocop/server/core.rb +1 -0
- data/lib/rubocop/target_ruby.rb +12 -12
- data/lib/rubocop/version.rb +3 -1
- data/lib/rubocop/yaml_duplication_checker.rb +20 -27
- data/lib/rubocop.rb +2 -0
- metadata +10 -8
@@ -180,7 +180,8 @@ module RuboCop
|
|
180
180
|
end
|
181
181
|
|
182
182
|
def only_forwards_all?(send_classifications)
|
183
|
-
|
183
|
+
all_classifications = %i[all all_anonymous].freeze
|
184
|
+
send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
|
184
185
|
end
|
185
186
|
|
186
187
|
# rubocop:disable Metrics/MethodLength
|
@@ -188,8 +189,8 @@ module RuboCop
|
|
188
189
|
_rest_arg, _kwrest_arg, block_arg = *forwardable_args
|
189
190
|
registered_block_arg_offense = false
|
190
191
|
|
191
|
-
send_classifications.each do |send_node,
|
192
|
-
if !forward_rest && !forward_kwrest
|
192
|
+
send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
|
193
|
+
if !forward_rest && !forward_kwrest && c != :all_anonymous
|
193
194
|
# Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
|
194
195
|
# in Ruby 3.3.0.
|
195
196
|
if outside_block?(forward_block_arg)
|
@@ -213,6 +214,7 @@ module RuboCop
|
|
213
214
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
214
215
|
def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
|
215
216
|
return unless use_anonymous_forwarding?
|
217
|
+
return if send_inside_block?(send_classifications)
|
216
218
|
|
217
219
|
rest_arg, kwrest_arg, block_arg = *forwardable_args
|
218
220
|
|
@@ -355,8 +357,15 @@ module RuboCop
|
|
355
357
|
cop_config.fetch('UseAnonymousForwarding', false)
|
356
358
|
end
|
357
359
|
|
360
|
+
def send_inside_block?(send_classifications)
|
361
|
+
send_classifications.any? do |send_node, *|
|
362
|
+
send_node.each_ancestor(:block, :numblock).any?
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
358
366
|
def add_parens_if_missing(node, corrector)
|
359
367
|
return if parentheses?(node)
|
368
|
+
return if node.send_type? && node.method?(:[])
|
360
369
|
|
361
370
|
add_parentheses(node, corrector)
|
362
371
|
end
|
@@ -374,6 +383,23 @@ module RuboCop
|
|
374
383
|
# @!method forwarded_block_arg?(node, block_name)
|
375
384
|
def_node_matcher :forwarded_block_arg?, '(block_pass {(lvar %1) nil?})'
|
376
385
|
|
386
|
+
# @!method def_all_anonymous_args?(node)
|
387
|
+
def_node_matcher :def_all_anonymous_args?, <<~PATTERN
|
388
|
+
(
|
389
|
+
def _
|
390
|
+
(args ... (restarg) (kwrestarg) (blockarg nil?))
|
391
|
+
_
|
392
|
+
)
|
393
|
+
PATTERN
|
394
|
+
|
395
|
+
# @!method send_all_anonymous_args?(node)
|
396
|
+
def_node_matcher :send_all_anonymous_args?, <<~PATTERN
|
397
|
+
(
|
398
|
+
send _ _
|
399
|
+
... (forwarded_restarg) (hash (forwarded_kwrestarg)) (block_pass nil?)
|
400
|
+
)
|
401
|
+
PATTERN
|
402
|
+
|
377
403
|
def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
|
378
404
|
@def_node = def_node
|
379
405
|
@send_node = send_node
|
@@ -405,7 +431,9 @@ module RuboCop
|
|
405
431
|
def classification
|
406
432
|
return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg
|
407
433
|
|
408
|
-
if
|
434
|
+
if ruby_32_only_anonymous_forwarding?
|
435
|
+
:all_anonymous
|
436
|
+
elsif can_forward_all?
|
409
437
|
:all
|
410
438
|
else
|
411
439
|
:rest_or_kwrest
|
@@ -414,16 +442,28 @@ module RuboCop
|
|
414
442
|
|
415
443
|
private
|
416
444
|
|
445
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
417
446
|
def can_forward_all?
|
418
447
|
return false if any_arg_referenced?
|
419
|
-
return false if
|
448
|
+
return false if ruby_30_or_lower_optarg?
|
449
|
+
return false if ruby_32_or_higher_missing_rest_or_kwest?
|
420
450
|
return false unless offensive_block_forwarding?
|
421
451
|
return false if additional_kwargs_or_forwarded_kwargs?
|
422
452
|
|
423
453
|
no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
|
424
454
|
end
|
455
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
456
|
+
|
457
|
+
# def foo(a = 41, ...) is a syntax error in 3.0.
|
458
|
+
def ruby_30_or_lower_optarg?
|
459
|
+
target_ruby_version <= 3.0 && @def_node.arguments.any?(&:optarg_type?)
|
460
|
+
end
|
461
|
+
|
462
|
+
def ruby_32_only_anonymous_forwarding?
|
463
|
+
def_all_anonymous_args?(@def_node) && send_all_anonymous_args?(@send_node)
|
464
|
+
end
|
425
465
|
|
426
|
-
def
|
466
|
+
def ruby_32_or_higher_missing_rest_or_kwest?
|
427
467
|
target_ruby_version >= 3.2 && !forwarded_rest_and_kwrest_args
|
428
468
|
end
|
429
469
|
|
@@ -176,6 +176,10 @@ module RuboCop
|
|
176
176
|
|
177
177
|
BRACES_REQUIRED_MESSAGE = "Brace delimiters `{...}` required for '%<method_name>s' method."
|
178
178
|
|
179
|
+
def self.autocorrect_incompatible_with
|
180
|
+
[Style::RedundantBegin]
|
181
|
+
end
|
182
|
+
|
179
183
|
def on_send(node)
|
180
184
|
return unless node.arguments?
|
181
185
|
return if node.parenthesized?
|
@@ -341,8 +345,9 @@ module RuboCop
|
|
341
345
|
end
|
342
346
|
end
|
343
347
|
|
348
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
344
349
|
def proper_block_style?(node)
|
345
|
-
return true if require_braces?(node)
|
350
|
+
return true if require_braces?(node) || require_do_end?(node)
|
346
351
|
return special_method_proper_block_style?(node) if special_method?(node.method_name)
|
347
352
|
|
348
353
|
case style
|
@@ -352,6 +357,7 @@ module RuboCop
|
|
352
357
|
when :always_braces then braces_style?(node)
|
353
358
|
end
|
354
359
|
end
|
360
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
355
361
|
|
356
362
|
def require_braces?(node)
|
357
363
|
return false unless node.braces?
|
@@ -361,6 +367,13 @@ module RuboCop
|
|
361
367
|
end
|
362
368
|
end
|
363
369
|
|
370
|
+
def require_do_end?(node)
|
371
|
+
return false if node.braces? || node.multiline?
|
372
|
+
return false unless (resbody = node.each_descendant(:resbody).first)
|
373
|
+
|
374
|
+
resbody.children.first&.array_type?
|
375
|
+
end
|
376
|
+
|
364
377
|
def special_method?(method_name)
|
365
378
|
allowed_method?(method_name) ||
|
366
379
|
matches_allowed_pattern?(method_name) ||
|
@@ -21,6 +21,7 @@ module RuboCop
|
|
21
21
|
# array.reject(&:nil?)
|
22
22
|
# array.reject { |e| e.nil? }
|
23
23
|
# array.select { |e| !e.nil? }
|
24
|
+
# array.filter { |e| !e.nil? }
|
24
25
|
# array.grep_v(nil)
|
25
26
|
# array.grep_v(NilClass)
|
26
27
|
#
|
@@ -29,10 +30,9 @@ module RuboCop
|
|
29
30
|
#
|
30
31
|
# # bad
|
31
32
|
# hash.reject!(&:nil?)
|
32
|
-
# array.delete_if(&:nil?)
|
33
33
|
# hash.reject! { |k, v| v.nil? }
|
34
|
-
# array.delete_if { |e| e.nil? }
|
35
34
|
# hash.select! { |k, v| !v.nil? }
|
35
|
+
# hash.filter! { |k, v| !v.nil? }
|
36
36
|
#
|
37
37
|
# # good
|
38
38
|
# hash.compact!
|
@@ -48,14 +48,15 @@ module RuboCop
|
|
48
48
|
extend TargetRubyVersion
|
49
49
|
|
50
50
|
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
51
|
-
RESTRICT_ON_SEND = %i[reject
|
51
|
+
RESTRICT_ON_SEND = %i[reject reject! select select! filter filter! grep_v].freeze
|
52
52
|
TO_ENUM_METHODS = %i[to_enum lazy].freeze
|
53
|
+
FILTER_METHODS = %i[filter filter!].freeze
|
53
54
|
|
54
55
|
minimum_target_ruby_version 2.4
|
55
56
|
|
56
57
|
# @!method reject_method_with_block_pass?(node)
|
57
58
|
def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
|
58
|
-
(call !nil? {:reject :
|
59
|
+
(call !nil? {:reject :reject!}
|
59
60
|
(block_pass
|
60
61
|
(sym :nil?)))
|
61
62
|
PATTERN
|
@@ -64,7 +65,7 @@ module RuboCop
|
|
64
65
|
def_node_matcher :reject_method?, <<~PATTERN
|
65
66
|
(block
|
66
67
|
(call
|
67
|
-
!nil? {:reject :
|
68
|
+
!nil? {:reject :reject!})
|
68
69
|
$(args ...)
|
69
70
|
(call
|
70
71
|
$(lvar _) :nil?))
|
@@ -74,7 +75,7 @@ module RuboCop
|
|
74
75
|
def_node_matcher :select_method?, <<~PATTERN
|
75
76
|
(block
|
76
77
|
(call
|
77
|
-
!nil? {:select :select!})
|
78
|
+
!nil? {:select :select! :filter :filter!})
|
78
79
|
$(args ...)
|
79
80
|
(call
|
80
81
|
(call
|
@@ -87,11 +88,10 @@ module RuboCop
|
|
87
88
|
PATTERN
|
88
89
|
|
89
90
|
def on_send(node)
|
91
|
+
return if target_ruby_version < 2.6 && FILTER_METHODS.include?(node.method_name)
|
90
92
|
return unless (range = offense_range(node))
|
91
93
|
return if allowed_receiver?(node.receiver)
|
92
|
-
if
|
93
|
-
return
|
94
|
-
end
|
94
|
+
return if target_ruby_version <= 3.0 && to_enum_method?(node)
|
95
95
|
|
96
96
|
good = good_method_name(node)
|
97
97
|
message = format(MSG, good: good, bad: range.source)
|
@@ -127,7 +127,7 @@ module RuboCop
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def good_method_name(node)
|
130
|
-
if node.bang_method?
|
130
|
+
if node.bang_method?
|
131
131
|
'compact!'
|
132
132
|
else
|
133
133
|
'compact'
|
@@ -7,6 +7,9 @@ module RuboCop
|
|
7
7
|
# can be combined into a single loop. It is very likely that combining them
|
8
8
|
# will make the code more efficient and more concise.
|
9
9
|
#
|
10
|
+
# NOTE: Autocorrection is not applied when the block variable names differ in separate loops,
|
11
|
+
# as it is impossible to determine which variable name should be prioritized.
|
12
|
+
#
|
10
13
|
# @safety
|
11
14
|
# The cop is unsafe, because the first loop might modify state that the
|
12
15
|
# second loop depends on; these two aren't combinable.
|
@@ -61,6 +64,7 @@ module RuboCop
|
|
61
64
|
|
62
65
|
MSG = 'Combine this loop with the previous loop.'
|
63
66
|
|
67
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
64
68
|
def on_block(node)
|
65
69
|
return unless node.parent&.begin_type?
|
66
70
|
return unless collection_looping_method?(node)
|
@@ -68,9 +72,12 @@ module RuboCop
|
|
68
72
|
return unless node.body && node.left_sibling.body
|
69
73
|
|
70
74
|
add_offense(node) do |corrector|
|
75
|
+
next unless node.arguments == node.left_sibling.arguments
|
76
|
+
|
71
77
|
combine_with_left_sibling(corrector, node)
|
72
78
|
end
|
73
79
|
end
|
80
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
74
81
|
|
75
82
|
alias on_numblock on_block
|
76
83
|
|
@@ -10,7 +10,7 @@ module RuboCop
|
|
10
10
|
#
|
11
11
|
# Note that some comments
|
12
12
|
# (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`)
|
13
|
-
# are allowed.
|
13
|
+
# and RBS::Inline annotation comments are allowed.
|
14
14
|
#
|
15
15
|
# Autocorrection removes comments from `end` keyword and keeps comments
|
16
16
|
# for `class`, `module`, `def` and `begin` above the keyword.
|
@@ -82,6 +82,8 @@ module RuboCop
|
|
82
82
|
|
83
83
|
def offensive?(comment)
|
84
84
|
line = source_line(comment)
|
85
|
+
return false if rbs_inline_annotation?(line, comment)
|
86
|
+
|
85
87
|
KEYWORD_REGEXES.any? { |r| r.match?(line) } &&
|
86
88
|
ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
|
87
89
|
end
|
@@ -89,6 +91,10 @@ module RuboCop
|
|
89
91
|
def source_line(comment)
|
90
92
|
comment.source_range.source_line
|
91
93
|
end
|
94
|
+
|
95
|
+
def rbs_inline_annotation?(line, comment)
|
96
|
+
comment.text.start_with?('#:') && line.start_with?(/\A\s*def\s/)
|
97
|
+
end
|
92
98
|
end
|
93
99
|
end
|
94
100
|
end
|
@@ -73,7 +73,7 @@ module RuboCop
|
|
73
73
|
elsif_branches << node.if_branch
|
74
74
|
|
75
75
|
else_branch = node.else_branch
|
76
|
-
if else_branch&.if_type? && else_branch
|
76
|
+
if else_branch&.if_type? && else_branch.elsif?
|
77
77
|
expand_elsif(else_branch, elsif_branches)
|
78
78
|
else
|
79
79
|
elsif_branches << else_branch
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
def on_class(node)
|
37
37
|
return unless data_define?(node.parent_class)
|
38
38
|
|
39
|
-
add_offense(node.parent_class
|
39
|
+
add_offense(node.parent_class) do |corrector|
|
40
40
|
corrector.remove(range_with_surrounding_space(node.loc.keyword, newlines: false))
|
41
41
|
corrector.replace(node.loc.operator, '=')
|
42
42
|
|
@@ -136,7 +136,7 @@ module RuboCop
|
|
136
136
|
actual: line_node.source,
|
137
137
|
expected: expected)
|
138
138
|
|
139
|
-
add_offense(line_node
|
139
|
+
add_offense(line_node, message: message) do |corrector|
|
140
140
|
corrector.replace(line_node, expected)
|
141
141
|
end
|
142
142
|
end
|
@@ -234,11 +234,11 @@ module RuboCop
|
|
234
234
|
end
|
235
235
|
|
236
236
|
def autocorrect_heredoc_argument(corrector, node, heredoc_branch, leave_branch, guard)
|
237
|
+
remove_whole_lines(corrector, node.loc.end)
|
237
238
|
return unless node.else?
|
238
239
|
|
239
240
|
remove_whole_lines(corrector, leave_branch.source_range)
|
240
241
|
remove_whole_lines(corrector, node.loc.else)
|
241
|
-
remove_whole_lines(corrector, node.loc.end)
|
242
242
|
remove_whole_lines(corrector, range_of_branch_to_remove(node, guard))
|
243
243
|
corrector.insert_after(
|
244
244
|
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
@@ -57,6 +57,11 @@ module RuboCop
|
|
57
57
|
(call $(call _ ${:keys :values}) :each (block_pass (sym _)))
|
58
58
|
PATTERN
|
59
59
|
|
60
|
+
# @!method hash_mutated?(node, receiver)
|
61
|
+
def_node_matcher :hash_mutated?, <<~PATTERN
|
62
|
+
`(send %1 :[]= ...)
|
63
|
+
PATTERN
|
64
|
+
|
60
65
|
def on_block(node)
|
61
66
|
return unless handleable?(node)
|
62
67
|
|
@@ -103,6 +108,7 @@ module RuboCop
|
|
103
108
|
def handleable?(node)
|
104
109
|
return false if use_array_converter_method_as_preceding?(node)
|
105
110
|
return false unless (root_receiver = root_receiver(node))
|
111
|
+
return false if hash_mutated?(node, root_receiver)
|
106
112
|
|
107
113
|
!root_receiver.literal? || root_receiver.hash_type?
|
108
114
|
end
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
# {a: 1, b: 2}
|
69
69
|
# {:c => 3, 'd' => 4}
|
70
70
|
#
|
71
|
-
# @example EnforcedShorthandSyntax: always
|
71
|
+
# @example EnforcedShorthandSyntax: always
|
72
72
|
#
|
73
73
|
# # bad
|
74
74
|
# {foo: foo, bar: bar}
|
@@ -84,7 +84,7 @@ module RuboCop
|
|
84
84
|
# # good
|
85
85
|
# {foo: foo, bar: bar}
|
86
86
|
#
|
87
|
-
# @example EnforcedShorthandSyntax: either
|
87
|
+
# @example EnforcedShorthandSyntax: either (default)
|
88
88
|
#
|
89
89
|
# # good
|
90
90
|
# {foo: foo, bar: bar}
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
71
71
|
|
72
72
|
else_branch = node.else_branch
|
73
73
|
|
74
|
-
return unless else_branch&.if_type? && else_branch
|
74
|
+
return unless else_branch&.if_type? && else_branch.if?
|
75
75
|
return if allow_if_modifier_in_else_branch?(else_branch)
|
76
76
|
|
77
77
|
add_offense(else_branch.loc.keyword) do |corrector|
|
@@ -81,16 +81,14 @@ module RuboCop
|
|
81
81
|
RUBY
|
82
82
|
end
|
83
83
|
|
84
|
-
# rubocop:disable Metrics/AbcSize
|
85
84
|
def build_expression(expr)
|
86
|
-
return expr.source
|
85
|
+
return expr.source unless require_argument_parentheses?(expr)
|
87
86
|
|
88
87
|
method = expr.source_range.begin.join(expr.loc.selector.end)
|
89
88
|
arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
|
90
89
|
|
91
90
|
"#{method.source}(#{arguments.source})"
|
92
91
|
end
|
93
|
-
# rubocop:enable Metrics/AbcSize
|
94
92
|
|
95
93
|
def build_else_branch(second_condition)
|
96
94
|
result = <<~RUBY
|
@@ -111,6 +109,12 @@ module RuboCop
|
|
111
109
|
|
112
110
|
result
|
113
111
|
end
|
112
|
+
|
113
|
+
def require_argument_parentheses?(node)
|
114
|
+
return false unless node.call_type?
|
115
|
+
|
116
|
+
!node.parenthesized? && node.arguments.any? && !node.method?(:[]) && !node.method?(:[]=)
|
117
|
+
end
|
114
118
|
end
|
115
119
|
end
|
116
120
|
end
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
|
69
69
|
return unless offending_selector?(node, selector)
|
70
70
|
|
71
|
-
add_offense(node.send_node
|
71
|
+
add_offense(node.send_node, message: message(node, selector)) do |corrector|
|
72
72
|
if node.send_node.lambda_literal?
|
73
73
|
LambdaLiteralToMethodCorrector.new(node).call(corrector)
|
74
74
|
else
|
@@ -13,8 +13,10 @@ module RuboCop
|
|
13
13
|
# return value of `Enumerable#map` is an `Array`. They are not autocorrected
|
14
14
|
# when a return value could be used because these types differ.
|
15
15
|
#
|
16
|
-
# NOTE: It only detects when the mapping destination is
|
17
|
-
# initialized as an empty array and referred to only by the
|
16
|
+
# NOTE: It only detects when the mapping destination is either:
|
17
|
+
# * a local variable initialized as an empty array and referred to only by the
|
18
|
+
# pushing operation;
|
19
|
+
# * or, if it is the single block argument to a `[].tap` block.
|
18
20
|
# This is because, if not, it's challenging to statically guarantee that the
|
19
21
|
# mapping destination variable remains an empty array:
|
20
22
|
#
|
@@ -42,6 +44,14 @@ module RuboCop
|
|
42
44
|
# # good
|
43
45
|
# dest = src.map { |e| e * 2 }
|
44
46
|
#
|
47
|
+
# # bad
|
48
|
+
# [].tap do |dest|
|
49
|
+
# src.each { |e| dest << e * 2 }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # good
|
53
|
+
# dest = src.map { |e| e * 2 }
|
54
|
+
#
|
45
55
|
# # good - contains another operation
|
46
56
|
# dest = []
|
47
57
|
# src.each { |e| dest << e * 2; puts e }
|
@@ -56,7 +66,7 @@ module RuboCop
|
|
56
66
|
# @!method each_block_with_push?(node)
|
57
67
|
def_node_matcher :each_block_with_push?, <<-PATTERN
|
58
68
|
[
|
59
|
-
^({begin kwbegin} ...)
|
69
|
+
^({begin kwbegin block} ...)
|
60
70
|
({block numblock} (send !{nil? self} :each) _
|
61
71
|
(send (lvar _) {:<< :push :append} {send lvar begin}))
|
62
72
|
]
|
@@ -74,6 +84,16 @@ module RuboCop
|
|
74
84
|
)
|
75
85
|
PATTERN
|
76
86
|
|
87
|
+
# @!method empty_array_tap(node)
|
88
|
+
def_node_matcher :empty_array_tap, <<~PATTERN
|
89
|
+
^^$(
|
90
|
+
block
|
91
|
+
(send (array) :tap)
|
92
|
+
(args (arg _))
|
93
|
+
...
|
94
|
+
)
|
95
|
+
PATTERN
|
96
|
+
|
77
97
|
# @!method lvar_ref?(node, name)
|
78
98
|
def_node_matcher :lvar_ref?, '(lvar %1)'
|
79
99
|
|
@@ -89,9 +109,14 @@ module RuboCop
|
|
89
109
|
return unless each_block_with_push?(node)
|
90
110
|
|
91
111
|
dest_var = find_dest_var(node)
|
92
|
-
|
93
|
-
|
94
|
-
|
112
|
+
|
113
|
+
if offending_empty_array_tap?(node, dest_var)
|
114
|
+
asgn = dest_var.declaration_node
|
115
|
+
else
|
116
|
+
return unless (asgn = find_closest_assignment(node, dest_var))
|
117
|
+
return unless empty_array_asgn?(asgn)
|
118
|
+
return unless dest_used_only_for_mapping?(node, dest_var, asgn)
|
119
|
+
end
|
95
120
|
|
96
121
|
register_offense(node, dest_var, asgn)
|
97
122
|
end
|
@@ -108,6 +133,15 @@ module RuboCop
|
|
108
133
|
candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
|
109
134
|
end
|
110
135
|
|
136
|
+
def offending_empty_array_tap?(node, dest_var)
|
137
|
+
return false unless (tap_block_node = empty_array_tap(dest_var.declaration_node))
|
138
|
+
|
139
|
+
# A `tap` block only offends if the array push is the only thing in it;
|
140
|
+
# otherwise we cannot guarantee that the block variable is still an empty
|
141
|
+
# array when pushed to.
|
142
|
+
tap_block_node.body == node
|
143
|
+
end
|
144
|
+
|
111
145
|
def find_closest_assignment(block, dest_var)
|
112
146
|
dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
|
113
147
|
node.source_range.end_pos < block.source_range.begin_pos
|
@@ -127,7 +161,13 @@ module RuboCop
|
|
127
161
|
next if return_value_used?(block)
|
128
162
|
|
129
163
|
corrector.replace(block.send_node.selector, new_method_name)
|
130
|
-
|
164
|
+
|
165
|
+
if (tap_block_node = empty_array_tap(dest_var.declaration_node))
|
166
|
+
remove_tap(corrector, block, tap_block_node)
|
167
|
+
else
|
168
|
+
remove_assignment(corrector, asgn)
|
169
|
+
end
|
170
|
+
|
131
171
|
correct_push_node(corrector, block.body)
|
132
172
|
correct_return_value_handling(corrector, block, dest_var)
|
133
173
|
end
|
@@ -159,6 +199,12 @@ module RuboCop
|
|
159
199
|
corrector.remove(range)
|
160
200
|
end
|
161
201
|
|
202
|
+
def remove_tap(corrector, node, block_node)
|
203
|
+
range = range_between(block_node.source_range.begin_pos, node.source_range.begin_pos)
|
204
|
+
corrector.remove(range)
|
205
|
+
corrector.remove(range_with_surrounding_space(block_node.loc.end, side: :left))
|
206
|
+
end
|
207
|
+
|
162
208
|
def correct_push_node(corrector, push_node)
|
163
209
|
range = push_node.source_range
|
164
210
|
arg_range = push_node.first_argument.source_range
|
@@ -7,6 +7,8 @@ module RuboCop
|
|
7
7
|
# Style omit_parentheses
|
8
8
|
# rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
9
9
|
module OmitParentheses
|
10
|
+
include RangeHelp
|
11
|
+
|
10
12
|
TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
|
11
13
|
OMIT_MSG = 'Omit parentheses for method calls with arguments.'
|
12
14
|
private_constant :OMIT_MSG
|
@@ -30,10 +32,13 @@ module RuboCop
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def autocorrect(corrector, node)
|
35
|
+
range = args_begin(node)
|
33
36
|
if parentheses_at_the_end_of_multiline_call?(node)
|
34
|
-
|
37
|
+
# Whitespace after line continuation (`\ `) is a syntax error
|
38
|
+
with_whitespace = range_with_surrounding_space(range, side: :right, newlines: false)
|
39
|
+
corrector.replace(with_whitespace, ' \\')
|
35
40
|
else
|
36
|
-
corrector.replace(
|
41
|
+
corrector.replace(range, ' ')
|
37
42
|
end
|
38
43
|
corrector.remove(node.loc.end)
|
39
44
|
end
|
@@ -47,11 +52,11 @@ module RuboCop
|
|
47
52
|
node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
|
48
53
|
end
|
49
54
|
|
50
|
-
def require_parentheses_for_hash_value_omission?(node)
|
55
|
+
def require_parentheses_for_hash_value_omission?(node) # rubocop:disable Metrics/PerceivedComplexity
|
51
56
|
return false unless (last_argument = node.last_argument)
|
52
57
|
return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?
|
53
58
|
|
54
|
-
node.parent&.conditional? || !last_expression?(node)
|
59
|
+
node.parent&.conditional? || node.parent&.single_line? || !last_expression?(node)
|
55
60
|
end
|
56
61
|
|
57
62
|
# Require hash value omission be enclosed in parentheses to prevent the following issue:
|
@@ -127,7 +132,7 @@ module RuboCop
|
|
127
132
|
end
|
128
133
|
|
129
134
|
def call_in_single_line_inheritance?(node)
|
130
|
-
node.parent&.class_type? && node.parent
|
135
|
+
node.parent&.class_type? && node.parent.single_line?
|
131
136
|
end
|
132
137
|
|
133
138
|
def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
|
@@ -147,7 +152,7 @@ module RuboCop
|
|
147
152
|
end
|
148
153
|
|
149
154
|
def call_in_argument_with_block?(node)
|
150
|
-
parent = node.parent&.block_type? && node.parent
|
155
|
+
parent = node.parent&.block_type? && node.parent.parent
|
151
156
|
return false unless parent
|
152
157
|
|
153
158
|
parent.call_type? || parent.super_type? || parent.yield_type?
|
@@ -211,7 +216,7 @@ module RuboCop
|
|
211
216
|
|
212
217
|
def unary_literal?(node)
|
213
218
|
(node.numeric_type? && node.sign?) ||
|
214
|
-
(node.parent&.send_type? && node.parent
|
219
|
+
(node.parent&.send_type? && node.parent.unary_operation?)
|
215
220
|
end
|
216
221
|
|
217
222
|
def assigned_before?(node, target)
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
next if allowed_omission?(nested)
|
40
40
|
|
41
41
|
message = format(MSG, source: nested.source)
|
42
|
-
add_offense(nested
|
42
|
+
add_offense(nested, message: message) do |corrector|
|
43
43
|
autocorrect(corrector, nested)
|
44
44
|
end
|
45
45
|
end
|