rubocop 1.73.1 → 1.75.1
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 +64 -10
- data/config/internal_affairs.yml +4 -0
- 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 -1
- data/lib/rubocop/config_loader_resolver.rb +2 -1
- data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
- 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/example_description.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +91 -0
- data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
- 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 +1 -1
- 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/empty_conditional_body.rb +15 -70
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
- data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
- data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +9 -3
- 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 +4 -11
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
- data/lib/rubocop/cop/lint/shared_mutable_default.rb +12 -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/useless_constant_scoping.rb +2 -11
- 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/forbidden_identifiers.rb +20 -0
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
- data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
- data/lib/rubocop/cop/mixin/range_help.rb +12 -0
- data/lib/rubocop/cop/mixin/target_ruby_version.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_and_module_children.rb +29 -7
- 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/commented_keyword.rb +9 -2
- data/lib/rubocop/cop/style/comparable_between.rb +75 -0
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
- data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
- data/lib/rubocop/cop/style/for.rb +1 -0
- data/lib/rubocop/cop/style/format_string_token.rb +38 -11
- 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/if_inside_else.rb +10 -13
- data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -2
- data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
- 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 +13 -7
- 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 +3 -3
- data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -0
- data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
- 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 +2 -3
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +14 -4
- data/lib/rubocop/cop/style/redundant_format.rb +10 -3
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- 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/rescue_modifier.rb +3 -0
- 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 -106
- 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/utils/format_string.rb +5 -2
- data/lib/rubocop/cop/variable_force/scope.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +1 -6
- data/lib/rubocop/cop/variable_force.rb +1 -1
- data/lib/rubocop/directive_comment.rb +1 -1
- data/lib/rubocop/ext/regexp_node.rb +0 -1
- data/lib/rubocop/lsp/runtime.rb +4 -4
- data/lib/rubocop/lsp/stdin_runner.rb +3 -1
- 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/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +14 -7
- data/lib/rubocop.rb +5 -0
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +20 -2
- metadata +12 -6
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for logical comparison which can be replaced with `Comparable#between?`.
|
7
|
+
#
|
8
|
+
# NOTE: `Comparable#between?` is on average slightly slower than logical comparison,
|
9
|
+
# although the difference generally isn't observable. If you require maximum
|
10
|
+
# performance, consider using logical comparison.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# x >= min && x <= max
|
16
|
+
#
|
17
|
+
# # bad
|
18
|
+
# x <= max && x >= min
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# x.between?(min, max)
|
22
|
+
#
|
23
|
+
class ComparableBetween < Base
|
24
|
+
extend AutoCorrector
|
25
|
+
|
26
|
+
MSG = 'Prefer `%<prefer>s` over logical comparison.'
|
27
|
+
|
28
|
+
# @!method logical_comparison_between_by_min_first?(node)
|
29
|
+
def_node_matcher :logical_comparison_between_by_min_first?, <<~PATTERN
|
30
|
+
(and
|
31
|
+
(send
|
32
|
+
{$_value :>= $_min | $_min :<= $_value})
|
33
|
+
(send
|
34
|
+
{$_value :<= $_max | $_max :>= $_value}))
|
35
|
+
PATTERN
|
36
|
+
|
37
|
+
# @!method logical_comparison_between_by_max_first?(node)
|
38
|
+
def_node_matcher :logical_comparison_between_by_max_first?, <<~PATTERN
|
39
|
+
(and
|
40
|
+
(send
|
41
|
+
{$_value :<= $_max | $_max :>= $_value})
|
42
|
+
(send
|
43
|
+
{$_value :>= $_min | $_min :<= $_value}))
|
44
|
+
PATTERN
|
45
|
+
|
46
|
+
def on_and(node)
|
47
|
+
logical_comparison_between_by_min_first?(node) do |*args|
|
48
|
+
min_and_value, max_and_value = args.each_slice(2).to_a
|
49
|
+
|
50
|
+
register_offense(node, min_and_value, max_and_value)
|
51
|
+
end
|
52
|
+
|
53
|
+
logical_comparison_between_by_max_first?(node) do |*args|
|
54
|
+
max_and_value, min_and_value = args.each_slice(2).to_a
|
55
|
+
|
56
|
+
register_offense(node, min_and_value, max_and_value)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def register_offense(node, min_and_value, max_and_value)
|
63
|
+
value = (min_and_value & max_and_value).first
|
64
|
+
min = min_and_value.find { _1 != value }
|
65
|
+
max = max_and_value.find { _1 != value }
|
66
|
+
|
67
|
+
prefer = "#{value.source}.between?(#{min.source}, #{max.source})"
|
68
|
+
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
69
|
+
corrector.replace(node, prefer)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -137,11 +137,11 @@ module RuboCop
|
|
137
137
|
|
138
138
|
case depth(stripped_current_path)
|
139
139
|
when 0
|
140
|
-
range = arguments_range(current_path)
|
140
|
+
range = arguments_range(current_path.parent)
|
141
141
|
|
142
142
|
corrector.replace(range, '__FILE__')
|
143
143
|
when 1
|
144
|
-
range = arguments_range(current_path)
|
144
|
+
range = arguments_range(current_path.parent)
|
145
145
|
|
146
146
|
corrector.replace(range, '__dir__')
|
147
147
|
else
|
@@ -185,11 +185,6 @@ module RuboCop
|
|
185
185
|
corrector.remove(node.loc.dot)
|
186
186
|
corrector.remove(node.loc.selector)
|
187
187
|
end
|
188
|
-
|
189
|
-
def arguments_range(node)
|
190
|
-
range_between(node.parent.first_argument.source_range.begin_pos,
|
191
|
-
node.parent.last_argument.source_range.end_pos)
|
192
|
-
end
|
193
188
|
end
|
194
189
|
end
|
195
190
|
end
|
@@ -60,8 +60,8 @@ module RuboCop
|
|
60
60
|
class ExponentialNotation < Base
|
61
61
|
include ConfigurableEnforcedStyle
|
62
62
|
MESSAGES = {
|
63
|
-
scientific: 'Use a mantissa
|
64
|
-
engineering: 'Use an exponent divisible by 3 and a mantissa
|
63
|
+
scientific: 'Use a mantissa >= 1 and < 10.',
|
64
|
+
engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',
|
65
65
|
integral: 'Use an integer as mantissa, without trailing zero.'
|
66
66
|
}.freeze
|
67
67
|
|
@@ -3,16 +3,24 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# Use a consistent style for
|
6
|
+
# Use a consistent style for tokens within a format string.
|
7
7
|
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
# The reason is that _unannotated_ format is very similar
|
12
|
-
# to encoded URLs or Date/Time formatting strings.
|
8
|
+
# By default, all strings are evaluated. In some cases, this may be undesirable,
|
9
|
+
# as they could be used as arguments to a method that does not consider
|
10
|
+
# them to be tokens, but rather other identifiers or just part of the string.
|
13
11
|
#
|
14
|
-
#
|
15
|
-
#
|
12
|
+
# `AllowedMethods` or `AllowedPatterns` can be configured with in order to mark specific
|
13
|
+
# methods as always allowed, thereby avoiding an offense from the cop. By default, there
|
14
|
+
# are no allowed methods.
|
15
|
+
#
|
16
|
+
# Additionally, the cop can be made conservative by configuring it with
|
17
|
+
# `Mode: conservative` (default `aggressive`). In this mode, tokens (regardless
|
18
|
+
# of `EnforcedStyle`) are only considered if used in the format string argument to the
|
19
|
+
# methods `printf`, `sprintf`, `format` and `%`.
|
20
|
+
#
|
21
|
+
# NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if
|
22
|
+
# configured with `Conservative: true`. This is done in order to prevent false positives,
|
23
|
+
# because this format is very similar to encoded URLs or Date/Time formatting strings.
|
16
24
|
#
|
17
25
|
# @example EnforcedStyle: annotated (default)
|
18
26
|
#
|
@@ -82,6 +90,20 @@ module RuboCop
|
|
82
90
|
# # good
|
83
91
|
# redirect('foo/%{bar_id}')
|
84
92
|
#
|
93
|
+
# @example Mode: conservative, EnforcedStyle: annotated
|
94
|
+
# # In `conservative` mode, offenses are only registered for strings
|
95
|
+
# # given to a known formatting method.
|
96
|
+
#
|
97
|
+
# # good
|
98
|
+
# "%{greeting}"
|
99
|
+
# foo("%{greeting}")
|
100
|
+
#
|
101
|
+
# # bad
|
102
|
+
# format("%{greeting}", greeting: 'Hello')
|
103
|
+
# printf("%{greeting}", greeting: 'Hello')
|
104
|
+
# sprintf("%{greeting}", greeting: 'Hello')
|
105
|
+
# "%{greeting}" % { greeting: 'Hello' }
|
106
|
+
#
|
85
107
|
class FormatStringToken < Base
|
86
108
|
include ConfigurableEnforcedStyle
|
87
109
|
include AllowedMethods
|
@@ -153,8 +175,9 @@ module RuboCop
|
|
153
175
|
corrector.replace(token_range, correction)
|
154
176
|
end
|
155
177
|
|
156
|
-
def
|
157
|
-
detected_style == :unannotated
|
178
|
+
def allowed_string?(node, detected_style)
|
179
|
+
(detected_style == :unannotated || conservative?) &&
|
180
|
+
!format_string_in_typical_context?(node)
|
158
181
|
end
|
159
182
|
|
160
183
|
def message(detected_style)
|
@@ -203,7 +226,7 @@ module RuboCop
|
|
203
226
|
def collect_detections(node)
|
204
227
|
detections = []
|
205
228
|
tokens(node) do |detected_sequence, token_range|
|
206
|
-
unless
|
229
|
+
unless allowed_string?(node, detected_sequence.style)
|
207
230
|
detections << [detected_sequence, token_range]
|
208
231
|
end
|
209
232
|
end
|
@@ -222,6 +245,10 @@ module RuboCop
|
|
222
245
|
def max_unannotated_placeholders_allowed
|
223
246
|
cop_config['MaxUnannotatedPlaceholdersAllowed']
|
224
247
|
end
|
248
|
+
|
249
|
+
def conservative?
|
250
|
+
cop_config.fetch('Mode', :aggressive).to_sym == :conservative
|
251
|
+
end
|
225
252
|
end
|
226
253
|
end
|
227
254
|
end
|
@@ -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)
|
@@ -164,8 +164,8 @@ module RuboCop
|
|
164
164
|
|
165
165
|
def too_long_due_to_comment_after_modifier?(node, comment)
|
166
166
|
source_length = processed_source.lines[node.first_line - 1].length
|
167
|
-
|
168
|
-
|
167
|
+
|
168
|
+
max_line_length.between?(source_length - comment.source_range.length, source_length)
|
169
169
|
end
|
170
170
|
|
171
171
|
def allowed_patterns
|
@@ -46,6 +46,7 @@ module RuboCop
|
|
46
46
|
|
47
47
|
MSG = 'Use `%<inverse>s` instead of inverting `%<method>s`.'
|
48
48
|
CLASS_COMPARISON_METHODS = %i[<= >= < >].freeze
|
49
|
+
SAFE_NAVIGATION_INCOMPATIBLE_METHODS = (CLASS_COMPARISON_METHODS + %i[any? none?]).freeze
|
49
50
|
EQUALITY_METHODS = %i[== != =~ !~ <= >= < >].freeze
|
50
51
|
NEGATED_EQUALITY_METHODS = %i[!= !~].freeze
|
51
52
|
CAMEL_CASE = /[A-Z]+[a-z]+/.freeze
|
@@ -77,7 +78,7 @@ module RuboCop
|
|
77
78
|
def on_send(node)
|
78
79
|
inverse_candidate?(node) do |method_call, lhs, method, rhs|
|
79
80
|
return unless inverse_methods.key?(method)
|
80
|
-
return if negated?(node) ||
|
81
|
+
return if negated?(node) || safe_navigation_incompatible?(method_call)
|
81
82
|
return if part_of_ignored_node?(node)
|
82
83
|
return if possible_class_hierarchy_check?(lhs, rhs, method)
|
83
84
|
|
@@ -105,6 +106,7 @@ module RuboCop
|
|
105
106
|
end
|
106
107
|
|
107
108
|
alias on_numblock on_block
|
109
|
+
alias on_itblock on_block
|
108
110
|
|
109
111
|
private
|
110
112
|
|
@@ -154,10 +156,6 @@ module RuboCop
|
|
154
156
|
node.parent.respond_to?(:method?) && node.parent.method?(:!)
|
155
157
|
end
|
156
158
|
|
157
|
-
def relational_comparison_with_safe_navigation?(node)
|
158
|
-
node.csend_type? && CLASS_COMPARISON_METHODS.include?(node.method_name)
|
159
|
-
end
|
160
|
-
|
161
159
|
def not_to_receiver(node, method_call)
|
162
160
|
node.loc.selector.begin.join(method_call.source_range.begin)
|
163
161
|
end
|
@@ -166,6 +164,12 @@ module RuboCop
|
|
166
164
|
method_call.source_range.end.join(node.source_range.end)
|
167
165
|
end
|
168
166
|
|
167
|
+
def safe_navigation_incompatible?(node)
|
168
|
+
return false unless node.csend_type?
|
169
|
+
|
170
|
+
SAFE_NAVIGATION_INCOMPATIBLE_METHODS.include?(node.method_name)
|
171
|
+
end
|
172
|
+
|
169
173
|
# When comparing classes, `!(Integer < Numeric)` is not the same as
|
170
174
|
# `Integer > Numeric`.
|
171
175
|
def possible_class_hierarchy_check?(lhs, rhs, method)
|
@@ -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,19 +42,25 @@ module RuboCop
|
|
42
42
|
return if kwarg_nodes.empty?
|
43
43
|
|
44
44
|
add_offense(node) do |corrector|
|
45
|
-
|
46
|
-
|
45
|
+
defining_node = node.each_ancestor(:def, :defs, :block).first
|
46
|
+
next if processed_source.contains_comment?(arguments_range(defining_node))
|
47
|
+
next unless node.parent.find(&:kwoptarg_type?) == node
|
47
48
|
|
48
|
-
|
49
|
-
append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
|
50
|
-
|
51
|
-
remove_kwargs(kwarg_nodes, corrector)
|
52
|
-
end
|
49
|
+
autocorrect(corrector, node, defining_node, kwarg_nodes)
|
53
50
|
end
|
54
51
|
end
|
55
52
|
|
56
53
|
private
|
57
54
|
|
55
|
+
def autocorrect(corrector, node, defining_node, kwarg_nodes)
|
56
|
+
corrector.insert_before(node, "#{kwarg_nodes.map(&:source).join(', ')}, ")
|
57
|
+
|
58
|
+
arguments = defining_node.arguments
|
59
|
+
append_newline_to_last_kwoptarg(arguments, corrector) unless parentheses?(arguments)
|
60
|
+
|
61
|
+
remove_kwargs(kwarg_nodes, corrector)
|
62
|
+
end
|
63
|
+
|
58
64
|
def append_newline_to_last_kwoptarg(arguments, corrector)
|
59
65
|
last_argument = arguments.last
|
60
66
|
return if last_argument.type?(:kwrestarg, :blockarg)
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def call_in_literals?(node)
|
111
|
-
parent = node.parent&.
|
111
|
+
parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
|
112
112
|
return false unless parent
|
113
113
|
|
114
114
|
parent.type?(:pair, :array, :range) ||
|
@@ -117,7 +117,7 @@ module RuboCop
|
|
117
117
|
end
|
118
118
|
|
119
119
|
def call_in_logical_operators?(node)
|
120
|
-
parent = node.parent&.
|
120
|
+
parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
|
121
121
|
return false unless parent
|
122
122
|
|
123
123
|
logical_operator?(parent) ||
|
@@ -153,7 +153,7 @@ module RuboCop
|
|
153
153
|
end
|
154
154
|
|
155
155
|
def call_in_argument_with_block?(node)
|
156
|
-
parent = node.parent&.
|
156
|
+
parent = node.parent&.any_block_type? && node.parent.parent
|
157
157
|
return false unless parent
|
158
158
|
|
159
159
|
parent.type?(:call, :super, :yield)
|