rubocop 1.57.2 → 1.60.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +4 -4
- data/config/default.yml +46 -3
- data/lib/rubocop/config.rb +0 -2
- data/lib/rubocop/config_loader.rb +0 -1
- data/lib/rubocop/config_obsoletion.rb +11 -8
- data/lib/rubocop/config_validator.rb +0 -2
- data/lib/rubocop/cop/base.rb +6 -0
- data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
- data/lib/rubocop/cop/exclude_limit.rb +1 -1
- data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
- data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
- data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +7 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
- data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
- data/lib/rubocop/cop/lint/debugger.rb +2 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
- data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
- data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
- data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
- data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
- data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
- data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
- data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
- data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
- data/lib/rubocop/cop/lint/syntax.rb +6 -3
- data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
- data/lib/rubocop/cop/lint/useless_times.rb +1 -1
- data/lib/rubocop/cop/lint/void.rb +14 -1
- data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
- data/lib/rubocop/cop/naming/constant_name.rb +1 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/security/open.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +127 -17
- data/lib/rubocop/cop/style/array_first_last.rb +64 -0
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
- data/lib/rubocop/cop/style/case_like_if.rb +4 -4
- data/lib/rubocop/cop/style/class_check.rb +1 -0
- data/lib/rubocop/cop/style/collection_compact.rb +18 -8
- data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
- data/lib/rubocop/cop/style/date_time.rb +5 -4
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
- data/lib/rubocop/cop/style/each_with_object.rb +2 -2
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
- data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
- data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +105 -11
- data/lib/rubocop/cop/style/hash_except.rb +2 -1
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
- data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
- data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
- data/lib/rubocop/cop/style/next.rb +1 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
- data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +3 -3
- data/lib/rubocop/cop/style/redundant_each.rb +7 -4
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +33 -10
- data/lib/rubocop/cop/style/redundant_return.rb +1 -1
- data/lib/rubocop/cop/style/redundant_self.rb +17 -2
- data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
- data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
- data/lib/rubocop/cop/style/sample.rb +2 -1
- data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
- data/lib/rubocop/cop/style/self_assignment.rb +1 -1
- data/lib/rubocop/cop/style/semicolon.rb +8 -0
- data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
- data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
- data/lib/rubocop/cop/style/string_chars.rb +1 -0
- data/lib/rubocop/cop/style/strip.rb +7 -4
- data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
- data/lib/rubocop/cop/style/unpack_first.rb +11 -14
- data/lib/rubocop/cops_documentation_generator.rb +11 -1
- data/lib/rubocop/ext/regexp_node.rb +9 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
- data/lib/rubocop/formatter/html_formatter.rb +1 -2
- data/lib/rubocop/formatter/json_formatter.rb +0 -1
- data/lib/rubocop/formatter.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +1 -1
- data/lib/rubocop/options.rb +0 -8
- data/lib/rubocop/result_cache.rb +0 -1
- data/lib/rubocop/rspec/shared_contexts.rb +6 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/server/cache.rb +1 -2
- data/lib/rubocop/server/client_command/exec.rb +0 -1
- data/lib/rubocop/server/server_command/exec.rb +0 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +4 -0
- metadata +15 -10
- /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -12,7 +12,16 @@ module RuboCop
|
|
12
12
|
#
|
13
13
|
# This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
|
14
14
|
# replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
|
15
|
-
# by setting UseAnonymousForwarding: false
|
15
|
+
# by setting `UseAnonymousForwarding: false`.
|
16
|
+
#
|
17
|
+
# And this cop has `RedundantRestArgumentNames`, `RedundantKeywordRestArgumentNames`,
|
18
|
+
# and `RedundantBlockArgumentNames` options. This configuration is a list of redundant names
|
19
|
+
# that are sufficient for anonymizing meaningless naming.
|
20
|
+
#
|
21
|
+
# Meaningless names that are commonly used can be anonymized by default:
|
22
|
+
# e.g., `*args`, `**options`, `&block`, and so on.
|
23
|
+
#
|
24
|
+
# Names not on this list are likely to be meaningful and are allowed by default.
|
16
25
|
#
|
17
26
|
# @example
|
18
27
|
# # bad
|
@@ -72,6 +81,38 @@ module RuboCop
|
|
72
81
|
# bar(**kwargs)
|
73
82
|
# end
|
74
83
|
#
|
84
|
+
# @example RedundantRestArgumentNames: ['args', 'arguments'] (default)
|
85
|
+
# # bad
|
86
|
+
# def foo(*args)
|
87
|
+
# bar(*args)
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# # good
|
91
|
+
# def foo(*)
|
92
|
+
# bar(*)
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# @example RedundantKeywordRestArgumentNames: ['kwargs', 'options', 'opts'] (default)
|
96
|
+
# # bad
|
97
|
+
# def foo(**kwargs)
|
98
|
+
# bar(**kwargs)
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# # good
|
102
|
+
# def foo(**)
|
103
|
+
# bar(**)
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# @example RedundantBlockArgumentNames: ['blk', 'block', 'proc'] (default)
|
107
|
+
# # bad - But it is good with `EnforcedStyle: explicit` set for `Naming/BlockForwarding`.
|
108
|
+
# def foo(&block)
|
109
|
+
# bar(&block)
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# # good
|
113
|
+
# def foo(&)
|
114
|
+
# bar(&)
|
115
|
+
# end
|
75
116
|
class ArgumentsForwarding < Base
|
76
117
|
include RangeHelp
|
77
118
|
extend AutoCorrector
|
@@ -85,17 +126,21 @@ module RuboCop
|
|
85
126
|
FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
|
86
127
|
ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
|
87
128
|
KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
|
129
|
+
BLOCK_MSG = 'Use anonymous block arguments forwarding (`&`).'
|
130
|
+
|
131
|
+
def self.autocorrect_incompatible_with
|
132
|
+
[Naming::BlockForwarding]
|
133
|
+
end
|
88
134
|
|
89
135
|
def on_def(node)
|
90
136
|
return unless node.body
|
91
137
|
|
92
|
-
|
138
|
+
restarg, kwrestarg, blockarg = extract_forwardable_args(node.arguments)
|
139
|
+
forwardable_args = redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
|
140
|
+
send_nodes = node.each_descendant(:send).to_a
|
93
141
|
|
94
142
|
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
|
143
|
+
node, send_nodes, non_splat_or_block_pass_lvar_references(node.body), forwardable_args
|
99
144
|
)
|
100
145
|
|
101
146
|
return if send_classifications.empty?
|
@@ -115,31 +160,54 @@ module RuboCop
|
|
115
160
|
[args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
|
116
161
|
end
|
117
162
|
|
163
|
+
def redundant_forwardable_named_args(restarg, kwrestarg, blockarg)
|
164
|
+
restarg_node = redundant_named_arg(restarg, 'RedundantRestArgumentNames', '*')
|
165
|
+
kwrestarg_node = redundant_named_arg(kwrestarg, 'RedundantKeywordRestArgumentNames', '**')
|
166
|
+
blockarg_node = redundant_named_arg(blockarg, 'RedundantBlockArgumentNames', '&')
|
167
|
+
|
168
|
+
[restarg_node, kwrestarg_node, blockarg_node]
|
169
|
+
end
|
170
|
+
|
118
171
|
def only_forwards_all?(send_classifications)
|
119
172
|
send_classifications.all? { |_, c, _, _| c == :all }
|
120
173
|
end
|
121
174
|
|
175
|
+
# rubocop:disable Metrics/MethodLength
|
122
176
|
def add_forward_all_offenses(node, send_classifications, forwardable_args)
|
123
|
-
|
124
|
-
|
177
|
+
_rest_arg, _kwrest_arg, block_arg = *forwardable_args
|
178
|
+
registered_block_arg_offense = false
|
179
|
+
|
180
|
+
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
|
181
|
+
if !forward_rest && !forward_kwrest
|
182
|
+
register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
|
183
|
+
register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
|
184
|
+
|
185
|
+
registered_block_arg_offense = true
|
186
|
+
break
|
187
|
+
else
|
188
|
+
register_forward_all_offense(send_node, send_node, forward_rest)
|
189
|
+
end
|
125
190
|
end
|
126
191
|
|
192
|
+
return if registered_block_arg_offense
|
193
|
+
|
127
194
|
rest_arg, _kwrest_arg, _block_arg = *forwardable_args
|
128
195
|
register_forward_all_offense(node, node.arguments, rest_arg)
|
129
196
|
end
|
197
|
+
# rubocop:enable Metrics/MethodLength
|
130
198
|
|
131
199
|
def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
|
132
200
|
return unless use_anonymous_forwarding?
|
133
201
|
|
134
202
|
rest_arg, kwrest_arg, _block_arg = *forwardable_args
|
135
203
|
|
136
|
-
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
|
137
|
-
if forward_rest
|
204
|
+
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, _forward_block_arg| # rubocop:disable Layout/LineLength
|
205
|
+
if outside_block?(forward_rest)
|
138
206
|
register_forward_args_offense(def_node.arguments, rest_arg)
|
139
207
|
register_forward_args_offense(send_node, forward_rest)
|
140
208
|
end
|
141
209
|
|
142
|
-
if forward_kwrest
|
210
|
+
if outside_block?(forward_kwrest)
|
143
211
|
register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
|
144
212
|
register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
|
145
213
|
end
|
@@ -173,10 +241,7 @@ module RuboCop
|
|
173
241
|
|
174
242
|
def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
|
175
243
|
classifier = SendNodeClassifier.new(
|
176
|
-
def_node,
|
177
|
-
send_node,
|
178
|
-
referenced_lvars,
|
179
|
-
forwardable_args,
|
244
|
+
def_node, send_node, referenced_lvars, forwardable_args,
|
180
245
|
target_ruby_version: target_ruby_version,
|
181
246
|
allow_only_rest_arguments: allow_only_rest_arguments?
|
182
247
|
)
|
@@ -185,7 +250,28 @@ module RuboCop
|
|
185
250
|
|
186
251
|
return unless classification
|
187
252
|
|
188
|
-
[
|
253
|
+
[
|
254
|
+
classification,
|
255
|
+
classifier.forwarded_rest_arg,
|
256
|
+
classifier.forwarded_kwrest_arg,
|
257
|
+
classifier.forwarded_block_arg
|
258
|
+
]
|
259
|
+
end
|
260
|
+
|
261
|
+
def redundant_named_arg(arg, config_name, keyword)
|
262
|
+
return nil unless arg
|
263
|
+
|
264
|
+
redundant_arg_names = cop_config.fetch(config_name, []).map do |redundant_arg_name|
|
265
|
+
"#{keyword}#{redundant_arg_name}"
|
266
|
+
end << keyword
|
267
|
+
|
268
|
+
redundant_arg_names.include?(arg.source) ? arg : nil
|
269
|
+
end
|
270
|
+
|
271
|
+
def outside_block?(node)
|
272
|
+
return false unless node
|
273
|
+
|
274
|
+
node.each_ancestor(:block, :numblock).none?
|
189
275
|
end
|
190
276
|
|
191
277
|
def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
|
@@ -204,6 +290,16 @@ module RuboCop
|
|
204
290
|
end
|
205
291
|
end
|
206
292
|
|
293
|
+
def register_forward_block_arg_offense(add_parens, def_arguments_or_send, block_arg)
|
294
|
+
return if target_ruby_version <= 3.0 || block_arg.source == '&' || explicit_block_name?
|
295
|
+
|
296
|
+
add_offense(block_arg, message: BLOCK_MSG) do |corrector|
|
297
|
+
add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
|
298
|
+
|
299
|
+
corrector.replace(block_arg, '&')
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
207
303
|
def register_forward_all_offense(def_or_send, send_or_arguments, rest_or_splat)
|
208
304
|
arg_range = arguments_range(def_or_send, rest_or_splat)
|
209
305
|
|
@@ -278,7 +374,7 @@ module RuboCop
|
|
278
374
|
end
|
279
375
|
|
280
376
|
def classification
|
281
|
-
return nil unless forwarded_rest_arg || forwarded_kwrest_arg
|
377
|
+
return nil unless forwarded_rest_arg || forwarded_kwrest_arg || forwarded_block_arg
|
282
378
|
|
283
379
|
if can_forward_all?
|
284
380
|
:all
|
@@ -362,9 +458,23 @@ module RuboCop
|
|
362
458
|
def no_additional_args?
|
363
459
|
forwardable_count = [@rest_arg, @kwrest_arg, @block_arg].compact.size
|
364
460
|
|
461
|
+
return false if missing_rest_arg_or_kwrest_arg?
|
462
|
+
|
365
463
|
@def_node.arguments.size == forwardable_count &&
|
366
464
|
@send_node.arguments.size == forwardable_count
|
367
465
|
end
|
466
|
+
|
467
|
+
def missing_rest_arg_or_kwrest_arg?
|
468
|
+
(@rest_arg_name && !forwarded_rest_arg) ||
|
469
|
+
(@kwrest_arg_name && !forwarded_kwrest_arg)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
def explicit_block_name?
|
474
|
+
block_forwarding_config = config.for_cop('Naming/BlockForwarding')
|
475
|
+
return false unless block_forwarding_config['Enabled']
|
476
|
+
|
477
|
+
block_forwarding_config['EnforcedStyle'] == 'explicit'
|
368
478
|
end
|
369
479
|
end
|
370
480
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Identifies usages of `arr[0]` and `arr[-1]` and suggests to change
|
7
|
+
# them to use `arr.first` and `arr.last` instead.
|
8
|
+
#
|
9
|
+
# The cop is disabled by default due to safety concerns.
|
10
|
+
#
|
11
|
+
# @safety
|
12
|
+
# This cop is unsafe because `[0]` or `[-1]` can be called on a Hash,
|
13
|
+
# which returns a value for `0` or `-1` key, but changing these to use
|
14
|
+
# `.first` or `.last` will return first/last tuple instead. Also, String
|
15
|
+
# does not implement `first`/`last` methods.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# arr[0]
|
20
|
+
# arr[-1]
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# arr.first
|
24
|
+
# arr.last
|
25
|
+
# arr[0] = 2
|
26
|
+
# arr[0][-2]
|
27
|
+
#
|
28
|
+
class ArrayFirstLast < Base
|
29
|
+
extend AutoCorrector
|
30
|
+
|
31
|
+
MSG = 'Use `%<preferred>s`.'
|
32
|
+
RESTRICT_ON_SEND = %i[[]].freeze
|
33
|
+
|
34
|
+
# rubocop:disable Metrics/AbcSize
|
35
|
+
def on_send(node)
|
36
|
+
return unless node.arguments.size == 1 && node.first_argument.int_type?
|
37
|
+
|
38
|
+
value = node.first_argument.value
|
39
|
+
return unless [0, -1].include?(value)
|
40
|
+
|
41
|
+
node = innermost_braces_node(node)
|
42
|
+
return if node.parent && brace_method?(node.parent)
|
43
|
+
|
44
|
+
preferred = (value.zero? ? 'first' : 'last')
|
45
|
+
add_offense(node.loc.selector, message: format(MSG, preferred: preferred)) do |corrector|
|
46
|
+
corrector.replace(node.loc.selector, ".#{preferred}")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
# rubocop:enable Metrics/AbcSize
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def innermost_braces_node(node)
|
54
|
+
node = node.receiver while node.receiver.send_type? && node.receiver.method?(:[])
|
55
|
+
node
|
56
|
+
end
|
57
|
+
|
58
|
+
def brace_method?(node)
|
59
|
+
node.send_type? && (node.method?(:[]) || node.method?(:[]=))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -16,31 +16,38 @@ module RuboCop
|
|
16
16
|
# File.open('file') do |f|
|
17
17
|
# # ...
|
18
18
|
# end
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# f = Tempfile.open('temp')
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# Tempfile.open('temp') do |f|
|
25
|
+
# # ...
|
26
|
+
# end
|
19
27
|
class AutoResourceCleanup < Base
|
20
|
-
MSG = 'Use the block version of `%<
|
21
|
-
|
22
|
-
TARGET_METHODS = { File: :open }.freeze
|
28
|
+
MSG = 'Use the block version of `%<current>s`.'
|
29
|
+
RESTRICT_ON_SEND = %i[open].freeze
|
23
30
|
|
24
|
-
|
31
|
+
# @!method file_open_method?(node)
|
32
|
+
def_node_matcher :file_open_method?, <<~PATTERN
|
33
|
+
(send (const {nil? cbase} {:File :Tempfile}) :open ...)
|
34
|
+
PATTERN
|
25
35
|
|
26
36
|
def on_send(node)
|
27
|
-
|
28
|
-
next if node.method_name != target_method
|
37
|
+
return if !file_open_method?(node) || cleanup?(node)
|
29
38
|
|
30
|
-
|
31
|
-
next if node.receiver != target_receiver
|
39
|
+
current = node.receiver.source_range.begin.join(node.selector.end).source
|
32
40
|
|
33
|
-
|
34
|
-
|
35
|
-
add_offense(node, message: format(MSG, class: target_class, method: target_method))
|
36
|
-
end
|
41
|
+
add_offense(node, message: format(MSG, current: current))
|
37
42
|
end
|
38
43
|
|
39
44
|
private
|
40
45
|
|
41
46
|
def cleanup?(node)
|
42
|
-
|
43
|
-
|
47
|
+
return true if node.block_argument?
|
48
|
+
return false unless (parent = node.parent)
|
49
|
+
|
50
|
+
parent.block_type? || !parent.lvasgn_type?
|
44
51
|
end
|
45
52
|
end
|
46
53
|
end
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
33
33
|
def on_class(class_node)
|
34
34
|
@macros_to_rewrite[class_node] = Set.new
|
35
35
|
|
36
|
-
find_macros(class_node.body).
|
36
|
+
find_macros(class_node.body).each_value do |macros|
|
37
37
|
bisected = find_bisection(macros)
|
38
38
|
next unless bisected.any?
|
39
39
|
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
74
74
|
def find_macros(class_def)
|
75
75
|
# Find all the macros (`attr_reader`, `attr_writer`, etc.) in the class body
|
76
76
|
# and turn them into `Macro` objects so that they can be processed.
|
77
|
-
return
|
77
|
+
return {} if !class_def || class_def.def_type?
|
78
78
|
|
79
79
|
send_nodes =
|
80
80
|
if class_def.send_type?
|
@@ -125,7 +125,7 @@ module RuboCop
|
|
125
125
|
when :==, :eql?, :equal?
|
126
126
|
find_target_in_equality_node(node)
|
127
127
|
when :===
|
128
|
-
node.
|
128
|
+
node.first_argument
|
129
129
|
when :include?, :cover?
|
130
130
|
find_target_in_include_or_cover_node(node)
|
131
131
|
when :match, :match?, :=~
|
@@ -134,7 +134,7 @@ module RuboCop
|
|
134
134
|
end
|
135
135
|
|
136
136
|
def find_target_in_equality_node(node)
|
137
|
-
argument = node.
|
137
|
+
argument = node.first_argument
|
138
138
|
receiver = node.receiver
|
139
139
|
return unless argument && receiver
|
140
140
|
|
@@ -152,7 +152,7 @@ module RuboCop
|
|
152
152
|
end
|
153
153
|
|
154
154
|
def find_target_in_match_node(node)
|
155
|
-
argument = node.
|
155
|
+
argument = node.first_argument
|
156
156
|
receiver = node.receiver
|
157
157
|
return unless receiver
|
158
158
|
|
@@ -185,7 +185,7 @@ module RuboCop
|
|
185
185
|
def condition_from_send_node(node, target)
|
186
186
|
case node.method_name
|
187
187
|
when :is_a?
|
188
|
-
node.
|
188
|
+
node.first_argument if node.receiver == target
|
189
189
|
when :==, :eql?, :equal?
|
190
190
|
condition_from_equality_node(node, target)
|
191
191
|
when :=~, :match, :match?
|
@@ -23,6 +23,8 @@ module RuboCop
|
|
23
23
|
# array.reject { |e| e.nil? }
|
24
24
|
# array.delete_if { |e| e.nil? }
|
25
25
|
# array.select { |e| !e.nil? }
|
26
|
+
# array.grep_v(nil)
|
27
|
+
# array.grep_v(NilClass)
|
26
28
|
#
|
27
29
|
# # good
|
28
30
|
# array.compact
|
@@ -46,14 +48,14 @@ module RuboCop
|
|
46
48
|
extend TargetRubyVersion
|
47
49
|
|
48
50
|
MSG = 'Use `%<good>s` instead of `%<bad>s`.'
|
49
|
-
RESTRICT_ON_SEND = %i[reject delete_if reject! select select!].freeze
|
51
|
+
RESTRICT_ON_SEND = %i[reject delete_if reject! select select! grep_v].freeze
|
50
52
|
TO_ENUM_METHODS = %i[to_enum lazy].freeze
|
51
53
|
|
52
54
|
minimum_target_ruby_version 2.4
|
53
55
|
|
54
56
|
# @!method reject_method_with_block_pass?(node)
|
55
57
|
def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
|
56
|
-
(
|
58
|
+
(call !nil? {:reject :delete_if :reject!}
|
57
59
|
(block_pass
|
58
60
|
(sym :nil?)))
|
59
61
|
PATTERN
|
@@ -61,24 +63,29 @@ module RuboCop
|
|
61
63
|
# @!method reject_method?(node)
|
62
64
|
def_node_matcher :reject_method?, <<~PATTERN
|
63
65
|
(block
|
64
|
-
(
|
66
|
+
(call
|
65
67
|
!nil? {:reject :delete_if :reject!})
|
66
68
|
$(args ...)
|
67
|
-
(
|
69
|
+
(call
|
68
70
|
$(lvar _) :nil?))
|
69
71
|
PATTERN
|
70
72
|
|
71
73
|
# @!method select_method?(node)
|
72
74
|
def_node_matcher :select_method?, <<~PATTERN
|
73
75
|
(block
|
74
|
-
(
|
76
|
+
(call
|
75
77
|
!nil? {:select :select!})
|
76
78
|
$(args ...)
|
77
|
-
(
|
78
|
-
(
|
79
|
+
(call
|
80
|
+
(call
|
79
81
|
$(lvar _) :nil?) :!))
|
80
82
|
PATTERN
|
81
83
|
|
84
|
+
# @!method grep_v_with_nil?(node)
|
85
|
+
def_node_matcher :grep_v_with_nil?, <<~PATTERN
|
86
|
+
(send _ :grep_v {(nil) (const {nil? cbase} :NilClass)})
|
87
|
+
PATTERN
|
88
|
+
|
82
89
|
def on_send(node)
|
83
90
|
return unless (range = offense_range(node))
|
84
91
|
return if allowed_receiver?(node.receiver)
|
@@ -91,11 +98,13 @@ module RuboCop
|
|
91
98
|
|
92
99
|
add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
|
93
100
|
end
|
101
|
+
alias on_csend on_send
|
94
102
|
|
95
103
|
private
|
96
104
|
|
105
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
97
106
|
def offense_range(node)
|
98
|
-
if reject_method_with_block_pass?(node)
|
107
|
+
if reject_method_with_block_pass?(node) || grep_v_with_nil?(node)
|
99
108
|
range(node, node)
|
100
109
|
else
|
101
110
|
block_node = node.parent
|
@@ -109,6 +118,7 @@ module RuboCop
|
|
109
118
|
range(node, block_node)
|
110
119
|
end
|
111
120
|
end
|
121
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
112
122
|
|
113
123
|
def to_enum_method?(node)
|
114
124
|
return false unless node.receiver.send_type?
|
@@ -59,8 +59,6 @@ module RuboCop
|
|
59
59
|
class CombinableLoops < Base
|
60
60
|
extend AutoCorrector
|
61
61
|
|
62
|
-
include RangeHelp
|
63
|
-
|
64
62
|
MSG = 'Combine this loop with the previous loop.'
|
65
63
|
|
66
64
|
def on_block(node)
|
@@ -105,11 +103,19 @@ module RuboCop
|
|
105
103
|
end
|
106
104
|
|
107
105
|
def combine_with_left_sibling(corrector, node)
|
108
|
-
corrector.
|
109
|
-
|
110
|
-
|
111
|
-
)
|
112
|
-
|
106
|
+
corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
|
107
|
+
corrector.remove(node.source_range.begin.join(node.body.source_range.begin))
|
108
|
+
|
109
|
+
correct_end_of_block(corrector, node)
|
110
|
+
end
|
111
|
+
|
112
|
+
def correct_end_of_block(corrector, node)
|
113
|
+
return unless node.left_sibling.respond_to?(:braces?)
|
114
|
+
return if node.right_sibling&.block_type? || node.right_sibling&.numblock_type?
|
115
|
+
|
116
|
+
end_of_block = node.left_sibling.braces? ? '}' : ' end'
|
117
|
+
corrector.remove(node.loc.end)
|
118
|
+
corrector.insert_before(node.source_range.end, end_of_block)
|
113
119
|
end
|
114
120
|
end
|
115
121
|
end
|
@@ -233,7 +233,7 @@ module RuboCop
|
|
233
233
|
PATTERN
|
234
234
|
|
235
235
|
ASSIGNMENT_TYPES.each do |type|
|
236
|
-
define_method "on_#{type}" do |node|
|
236
|
+
define_method :"on_#{type}" do |node|
|
237
237
|
return if part_of_ignored_node?(node)
|
238
238
|
return if node.parent&.shorthand_asgn?
|
239
239
|
|
@@ -534,7 +534,7 @@ module RuboCop
|
|
534
534
|
end
|
535
535
|
|
536
536
|
def element_assignment?(node)
|
537
|
-
node.send_type? && node.
|
537
|
+
node.send_type? && !node.method?(:[]=)
|
538
538
|
end
|
539
539
|
|
540
540
|
def extract_branches(node)
|
@@ -49,12 +49,12 @@ module RuboCop
|
|
49
49
|
class DateTime < Base
|
50
50
|
extend AutoCorrector
|
51
51
|
|
52
|
-
CLASS_MSG = 'Prefer Time over DateTime
|
53
|
-
COERCION_MSG = 'Do not use
|
52
|
+
CLASS_MSG = 'Prefer `Time` over `DateTime`.'
|
53
|
+
COERCION_MSG = 'Do not use `#to_datetime`.'
|
54
54
|
|
55
55
|
# @!method date_time?(node)
|
56
56
|
def_node_matcher :date_time?, <<~PATTERN
|
57
|
-
(
|
57
|
+
(call (const {nil? (cbase)} :DateTime) ...)
|
58
58
|
PATTERN
|
59
59
|
|
60
60
|
# @!method historic_date?(node)
|
@@ -64,7 +64,7 @@ module RuboCop
|
|
64
64
|
|
65
65
|
# @!method to_datetime?(node)
|
66
66
|
def_node_matcher :to_datetime?, <<~PATTERN
|
67
|
-
(
|
67
|
+
(call _ :to_datetime)
|
68
68
|
PATTERN
|
69
69
|
|
70
70
|
def on_send(node)
|
@@ -75,6 +75,7 @@ module RuboCop
|
|
75
75
|
|
76
76
|
add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
|
77
77
|
end
|
78
|
+
alias on_csend on_send
|
78
79
|
|
79
80
|
private
|
80
81
|
|
@@ -32,27 +32,27 @@ module RuboCop
|
|
32
32
|
|
33
33
|
send_node = node.send_node
|
34
34
|
|
35
|
-
|
36
|
-
|
37
|
-
add_offense(range) do |corrector|
|
35
|
+
add_offense(send_node) do |corrector|
|
38
36
|
range_type, min, max = each_range(node)
|
39
37
|
|
40
38
|
max += 1 if range_type == :irange
|
41
39
|
|
42
|
-
corrector.replace(
|
40
|
+
corrector.replace(send_node, "#{max - min}.times")
|
43
41
|
end
|
44
42
|
end
|
45
43
|
|
46
44
|
private
|
47
45
|
|
48
46
|
def offending?(node)
|
47
|
+
return false unless node.arguments.empty?
|
48
|
+
|
49
49
|
each_range_with_zero_origin?(node) || each_range_without_block_argument?(node)
|
50
50
|
end
|
51
51
|
|
52
52
|
# @!method each_range(node)
|
53
53
|
def_node_matcher :each_range, <<~PATTERN
|
54
54
|
(block
|
55
|
-
(
|
55
|
+
(call
|
56
56
|
(begin
|
57
57
|
(${irange erange}
|
58
58
|
(int $_) (int $_)))
|
@@ -64,7 +64,7 @@ module RuboCop
|
|
64
64
|
# @!method each_range_with_zero_origin?(node)
|
65
65
|
def_node_matcher :each_range_with_zero_origin?, <<~PATTERN
|
66
66
|
(block
|
67
|
-
(
|
67
|
+
(call
|
68
68
|
(begin
|
69
69
|
({irange erange}
|
70
70
|
(int 0) (int _)))
|
@@ -76,7 +76,7 @@ module RuboCop
|
|
76
76
|
# @!method each_range_without_block_argument?(node)
|
77
77
|
def_node_matcher :each_range_without_block_argument?, <<~PATTERN
|
78
78
|
(block
|
79
|
-
(
|
79
|
+
(call
|
80
80
|
(begin
|
81
81
|
({irange erange}
|
82
82
|
(int _) (int _)))
|
@@ -58,12 +58,12 @@ module RuboCop
|
|
58
58
|
|
59
59
|
# @!method each_with_object_block_candidate?(node)
|
60
60
|
def_node_matcher :each_with_object_block_candidate?, <<~PATTERN
|
61
|
-
(block $(
|
61
|
+
(block $(call _ {:inject :reduce} _) $_ $_)
|
62
62
|
PATTERN
|
63
63
|
|
64
64
|
# @!method each_with_object_numblock_candidate?(node)
|
65
65
|
def_node_matcher :each_with_object_numblock_candidate?, <<~PATTERN
|
66
|
-
(numblock $(
|
66
|
+
(numblock $(call _ {:inject :reduce} _) 2 $_)
|
67
67
|
PATTERN
|
68
68
|
|
69
69
|
def autocorrect_block(corrector, node, return_value)
|
@@ -83,7 +83,7 @@ module RuboCop
|
|
83
83
|
parent = node.parent
|
84
84
|
return false unless parent && %i[send super zsuper].include?(parent.type)
|
85
85
|
|
86
|
-
node.equal?(parent.
|
86
|
+
node.equal?(parent.first_argument) && !parentheses?(node.parent)
|
87
87
|
end
|
88
88
|
|
89
89
|
def replacement_range(node)
|