rubocop 1.77.0 → 1.80.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 +1 -3
- data/config/default.yml +36 -20
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli.rb +17 -1
- data/lib/rubocop/config_loader.rb +1 -38
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
- data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -1
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/self_assignment.rb +5 -4
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/naming/method_name.rb +127 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +30 -4
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
- data/lib/rubocop/cop/style/array_intersect.rb +98 -34
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
- data/lib/rubocop/cop/style/map_to_set.rb +1 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
- data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +28 -11
- data/lib/rubocop/cop/style/safe_navigation.rb +20 -1
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +25 -8
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/routes.rb +35 -6
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/result_cache.rb +14 -12
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/server/cache.rb +4 -2
- data/lib/rubocop/server/client_command/base.rb +10 -0
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/server/client_command/start.rb +11 -1
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- metadata +9 -6
@@ -39,6 +39,26 @@ module RuboCop
|
|
39
39
|
# # good
|
40
40
|
# def foo_bar; end
|
41
41
|
#
|
42
|
+
# # bad
|
43
|
+
# define_method :fooBar do
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# # good
|
47
|
+
# define_method :foo_bar do
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# # bad
|
51
|
+
# Struct.new(:fooBar)
|
52
|
+
#
|
53
|
+
# # good
|
54
|
+
# Struct.new(:foo_bar)
|
55
|
+
#
|
56
|
+
# # bad
|
57
|
+
# alias_method :fooBar, :some_method
|
58
|
+
#
|
59
|
+
# # good
|
60
|
+
# alias_method :foo_bar, :some_method
|
61
|
+
#
|
42
62
|
# @example EnforcedStyle: camelCase
|
43
63
|
# # bad
|
44
64
|
# def foo_bar; end
|
@@ -46,6 +66,26 @@ module RuboCop
|
|
46
66
|
# # good
|
47
67
|
# def fooBar; end
|
48
68
|
#
|
69
|
+
# # bad
|
70
|
+
# define_method :foo_bar do
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # good
|
74
|
+
# define_method :fooBar do
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# # bad
|
78
|
+
# Struct.new(:foo_bar)
|
79
|
+
#
|
80
|
+
# # good
|
81
|
+
# Struct.new(:fooBar)
|
82
|
+
#
|
83
|
+
# # bad
|
84
|
+
# alias_method :foo_bar, :some_method
|
85
|
+
#
|
86
|
+
# # good
|
87
|
+
# alias_method :fooBar, :some_method
|
88
|
+
#
|
49
89
|
# @example ForbiddenIdentifiers: ['def', 'super']
|
50
90
|
# # bad
|
51
91
|
# def def; end
|
@@ -66,13 +106,79 @@ module RuboCop
|
|
66
106
|
MSG = 'Use %<style>s for method names.'
|
67
107
|
MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
|
68
108
|
|
109
|
+
OPERATOR_METHODS = %i[| ^ & <=> == === =~ > >= < <= << >> + - * /
|
110
|
+
% ** ~ +@ -@ !@ ~@ [] []= ! != !~ `].to_set.freeze
|
111
|
+
|
69
112
|
# @!method sym_name(node)
|
70
113
|
def_node_matcher :sym_name, '(sym $_name)'
|
71
114
|
|
72
115
|
# @!method str_name(node)
|
73
116
|
def_node_matcher :str_name, '(str $_name)'
|
74
117
|
|
118
|
+
# @!method new_struct?(node)
|
119
|
+
def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
|
120
|
+
|
121
|
+
# @!method define_data?(node)
|
122
|
+
def_node_matcher :define_data?, '(send (const {nil? cbase} :Data) :define ...)'
|
123
|
+
|
75
124
|
def on_send(node)
|
125
|
+
if node.method?(:define_method) || node.method?(:define_singleton_method)
|
126
|
+
handle_define_method(node)
|
127
|
+
elsif new_struct?(node)
|
128
|
+
handle_new_struct(node)
|
129
|
+
elsif define_data?(node)
|
130
|
+
handle_define_data(node)
|
131
|
+
elsif node.method?(:alias_method)
|
132
|
+
handle_alias_method(node)
|
133
|
+
else
|
134
|
+
handle_attr_accessor(node)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def on_def(node)
|
139
|
+
return if node.operator_method? || matches_allowed_pattern?(node.method_name)
|
140
|
+
|
141
|
+
if forbidden_name?(node.method_name.to_s)
|
142
|
+
register_forbidden_name(node)
|
143
|
+
else
|
144
|
+
check_name(node, node.method_name, node.loc.name)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
alias on_defs on_def
|
148
|
+
|
149
|
+
def on_alias(node)
|
150
|
+
handle_method_name(node.new_identifier, node.new_identifier.value)
|
151
|
+
end
|
152
|
+
|
153
|
+
private
|
154
|
+
|
155
|
+
def handle_define_method(node)
|
156
|
+
return unless node.first_argument&.type?(:str, :sym)
|
157
|
+
|
158
|
+
handle_method_name(node, node.first_argument.value)
|
159
|
+
end
|
160
|
+
|
161
|
+
def handle_new_struct(node)
|
162
|
+
arguments = node.first_argument&.str_type? ? node.arguments[1..] : node.arguments
|
163
|
+
arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
|
164
|
+
handle_method_name(name, name.value)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def handle_define_data(node)
|
169
|
+
node.arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
|
170
|
+
handle_method_name(name, name.value)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def handle_alias_method(node)
|
175
|
+
return unless node.arguments.size == 2
|
176
|
+
return unless node.first_argument.type?(:str, :sym)
|
177
|
+
|
178
|
+
handle_method_name(node.first_argument, node.first_argument.value)
|
179
|
+
end
|
180
|
+
|
181
|
+
def handle_attr_accessor(node)
|
76
182
|
return unless (attrs = node.attribute_accessor?)
|
77
183
|
|
78
184
|
attrs.last.each do |name_item|
|
@@ -87,45 +193,53 @@ module RuboCop
|
|
87
193
|
end
|
88
194
|
end
|
89
195
|
|
90
|
-
def
|
91
|
-
return if
|
196
|
+
def handle_method_name(node, name)
|
197
|
+
return if !name || matches_allowed_pattern?(name)
|
92
198
|
|
93
|
-
if forbidden_name?(
|
199
|
+
if forbidden_name?(name.to_s)
|
94
200
|
register_forbidden_name(node)
|
95
|
-
|
96
|
-
check_name(node,
|
201
|
+
elsif !OPERATOR_METHODS.include?(name.to_sym)
|
202
|
+
check_name(node, name, range_position(node))
|
97
203
|
end
|
98
204
|
end
|
99
|
-
alias on_defs on_def
|
100
|
-
|
101
|
-
private
|
102
205
|
|
103
206
|
def forbidden_name?(name)
|
104
207
|
forbidden_identifier?(name) || forbidden_pattern?(name)
|
105
208
|
end
|
106
209
|
|
210
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
107
211
|
def register_forbidden_name(node)
|
108
212
|
if node.any_def_type?
|
109
213
|
name_node = node.loc.name
|
110
214
|
method_name = node.method_name
|
111
|
-
|
112
|
-
|
215
|
+
elsif node.literal?
|
216
|
+
name_node = node
|
217
|
+
method_name = node.value
|
218
|
+
elsif (attrs = node.attribute_accessor?)
|
113
219
|
name_node = attrs.last.last
|
114
220
|
method_name = attr_name(name_node)
|
221
|
+
else
|
222
|
+
name_node = node.first_argument
|
223
|
+
method_name = node.first_argument.value
|
115
224
|
end
|
116
225
|
message = format(MSG_FORBIDDEN, identifier: method_name)
|
117
226
|
add_offense(name_node, message: message)
|
118
227
|
end
|
228
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
119
229
|
|
120
230
|
def attr_name(name_item)
|
121
231
|
sym_name(name_item) || str_name(name_item)
|
122
232
|
end
|
123
233
|
|
124
234
|
def range_position(node)
|
125
|
-
|
126
|
-
|
235
|
+
if node.loc.respond_to?(:selector)
|
236
|
+
selector_end_pos = node.loc.selector.end_pos + 1
|
237
|
+
expr_end_pos = node.source_range.end_pos
|
127
238
|
|
128
|
-
|
239
|
+
range_between(selector_end_pos, expr_end_pos)
|
240
|
+
else
|
241
|
+
node.source_range
|
242
|
+
end
|
129
243
|
end
|
130
244
|
|
131
245
|
def message(style)
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
# method calls are assumed to return boolean values. The cop does not make an assessment
|
15
15
|
# if the return type is unknown (non-predicate method calls, variables, etc.).
|
16
16
|
#
|
17
|
-
# NOTE:
|
17
|
+
# NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
|
18
18
|
#
|
19
19
|
# By default, the cop runs in `conservative` mode, which allows a method to be named
|
20
20
|
# with a question mark as long as at least one return value is boolean. In `aggressive`
|
@@ -26,6 +26,11 @@ module RuboCop
|
|
26
26
|
# guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
|
27
27
|
# configuration to allow method names by regular expression.
|
28
28
|
#
|
29
|
+
# Although returning a call to another predicate method is treated as a boolean value,
|
30
|
+
# certain method names can be known to not return a boolean, despite ending in a `?`
|
31
|
+
# (for example, `Numeric#nonzero?` returns `self` or `nil`). These methods can be
|
32
|
+
# configured using `NonBooleanPredicates`.
|
33
|
+
#
|
29
34
|
# The cop can furthermore be configured to allow all bang methods (method names
|
30
35
|
# ending with `!`), with `AllowBangMethods: true` (default false).
|
31
36
|
#
|
@@ -144,7 +149,8 @@ module RuboCop
|
|
144
149
|
private
|
145
150
|
|
146
151
|
def allowed?(node)
|
147
|
-
|
152
|
+
node.method?(:initialize) ||
|
153
|
+
allowed_method?(node.method_name) ||
|
148
154
|
matches_allowed_pattern?(node.method_name) ||
|
149
155
|
allowed_bang_method?(node) ||
|
150
156
|
node.operator_method? ||
|
@@ -164,7 +170,7 @@ module RuboCop
|
|
164
170
|
def unknown_method_call?(value)
|
165
171
|
return false unless value.call_type?
|
166
172
|
|
167
|
-
!
|
173
|
+
!method_returning_boolean?(value)
|
168
174
|
end
|
169
175
|
|
170
176
|
def return_values(node)
|
@@ -190,7 +196,13 @@ module RuboCop
|
|
190
196
|
|
191
197
|
def boolean_return?(value)
|
192
198
|
return true if value.boolean_type?
|
199
|
+
|
200
|
+
method_returning_boolean?(value)
|
201
|
+
end
|
202
|
+
|
203
|
+
def method_returning_boolean?(value)
|
193
204
|
return false unless value.call_type?
|
205
|
+
return false if wayward_predicate?(value.method_name)
|
194
206
|
|
195
207
|
value.comparison_method? || value.predicate_method? || value.negation_method?
|
196
208
|
end
|
@@ -258,7 +270,10 @@ module RuboCop
|
|
258
270
|
node.body ? [last_value(node.body)] : [s(:nil)]
|
259
271
|
else
|
260
272
|
# Branches with no value act as an implicit `nil`.
|
261
|
-
node.branches.
|
273
|
+
branches = node.branches.map { |branch| branch ? last_value(branch) : s(:nil) }
|
274
|
+
# Missing else branches also act as an implicit `nil`.
|
275
|
+
branches.push(s(:nil)) unless node.else_branch
|
276
|
+
branches
|
262
277
|
end
|
263
278
|
end
|
264
279
|
|
@@ -275,6 +290,17 @@ module RuboCop
|
|
275
290
|
def allow_bang_methods?
|
276
291
|
cop_config.fetch('AllowBangMethods', false)
|
277
292
|
end
|
293
|
+
|
294
|
+
# If a method ending in `?` is known to not return a boolean value,
|
295
|
+
# (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
|
296
|
+
# value, despite the method naming.
|
297
|
+
def wayward_predicate?(name)
|
298
|
+
wayward_predicates.include?(name.to_s)
|
299
|
+
end
|
300
|
+
|
301
|
+
def wayward_predicates
|
302
|
+
Array(cop_config.fetch('WaywardPredicates', []))
|
303
|
+
end
|
278
304
|
end
|
279
305
|
end
|
280
306
|
end
|
@@ -11,13 +11,14 @@ module RuboCop
|
|
11
11
|
#
|
12
12
|
# eval(something)
|
13
13
|
# binding.eval(something)
|
14
|
+
# Kernel.eval(something)
|
14
15
|
class Eval < Base
|
15
16
|
MSG = 'The use of `eval` is a serious security risk.'
|
16
17
|
RESTRICT_ON_SEND = %i[eval].freeze
|
17
18
|
|
18
19
|
# @!method eval?(node)
|
19
20
|
def_node_matcher :eval?, <<~PATTERN
|
20
|
-
(send {nil? (send nil? :binding)} :eval $!str ...)
|
21
|
+
(send {nil? (send nil? :binding) (const {cbase nil?} :Kernel)} :eval $!str ...)
|
21
22
|
PATTERN
|
22
23
|
|
23
24
|
def on_send(node)
|
@@ -348,7 +348,7 @@ module RuboCop
|
|
348
348
|
end
|
349
349
|
|
350
350
|
def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
|
351
|
-
def_node = begin_node.children[1]
|
351
|
+
def_node = begin_node.children[begin_node.children.index(modifier_node) + 1]
|
352
352
|
range = modifier_node.source_range.begin.join(def_node.source_range.begin)
|
353
353
|
corrector.remove(range)
|
354
354
|
end
|
@@ -84,7 +84,10 @@ module RuboCop
|
|
84
84
|
|
85
85
|
def autocorrect(corrector, node)
|
86
86
|
if (preferred_accessors = preferred_accessors(node))
|
87
|
-
corrector.replace(
|
87
|
+
corrector.replace(
|
88
|
+
grouped_style? ? node : range_with_trailing_argument_comment(node),
|
89
|
+
preferred_accessors
|
90
|
+
)
|
88
91
|
else
|
89
92
|
range = range_with_surrounding_space(node.source_range, side: :left)
|
90
93
|
corrector.remove(range)
|
@@ -196,6 +199,15 @@ module RuboCop
|
|
196
199
|
end
|
197
200
|
end.join("\n")
|
198
201
|
end
|
202
|
+
|
203
|
+
def range_with_trailing_argument_comment(node)
|
204
|
+
comment = processed_source.ast_with_comments[node.last_argument].last
|
205
|
+
if comment
|
206
|
+
add_range(node.source_range, comment.source_range)
|
207
|
+
else
|
208
|
+
node
|
209
|
+
end
|
210
|
+
end
|
199
211
|
end
|
200
212
|
end
|
201
213
|
end
|
@@ -146,7 +146,6 @@ module RuboCop
|
|
146
146
|
minimum_target_ruby_version 2.7
|
147
147
|
|
148
148
|
FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
|
149
|
-
ADDITIONAL_ARG_TYPES = %i[lvar arg optarg].freeze
|
150
149
|
|
151
150
|
FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
|
152
151
|
ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
|
@@ -198,9 +197,9 @@ module RuboCop
|
|
198
197
|
send_classifications.all? { |_, c, _, _| all_classifications.include?(c) }
|
199
198
|
end
|
200
199
|
|
201
|
-
# rubocop:disable Metrics/MethodLength
|
200
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
202
201
|
def add_forward_all_offenses(node, send_classifications, forwardable_args)
|
203
|
-
|
202
|
+
rest_arg, kwrest_arg, block_arg = *forwardable_args
|
204
203
|
registered_block_arg_offense = false
|
205
204
|
|
206
205
|
send_classifications.each do |send_node, c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
|
@@ -212,16 +211,20 @@ module RuboCop
|
|
212
211
|
registered_block_arg_offense = true
|
213
212
|
break
|
214
213
|
else
|
215
|
-
|
214
|
+
first_arg = forward_rest || forward_kwrest || forward_all_first_argument(send_node)
|
215
|
+
register_forward_all_offense(send_node, send_node, first_arg)
|
216
216
|
end
|
217
217
|
end
|
218
218
|
|
219
219
|
return if registered_block_arg_offense
|
220
220
|
|
221
|
-
|
222
|
-
|
221
|
+
register_forward_all_offense(node, node.arguments, rest_arg || kwrest_arg)
|
222
|
+
end
|
223
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
224
|
+
|
225
|
+
def forward_all_first_argument(node)
|
226
|
+
node.arguments.reverse_each.find(&:forwarded_restarg_type?)
|
223
227
|
end
|
224
|
-
# rubocop:enable Metrics/MethodLength
|
225
228
|
|
226
229
|
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
227
230
|
def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
|
@@ -361,18 +364,9 @@ module RuboCop
|
|
361
364
|
end
|
362
365
|
end
|
363
366
|
|
364
|
-
# rubocop:disable Metrics/AbcSize
|
365
367
|
def arguments_range(node, first_node)
|
366
|
-
|
367
|
-
next true if ADDITIONAL_ARG_TYPES.include?(arg.type) || arg.variable? || arg.call_type?
|
368
|
-
|
369
|
-
arg.literal? && arg.each_descendant(:kwsplat).none?
|
370
|
-
end
|
371
|
-
|
372
|
-
start_node = first_node || arguments.first
|
373
|
-
start_node.source_range.begin.join(arguments.last.source_range.end)
|
368
|
+
first_node.source_range.begin.join(node.last_argument.source_range.end)
|
374
369
|
end
|
375
|
-
# rubocop:enable Metrics/AbcSize
|
376
370
|
|
377
371
|
def allow_only_rest_arguments?
|
378
372
|
cop_config.fetch('AllowOnlyRestArgument', true)
|
@@ -5,12 +5,17 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# In Ruby 3.1, `Array#intersect?` has been added.
|
7
7
|
#
|
8
|
-
# This cop identifies places where
|
9
|
-
# or `(array1.intersection(array2)).any?` can be replaced by
|
10
|
-
# `array1.intersect?(array2)`.
|
8
|
+
# This cop identifies places where:
|
11
9
|
#
|
12
|
-
#
|
13
|
-
# `(array1
|
10
|
+
# * `(array1 & array2).any?`
|
11
|
+
# * `(array1.intersection(array2)).any?`
|
12
|
+
# * `array1.any? { |elem| array2.member?(elem) }`
|
13
|
+
# * `(array1 & array2).count > 0`
|
14
|
+
# * `(array1 & array2).size > 0`
|
15
|
+
#
|
16
|
+
# can be replaced with `array1.intersect?(array2)`.
|
17
|
+
#
|
18
|
+
# `array1.intersect?(array2)` is faster and more readable.
|
14
19
|
#
|
15
20
|
# In cases like the following, compatibility is not ensured,
|
16
21
|
# so it will not be detected when using block argument.
|
@@ -40,10 +45,27 @@ module RuboCop
|
|
40
45
|
# array1.intersection(array2).empty?
|
41
46
|
# array1.intersection(array2).none?
|
42
47
|
#
|
48
|
+
# # bad
|
49
|
+
# array1.any? { |elem| array2.member?(elem) }
|
50
|
+
# array1.none? { |elem| array2.member?(elem) }
|
51
|
+
#
|
43
52
|
# # good
|
44
53
|
# array1.intersect?(array2)
|
45
54
|
# !array1.intersect?(array2)
|
46
55
|
#
|
56
|
+
# # bad
|
57
|
+
# (array1 & array2).count > 0
|
58
|
+
# (array1 & array2).count.positive?
|
59
|
+
# (array1 & array2).count != 0
|
60
|
+
#
|
61
|
+
# (array1 & array2).count == 0
|
62
|
+
# (array1 & array2).count.zero?
|
63
|
+
#
|
64
|
+
# # good
|
65
|
+
# array1.intersect?(array2)
|
66
|
+
#
|
67
|
+
# !array1.intersect?(array2)
|
68
|
+
#
|
47
69
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
48
70
|
# # good
|
49
71
|
# (array1 & array2).present?
|
@@ -66,9 +88,11 @@ module RuboCop
|
|
66
88
|
PREDICATES = %i[any? empty? none?].to_set.freeze
|
67
89
|
ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
|
68
90
|
|
91
|
+
ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
|
92
|
+
|
69
93
|
# @!method bad_intersection_check?(node, predicates)
|
70
94
|
def_node_matcher :bad_intersection_check?, <<~PATTERN
|
71
|
-
(call
|
95
|
+
$(call
|
72
96
|
{
|
73
97
|
(begin (send $_ :& $_))
|
74
98
|
(call $_ :intersection $_)
|
@@ -77,53 +101,93 @@ module RuboCop
|
|
77
101
|
)
|
78
102
|
PATTERN
|
79
103
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
104
|
+
# @!method intersection_size_check?(node, predicates)
|
105
|
+
def_node_matcher :intersection_size_check?, <<~PATTERN
|
106
|
+
(call
|
107
|
+
$(call
|
108
|
+
{
|
109
|
+
(begin (send $_ :& $_))
|
110
|
+
(call $_ :intersection $_)
|
111
|
+
}
|
112
|
+
%ARRAY_SIZE_METHODS
|
113
|
+
)
|
114
|
+
{$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
|
115
|
+
)
|
116
|
+
PATTERN
|
117
|
+
|
118
|
+
# @!method any_none_block_intersection(node)
|
119
|
+
def_node_matcher :any_none_block_intersection, <<~PATTERN
|
120
|
+
{
|
121
|
+
(block
|
122
|
+
(call $_receiver ${:any? :none?})
|
123
|
+
(args (arg _key))
|
124
|
+
(send $_argument :member? (lvar _key))
|
125
|
+
)
|
126
|
+
(numblock
|
127
|
+
(call $_receiver ${:any? :none?}) 1
|
128
|
+
(send $_argument :member? (lvar :_1))
|
129
|
+
)
|
130
|
+
(itblock
|
131
|
+
(call $_receiver ${:any? :none?}) :it
|
132
|
+
(send $_argument :member? (lvar :it))
|
133
|
+
)
|
134
|
+
}
|
135
|
+
PATTERN
|
136
|
+
|
137
|
+
MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
|
138
|
+
STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
|
139
|
+
NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
|
84
140
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
85
141
|
|
86
142
|
def on_send(node)
|
87
143
|
return if node.block_literal?
|
88
|
-
return unless (receiver, argument, method_name = bad_intersection?(node))
|
89
|
-
|
90
|
-
dot = node.loc.dot.source
|
91
|
-
message = message(receiver.source, argument.source, method_name, dot, node.source)
|
144
|
+
return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
|
92
145
|
|
93
|
-
|
94
|
-
|
146
|
+
dot = dot_node.loc.dot.source
|
147
|
+
bang = straight?(method_name) ? '' : '!'
|
148
|
+
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
95
149
|
|
96
|
-
|
97
|
-
end
|
150
|
+
register_offense(node, replacement)
|
98
151
|
end
|
99
152
|
alias on_csend on_send
|
100
153
|
|
154
|
+
def on_block(node)
|
155
|
+
return unless (receiver, method_name, argument = any_none_block_intersection(node))
|
156
|
+
|
157
|
+
dot = node.send_node.loc.dot.source
|
158
|
+
bang = method_name == :any? ? '' : '!'
|
159
|
+
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
160
|
+
|
161
|
+
register_offense(node, replacement)
|
162
|
+
end
|
163
|
+
alias on_numblock on_block
|
164
|
+
alias on_itblock on_block
|
165
|
+
|
101
166
|
private
|
102
167
|
|
103
168
|
def bad_intersection?(node)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
PREDICATES
|
108
|
-
end
|
169
|
+
bad_intersection_check?(node, bad_intersection_predicates) ||
|
170
|
+
intersection_size_check?(node)
|
171
|
+
end
|
109
172
|
|
110
|
-
|
173
|
+
def bad_intersection_predicates
|
174
|
+
if active_support_extensions_enabled?
|
175
|
+
ACTIVE_SUPPORT_PREDICATES
|
176
|
+
else
|
177
|
+
PREDICATES
|
178
|
+
end
|
111
179
|
end
|
112
180
|
|
113
181
|
def straight?(method_name)
|
114
182
|
STRAIGHT_METHODS.include?(method_name.to_sym)
|
115
183
|
end
|
116
184
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
argument: argument,
|
124
|
-
dot: dot,
|
125
|
-
existing: existing
|
126
|
-
)
|
185
|
+
def register_offense(node, replacement)
|
186
|
+
message = format(MSG, replacement: replacement, existing: node.source)
|
187
|
+
|
188
|
+
add_offense(node, message: message) do |corrector|
|
189
|
+
corrector.replace(node, replacement)
|
190
|
+
end
|
127
191
|
end
|
128
192
|
end
|
129
193
|
end
|
@@ -70,18 +70,25 @@ module RuboCop
|
|
70
70
|
(send _ :& _))
|
71
71
|
PATTERN
|
72
72
|
|
73
|
+
# rubocop:disable Metrics/AbcSize
|
73
74
|
def on_send(node)
|
74
75
|
return unless node.receiver&.begin_type?
|
75
76
|
return unless (preferred_method = preferred_method(node))
|
76
77
|
|
77
78
|
bit_operation = node.receiver.children.first
|
78
79
|
lhs, _operator, rhs = *bit_operation
|
79
|
-
|
80
|
+
|
81
|
+
preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
|
82
|
+
"#{rhs.source}.allbits?(#{lhs.source})"
|
83
|
+
else
|
84
|
+
"#{lhs.source}.#{preferred_method}(#{rhs.source})"
|
85
|
+
end
|
80
86
|
|
81
87
|
add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
|
82
88
|
corrector.replace(node, preferred)
|
83
89
|
end
|
84
90
|
end
|
91
|
+
# rubocop:enable Metrics/AbcSize
|
85
92
|
|
86
93
|
private
|
87
94
|
|
@@ -111,7 +111,7 @@ module RuboCop
|
|
111
111
|
end
|
112
112
|
end
|
113
113
|
|
114
|
-
#
|
114
|
+
# Checks for `if` and `case` statements where each branch is used for
|
115
115
|
# both the assignment and comparison of the same variable
|
116
116
|
# when using the return of the condition can be used instead.
|
117
117
|
#
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
#
|
6
|
+
# Checks for chained `dig` calls that can be collapsed into a single `dig`.
|
7
7
|
#
|
8
8
|
# @safety
|
9
9
|
# This cop is unsafe because it cannot be guaranteed that the receiver
|