rubocop 1.53.1 → 1.57.2
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 +3 -1
- data/config/default.yml +34 -8
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/config_finder.rb +2 -2
- data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
- data/lib/rubocop/cop/base.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
- data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
- data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
- data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -21
- data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
- data/lib/rubocop/cop/layout/dot_position.rb +1 -5
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +26 -3
- data/lib/rubocop/cop/layout/end_alignment.rb +7 -1
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
- data/lib/rubocop/cop/layout/redundant_line_break.rb +13 -3
- data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
- data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
- data/lib/rubocop/cop/layout/space_around_operators.rb +3 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
- data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
- data/lib/rubocop/cop/lint/debugger.rb +17 -4
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -1
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +10 -0
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
- data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
- data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
- data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
- data/lib/rubocop/cop/lint/void.rb +32 -20
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/class_length.rb +8 -3
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
- data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
- data/lib/rubocop/cop/mixin/def_node.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
- data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
- data/lib/rubocop/cop/mixin/string_help.rb +4 -2
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +1 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
- data/lib/rubocop/cop/style/alias.rb +9 -8
- data/lib/rubocop/cop/style/arguments_forwarding.rb +280 -63
- data/lib/rubocop/cop/style/array_intersect.rb +13 -5
- data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
- data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -0
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +4 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
- data/lib/rubocop/cop/style/for.rb +1 -1
- data/lib/rubocop/cop/style/format_string.rb +24 -3
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
- data/lib/rubocop/cop/style/guard_clause.rb +26 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +25 -3
- data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
- data/lib/rubocop/cop/style/lambda.rb +3 -3
- data/lib/rubocop/cop/style/lambda_call.rb +5 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -1
- data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
- data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +6 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
- data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
- data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -15
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
- data/lib/rubocop/cop/style/redundant_return.rb +7 -2
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
- data/lib/rubocop/cop/style/return_nil.rb +6 -2
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +23 -9
- data/lib/rubocop/cop/style/semicolon.rb +0 -3
- data/lib/rubocop/cop/style/single_argument_dig.rb +2 -1
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
- data/lib/rubocop/cop/style/symbol_array.rb +35 -15
- data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
- data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
- data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
- data/lib/rubocop/cop/variable_force/assignment.rb +14 -5
- data/lib/rubocop/file_finder.rb +4 -7
- data/lib/rubocop/formatter/html_formatter.rb +4 -2
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +41 -18
- data/lib/rubocop/lsp/runtime.rb +22 -2
- data/lib/rubocop/lsp/server.rb +10 -4
- data/lib/rubocop/magic_comment.rb +12 -10
- data/lib/rubocop/result_cache.rb +4 -0
- data/lib/rubocop/rspec/shared_contexts.rb +2 -3
- data/lib/rubocop/runner.rb +5 -3
- data/lib/rubocop/server/cache.rb +1 -0
- data/lib/rubocop/server/client_command/exec.rb +1 -1
- data/lib/rubocop/string_interpreter.rb +3 -3
- data/lib/rubocop/target_finder.rb +7 -3
- data/lib/rubocop/target_ruby.rb +9 -5
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +2 -0
- metadata +16 -14
@@ -75,9 +75,7 @@ module RuboCop
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def aligned_token?(range, line)
|
78
|
-
aligned_words?(range, line) ||
|
79
|
-
aligned_char?(range, line) ||
|
80
|
-
aligned_assignment?(range, line)
|
78
|
+
aligned_words?(range, line) || aligned_assignment?(range, line)
|
81
79
|
end
|
82
80
|
|
83
81
|
def aligned_operator?(range, line)
|
@@ -85,11 +83,11 @@ module RuboCop
|
|
85
83
|
end
|
86
84
|
|
87
85
|
def aligned_words?(range, line)
|
88
|
-
|
89
|
-
|
86
|
+
left_edge = range.column
|
87
|
+
return true if /\s\S/.match?(line[left_edge - 1, 2])
|
90
88
|
|
91
|
-
|
92
|
-
line[
|
89
|
+
token = range.source
|
90
|
+
token == line[left_edge, token.length]
|
93
91
|
end
|
94
92
|
|
95
93
|
def aligned_assignment?(range, line)
|
@@ -30,8 +30,10 @@ module RuboCop
|
|
30
30
|
private
|
31
31
|
|
32
32
|
def inside_interpolation?(node)
|
33
|
-
# A :begin node inside a :dstr node is an interpolation.
|
34
|
-
node.ancestors
|
33
|
+
# A :begin node inside a :dstr, :dsym, or :regexp node is an interpolation.
|
34
|
+
node.ancestors
|
35
|
+
.drop_while { |a| !a.begin_type? }
|
36
|
+
.any? { |a| a.dstr_type? || a.dsym_type? || a.regexp_type? }
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
@@ -106,7 +106,7 @@ module RuboCop
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def elements(node)
|
109
|
-
return node.children unless
|
109
|
+
return node.children unless node.call_type?
|
110
110
|
|
111
111
|
node.arguments.flat_map do |argument|
|
112
112
|
# For each argument, if it is a multi-line hash without braces,
|
@@ -136,7 +136,7 @@ module RuboCop
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def filename_good?(basename)
|
139
|
-
basename = basename.
|
139
|
+
basename = basename.delete_prefix('.')
|
140
140
|
basename = basename.sub(/\.[^.]+$/, '')
|
141
141
|
# special handling for Action Pack Variants file names like
|
142
142
|
# some_file.xlsx+mobile.axlsx
|
@@ -122,7 +122,7 @@ module RuboCop
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def bareword?(sym_node)
|
125
|
-
!sym_node.source.start_with?(':')
|
125
|
+
!sym_node.source.start_with?(':') || sym_node.dsym_type?
|
126
126
|
end
|
127
127
|
|
128
128
|
def correct_alias_method_to_alias(corrector, send_node)
|
@@ -134,9 +134,7 @@ module RuboCop
|
|
134
134
|
|
135
135
|
def correct_alias_to_alias_method(corrector, node)
|
136
136
|
replacement =
|
137
|
-
|
138
|
-
":#{identifier(node.new_identifier)}, " \
|
139
|
-
":#{identifier(node.old_identifier)}"
|
137
|
+
"alias_method #{identifier(node.new_identifier)}, #{identifier(node.old_identifier)}"
|
140
138
|
|
141
139
|
corrector.replace(node, replacement)
|
142
140
|
end
|
@@ -146,10 +144,13 @@ module RuboCop
|
|
146
144
|
corrector.replace(node.old_identifier, node.old_identifier.source[1..])
|
147
145
|
end
|
148
146
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
147
|
+
def identifier(node)
|
148
|
+
if node.sym_type?
|
149
|
+
":#{node.children.first}"
|
150
|
+
else
|
151
|
+
node.source
|
152
|
+
end
|
153
|
+
end
|
153
154
|
end
|
154
155
|
end
|
155
156
|
end
|
@@ -8,6 +8,12 @@ module RuboCop
|
|
8
8
|
# This cop identifies places where `do_something(*args, &block)`
|
9
9
|
# can be replaced by `do_something(...)`.
|
10
10
|
#
|
11
|
+
# In Ruby 3.2, anonymous args/kwargs forwarding has been added.
|
12
|
+
#
|
13
|
+
# This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
|
14
|
+
# replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
|
15
|
+
# by setting UseAnonymousForwarding: false.
|
16
|
+
#
|
11
17
|
# @example
|
12
18
|
# # bad
|
13
19
|
# def foo(*args, &block)
|
@@ -24,7 +30,27 @@ module RuboCop
|
|
24
30
|
# bar(...)
|
25
31
|
# end
|
26
32
|
#
|
27
|
-
# @example
|
33
|
+
# @example UseAnonymousForwarding: true (default, only relevant for Ruby >= 3.2)
|
34
|
+
# # bad
|
35
|
+
# def foo(*args, **kwargs)
|
36
|
+
# args_only(*args)
|
37
|
+
# kwargs_only(**kwargs)
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# def foo(*, **)
|
42
|
+
# args_only(*)
|
43
|
+
# kwargs_only(**)
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @example UseAnonymousForwarding: false (only relevant for Ruby >= 3.2)
|
47
|
+
# # good
|
48
|
+
# def foo(*args, **kwargs)
|
49
|
+
# args_only(*args)
|
50
|
+
# kwargs_only(**kwargs)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @example AllowOnlyRestArgument: true (default, only relevant for Ruby < 3.2)
|
28
54
|
# # good
|
29
55
|
# def foo(*args)
|
30
56
|
# bar(*args)
|
@@ -34,7 +60,7 @@ module RuboCop
|
|
34
60
|
# bar(**kwargs)
|
35
61
|
# end
|
36
62
|
#
|
37
|
-
# @example AllowOnlyRestArgument: false
|
63
|
+
# @example AllowOnlyRestArgument: false (only relevant for Ruby < 3.2)
|
38
64
|
# # bad
|
39
65
|
# # The following code can replace the arguments with `...`,
|
40
66
|
# # but it will change the behavior. Because `...` forwards block also.
|
@@ -53,102 +79,293 @@ module RuboCop
|
|
53
79
|
|
54
80
|
minimum_target_ruby_version 2.7
|
55
81
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
# @!method only_rest_arguments?(node, name)
|
64
|
-
def_node_matcher :only_rest_arguments?, <<~PATTERN
|
65
|
-
{
|
66
|
-
(send _ _ (splat (lvar %1)))
|
67
|
-
(send _ _ (hash (kwsplat (lvar %1))))
|
68
|
-
}
|
69
|
-
PATTERN
|
70
|
-
|
71
|
-
# @!method forwarding_method_arguments?(node, rest_name, block_name, kwargs_name)
|
72
|
-
def_node_matcher :forwarding_method_arguments?, <<~PATTERN
|
73
|
-
{
|
74
|
-
(send _ _
|
75
|
-
(splat (lvar %1))
|
76
|
-
(block-pass {(lvar %2) nil?}))
|
77
|
-
(send _ _
|
78
|
-
(splat (lvar %1))
|
79
|
-
(hash (kwsplat (lvar %3)))
|
80
|
-
(block-pass {(lvar %2) nil?}))
|
81
|
-
}
|
82
|
-
PATTERN
|
82
|
+
FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
|
83
|
+
ADDITIONAL_ARG_TYPES = %i[lvar arg].freeze
|
84
|
+
|
85
|
+
FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
|
86
|
+
ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
|
87
|
+
KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
|
83
88
|
|
84
89
|
def on_def(node)
|
85
90
|
return unless node.body
|
86
|
-
return unless (rest_args_name, args = use_rest_arguments?(node.arguments))
|
87
|
-
return if args.any?(&:default?)
|
88
91
|
|
89
|
-
node.
|
90
|
-
|
92
|
+
forwardable_args = extract_forwardable_args(node.arguments)
|
93
|
+
|
94
|
+
send_classifications = classify_send_nodes(
|
95
|
+
node,
|
96
|
+
node.each_descendant(:send).to_a,
|
97
|
+
non_splat_or_block_pass_lvar_references(node.body),
|
98
|
+
forwardable_args
|
99
|
+
)
|
91
100
|
|
92
|
-
|
93
|
-
all_lvars_as_forwarding_method_arguments?(node, send_node)
|
101
|
+
return if send_classifications.empty?
|
94
102
|
|
95
|
-
|
96
|
-
|
103
|
+
if only_forwards_all?(send_classifications)
|
104
|
+
add_forward_all_offenses(node, send_classifications, forwardable_args)
|
105
|
+
elsif target_ruby_version >= 3.2
|
106
|
+
add_post_ruby_32_offenses(node, send_classifications, forwardable_args)
|
97
107
|
end
|
98
108
|
end
|
109
|
+
|
99
110
|
alias on_defs on_def
|
100
111
|
|
101
112
|
private
|
102
113
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
114
|
+
def extract_forwardable_args(args)
|
115
|
+
[args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
|
116
|
+
end
|
106
117
|
|
107
|
-
|
118
|
+
def only_forwards_all?(send_classifications)
|
119
|
+
send_classifications.all? { |_, c, _, _| c == :all }
|
108
120
|
end
|
109
121
|
|
110
|
-
def
|
111
|
-
|
122
|
+
def add_forward_all_offenses(node, send_classifications, forwardable_args)
|
123
|
+
send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
|
124
|
+
register_forward_all_offense(send_node, send_node, forward_rest)
|
125
|
+
end
|
112
126
|
|
113
|
-
|
127
|
+
rest_arg, _kwrest_arg, _block_arg = *forwardable_args
|
128
|
+
register_forward_all_offense(node, node.arguments, rest_arg)
|
114
129
|
end
|
115
130
|
|
116
|
-
def
|
117
|
-
|
131
|
+
def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
|
132
|
+
return unless use_anonymous_forwarding?
|
133
|
+
|
134
|
+
rest_arg, kwrest_arg, _block_arg = *forwardable_args
|
118
135
|
|
119
|
-
|
120
|
-
|
136
|
+
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
|
137
|
+
if forward_rest
|
138
|
+
register_forward_args_offense(def_node.arguments, rest_arg)
|
139
|
+
register_forward_args_offense(send_node, forward_rest)
|
140
|
+
end
|
121
141
|
|
122
|
-
|
142
|
+
if forward_kwrest
|
143
|
+
register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
|
144
|
+
register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
|
145
|
+
end
|
146
|
+
end
|
123
147
|
end
|
124
148
|
|
125
|
-
def
|
126
|
-
|
127
|
-
|
128
|
-
range = range_between(begin_pos, forwarding_method.source_range.end_pos)
|
149
|
+
def non_splat_or_block_pass_lvar_references(body)
|
150
|
+
body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
|
151
|
+
parent = lvar.parent
|
129
152
|
|
130
|
-
|
131
|
-
|
153
|
+
next if lvar.lvar_type? && FORWARDING_LVAR_TYPES.include?(parent.type)
|
154
|
+
|
155
|
+
lvar.children.first
|
156
|
+
end.uniq
|
132
157
|
end
|
133
158
|
|
134
|
-
def
|
135
|
-
|
136
|
-
|
137
|
-
|
159
|
+
def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
|
160
|
+
send_nodes.filter_map do |send_node|
|
161
|
+
classification_and_forwards = classification_and_forwards(
|
162
|
+
def_node,
|
163
|
+
send_node,
|
164
|
+
referenced_lvars,
|
165
|
+
forwardable_args
|
138
166
|
)
|
139
|
-
|
167
|
+
|
168
|
+
next unless classification_and_forwards
|
169
|
+
|
170
|
+
[send_node, *classification_and_forwards]
|
140
171
|
end
|
141
172
|
end
|
142
173
|
|
143
|
-
def
|
144
|
-
|
174
|
+
def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
|
175
|
+
classifier = SendNodeClassifier.new(
|
176
|
+
def_node,
|
177
|
+
send_node,
|
178
|
+
referenced_lvars,
|
179
|
+
forwardable_args,
|
180
|
+
target_ruby_version: target_ruby_version,
|
181
|
+
allow_only_rest_arguments: allow_only_rest_arguments?
|
182
|
+
)
|
183
|
+
|
184
|
+
classification = classifier.classification
|
145
185
|
|
146
|
-
|
186
|
+
return unless classification
|
187
|
+
|
188
|
+
[classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
|
189
|
+
end
|
190
|
+
|
191
|
+
def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
|
192
|
+
add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
|
193
|
+
add_parens_if_missing(def_arguments_or_send, corrector)
|
194
|
+
|
195
|
+
corrector.replace(rest_arg_or_splat, '*')
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def register_forward_kwargs_offense(add_parens, def_arguments_or_send, kwrest_arg_or_splat)
|
200
|
+
add_offense(kwrest_arg_or_splat, message: KWARGS_MSG) do |corrector|
|
201
|
+
add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
|
202
|
+
|
203
|
+
corrector.replace(kwrest_arg_or_splat, '**')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def register_forward_all_offense(def_or_send, send_or_arguments, rest_or_splat)
|
208
|
+
arg_range = arguments_range(def_or_send, rest_or_splat)
|
209
|
+
|
210
|
+
add_offense(arg_range, message: FORWARDING_MSG) do |corrector|
|
211
|
+
add_parens_if_missing(send_or_arguments, corrector)
|
212
|
+
|
213
|
+
corrector.replace(arg_range, '...')
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def arguments_range(node, first_node)
|
218
|
+
arguments = node.arguments.reject { |arg| ADDITIONAL_ARG_TYPES.include?(arg.type) }
|
219
|
+
|
220
|
+
start_node = first_node || arguments.first
|
221
|
+
|
222
|
+
range_between(start_node.source_range.begin_pos, arguments.last.source_range.end_pos)
|
147
223
|
end
|
148
224
|
|
149
225
|
def allow_only_rest_arguments?
|
150
226
|
cop_config.fetch('AllowOnlyRestArgument', true)
|
151
227
|
end
|
228
|
+
|
229
|
+
def use_anonymous_forwarding?
|
230
|
+
cop_config.fetch('UseAnonymousForwarding', false)
|
231
|
+
end
|
232
|
+
|
233
|
+
def add_parens_if_missing(node, corrector)
|
234
|
+
return if parentheses?(node)
|
235
|
+
|
236
|
+
add_parentheses(node, corrector)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Classifies send nodes for possible rest/kwrest/all (including block) forwarding.
|
240
|
+
class SendNodeClassifier
|
241
|
+
extend NodePattern::Macros
|
242
|
+
|
243
|
+
# @!method forwarded_rest_arg?(node, rest_name)
|
244
|
+
def_node_matcher :forwarded_rest_arg?, '(splat (lvar %1))'
|
245
|
+
|
246
|
+
# @!method extract_forwarded_kwrest_arg(node, kwrest_name)
|
247
|
+
def_node_matcher :extract_forwarded_kwrest_arg, '(hash <$(kwsplat (lvar %1)) ...>)'
|
248
|
+
|
249
|
+
# @!method forwarded_block_arg?(node, block_name)
|
250
|
+
def_node_matcher :forwarded_block_arg?, '(block_pass {(lvar %1) nil?})'
|
251
|
+
|
252
|
+
def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
|
253
|
+
@def_node = def_node
|
254
|
+
@send_node = send_node
|
255
|
+
@referenced_lvars = referenced_lvars
|
256
|
+
@rest_arg, @kwrest_arg, @block_arg = *forwardable_args
|
257
|
+
@rest_arg_name, @kwrest_arg_name, @block_arg_name =
|
258
|
+
*forwardable_args.map { |a| a&.name }
|
259
|
+
@config = config
|
260
|
+
end
|
261
|
+
|
262
|
+
def forwarded_rest_arg
|
263
|
+
return nil if referenced_rest_arg?
|
264
|
+
|
265
|
+
arguments.find { |arg| forwarded_rest_arg?(arg, @rest_arg_name) }
|
266
|
+
end
|
267
|
+
|
268
|
+
def forwarded_kwrest_arg
|
269
|
+
return nil if referenced_kwrest_arg?
|
270
|
+
|
271
|
+
arguments.filter_map { |arg| extract_forwarded_kwrest_arg(arg, @kwrest_arg_name) }.first
|
272
|
+
end
|
273
|
+
|
274
|
+
def forwarded_block_arg
|
275
|
+
return nil if referenced_block_arg?
|
276
|
+
|
277
|
+
arguments.find { |arg| forwarded_block_arg?(arg, @block_arg_name) }
|
278
|
+
end
|
279
|
+
|
280
|
+
def classification
|
281
|
+
return nil unless forwarded_rest_arg || forwarded_kwrest_arg
|
282
|
+
|
283
|
+
if can_forward_all?
|
284
|
+
:all
|
285
|
+
else
|
286
|
+
:rest_or_kwrest
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
private
|
291
|
+
|
292
|
+
def can_forward_all?
|
293
|
+
return false if any_arg_referenced?
|
294
|
+
return false if ruby_32_missing_rest_or_kwest?
|
295
|
+
return false unless offensive_block_forwarding?
|
296
|
+
return false if additional_kwargs_or_forwarded_kwargs?
|
297
|
+
|
298
|
+
no_additional_args? || (target_ruby_version >= 3.0 && no_post_splat_args?)
|
299
|
+
end
|
300
|
+
|
301
|
+
def ruby_32_missing_rest_or_kwest?
|
302
|
+
target_ruby_version >= 3.2 && !forwarded_rest_and_kwrest_args
|
303
|
+
end
|
304
|
+
|
305
|
+
def offensive_block_forwarding?
|
306
|
+
@block_arg ? forwarded_block_arg : allow_offense_for_no_block?
|
307
|
+
end
|
308
|
+
|
309
|
+
def forwarded_rest_and_kwrest_args
|
310
|
+
forwarded_rest_arg && forwarded_kwrest_arg
|
311
|
+
end
|
312
|
+
|
313
|
+
def arguments
|
314
|
+
@send_node.arguments
|
315
|
+
end
|
316
|
+
|
317
|
+
def referenced_rest_arg?
|
318
|
+
@referenced_lvars.include?(@rest_arg_name)
|
319
|
+
end
|
320
|
+
|
321
|
+
def referenced_kwrest_arg?
|
322
|
+
@referenced_lvars.include?(@kwrest_arg_name)
|
323
|
+
end
|
324
|
+
|
325
|
+
def referenced_block_arg?
|
326
|
+
@referenced_lvars.include?(@block_arg_name)
|
327
|
+
end
|
328
|
+
|
329
|
+
def any_arg_referenced?
|
330
|
+
referenced_rest_arg? || referenced_kwrest_arg? || referenced_block_arg?
|
331
|
+
end
|
332
|
+
|
333
|
+
def target_ruby_version
|
334
|
+
@config.fetch(:target_ruby_version)
|
335
|
+
end
|
336
|
+
|
337
|
+
def no_post_splat_args?
|
338
|
+
return true unless (splat_index = arguments.index(forwarded_rest_arg))
|
339
|
+
|
340
|
+
arg_after_splat = arguments[splat_index + 1]
|
341
|
+
[nil, :hash, :block_pass].include?(arg_after_splat&.type)
|
342
|
+
end
|
343
|
+
|
344
|
+
def additional_kwargs_or_forwarded_kwargs?
|
345
|
+
additional_kwargs? || forward_additional_kwargs?
|
346
|
+
end
|
347
|
+
|
348
|
+
def additional_kwargs?
|
349
|
+
@def_node.arguments.any? { |a| a.kwarg_type? || a.kwoptarg_type? }
|
350
|
+
end
|
351
|
+
|
352
|
+
def forward_additional_kwargs?
|
353
|
+
return false unless forwarded_kwrest_arg
|
354
|
+
|
355
|
+
!forwarded_kwrest_arg.parent.children.one?
|
356
|
+
end
|
357
|
+
|
358
|
+
def allow_offense_for_no_block?
|
359
|
+
!@config.fetch(:allow_only_rest_arguments)
|
360
|
+
end
|
361
|
+
|
362
|
+
def no_additional_args?
|
363
|
+
forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
|
364
|
+
|
365
|
+
@def_node.arguments.size == forwardable_count &&
|
366
|
+
@send_node.arguments.size == forwardable_count
|
367
|
+
end
|
368
|
+
end
|
152
369
|
end
|
153
370
|
end
|
154
371
|
end
|
@@ -11,6 +11,15 @@ module RuboCop
|
|
11
11
|
# The `array1.intersect?(array2)` method is faster than
|
12
12
|
# `(array1 & array2).any?` and is more readable.
|
13
13
|
#
|
14
|
+
# In cases like the following, compatibility is not ensured,
|
15
|
+
# so it will not be detected when using block argument.
|
16
|
+
#
|
17
|
+
# [source,ruby]
|
18
|
+
# ----
|
19
|
+
# ([1] & [1,2]).any? { |x| false } # => false
|
20
|
+
# [1].intersect?([1,2]) { |x| false } # => true
|
21
|
+
# ----
|
22
|
+
#
|
14
23
|
# @safety
|
15
24
|
# This cop cannot guarantee that `array1` and `array2` are
|
16
25
|
# actually arrays while method `intersect?` is for arrays only.
|
@@ -68,16 +77,15 @@ module RuboCop
|
|
68
77
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
69
78
|
|
70
79
|
def on_send(node)
|
80
|
+
return if (parent = node.parent) && (parent.block_type? || parent.numblock_type?)
|
71
81
|
return unless (receiver, argument, method_name = bad_intersection_check?(node))
|
72
82
|
|
73
83
|
message = message(receiver.source, argument.source, method_name)
|
74
84
|
|
75
85
|
add_offense(node, message: message) do |corrector|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
|
80
|
-
end
|
86
|
+
bang = straight?(method_name) ? '' : '!'
|
87
|
+
|
88
|
+
corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
@@ -370,7 +370,8 @@ module RuboCop
|
|
370
370
|
def special_method_proper_block_style?(node)
|
371
371
|
method_name = node.method_name
|
372
372
|
return true if allowed_method?(method_name) || matches_allowed_pattern?(method_name)
|
373
|
-
|
373
|
+
|
374
|
+
node.braces? if braces_required_method?(method_name)
|
374
375
|
end
|
375
376
|
|
376
377
|
def braces_required_method?(method_name)
|
@@ -8,6 +8,11 @@ module RuboCop
|
|
8
8
|
# `==`, `equal?`, and `eql?` custom method definitions are allowed by default.
|
9
9
|
# These are customizable with `AllowedMethods` option.
|
10
10
|
#
|
11
|
+
# @safety
|
12
|
+
# This cop's autocorrection is unsafe because there is no guarantee that
|
13
|
+
# the constant `Foo` exists when autocorrecting `var.class.name == 'Foo'` to
|
14
|
+
# `var.instance_of?(Foo)`.
|
15
|
+
#
|
11
16
|
# @example
|
12
17
|
# # bad
|
13
18
|
# var.class == Date
|
@@ -69,6 +74,8 @@ module RuboCop
|
|
69
74
|
matches_allowed_pattern?(def_node.method_name))
|
70
75
|
|
71
76
|
class_comparison_candidate?(node) do |receiver_node, class_node|
|
77
|
+
return if class_node.dstr_type?
|
78
|
+
|
72
79
|
range = offense_range(receiver_node, node)
|
73
80
|
class_argument = (class_name = class_name(class_node, node)) ? "(#{class_name})" : ''
|
74
81
|
|
@@ -25,6 +25,7 @@ module RuboCop
|
|
25
25
|
# # bad
|
26
26
|
# items.collect
|
27
27
|
# items.collect!
|
28
|
+
# items.collect_concat
|
28
29
|
# items.inject
|
29
30
|
# items.detect
|
30
31
|
# items.find_all
|
@@ -33,6 +34,7 @@ module RuboCop
|
|
33
34
|
# # good
|
34
35
|
# items.map
|
35
36
|
# items.map!
|
37
|
+
# items.flat_map
|
36
38
|
# items.reduce
|
37
39
|
# items.find
|
38
40
|
# items.select
|
@@ -67,6 +67,7 @@ module RuboCop
|
|
67
67
|
return unless node.parent&.begin_type?
|
68
68
|
return unless collection_looping_method?(node)
|
69
69
|
return unless same_collection_looping_block?(node, node.left_sibling)
|
70
|
+
return unless node.body && node.left_sibling.body
|
70
71
|
|
71
72
|
add_offense(node) do |corrector|
|
72
73
|
combine_with_left_sibling(corrector, node)
|
@@ -92,8 +93,9 @@ module RuboCop
|
|
92
93
|
end
|
93
94
|
|
94
95
|
def same_collection_looping_block?(node, sibling)
|
95
|
-
(sibling
|
96
|
-
|
96
|
+
return false if sibling.nil? || (!sibling.block_type? && !sibling.numblock_type?)
|
97
|
+
|
98
|
+
sibling.method?(node.method_name) &&
|
97
99
|
sibling.receiver == node.receiver &&
|
98
100
|
sibling.send_node.arguments == node.send_node.arguments
|
99
101
|
end
|
@@ -40,9 +40,13 @@ module RuboCop
|
|
40
40
|
extend AutoCorrector
|
41
41
|
|
42
42
|
MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
|
43
|
+
NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend].freeze
|
43
44
|
|
45
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
44
46
|
def on_case(case_node)
|
45
|
-
|
47
|
+
if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
|
48
|
+
return
|
49
|
+
end
|
46
50
|
|
47
51
|
branch_bodies = [*case_node.when_branches.map(&:body), case_node.else_branch].compact
|
48
52
|
|
@@ -52,6 +56,7 @@ module RuboCop
|
|
52
56
|
|
53
57
|
add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
|
54
58
|
end
|
59
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
55
60
|
|
56
61
|
private
|
57
62
|
|