rubocop 1.66.1 → 1.67.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|