rubocop 1.66.0 → 1.67.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|