rubocop 1.66.0 → 1.67.0
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 +15 -6
- data/config/internal_affairs.yml +11 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
- data/lib/rubocop/cli/command/lsp.rb +2 -2
- data/lib/rubocop/comment_config.rb +4 -8
- data/lib/rubocop/config.rb +4 -16
- data/lib/rubocop/config_loader.rb +14 -8
- data/lib/rubocop/config_loader_resolver.rb +3 -3
- data/lib/rubocop/config_validator.rb +7 -10
- data/lib/rubocop/cop/base.rb +6 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
- data/lib/rubocop/cop/cop.rb +8 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
- data/lib/rubocop/cop/internal_affairs.rb +16 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
- data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
- data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
- data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
- data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
- data/lib/rubocop/cop/style/collection_compact.rb +10 -10
- data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/empty_else.rb +1 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +1 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -5
- data/lib/rubocop/cop/style/lambda.rb +1 -1
- data/lib/rubocop/cop/style/magic_comment_format.rb +3 -8
- data/lib/rubocop/cop/style/map_into_array.rb +54 -10
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
- data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +73 -34
- data/lib/rubocop/file_finder.rb +9 -4
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -0
- data/lib/rubocop/runner.rb +3 -0
- data/lib/rubocop/server/cache.rb +6 -1
- data/lib/rubocop/server/core.rb +1 -0
- data/lib/rubocop/target_ruby.rb +12 -12
- data/lib/rubocop/version.rb +3 -1
- data/lib/rubocop/yaml_duplication_checker.rb +20 -26
- data/lib/rubocop.rb +2 -0
- metadata +12 -10
@@ -57,6 +57,7 @@ module RuboCop
|
|
57
57
|
# # good
|
58
58
|
# (a.foo)..(b.bar)
|
59
59
|
class AmbiguousRange < Base
|
60
|
+
include RationalLiteral
|
60
61
|
extend AutoCorrector
|
61
62
|
|
62
63
|
MSG = 'Wrap complex range boundaries with parentheses to avoid ambiguity.'
|
@@ -79,12 +80,14 @@ module RuboCop
|
|
79
80
|
yield range.end if range.end
|
80
81
|
end
|
81
82
|
|
83
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
82
84
|
def acceptable?(node)
|
83
85
|
node.begin_type? ||
|
84
|
-
node.literal? ||
|
86
|
+
node.literal? || rational_literal?(node) ||
|
85
87
|
node.variable? || node.const_type? || node.self_type? ||
|
86
88
|
(node.call_type? && acceptable_call?(node))
|
87
89
|
end
|
90
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
88
91
|
|
89
92
|
def acceptable_call?(node)
|
90
93
|
return true if node.unary_operation?
|
@@ -17,8 +17,7 @@ module RuboCop
|
|
17
17
|
class BigDecimalNew < Base
|
18
18
|
extend AutoCorrector
|
19
19
|
|
20
|
-
MSG = '
|
21
|
-
'Use `%<double_colon>sBigDecimal()` instead.'
|
20
|
+
MSG = '`BigDecimal.new()` is deprecated. Use `BigDecimal()` instead.'
|
22
21
|
RESTRICT_ON_SEND = %i[new].freeze
|
23
22
|
|
24
23
|
# @!method big_decimal_new(node)
|
@@ -28,13 +27,11 @@ module RuboCop
|
|
28
27
|
PATTERN
|
29
28
|
|
30
29
|
def on_send(node)
|
31
|
-
big_decimal_new(node) do |
|
32
|
-
|
33
|
-
message = format(MSG, double_colon: double_colon)
|
34
|
-
|
35
|
-
add_offense(node.loc.selector, message: message) do |corrector|
|
30
|
+
big_decimal_new(node) do |cbase|
|
31
|
+
add_offense(node.loc.selector) do |corrector|
|
36
32
|
corrector.remove(node.loc.selector)
|
37
33
|
corrector.remove(node.loc.dot)
|
34
|
+
corrector.remove(cbase) if cbase
|
38
35
|
end
|
39
36
|
end
|
40
37
|
end
|
@@ -36,7 +36,7 @@ module RuboCop
|
|
36
36
|
return unless boolean_symbol?(node)
|
37
37
|
|
38
38
|
parent = node.parent
|
39
|
-
return if parent&.array_type? && parent
|
39
|
+
return if parent&.array_type? && parent.percent_literal?(:symbol)
|
40
40
|
|
41
41
|
add_offense(node, message: format(MSG, boolean: node.value)) do |corrector|
|
42
42
|
autocorrect(corrector, node)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks for duplicate literal, constant, or variable elements in Set.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
# Set[:foo, :bar, :foo]
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# Set[:foo, :bar]
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# Set.new([:foo, :bar, :foo])
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# Set.new([:foo, :bar])
|
21
|
+
#
|
22
|
+
# # bad
|
23
|
+
# [:foo, :bar, :foo].to_set
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# [:foo, :bar].to_set
|
27
|
+
#
|
28
|
+
class DuplicateSetElement < Base
|
29
|
+
extend AutoCorrector
|
30
|
+
|
31
|
+
MSG = 'Remove the duplicate element in Set.'
|
32
|
+
RESTRICT_ON_SEND = %i[\[\] new to_set].freeze
|
33
|
+
|
34
|
+
# @!method set_init_elements(node)
|
35
|
+
def_node_matcher :set_init_elements, <<~PATTERN
|
36
|
+
{
|
37
|
+
(send (const {nil? cbase} :Set) :[] $...)
|
38
|
+
(send (const {nil? cbase} :Set) :new (array $...))
|
39
|
+
(call (array $...) :to_set)
|
40
|
+
}
|
41
|
+
PATTERN
|
42
|
+
|
43
|
+
def on_send(node)
|
44
|
+
return unless (set_elements = set_init_elements(node))
|
45
|
+
|
46
|
+
seen_elements = Set[]
|
47
|
+
|
48
|
+
set_elements.each_with_index do |set_element, index|
|
49
|
+
# NOTE: Skip due to the possibility of corner cases where Set elements
|
50
|
+
# may have changing return values if they are not literals, constants, or variables.
|
51
|
+
next if !set_element.literal? && !set_element.const_type? && !set_element.variable?
|
52
|
+
|
53
|
+
if seen_elements.include?(set_element)
|
54
|
+
register_offense(set_element, set_elements[index - 1])
|
55
|
+
else
|
56
|
+
seen_elements << set_element
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
alias on_csend on_send
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def register_offense(current_element, prev_element)
|
65
|
+
add_offense(current_element) do |corrector|
|
66
|
+
range = prev_element.source_range.end.join(current_element.source_range.end)
|
67
|
+
|
68
|
+
corrector.remove(range)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
FOR_METHOD = ' Or, if they were intended to be separate method ' \
|
29
29
|
'arguments, separate them with a comma.'
|
30
30
|
|
31
|
-
# rubocop:disable Metrics/AbcSize
|
31
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
32
32
|
def on_dstr(node)
|
33
33
|
each_bad_cons(node) do |lhs_node, rhs_node|
|
34
34
|
range = lhs_node.source_range.join(rhs_node.source_range)
|
@@ -40,13 +40,19 @@ module RuboCop
|
|
40
40
|
end
|
41
41
|
|
42
42
|
add_offense(range, message: message) do |corrector|
|
43
|
-
|
43
|
+
if lhs_node.value == ''
|
44
|
+
corrector.remove(lhs_node)
|
45
|
+
elsif rhs_node.value == ''
|
46
|
+
corrector.remove(rhs_node)
|
47
|
+
else
|
48
|
+
range = lhs_node.source_range.end.join(rhs_node.source_range.begin)
|
44
49
|
|
45
|
-
|
50
|
+
corrector.replace(range, ' + ')
|
51
|
+
end
|
46
52
|
end
|
47
53
|
end
|
48
54
|
end
|
49
|
-
# rubocop:enable Metrics/AbcSize
|
55
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
|
50
56
|
|
51
57
|
private
|
52
58
|
|
@@ -29,25 +29,16 @@ module RuboCop
|
|
29
29
|
|
30
30
|
MSG = '`it` calls without arguments will refer to the first block param in Ruby 3.4; ' \
|
31
31
|
'use `it()` or `self.it`.'
|
32
|
+
RESTRICT_ON_SEND = %i[it].freeze
|
32
33
|
|
33
|
-
def
|
34
|
-
return unless (
|
35
|
-
return unless
|
34
|
+
def on_send(node)
|
35
|
+
return unless (block_node = node.each_ancestor(:block).first)
|
36
|
+
return unless block_node.arguments.empty_and_without_delimiters?
|
36
37
|
|
37
|
-
if
|
38
|
-
add_offense(body)
|
39
|
-
else
|
40
|
-
body.each_descendant(:send).each do |send_node|
|
41
|
-
next unless deprecated_it_method?(send_node)
|
42
|
-
|
43
|
-
add_offense(send_node)
|
44
|
-
end
|
45
|
-
end
|
38
|
+
add_offense(node) if deprecated_it_method?(node)
|
46
39
|
end
|
47
40
|
|
48
41
|
def deprecated_it_method?(node)
|
49
|
-
return false unless node.method?(:it)
|
50
|
-
|
51
42
|
!node.receiver && node.arguments.empty? && !node.parenthesized? && !node.block_literal?
|
52
43
|
end
|
53
44
|
end
|
@@ -30,6 +30,8 @@ module RuboCop
|
|
30
30
|
# interpolation should not be removed if the expanded value
|
31
31
|
# contains a space character.
|
32
32
|
expanded_value = autocorrected_value(final_node)
|
33
|
+
expanded_value = handle_special_regexp_chars(begin_node, expanded_value)
|
34
|
+
|
33
35
|
return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
|
34
36
|
|
35
37
|
add_offense(final_node) do |corrector|
|
@@ -77,6 +79,27 @@ module RuboCop
|
|
77
79
|
end
|
78
80
|
# rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
|
79
81
|
|
82
|
+
def handle_special_regexp_chars(begin_node, value)
|
83
|
+
parent_node = begin_node.parent
|
84
|
+
|
85
|
+
return value unless parent_node.regexp_type? && parent_node.slash_literal? && value['/']
|
86
|
+
|
87
|
+
# When a literal string containing a forward slash preceded by backslashes
|
88
|
+
# is interpolated inside a regexp, the number of resultant backslashes in the
|
89
|
+
# compiled Regexp is `(2(n+1) / 4)+1`, where `n` is the number of backslashes
|
90
|
+
# inside the interpolation.
|
91
|
+
# ie. 0-2 backslashes is compiled to 1, 3-6 is compiled to 3, etc.
|
92
|
+
# This maintains that same behavior in order to ensure the Regexp behavior
|
93
|
+
# does not change upon removing the interpolation.
|
94
|
+
value.gsub(%r{(\\*)/}) do
|
95
|
+
backslashes = Regexp.last_match[1]
|
96
|
+
backslash_count = backslashes.length
|
97
|
+
needed_backslashes = (2 * ((backslash_count + 1) / 4)) + 1
|
98
|
+
|
99
|
+
"#{'\\' * needed_backslashes}/"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
80
103
|
def autocorrected_value_for_string(node)
|
81
104
|
if node.source.start_with?("'", '%q')
|
82
105
|
node.children.last.inspect[1..-2]
|
@@ -150,7 +173,7 @@ module RuboCop
|
|
150
173
|
|
151
174
|
def ends_heredoc_line?(node)
|
152
175
|
grandparent = node.parent.parent
|
153
|
-
return false unless grandparent&.dstr_type? && grandparent
|
176
|
+
return false unless grandparent&.dstr_type? && grandparent.heredoc?
|
154
177
|
|
155
178
|
line = processed_source.lines[node.last_line - 1]
|
156
179
|
line.size == node.loc.last_column + 1
|
@@ -161,7 +184,7 @@ module RuboCop
|
|
161
184
|
return false unless parent.dstr_type? || parent.dsym_type?
|
162
185
|
|
163
186
|
grandparent = parent.parent
|
164
|
-
grandparent&.array_type? && grandparent
|
187
|
+
grandparent&.array_type? && grandparent.percent_literal?
|
165
188
|
end
|
166
189
|
end
|
167
190
|
end
|
@@ -37,9 +37,7 @@ module RuboCop
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def valid_context?(node)
|
40
|
-
unless node.arguments.one? &&
|
41
|
-
return true
|
42
|
-
end
|
40
|
+
return true unless node.arguments.one? && node.first_argument.parenthesized_call?
|
43
41
|
return true if first_argument_block_type?(node.first_argument)
|
44
42
|
|
45
43
|
node.operator_method? || node.setter_method? || chained_calls?(node) ||
|
@@ -51,11 +49,12 @@ module RuboCop
|
|
51
49
|
end
|
52
50
|
|
53
51
|
def valid_first_argument?(first_arg)
|
54
|
-
first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg)
|
52
|
+
first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg) ||
|
53
|
+
compound_range?(first_arg)
|
55
54
|
end
|
56
55
|
|
57
|
-
def
|
58
|
-
|
56
|
+
def compound_range?(first_arg)
|
57
|
+
first_arg.range_type? && first_arg.parenthesized_call?
|
59
58
|
end
|
60
59
|
|
61
60
|
def chained_calls?(node)
|
@@ -131,7 +131,7 @@ module RuboCop
|
|
131
131
|
private
|
132
132
|
|
133
133
|
def assume_receiver_instance_exists?(receiver)
|
134
|
-
return true if receiver.const_type? && !receiver.
|
134
|
+
return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
|
135
135
|
|
136
136
|
receiver.literal? && !receiver.nil_type?
|
137
137
|
end
|
@@ -3,88 +3,152 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# Check to make sure that if safe navigation is used
|
7
|
-
#
|
8
|
-
# method calls on
|
6
|
+
# Check to make sure that if safe navigation is used in an `&&` or `||` condition,
|
7
|
+
# consistent and appropriate safe navigation, without excess or deficiency,
|
8
|
+
# is used for all method calls on the same object.
|
9
9
|
#
|
10
10
|
# @example
|
11
11
|
# # bad
|
12
|
-
# foo&.bar && foo
|
12
|
+
# foo&.bar && foo&.baz
|
13
13
|
#
|
14
|
-
# #
|
15
|
-
# foo
|
14
|
+
# # good
|
15
|
+
# foo&.bar && foo.baz
|
16
16
|
#
|
17
17
|
# # bad
|
18
|
-
# foo
|
18
|
+
# foo.bar && foo&.baz
|
19
19
|
#
|
20
20
|
# # good
|
21
21
|
# foo.bar && foo.baz
|
22
22
|
#
|
23
|
+
# # bad
|
24
|
+
# foo&.bar || foo.baz
|
25
|
+
#
|
23
26
|
# # good
|
24
27
|
# foo&.bar || foo&.baz
|
25
28
|
#
|
29
|
+
# # bad
|
30
|
+
# foo.bar || foo&.baz
|
31
|
+
#
|
26
32
|
# # good
|
33
|
+
# foo.bar || foo.baz
|
34
|
+
#
|
35
|
+
# # bad
|
27
36
|
# foo&.bar && (foobar.baz || foo&.baz)
|
28
37
|
#
|
38
|
+
# # good
|
39
|
+
# foo&.bar && (foobar.baz || foo.baz)
|
40
|
+
#
|
29
41
|
class SafeNavigationConsistency < Base
|
30
|
-
include IgnoredNode
|
31
42
|
include NilMethods
|
32
43
|
extend AutoCorrector
|
33
44
|
|
34
|
-
|
45
|
+
USE_DOT_MSG = 'Use `.` instead of unnecessary `&.`.'
|
46
|
+
USE_SAFE_NAVIGATION_MSG = 'Use `&.` for consistency with safe navigation.'
|
47
|
+
|
48
|
+
def on_and(node)
|
49
|
+
all_operands = collect_operands(node, [])
|
50
|
+
operand_groups = all_operands.group_by { |operand| receiver_name_as_key(operand, +'') }
|
51
|
+
|
52
|
+
operand_groups.each_value do |grouped_operands|
|
53
|
+
next unless (dot_op, begin_of_rest_operands = find_consistent_parts(grouped_operands))
|
35
54
|
|
36
|
-
|
37
|
-
|
55
|
+
rest_operands = grouped_operands[begin_of_rest_operands..]
|
56
|
+
rest_operands.each do |operand|
|
57
|
+
next if already_appropriate_call?(operand, dot_op)
|
38
58
|
|
39
|
-
|
59
|
+
register_offense(operand, dot_op)
|
60
|
+
end
|
61
|
+
end
|
40
62
|
end
|
63
|
+
alias on_or on_and
|
41
64
|
|
42
|
-
|
43
|
-
ancestor = top_conditional_ancestor(node)
|
44
|
-
conditions = ancestor.conditions
|
45
|
-
safe_nav_receiver = node.receiver
|
65
|
+
private
|
46
66
|
|
47
|
-
|
48
|
-
|
67
|
+
def collect_operands(node, operand_nodes)
|
68
|
+
operand_nodes(node.lhs, operand_nodes)
|
69
|
+
operand_nodes(node.rhs, operand_nodes)
|
49
70
|
|
50
|
-
|
51
|
-
|
71
|
+
operand_nodes
|
72
|
+
end
|
52
73
|
|
53
|
-
|
74
|
+
def receiver_name_as_key(method, fully_receivers)
|
75
|
+
if method.parent.call_type?
|
76
|
+
receiver(method.parent, fully_receivers)
|
77
|
+
else
|
78
|
+
fully_receivers << method.receiver&.source.to_s
|
79
|
+
end
|
80
|
+
end
|
54
81
|
|
55
|
-
|
82
|
+
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
83
|
+
def find_consistent_parts(grouped_operands)
|
84
|
+
csend_in_and, csend_in_or, send_in_and, send_in_or = most_left_indices(grouped_operands)
|
85
|
+
|
86
|
+
if csend_in_and
|
87
|
+
['.', (send_in_and ? [send_in_and, csend_in_and].min : csend_in_and) + 1]
|
88
|
+
elsif send_in_or && csend_in_or
|
89
|
+
send_in_or < csend_in_or ? ['.', send_in_or + 1] : ['&.', csend_in_or + 1]
|
90
|
+
elsif send_in_and && csend_in_or && send_in_and < csend_in_or
|
91
|
+
['.', csend_in_or]
|
56
92
|
end
|
57
93
|
end
|
94
|
+
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
58
95
|
|
59
|
-
|
96
|
+
def already_appropriate_call?(operand, dot_op)
|
97
|
+
(operand.safe_navigation? && dot_op == '&.') || (operand.dot? && dot_op == '.')
|
98
|
+
end
|
99
|
+
|
100
|
+
def register_offense(operand, dot_operator)
|
101
|
+
offense_range = operand.operator_method? ? operand : operand.loc.dot
|
102
|
+
message = dot_operator == '.' ? USE_DOT_MSG : USE_SAFE_NAVIGATION_MSG
|
60
103
|
|
61
|
-
|
62
|
-
|
104
|
+
add_offense(offense_range, message: message) do |corrector|
|
105
|
+
next if operand.operator_method?
|
63
106
|
|
64
|
-
|
107
|
+
corrector.replace(operand.loc.dot, dot_operator)
|
108
|
+
end
|
65
109
|
end
|
66
110
|
|
67
|
-
def
|
68
|
-
|
111
|
+
def operand_nodes(operand, operand_nodes)
|
112
|
+
if operand.operator_keyword?
|
113
|
+
collect_operands(operand, operand_nodes)
|
114
|
+
elsif operand.call_type?
|
115
|
+
operand_nodes << operand
|
116
|
+
end
|
69
117
|
end
|
70
118
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
119
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
120
|
+
def most_left_indices(grouped_operands)
|
121
|
+
indices = { csend_in_and: nil, csend_in_or: nil, send_in_and: nil, send_in_or: nil }
|
122
|
+
|
123
|
+
grouped_operands.each_with_index do |operand, index|
|
124
|
+
indices[:csend_in_and] ||= index if operand_in_and?(operand) && operand.csend_type?
|
125
|
+
indices[:csend_in_or] ||= index if operand_in_or?(operand) && operand.csend_type?
|
126
|
+
indices[:send_in_and] ||= index if operand_in_and?(operand) && !nilable?(operand)
|
127
|
+
indices[:send_in_or] ||= index if operand_in_or?(operand) && !nilable?(operand)
|
77
128
|
end
|
78
129
|
|
79
|
-
|
130
|
+
indices.values
|
80
131
|
end
|
132
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
81
133
|
|
82
|
-
def
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
134
|
+
def operand_in_and?(node)
|
135
|
+
return true if node.parent.and_type?
|
136
|
+
|
137
|
+
parent = node.parent.parent while node.parent.begin_type?
|
138
|
+
|
139
|
+
parent&.and_type?
|
140
|
+
end
|
141
|
+
|
142
|
+
def operand_in_or?(node)
|
143
|
+
return true if node.parent.or_type?
|
144
|
+
|
145
|
+
parent = node.parent.parent while node.parent.begin_type?
|
146
|
+
|
147
|
+
parent&.or_type?
|
148
|
+
end
|
149
|
+
|
150
|
+
def nilable?(node)
|
151
|
+
node.csend_type? || nil_methods.include?(node.method_name)
|
88
152
|
end
|
89
153
|
end
|
90
154
|
end
|
@@ -3,29 +3,47 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
|
-
# Identifies places where `URI.regexp` is obsolete and should
|
7
|
-
#
|
6
|
+
# Identifies places where `URI.regexp` is obsolete and should not be used.
|
7
|
+
#
|
8
|
+
# For Ruby 3.3 or lower, use `URI::DEFAULT_PARSER.make_regexp`.
|
9
|
+
# For Ruby 3.4 or higher, use `URI::RFC2396_PARSER.make_regexp`.
|
10
|
+
#
|
11
|
+
# NOTE: If you need to support both Ruby 3.3 and lower as well as Ruby 3.4 and higher,
|
12
|
+
# consider manually changing the code as follows:
|
13
|
+
#
|
14
|
+
# [source,ruby]
|
15
|
+
# ----
|
16
|
+
# defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
|
17
|
+
# ----
|
8
18
|
#
|
9
19
|
# @example
|
10
20
|
# # bad
|
11
21
|
# URI.regexp('http://example.com')
|
12
22
|
#
|
13
|
-
# # good
|
23
|
+
# # good - Ruby 3.3 or lower
|
14
24
|
# URI::DEFAULT_PARSER.make_regexp('http://example.com')
|
15
25
|
#
|
26
|
+
# # good - Ruby 3.4 or higher
|
27
|
+
# URI::RFC2396_PARSER.make_regexp('http://example.com')
|
28
|
+
#
|
16
29
|
class UriRegexp < Base
|
17
30
|
extend AutoCorrector
|
18
31
|
|
19
32
|
MSG = '`%<current>s` is obsolete and should not be used. Instead, use `%<preferred>s`.'
|
20
|
-
URI_CONSTANTS = ['URI', '::URI'].freeze
|
21
33
|
RESTRICT_ON_SEND = %i[regexp].freeze
|
22
34
|
|
35
|
+
# @!method uri_constant?(node)
|
36
|
+
def_node_matcher :uri_constant?, <<~PATTERN
|
37
|
+
(const {cbase nil?} :URI)
|
38
|
+
PATTERN
|
39
|
+
|
23
40
|
def on_send(node)
|
24
|
-
return unless node.receiver
|
25
|
-
return unless URI_CONSTANTS.include?(node.receiver.source)
|
41
|
+
return unless uri_constant?(node.receiver)
|
26
42
|
|
43
|
+
parser = target_ruby_version >= 3.4 ? 'RFC2396_PARSER' : 'DEFAULT_PARSER'
|
27
44
|
argument = node.first_argument ? "(#{node.first_argument.source})" : ''
|
28
|
-
|
45
|
+
|
46
|
+
preferred_method = "#{node.receiver.source}::#{parser}.make_regexp#{argument}"
|
29
47
|
message = format(MSG, current: node.source, preferred: preferred_method)
|
30
48
|
|
31
49
|
add_offense(node.loc.selector, message: message) do |corrector|
|
@@ -15,7 +15,7 @@ module RuboCop
|
|
15
15
|
parent = node.parent
|
16
16
|
|
17
17
|
parent&.send_type? && parent.arguments.include?(node) &&
|
18
|
-
!parent.parenthesized? && parent
|
18
|
+
!parent.parenthesized? && parent.block_literal?
|
19
19
|
end
|
20
20
|
|
21
21
|
# Override to determine values that are invalid in a percent array
|
@@ -5,7 +5,6 @@ module RuboCop
|
|
5
5
|
# Common functionality for modifier cops.
|
6
6
|
module StatementModifier
|
7
7
|
include LineLengthHelp
|
8
|
-
include RangeHelp
|
9
8
|
|
10
9
|
private
|
11
10
|
|
@@ -65,7 +64,9 @@ module RuboCop
|
|
65
64
|
end
|
66
65
|
|
67
66
|
def method_source(if_body)
|
68
|
-
|
67
|
+
end_range = if_body.implicit_call? ? if_body.loc.dot.end : if_body.loc.selector
|
68
|
+
|
69
|
+
if_body.source_range.begin.join(end_range).source
|
69
70
|
end
|
70
71
|
|
71
72
|
def first_line_comment(node)
|
@@ -116,9 +116,9 @@ module RuboCop
|
|
116
116
|
add_offense(range, message: create_message(word)) do |corrector|
|
117
117
|
suggestions = find_flagged_term(word)['Suggestions']
|
118
118
|
|
119
|
-
|
120
|
-
|
121
|
-
|
119
|
+
if (preferred_term = preferred_sole_term(suggestions))
|
120
|
+
corrector.replace(range, preferred_term)
|
121
|
+
end
|
122
122
|
end
|
123
123
|
end
|
124
124
|
end
|
@@ -157,6 +157,15 @@ module RuboCop
|
|
157
157
|
set_regexes(flagged_term_strings, allowed_strings)
|
158
158
|
end
|
159
159
|
|
160
|
+
def preferred_sole_term(suggestions)
|
161
|
+
case suggestions
|
162
|
+
when Array
|
163
|
+
suggestions.one? && preferred_sole_term(suggestions.first)
|
164
|
+
when String
|
165
|
+
suggestions
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
160
169
|
def extract_regexp(term, term_definition)
|
161
170
|
return term_definition['Regex'] if term_definition['Regex']
|
162
171
|
return /(?:\b|(?<=[\W_]))#{term}(?:\b|(?=[\W_]))/ if term_definition['WholeWord']
|
data/lib/rubocop/cop/offense.rb
CHANGED
@@ -97,8 +97,8 @@ module RuboCop
|
|
97
97
|
# @!attribute [r] correctable?
|
98
98
|
#
|
99
99
|
# @return [Boolean]
|
100
|
-
# whether this offense can be automatically corrected via
|
101
|
-
#
|
100
|
+
# whether this offense can be automatically corrected via autocorrect.
|
101
|
+
# This includes todo comments, for example when requested with `--disable-uncorrectable`.
|
102
102
|
def correctable?
|
103
103
|
@status != :unsupported
|
104
104
|
end
|