rubocop 1.74.0 → 1.75.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 -1
- data/config/default.yml +32 -6
- data/config/obsoletion.yml +3 -1
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/config.rb +35 -6
- data/lib/rubocop/config_loader.rb +4 -0
- data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
- data/lib/rubocop/config_obsoletion.rb +46 -2
- data/lib/rubocop/config_validator.rb +1 -0
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -1
- data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
- data/lib/rubocop/cop/layout/block_alignment.rb +2 -2
- data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +3 -3
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
- data/lib/rubocop/cop/layout/line_length.rb +5 -1
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
- data/lib/rubocop/cop/layout/redundant_line_break.rb +9 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -5
- data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
- data/lib/rubocop/cop/lint/debugger.rb +2 -2
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -3
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
- data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
- data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -4
- data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
- data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
- data/lib/rubocop/cop/lint/return_in_void_context.rb +7 -2
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
- data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
- data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
- data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -0
- data/lib/rubocop/cop/lint/void.rb +1 -0
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
- data/lib/rubocop/cop/mixin/def_node.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +0 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +64 -8
- data/lib/rubocop/cop/naming/variable_name.rb +6 -19
- data/lib/rubocop/cop/registry.rb +9 -6
- data/lib/rubocop/cop/style/array_intersect.rb +39 -28
- data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
- data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +1 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +3 -0
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/empty_literal.rb +4 -0
- data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
- data/lib/rubocop/cop/style/for.rb +1 -0
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
- data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
- data/lib/rubocop/cop/style/guard_clause.rb +2 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +3 -2
- data/lib/rubocop/cop/style/hash_fetch_chain.rb +105 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
- data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -0
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
- data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +100 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
- data/lib/rubocop/cop/style/lambda.rb +1 -0
- data/lib/rubocop/cop/style/map_into_array.rb +1 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -0
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -0
- data/lib/rubocop/cop/style/next.rb +44 -0
- data/lib/rubocop/cop/style/object_then.rb +1 -0
- data/lib/rubocop/cop/style/proc.rb +1 -0
- data/lib/rubocop/cop/style/raise_args.rb +8 -8
- data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +13 -1
- data/lib/rubocop/cop/style/redundant_format.rb +10 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -1
- data/lib/rubocop/cop/style/redundant_self.rb +1 -0
- data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
- data/lib/rubocop/cop/style/return_nil.rb +2 -2
- data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +41 -100
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
- data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -0
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cop/variable_force/scope.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +2 -7
- data/lib/rubocop/cop/variable_force.rb +1 -1
- data/lib/rubocop/lsp/runtime.rb +4 -4
- data/lib/rubocop/lsp/stdin_runner.rb +3 -1
- data/lib/rubocop/magic_comment.rb +8 -0
- data/lib/rubocop/rspec/cop_helper.rb +4 -1
- data/lib/rubocop/rspec/shared_contexts.rb +20 -0
- data/lib/rubocop/rspec/support.rb +2 -0
- data/lib/rubocop/runner.rb +5 -1
- data/lib/rubocop/server/cache.rb +13 -10
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +14 -7
- data/lib/rubocop.rb +4 -0
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +20 -2
- metadata +10 -6
data/lib/rubocop/cop/registry.rb
CHANGED
@@ -23,8 +23,8 @@ module RuboCop
|
|
23
23
|
global.without_department(:Test).cops
|
24
24
|
end
|
25
25
|
|
26
|
-
def self.qualified_cop_name(name, origin)
|
27
|
-
global.qualified_cop_name(name, origin)
|
26
|
+
def self.qualified_cop_name(name, origin, warn: true)
|
27
|
+
global.qualified_cop_name(name, origin, warn: warn)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Changes momentarily the global registry
|
@@ -139,7 +139,7 @@ module RuboCop
|
|
139
139
|
|
140
140
|
case potential_badges.size
|
141
141
|
when 0 then name # No namespace found. Deal with it later in caller.
|
142
|
-
when 1 then resolve_badge(badge, potential_badges.first, path)
|
142
|
+
when 1 then resolve_badge(badge, potential_badges.first, path, warn: warn)
|
143
143
|
else raise AmbiguousCopName.new(badge, path, potential_badges)
|
144
144
|
end
|
145
145
|
end
|
@@ -296,11 +296,14 @@ module RuboCop
|
|
296
296
|
self.class.new(cops)
|
297
297
|
end
|
298
298
|
|
299
|
-
def resolve_badge(given_badge, real_badge, source_path)
|
299
|
+
def resolve_badge(given_badge, real_badge, source_path, warn: true)
|
300
300
|
unless given_badge.match?(real_badge)
|
301
301
|
path = PathUtil.smart_path(source_path)
|
302
|
-
|
303
|
-
|
302
|
+
|
303
|
+
if warn
|
304
|
+
warn("#{path}: #{given_badge} has the wrong namespace - " \
|
305
|
+
"replace it with #{given_badge.with_department(real_badge.department)}")
|
306
|
+
end
|
304
307
|
end
|
305
308
|
|
306
309
|
real_badge.to_s
|
@@ -6,7 +6,8 @@ module RuboCop
|
|
6
6
|
# In Ruby 3.1, `Array#intersect?` has been added.
|
7
7
|
#
|
8
8
|
# This cop identifies places where `(array1 & array2).any?`
|
9
|
-
# can be replaced by
|
9
|
+
# or `(array1.intersection(array2)).any?` can be replaced by
|
10
|
+
# `array1.intersect?(array2)`.
|
10
11
|
#
|
11
12
|
# The `array1.intersect?(array2)` method is faster than
|
12
13
|
# `(array1 & array2).any?` and is more readable.
|
@@ -20,6 +21,10 @@ module RuboCop
|
|
20
21
|
# [1].intersect?([1,2]) { |x| false } # => true
|
21
22
|
# ----
|
22
23
|
#
|
24
|
+
# NOTE: Although `Array#intersection` can take zero or multiple arguments,
|
25
|
+
# only cases where exactly one argument is provided can be replaced with
|
26
|
+
# `Array#intersect?` and are handled by this cop.
|
27
|
+
#
|
23
28
|
# @safety
|
24
29
|
# This cop cannot guarantee that `array1` and `array2` are
|
25
30
|
# actually arrays while method `intersect?` is for arrays only.
|
@@ -30,6 +35,11 @@ module RuboCop
|
|
30
35
|
# (array1 & array2).empty?
|
31
36
|
# (array1 & array2).none?
|
32
37
|
#
|
38
|
+
# # bad
|
39
|
+
# array1.intersection(array2).any?
|
40
|
+
# array1.intersection(array2).empty?
|
41
|
+
# array1.intersection(array2).none?
|
42
|
+
#
|
33
43
|
# # good
|
34
44
|
# array1.intersect?(array2)
|
35
45
|
# !array1.intersect?(array2)
|
@@ -53,65 +63,66 @@ module RuboCop
|
|
53
63
|
|
54
64
|
minimum_target_ruby_version 3.1
|
55
65
|
|
56
|
-
|
57
|
-
|
58
|
-
(send
|
59
|
-
(begin
|
60
|
-
(send $(...) :& $(...))
|
61
|
-
) ${:any? :empty? :none?}
|
62
|
-
)
|
63
|
-
PATTERN
|
66
|
+
PREDICATES = %i[any? empty? none?].to_set.freeze
|
67
|
+
ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
|
64
68
|
|
65
|
-
# @!method
|
66
|
-
def_node_matcher :
|
67
|
-
(
|
68
|
-
|
69
|
-
(send $
|
70
|
-
|
69
|
+
# @!method bad_intersection_check?(node, predicates)
|
70
|
+
def_node_matcher :bad_intersection_check?, <<~PATTERN
|
71
|
+
(call
|
72
|
+
{
|
73
|
+
(begin (send $_ :& $_))
|
74
|
+
(call $_ :intersection $_)
|
75
|
+
}
|
76
|
+
$%1
|
71
77
|
)
|
72
78
|
PATTERN
|
73
79
|
|
74
|
-
MSG = 'Use `%<negated>s%<receiver>s
|
75
|
-
'instead of
|
80
|
+
MSG = 'Use `%<negated>s%<receiver>s%<dot>sintersect?(%<argument>s)` ' \
|
81
|
+
'instead of `%<existing>s`.'
|
76
82
|
STRAIGHT_METHODS = %i[present? any?].freeze
|
77
83
|
NEGATED_METHODS = %i[blank? empty? none?].freeze
|
78
84
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
79
85
|
|
80
86
|
def on_send(node)
|
81
87
|
return if node.block_literal?
|
82
|
-
return unless (receiver, argument, method_name =
|
88
|
+
return unless (receiver, argument, method_name = bad_intersection?(node))
|
83
89
|
|
84
|
-
|
90
|
+
dot = node.loc.dot.source
|
91
|
+
message = message(receiver.source, argument.source, method_name, dot, node.source)
|
85
92
|
|
86
93
|
add_offense(node, message: message) do |corrector|
|
87
94
|
bang = straight?(method_name) ? '' : '!'
|
88
95
|
|
89
|
-
corrector.replace(node, "#{bang}#{receiver.source}
|
96
|
+
corrector.replace(node, "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})")
|
90
97
|
end
|
91
98
|
end
|
99
|
+
alias on_csend on_send
|
92
100
|
|
93
101
|
private
|
94
102
|
|
95
|
-
def
|
96
|
-
if active_support_extensions_enabled?
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
103
|
+
def bad_intersection?(node)
|
104
|
+
predicates = if active_support_extensions_enabled?
|
105
|
+
ACTIVE_SUPPORT_PREDICATES
|
106
|
+
else
|
107
|
+
PREDICATES
|
108
|
+
end
|
109
|
+
|
110
|
+
bad_intersection_check?(node, predicates)
|
101
111
|
end
|
102
112
|
|
103
113
|
def straight?(method_name)
|
104
114
|
STRAIGHT_METHODS.include?(method_name.to_sym)
|
105
115
|
end
|
106
116
|
|
107
|
-
def message(receiver, argument, method_name)
|
117
|
+
def message(receiver, argument, method_name, dot, existing)
|
108
118
|
negated = straight?(method_name) ? '' : '!'
|
109
119
|
format(
|
110
120
|
MSG,
|
111
121
|
negated: negated,
|
112
122
|
receiver: receiver,
|
113
123
|
argument: argument,
|
114
|
-
|
124
|
+
dot: dot,
|
125
|
+
existing: existing
|
115
126
|
)
|
116
127
|
end
|
117
128
|
end
|
@@ -208,6 +208,7 @@ module RuboCop
|
|
208
208
|
end
|
209
209
|
|
210
210
|
alias on_numblock on_block
|
211
|
+
alias on_itblock on_block
|
211
212
|
|
212
213
|
private
|
213
214
|
|
@@ -347,7 +348,7 @@ module RuboCop
|
|
347
348
|
# rubocop:disable Metrics/CyclomaticComplexity
|
348
349
|
def get_blocks(node, &block)
|
349
350
|
case node.type
|
350
|
-
when :block, :numblock
|
351
|
+
when :block, :numblock, :itblock
|
351
352
|
yield node
|
352
353
|
when :send, :csend
|
353
354
|
# When a method has an argument which is another method with a block,
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
PATTERN
|
69
69
|
|
70
70
|
def on_send(node)
|
71
|
-
def_node = node.each_ancestor(:
|
71
|
+
def_node = node.each_ancestor(:any_def).first
|
72
72
|
return if def_node &&
|
73
73
|
(allowed_method?(def_node.method_name) ||
|
74
74
|
matches_allowed_pattern?(def_node.method_name))
|
@@ -436,9 +436,11 @@ module RuboCop
|
|
436
436
|
# Helper module to provide common methods to ConditionalAssignment
|
437
437
|
# correctors
|
438
438
|
module ConditionalCorrectorHelper
|
439
|
+
# rubocop:disable Metrics/AbcSize
|
439
440
|
def remove_whitespace_in_branches(corrector, branch, condition, column)
|
440
441
|
branch.each_node do |child|
|
441
442
|
next if child.source_range.nil?
|
443
|
+
next if child.parent.dstr_type?
|
442
444
|
|
443
445
|
white_space = white_space_range(child, column)
|
444
446
|
corrector.remove(white_space) if white_space.source.strip.empty?
|
@@ -450,6 +452,7 @@ module RuboCop
|
|
450
452
|
corrector.remove_preceding(loc, loc.column - column)
|
451
453
|
end
|
452
454
|
end
|
455
|
+
# rubocop:enable Metrics/AbcSize
|
453
456
|
|
454
457
|
def white_space_range(node, column)
|
455
458
|
expression = node.source_range
|
@@ -102,7 +102,7 @@ module RuboCop
|
|
102
102
|
|
103
103
|
def find_def_node_from_ascendant(node)
|
104
104
|
return unless (parent = node.parent)
|
105
|
-
return parent if parent.
|
105
|
+
return parent if parent.any_def_type?
|
106
106
|
return node.parent.child_nodes.first if define_method?(parent)
|
107
107
|
|
108
108
|
find_def_node_from_ascendant(node.parent)
|
@@ -6,6 +6,10 @@ module RuboCop
|
|
6
6
|
# Checks for the use of a method, the result of which
|
7
7
|
# would be a literal, like an empty array, hash, or string.
|
8
8
|
#
|
9
|
+
# NOTE: When frozen string literals are enabled, `String.new`
|
10
|
+
# isn't corrected to an empty string since the former is
|
11
|
+
# mutable and the latter would be frozen.
|
12
|
+
#
|
9
13
|
# @example
|
10
14
|
# # bad
|
11
15
|
# a = Array.new
|
@@ -65,7 +65,7 @@ module RuboCop
|
|
65
65
|
yielding_block?(block_node) do |send_node, block_args, yield_args|
|
66
66
|
return unless yielding_arguments?(block_args, yield_args)
|
67
67
|
|
68
|
-
def_node = block_node.each_ancestor(:
|
68
|
+
def_node = block_node.each_ancestor(:any_def).first
|
69
69
|
# if `yield` is being called outside of a method context, ignore
|
70
70
|
# this is not a valid ruby pattern, but can happen in haml or erb,
|
71
71
|
# so this can cause crashes in haml_lint
|
@@ -151,7 +151,7 @@ module RuboCop
|
|
151
151
|
end
|
152
152
|
|
153
153
|
def build_new_arguments_for_zsuper(node)
|
154
|
-
def_node = node.each_ancestor(:
|
154
|
+
def_node = node.each_ancestor(:any_def).first
|
155
155
|
def_node.arguments.map do |arg|
|
156
156
|
arg.optarg_type? ? arg.node_parts[0] : arg.source
|
157
157
|
end
|
@@ -151,7 +151,7 @@ module RuboCop
|
|
151
151
|
|
152
152
|
def frozen_string_literal_comment(processed_source)
|
153
153
|
processed_source.tokens.find do |token|
|
154
|
-
token.text.
|
154
|
+
MagicComment.parse(token.text).frozen_string_literal_specified?
|
155
155
|
end
|
156
156
|
end
|
157
157
|
|
@@ -189,8 +189,9 @@ module RuboCop
|
|
189
189
|
|
190
190
|
def enable_comment(corrector)
|
191
191
|
comment = frozen_string_literal_comment(processed_source)
|
192
|
+
replacement = MagicComment.parse(comment.text).new_frozen_string_literal(true)
|
192
193
|
|
193
|
-
corrector.replace(line_range(comment.line),
|
194
|
+
corrector.replace(line_range(comment.line), replacement)
|
194
195
|
end
|
195
196
|
|
196
197
|
def insert_comment(corrector)
|
@@ -8,6 +8,9 @@ module RuboCop
|
|
8
8
|
# reassign (possibly to redirect some stream) constants in Ruby, you'll get
|
9
9
|
# an interpreter warning if you do so.
|
10
10
|
#
|
11
|
+
# Additionally, `$stdout/$stderr/$stdin` can safely be accessed in a Ractor because they
|
12
|
+
# are ractor-local, while `STDOUT/STDERR/STDIN` will raise `Ractor::IsolationError`.
|
13
|
+
#
|
11
14
|
# @safety
|
12
15
|
# Autocorrection is unsafe because `STDOUT` and `$stdout` may point to different
|
13
16
|
# objects, for example.
|
@@ -135,6 +135,7 @@ module RuboCop
|
|
135
135
|
on_def(node)
|
136
136
|
end
|
137
137
|
alias on_numblock on_block
|
138
|
+
alias on_itblock on_block
|
138
139
|
|
139
140
|
def on_if(node)
|
140
141
|
return if accepted_form?(node)
|
@@ -213,7 +214,7 @@ module RuboCop
|
|
213
214
|
if_branch = node.if_branch
|
214
215
|
else_branch = node.else_branch
|
215
216
|
|
216
|
-
corrector.replace(node.loc.begin, "\n") if node.
|
217
|
+
corrector.replace(node.loc.begin, "\n") if node.then?
|
217
218
|
|
218
219
|
if if_branch&.send_type? && heredoc?(if_branch.last_argument)
|
219
220
|
autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)
|
@@ -74,6 +74,7 @@ module RuboCop
|
|
74
74
|
check_unused_block_args(node, key, value)
|
75
75
|
end
|
76
76
|
alias on_numblock on_block
|
77
|
+
alias on_itblock on_block
|
77
78
|
|
78
79
|
# rubocop:disable Metrics/AbcSize
|
79
80
|
def check_unused_block_args(node, key, value)
|
@@ -128,8 +129,8 @@ module RuboCop
|
|
128
129
|
lvar_sources = node.body.each_descendant(:lvar).map(&:source)
|
129
130
|
|
130
131
|
if block_arg.mlhs_type?
|
131
|
-
block_arg.each_descendant(:arg, :restarg).all? do |
|
132
|
-
lvar_sources.none?(
|
132
|
+
block_arg.each_descendant(:arg, :restarg).all? do |descendant|
|
133
|
+
lvar_sources.none?(descendant.source.delete_prefix('*'))
|
133
134
|
end
|
134
135
|
else
|
135
136
|
lvar_sources.none?(block_arg.source.delete_prefix('*'))
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Use `Hash#dig` instead of chaining potentially null `fetch` calls.
|
7
|
+
#
|
8
|
+
# When `fetch(identifier, nil)` calls are chained on a hash, the expectation
|
9
|
+
# is that each step in the chain returns either `nil` or another hash,
|
10
|
+
# and in both cases, these can be simplified with a single call to `dig` with
|
11
|
+
# multiple arguments.
|
12
|
+
#
|
13
|
+
# If the 2nd parameter is `{}` or `Hash.new`, an offense will also be registered,
|
14
|
+
# as long as the final call in the chain is a nil value. If a non-nil value is given,
|
15
|
+
# the chain will not be registered as an offense, as the default value cannot be safely
|
16
|
+
# given with `dig`.
|
17
|
+
#
|
18
|
+
# NOTE: See `Style/DigChain` for replacing chains of `dig` calls with
|
19
|
+
# a single method call.
|
20
|
+
#
|
21
|
+
# @safety
|
22
|
+
# This cop is unsafe because it cannot be guaranteed that the receiver
|
23
|
+
# is a `Hash` or that `fetch` or `dig` have the expected standard implementation.
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# # bad
|
27
|
+
# hash.fetch('foo', nil)&.fetch('bar', nil)
|
28
|
+
#
|
29
|
+
# # bad
|
30
|
+
# # earlier members of the chain can return `{}` as long as the final `fetch`
|
31
|
+
# # has `nil` as a default value
|
32
|
+
# hash.fetch('foo', {}).fetch('bar', nil)
|
33
|
+
#
|
34
|
+
# # good
|
35
|
+
# hash.dig('foo', 'bar')
|
36
|
+
#
|
37
|
+
# # ok - not handled by the cop since the final `fetch` value is non-nil
|
38
|
+
# hash.fetch('foo', {}).fetch('bar', {})
|
39
|
+
#
|
40
|
+
class HashFetchChain < Base
|
41
|
+
extend AutoCorrector
|
42
|
+
extend TargetRubyVersion
|
43
|
+
include IgnoredNode
|
44
|
+
|
45
|
+
MSG = 'Use `%<replacement>s` instead.'
|
46
|
+
RESTRICT_ON_SEND = %i[fetch].freeze
|
47
|
+
|
48
|
+
minimum_target_ruby_version 2.3
|
49
|
+
|
50
|
+
# @!method diggable?(node)
|
51
|
+
def_node_matcher :diggable?, <<~PATTERN
|
52
|
+
(call _ :fetch $_arg {nil (hash) (send (const {nil? cbase} :Hash) :new)})
|
53
|
+
PATTERN
|
54
|
+
|
55
|
+
def on_send(node)
|
56
|
+
return if ignored_node?(node)
|
57
|
+
return if last_fetch_non_nil?(node)
|
58
|
+
|
59
|
+
last_replaceable_node, arguments = inspect_chain(node)
|
60
|
+
return unless last_replaceable_node
|
61
|
+
return unless arguments.size > 1
|
62
|
+
|
63
|
+
range = last_replaceable_node.selector.join(node.loc.end)
|
64
|
+
replacement = replacement(arguments)
|
65
|
+
message = format(MSG, replacement: replacement)
|
66
|
+
|
67
|
+
add_offense(range, message: message) do |corrector|
|
68
|
+
corrector.replace(range, replacement)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
alias on_csend on_send
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def last_fetch_non_nil?(node)
|
76
|
+
# When chaining `fetch` methods, `fetch(x, {})` is acceptable within
|
77
|
+
# the chain, as long as the last method in the chain has a `nil`
|
78
|
+
# default value.
|
79
|
+
|
80
|
+
return false unless node.method?(:fetch)
|
81
|
+
|
82
|
+
!node.last_argument&.nil_type?
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect_chain(node)
|
86
|
+
arguments = []
|
87
|
+
|
88
|
+
while (arg = diggable?(node))
|
89
|
+
arguments.unshift(arg)
|
90
|
+
ignore_node(node)
|
91
|
+
last_replaceable_node = node
|
92
|
+
node = node.receiver
|
93
|
+
end
|
94
|
+
|
95
|
+
[last_replaceable_node, arguments]
|
96
|
+
end
|
97
|
+
|
98
|
+
def replacement(arguments)
|
99
|
+
values = arguments.map(&:source).join(', ')
|
100
|
+
"dig(#{values})"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -97,34 +97,31 @@ module RuboCop
|
|
97
97
|
else
|
98
98
|
correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
|
99
99
|
end
|
100
|
-
|
101
|
-
corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
|
102
|
-
return unless (if_branch = node.if_branch)
|
103
|
-
|
104
|
-
range = range_by_whole_lines(if_branch.source_range, include_final_newline: true)
|
105
|
-
corrector.remove(range)
|
106
100
|
end
|
107
101
|
|
108
102
|
def correct_to_elsif_from_modifier_form(corrector, node)
|
109
|
-
corrector.replace(node.parent.loc.else,
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
103
|
+
corrector.replace(node.parent.loc.else, "elsif #{node.condition.source}")
|
104
|
+
|
105
|
+
condition_range = range_between(
|
106
|
+
node.if_branch.source_range.end_pos, node.condition.source_range.end_pos
|
107
|
+
)
|
108
|
+
corrector.remove(condition_range)
|
114
109
|
end
|
115
110
|
|
116
|
-
def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
|
111
|
+
def correct_to_elsif_from_if_inside_else_form(corrector, node, condition) # rubocop:disable Metrics/AbcSize
|
117
112
|
corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
|
118
113
|
|
119
114
|
if_condition_range = if_condition_range(node, condition)
|
120
115
|
|
121
116
|
if (if_branch = node.if_branch)
|
122
|
-
corrector.replace(if_condition_range, if_branch.source)
|
117
|
+
corrector.replace(if_condition_range, range_with_comments(if_branch).source)
|
118
|
+
corrector.remove(range_with_comments_and_lines(if_branch))
|
123
119
|
else
|
124
120
|
corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
|
125
121
|
end
|
126
122
|
|
127
123
|
corrector.remove(condition)
|
124
|
+
corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
|
128
125
|
end
|
129
126
|
|
130
127
|
def then?(node)
|
@@ -89,8 +89,8 @@ module RuboCop
|
|
89
89
|
|
90
90
|
def inheritance_check?(node)
|
91
91
|
argument = node.first_argument
|
92
|
-
node.method?(:<) &&
|
93
|
-
|
92
|
+
node.method?(:<) && argument.const_type? &&
|
93
|
+
argument.short_name.to_s.upcase != argument.short_name.to_s
|
94
94
|
end
|
95
95
|
|
96
96
|
def preferred_condition(node)
|
@@ -32,7 +32,7 @@ module RuboCop
|
|
32
32
|
|
33
33
|
# To try to avoid doing two regex checks on every string,
|
34
34
|
# shortcut out if the string does not look like an IP address
|
35
|
-
return false unless
|
35
|
+
return false unless potential_ip?(contents)
|
36
36
|
|
37
37
|
::Resolv::IPv4::Regex.match?(contents) || ::Resolv::IPv6::Regex.match?(contents)
|
38
38
|
end
|
@@ -52,7 +52,7 @@ module RuboCop
|
|
52
52
|
Array(allowed_addresses).map(&:downcase)
|
53
53
|
end
|
54
54
|
|
55
|
-
def
|
55
|
+
def potential_ip?(str)
|
56
56
|
# If the string is too long, it can't be an IP
|
57
57
|
return false if too_long?(str)
|
58
58
|
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for blocks with one argument where `it` block parameter can be used.
|
7
|
+
#
|
8
|
+
# It provides three `EnforcedStyle` options:
|
9
|
+
#
|
10
|
+
# 1. `only_numbered_parameters` (default) ... Detects only numbered block parameters.
|
11
|
+
# 2. `always` ... Always uses the `it` block parameter.
|
12
|
+
# 3. `disallow` ... Disallows the `it` block parameter.
|
13
|
+
#
|
14
|
+
# A single numbered parameter is detected when `only_numbered_parameters` or `always`.
|
15
|
+
#
|
16
|
+
# @example EnforcedStyle: only_numbered_parameters (default)
|
17
|
+
# # bad
|
18
|
+
# block { do_something(_1) }
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# block { do_something(it) }
|
22
|
+
# block { |named_param| do_something(named_param) }
|
23
|
+
#
|
24
|
+
# @example EnforcedStyle: always
|
25
|
+
# # bad
|
26
|
+
# block { do_something(_1) }
|
27
|
+
# block { |named_param| do_something(named_param) }
|
28
|
+
#
|
29
|
+
# # good
|
30
|
+
# block { do_something(it) }
|
31
|
+
#
|
32
|
+
# @example EnforcedStyle: disallow
|
33
|
+
# # bad
|
34
|
+
# block { do_something(it) }
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# block { do_something(_1) }
|
38
|
+
# block { |named_param| do_something(named_param) }
|
39
|
+
#
|
40
|
+
class ItBlockParameter < Base
|
41
|
+
include ConfigurableEnforcedStyle
|
42
|
+
extend TargetRubyVersion
|
43
|
+
extend AutoCorrector
|
44
|
+
|
45
|
+
MSG_USE_IT_BLOCK_PARAMETER = 'Use `it` block parameter.'
|
46
|
+
MSG_AVOID_IT_BLOCK_PARAMETER = 'Avoid using `it` block parameter.'
|
47
|
+
|
48
|
+
minimum_target_ruby_version 3.4
|
49
|
+
|
50
|
+
def on_block(node)
|
51
|
+
return unless style == :always
|
52
|
+
return unless node.arguments.one?
|
53
|
+
|
54
|
+
# `restarg`, `kwrestarg`, `blockarg` nodes can return early.
|
55
|
+
return unless node.first_argument.arg_type?
|
56
|
+
|
57
|
+
variables = find_block_variables(node, node.first_argument.source)
|
58
|
+
|
59
|
+
variables.each do |variable|
|
60
|
+
add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
|
61
|
+
corrector.remove(node.arguments)
|
62
|
+
corrector.replace(variable, 'it')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def on_numblock(node)
|
68
|
+
return if style == :disallow
|
69
|
+
return unless node.children[1] == 1
|
70
|
+
|
71
|
+
variables = find_block_variables(node, '_1')
|
72
|
+
|
73
|
+
variables.each do |variable|
|
74
|
+
add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
|
75
|
+
corrector.replace(variable, 'it')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def on_itblock(node)
|
81
|
+
return unless style == :disallow
|
82
|
+
|
83
|
+
variables = find_block_variables(node, 'it')
|
84
|
+
|
85
|
+
variables.each do |variable|
|
86
|
+
add_offense(variable, message: MSG_AVOID_IT_BLOCK_PARAMETER)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def find_block_variables(node, block_argument_name)
|
93
|
+
node.each_descendant(:lvar).select do |descendant|
|
94
|
+
descendant.source == block_argument_name
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -42,7 +42,7 @@ module RuboCop
|
|
42
42
|
return if kwarg_nodes.empty?
|
43
43
|
|
44
44
|
add_offense(node) do |corrector|
|
45
|
-
defining_node = node.each_ancestor(:
|
45
|
+
defining_node = node.each_ancestor(:any_def, :block).first
|
46
46
|
next if processed_source.contains_comment?(arguments_range(defining_node))
|
47
47
|
next unless node.parent.find(&:kwoptarg_type?) == node
|
48
48
|
|