rubocop 1.79.2 → 1.80.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/exe/rubocop +1 -8
- data/lib/rubocop/cli.rb +5 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +6 -3
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -0
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/self_assignment.rb +5 -4
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/naming/method_name.rb +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +3 -2
- data/lib/rubocop/cop/style/array_intersect.rb +45 -11
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -11
- data/lib/rubocop/cop/style/safe_navigation.rb +18 -1
- data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
- data/lib/rubocop/cop/style/symbol_array.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +9 -7
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/lsp/routes.rb +31 -2
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 427aedeb12fa3d9d84e7ccd8ea4c125b418a3170c0f91d02e66ef8717006fc0a
|
4
|
+
data.tar.gz: f5a5b09520f27ed77d9cc5e2077ce9a55fa77db97bdd1d593e399551abd45bb8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ddcf6fc4b8e900f900f5734c647b3d681d731d6d4bf37729c4960a751ace231d8835ca9800d4e499d1908f7e0474445bbf2490f5c8b9408d1301d6bff1196794
|
7
|
+
data.tar.gz: 94f856e78e6af3c0c78fd926268ae8349af663c2e7a1bc40a11842bb04db7a6baa1f239cb2c537ef8b442a4a76dfc09a8a7bfe10244abb2a78ed3b3b6600d38b
|
data/README.md
CHANGED
@@ -51,7 +51,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
|
|
51
51
|
in your `Gemfile`:
|
52
52
|
|
53
53
|
```rb
|
54
|
-
gem 'rubocop', '~> 1.
|
54
|
+
gem 'rubocop', '~> 1.80', require: false
|
55
55
|
```
|
56
56
|
|
57
57
|
See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
|
data/exe/rubocop
CHANGED
@@ -12,13 +12,6 @@ if RuboCop::Server.running?
|
|
12
12
|
exit_status = RuboCop::Server::ClientCommand::Exec.new.run
|
13
13
|
else
|
14
14
|
require 'rubocop'
|
15
|
-
|
16
|
-
cli = RuboCop::CLI.new
|
17
|
-
|
18
|
-
time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
19
|
-
exit_status = cli.run
|
20
|
-
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_start
|
21
|
-
|
22
|
-
puts "Finished in #{elapsed_time} seconds" if cli.options[:debug] || cli.options[:display_time]
|
15
|
+
exit_status = RuboCop::CLI.new.run
|
23
16
|
end
|
24
17
|
exit exit_status
|
data/lib/rubocop/cli.rb
CHANGED
@@ -37,6 +37,8 @@ module RuboCop
|
|
37
37
|
#
|
38
38
|
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
39
39
|
def run(args = ARGV)
|
40
|
+
time_start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
41
|
+
|
40
42
|
@options, paths = Options.new.parse(args)
|
41
43
|
@env = Environment.new(@options, @config_store, paths)
|
42
44
|
|
@@ -72,6 +74,9 @@ module RuboCop
|
|
72
74
|
warn e.message
|
73
75
|
warn e.backtrace
|
74
76
|
STATUS_ERROR
|
77
|
+
ensure
|
78
|
+
elapsed_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) - time_start
|
79
|
+
puts "Finished in #{elapsed_time} seconds" if @options[:debug] || @options[:display_time]
|
75
80
|
end
|
76
81
|
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
77
82
|
|
@@ -29,10 +29,13 @@ module RuboCop
|
|
29
29
|
def align_end(corrector, processed_source, node, align_to)
|
30
30
|
@processed_source = processed_source
|
31
31
|
whitespace = whitespace_range(node)
|
32
|
-
return false unless whitespace.source.strip.empty?
|
33
|
-
|
34
32
|
column = alignment_column(align_to)
|
35
|
-
|
33
|
+
|
34
|
+
if whitespace.source.strip.empty?
|
35
|
+
corrector.replace(whitespace, ' ' * column)
|
36
|
+
else
|
37
|
+
corrector.insert_after(whitespace, "\n#{' ' * column}")
|
38
|
+
end
|
36
39
|
end
|
37
40
|
|
38
41
|
private
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
class ForToEachCorrector
|
7
7
|
extend NodePattern::Macros
|
8
8
|
|
9
|
-
CORRECTION = '%<collection>s
|
9
|
+
CORRECTION = '%<collection>s%<dot>seach do |%<argument>s|'
|
10
10
|
|
11
11
|
def initialize(for_node)
|
12
12
|
@for_node = for_node
|
@@ -25,7 +25,12 @@ module RuboCop
|
|
25
25
|
attr_reader :for_node, :variable_node, :collection_node
|
26
26
|
|
27
27
|
def correction
|
28
|
-
format(
|
28
|
+
format(
|
29
|
+
CORRECTION,
|
30
|
+
collection: collection_source,
|
31
|
+
dot: collection_node.csend_type? ? '&.' : '.',
|
32
|
+
argument: variable_node.source
|
33
|
+
)
|
29
34
|
end
|
30
35
|
|
31
36
|
def collection_source
|
@@ -38,7 +38,7 @@ module RuboCop
|
|
38
38
|
RESTRICT_ON_SEND = MODULE_INCLUSION_METHODS
|
39
39
|
|
40
40
|
def on_send(node)
|
41
|
-
return if node.receiver
|
41
|
+
return if node.receiver || node.arguments.empty?
|
42
42
|
return if node.parent&.type?(:send, :any_block)
|
43
43
|
|
44
44
|
return if next_line_empty_or_enable_directive_comment?(node.last_line)
|
@@ -10,6 +10,8 @@ module RuboCop
|
|
10
10
|
# condition, an explicit `return` statement, etc. In other contexts, the second operand should
|
11
11
|
# be indented regardless of enforced style.
|
12
12
|
#
|
13
|
+
# In both styles, operators should be aligned when an assignment begins on the next line.
|
14
|
+
#
|
13
15
|
# @example EnforcedStyle: aligned (default)
|
14
16
|
# # bad
|
15
17
|
# if a +
|
@@ -24,8 +24,6 @@ module RuboCop
|
|
24
24
|
|
25
25
|
MSG_REPEATED_ELEMENT = 'Duplicate element inside regexp character class'
|
26
26
|
|
27
|
-
OCTAL_DIGITS_AFTER_ESCAPE = 2
|
28
|
-
|
29
27
|
def on_regexp(node)
|
30
28
|
each_repeated_character_class_element_loc(node) do |loc|
|
31
29
|
add_offense(loc, message: MSG_REPEATED_ELEMENT) do |corrector|
|
@@ -40,9 +38,9 @@ module RuboCop
|
|
40
38
|
|
41
39
|
seen = Set.new
|
42
40
|
group_expressions(node, expr.expressions) do |group|
|
43
|
-
group_source = group.
|
41
|
+
group_source = group.to_s
|
44
42
|
|
45
|
-
yield
|
43
|
+
yield group.expression if seen.include?(group_source)
|
46
44
|
|
47
45
|
seen << group_source
|
48
46
|
end
|
@@ -52,40 +50,13 @@ module RuboCop
|
|
52
50
|
private
|
53
51
|
|
54
52
|
def group_expressions(node, expressions)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
until expressions.empty?
|
59
|
-
# With we may need to compose a group of multiple expressions.
|
60
|
-
group = [expressions.shift]
|
61
|
-
next if within_interpolation?(node, group.first)
|
62
|
-
|
63
|
-
# With regexp_parser < 2.7 escaped octal sequences may be up to 3
|
64
|
-
# separate expressions ("\\0", "0", "1").
|
65
|
-
pop_octal_digits(group, expressions) if escaped_octal?(group.first.to_s)
|
66
|
-
|
67
|
-
yield(group)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def pop_octal_digits(current_child, expressions)
|
72
|
-
OCTAL_DIGITS_AFTER_ESCAPE.times do
|
73
|
-
next_child = expressions.first
|
74
|
-
break unless octal?(next_child.to_s)
|
53
|
+
expressions.each do |expression|
|
54
|
+
next if within_interpolation?(node, expression)
|
75
55
|
|
76
|
-
|
56
|
+
yield(expression)
|
77
57
|
end
|
78
58
|
end
|
79
59
|
|
80
|
-
def source_range(children)
|
81
|
-
return children.first.expression if children.size == 1
|
82
|
-
|
83
|
-
range_between(
|
84
|
-
children.first.expression.begin_pos,
|
85
|
-
children.last.expression.begin_pos + children.last.to_s.length
|
86
|
-
)
|
87
|
-
end
|
88
|
-
|
89
60
|
def skip_expression?(expr)
|
90
61
|
expr.type != :set || expr.token == :intersection
|
91
62
|
end
|
@@ -99,14 +70,6 @@ module RuboCop
|
|
99
70
|
interpolation_locs(node).any? { |il| il.overlaps?(parse_tree_child_loc) }
|
100
71
|
end
|
101
72
|
|
102
|
-
def escaped_octal?(string)
|
103
|
-
string.length == 2 && string[0] == '\\' && octal?(string[1])
|
104
|
-
end
|
105
|
-
|
106
|
-
def octal?(char)
|
107
|
-
('0'..'7').cover?(char)
|
108
|
-
end
|
109
|
-
|
110
73
|
def interpolation_locs(node)
|
111
74
|
@interpolation_locs ||= {}
|
112
75
|
|
@@ -52,10 +52,9 @@ module RuboCop
|
|
52
52
|
each_missing_enable do |cop, line_range|
|
53
53
|
next if acceptable_range?(cop, line_range)
|
54
54
|
|
55
|
-
range = source_range(processed_source.buffer, line_range.min, 0..0)
|
56
55
|
comment = processed_source.comment_at_line(line_range.begin)
|
57
56
|
|
58
|
-
add_offense(
|
57
|
+
add_offense(comment, message: message(cop, comment))
|
59
58
|
end
|
60
59
|
end
|
61
60
|
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
return if allow_rbs_inline_annotation? && rbs_inline_annotation?(node.receiver)
|
46
46
|
|
47
47
|
if node.method?(:[]=)
|
48
|
-
handle_key_assignment(node)
|
48
|
+
handle_key_assignment(node)
|
49
49
|
elsif node.assignment_method?
|
50
50
|
handle_attribute_assignment(node) if node.arguments.size == 1
|
51
51
|
end
|
@@ -105,12 +105,13 @@ module RuboCop
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def handle_key_assignment(node)
|
108
|
-
value_node = node.
|
108
|
+
value_node = node.last_argument
|
109
|
+
node_arguments = node.arguments[0...-1]
|
109
110
|
|
110
111
|
if value_node.send_type? && value_node.method?(:[]) &&
|
111
112
|
node.receiver == value_node.receiver &&
|
112
|
-
|
113
|
-
|
113
|
+
node_arguments.none?(&:call_type?) &&
|
114
|
+
node_arguments == value_node.arguments
|
114
115
|
add_offense(node)
|
115
116
|
end
|
116
117
|
end
|
@@ -17,6 +17,7 @@ module RuboCop
|
|
17
17
|
#
|
18
18
|
# # good
|
19
19
|
# CGI.escape('http://example.com')
|
20
|
+
# URI.encode_uri_component(uri) # Since Ruby 3.1
|
20
21
|
# URI.encode_www_form([['example', 'param'], ['lang', 'en']])
|
21
22
|
# URI.encode_www_form(page: 10, locale: 'en')
|
22
23
|
# URI.encode_www_form_component('http://example.com')
|
@@ -27,6 +28,7 @@ module RuboCop
|
|
27
28
|
#
|
28
29
|
# # good
|
29
30
|
# CGI.unescape(enc_uri)
|
31
|
+
# URI.decode_uri_component(uri) # Since Ruby 3.1
|
30
32
|
# URI.decode_www_form(enc_uri)
|
31
33
|
# URI.decode_www_form_component(enc_uri)
|
32
34
|
class UriEscapeUnescape < Base
|
@@ -19,8 +19,7 @@ module RuboCop
|
|
19
19
|
def check_end_kw_alignment(node, align_ranges)
|
20
20
|
return if ignored_node?(node)
|
21
21
|
|
22
|
-
end_loc = node.loc.end
|
23
|
-
return if accept_end_kw_alignment?(end_loc)
|
22
|
+
return unless (end_loc = node.loc.end)
|
24
23
|
|
25
24
|
matching = matching_ranges(end_loc, align_ranges)
|
26
25
|
|
@@ -57,11 +56,6 @@ module RuboCop
|
|
57
56
|
add_offense(end_loc, message: msg) { |corrector| autocorrect(corrector, node) }
|
58
57
|
end
|
59
58
|
|
60
|
-
def accept_end_kw_alignment?(end_loc)
|
61
|
-
end_loc.nil? || # Discard modifier forms of if/while/until.
|
62
|
-
!/\A[ \t]*end/.match?(processed_source.lines[end_loc.line - 1])
|
63
|
-
end
|
64
|
-
|
65
59
|
def style_parameter_name
|
66
60
|
'EnforcedStyleAlignWith'
|
67
61
|
end
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
14
14
|
# method calls are assumed to return boolean values. The cop does not make an assessment
|
15
15
|
# if the return type is unknown (non-predicate method calls, variables, etc.).
|
16
16
|
#
|
17
|
-
# NOTE:
|
17
|
+
# NOTE: The `initialize` method and operator methods (`def ==`, etc.) are ignored.
|
18
18
|
#
|
19
19
|
# By default, the cop runs in `conservative` mode, which allows a method to be named
|
20
20
|
# with a question mark as long as at least one return value is boolean. In `aggressive`
|
@@ -149,7 +149,8 @@ module RuboCop
|
|
149
149
|
private
|
150
150
|
|
151
151
|
def allowed?(node)
|
152
|
-
|
152
|
+
node.method?(:initialize) ||
|
153
|
+
allowed_method?(node.method_name) ||
|
153
154
|
matches_allowed_pattern?(node.method_name) ||
|
154
155
|
allowed_bang_method?(node) ||
|
155
156
|
node.operator_method? ||
|
@@ -10,6 +10,8 @@ module RuboCop
|
|
10
10
|
# * `(array1 & array2).any?`
|
11
11
|
# * `(array1.intersection(array2)).any?`
|
12
12
|
# * `array1.any? { |elem| array2.member?(elem) }`
|
13
|
+
# * `(array1 & array2).count > 0`
|
14
|
+
# * `(array1 & array2).size > 0`
|
13
15
|
#
|
14
16
|
# can be replaced with `array1.intersect?(array2)`.
|
15
17
|
#
|
@@ -51,6 +53,19 @@ module RuboCop
|
|
51
53
|
# array1.intersect?(array2)
|
52
54
|
# !array1.intersect?(array2)
|
53
55
|
#
|
56
|
+
# # bad
|
57
|
+
# (array1 & array2).count > 0
|
58
|
+
# (array1 & array2).count.positive?
|
59
|
+
# (array1 & array2).count != 0
|
60
|
+
#
|
61
|
+
# (array1 & array2).count == 0
|
62
|
+
# (array1 & array2).count.zero?
|
63
|
+
#
|
64
|
+
# # good
|
65
|
+
# array1.intersect?(array2)
|
66
|
+
#
|
67
|
+
# !array1.intersect?(array2)
|
68
|
+
#
|
54
69
|
# @example AllCops:ActiveSupportExtensionsEnabled: false (default)
|
55
70
|
# # good
|
56
71
|
# (array1 & array2).present?
|
@@ -73,9 +88,11 @@ module RuboCop
|
|
73
88
|
PREDICATES = %i[any? empty? none?].to_set.freeze
|
74
89
|
ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
|
75
90
|
|
91
|
+
ARRAY_SIZE_METHODS = %i[count length size].to_set.freeze
|
92
|
+
|
76
93
|
# @!method bad_intersection_check?(node, predicates)
|
77
94
|
def_node_matcher :bad_intersection_check?, <<~PATTERN
|
78
|
-
(call
|
95
|
+
$(call
|
79
96
|
{
|
80
97
|
(begin (send $_ :& $_))
|
81
98
|
(call $_ :intersection $_)
|
@@ -84,6 +101,20 @@ module RuboCop
|
|
84
101
|
)
|
85
102
|
PATTERN
|
86
103
|
|
104
|
+
# @!method intersection_size_check?(node, predicates)
|
105
|
+
def_node_matcher :intersection_size_check?, <<~PATTERN
|
106
|
+
(call
|
107
|
+
$(call
|
108
|
+
{
|
109
|
+
(begin (send $_ :& $_))
|
110
|
+
(call $_ :intersection $_)
|
111
|
+
}
|
112
|
+
%ARRAY_SIZE_METHODS
|
113
|
+
)
|
114
|
+
{$:> (int 0) | $:positive? | $:!= (int 0) | $:== (int 0) | $:zero?}
|
115
|
+
)
|
116
|
+
PATTERN
|
117
|
+
|
87
118
|
# @!method any_none_block_intersection(node)
|
88
119
|
def_node_matcher :any_none_block_intersection, <<~PATTERN
|
89
120
|
{
|
@@ -104,15 +135,15 @@ module RuboCop
|
|
104
135
|
PATTERN
|
105
136
|
|
106
137
|
MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
|
107
|
-
STRAIGHT_METHODS = %i[present? any?].freeze
|
108
|
-
NEGATED_METHODS = %i[blank? empty? none?].freeze
|
138
|
+
STRAIGHT_METHODS = %i[present? any? > positive? !=].freeze
|
139
|
+
NEGATED_METHODS = %i[blank? empty? none? == zero?].freeze
|
109
140
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
110
141
|
|
111
142
|
def on_send(node)
|
112
143
|
return if node.block_literal?
|
113
|
-
return unless (receiver, argument, method_name = bad_intersection?(node))
|
144
|
+
return unless (dot_node, receiver, argument, method_name = bad_intersection?(node))
|
114
145
|
|
115
|
-
dot =
|
146
|
+
dot = dot_node.loc.dot.source
|
116
147
|
bang = straight?(method_name) ? '' : '!'
|
117
148
|
replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
|
118
149
|
|
@@ -135,13 +166,16 @@ module RuboCop
|
|
135
166
|
private
|
136
167
|
|
137
168
|
def bad_intersection?(node)
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
PREDICATES
|
142
|
-
end
|
169
|
+
bad_intersection_check?(node, bad_intersection_predicates) ||
|
170
|
+
intersection_size_check?(node)
|
171
|
+
end
|
143
172
|
|
144
|
-
|
173
|
+
def bad_intersection_predicates
|
174
|
+
if active_support_extensions_enabled?
|
175
|
+
ACTIVE_SUPPORT_PREDICATES
|
176
|
+
else
|
177
|
+
PREDICATES
|
178
|
+
end
|
145
179
|
end
|
146
180
|
|
147
181
|
def straight?(method_name)
|
@@ -70,18 +70,25 @@ module RuboCop
|
|
70
70
|
(send _ :& _))
|
71
71
|
PATTERN
|
72
72
|
|
73
|
+
# rubocop:disable Metrics/AbcSize
|
73
74
|
def on_send(node)
|
74
75
|
return unless node.receiver&.begin_type?
|
75
76
|
return unless (preferred_method = preferred_method(node))
|
76
77
|
|
77
78
|
bit_operation = node.receiver.children.first
|
78
79
|
lhs, _operator, rhs = *bit_operation
|
79
|
-
|
80
|
+
|
81
|
+
preferred = if preferred_method == 'allbits?' && lhs.source == node.first_argument.source
|
82
|
+
"#{rhs.source}.allbits?(#{lhs.source})"
|
83
|
+
else
|
84
|
+
"#{lhs.source}.#{preferred_method}(#{rhs.source})"
|
85
|
+
end
|
80
86
|
|
81
87
|
add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
|
82
88
|
corrector.replace(node, preferred)
|
83
89
|
end
|
84
90
|
end
|
91
|
+
# rubocop:enable Metrics/AbcSize
|
85
92
|
|
86
93
|
private
|
87
94
|
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
end
|
69
69
|
|
70
70
|
def autocorrect(corrector, node)
|
71
|
-
if node.
|
71
|
+
if node.post_condition_loop?
|
72
72
|
replace_begin_end_with_modifier(corrector, node)
|
73
73
|
elsif node.modifier_form?
|
74
74
|
replace_source(corrector, node.source_range, modifier_replacement(node))
|
@@ -85,6 +85,29 @@ module RuboCop
|
|
85
85
|
end
|
86
86
|
alias on_defs on_def
|
87
87
|
|
88
|
+
def on_if(node)
|
89
|
+
return if node.modifier_form?
|
90
|
+
|
91
|
+
inspect_branches(node)
|
92
|
+
end
|
93
|
+
|
94
|
+
def on_case(node)
|
95
|
+
inspect_branches(node)
|
96
|
+
end
|
97
|
+
alias on_case_match on_case
|
98
|
+
|
99
|
+
def on_while(node)
|
100
|
+
return if node.modifier_form?
|
101
|
+
|
102
|
+
body = node.body
|
103
|
+
|
104
|
+
return unless body&.kwbegin_type?
|
105
|
+
return if body.rescue_node || body.ensure_node
|
106
|
+
|
107
|
+
register_offense(body)
|
108
|
+
end
|
109
|
+
alias on_until on_while
|
110
|
+
|
88
111
|
def on_block(node)
|
89
112
|
return if target_ruby_version < 2.5
|
90
113
|
return if node.send_node.lambda_literal?
|
@@ -180,6 +203,8 @@ module RuboCop
|
|
180
203
|
end
|
181
204
|
|
182
205
|
def begin_block_has_multiline_statements?(node)
|
206
|
+
return false unless node.parent
|
207
|
+
|
183
208
|
node.children.count >= 2
|
184
209
|
end
|
185
210
|
|
@@ -199,6 +224,15 @@ module RuboCop
|
|
199
224
|
def valid_begin_assignment?(node)
|
200
225
|
node.parent&.assignment? && !node.children.one?
|
201
226
|
end
|
227
|
+
|
228
|
+
def inspect_branches(node)
|
229
|
+
node.branches.each do |branch|
|
230
|
+
next unless branch&.kwbegin_type?
|
231
|
+
next if branch.rescue_node || branch.ensure_node
|
232
|
+
|
233
|
+
register_offense(branch)
|
234
|
+
end
|
235
|
+
end
|
202
236
|
end
|
203
237
|
end
|
204
238
|
end
|
@@ -247,7 +247,7 @@ module RuboCop
|
|
247
247
|
"#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
|
248
248
|
elsif if_branch.true_type?
|
249
249
|
condition = if_branch.parent.condition
|
250
|
-
return condition.source if condition.arguments.empty?
|
250
|
+
return condition.source if condition.arguments.empty? || condition.parenthesized?
|
251
251
|
|
252
252
|
wrap_arguments_with_parens(condition)
|
253
253
|
else
|
@@ -24,9 +24,6 @@ module RuboCop
|
|
24
24
|
(send `{(send _recv _msg) str array hash const #variable?} :[] ...)
|
25
25
|
PATTERN
|
26
26
|
|
27
|
-
# @!method method_node_and_args(node)
|
28
|
-
def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
|
29
|
-
|
30
27
|
# @!method rescue?(node)
|
31
28
|
def_node_matcher :rescue?, '{^resbody ^^resbody}'
|
32
29
|
|
@@ -220,7 +217,7 @@ module RuboCop
|
|
220
217
|
end
|
221
218
|
|
222
219
|
def call_node?(node)
|
223
|
-
node.call_type? || (node.any_block_type? && !node.lambda_or_proc?)
|
220
|
+
node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?)
|
224
221
|
end
|
225
222
|
|
226
223
|
def check_send(begin_node, node)
|
@@ -228,7 +225,7 @@ module RuboCop
|
|
228
225
|
|
229
226
|
return check_unary(begin_node, node) if node.unary_operation?
|
230
227
|
|
231
|
-
return unless method_call_with_redundant_parentheses?(node)
|
228
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
232
229
|
return if call_chain_starts_with_int?(begin_node, node) ||
|
233
230
|
do_end_block_in_method_chain?(begin_node, node)
|
234
231
|
|
@@ -239,8 +236,7 @@ module RuboCop
|
|
239
236
|
return if begin_node.chained?
|
240
237
|
|
241
238
|
node = node.children.first while suspect_unary?(node)
|
242
|
-
|
243
|
-
return if node.send_type? && !method_call_with_redundant_parentheses?(node)
|
239
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
244
240
|
|
245
241
|
offense(begin_node, 'a unary operation')
|
246
242
|
end
|
@@ -302,13 +298,19 @@ module RuboCop
|
|
302
298
|
end
|
303
299
|
end
|
304
300
|
|
305
|
-
def method_call_with_redundant_parentheses?(node)
|
306
|
-
return false unless node.
|
301
|
+
def method_call_with_redundant_parentheses?(begin_node, node)
|
302
|
+
return false unless node.type?(:call, :super, :yield, :defined?)
|
307
303
|
return false if node.prefix_not?
|
304
|
+
return true if singular_parenthesized_parent?(begin_node)
|
305
|
+
|
306
|
+
node.arguments.empty? || parentheses?(node) || square_brackets?(node)
|
307
|
+
end
|
308
308
|
|
309
|
-
|
309
|
+
def singular_parenthesized_parent?(begin_node)
|
310
|
+
return true unless begin_node.parent
|
311
|
+
return false if begin_node.parent.type?(:splat, :kwsplat)
|
310
312
|
|
311
|
-
|
313
|
+
parentheses?(begin_node) && begin_node.parent.children.one?
|
312
314
|
end
|
313
315
|
|
314
316
|
def only_begin_arg?(args)
|
@@ -142,6 +142,7 @@ module RuboCop
|
|
142
142
|
# @!method strip_begin(node)
|
143
143
|
def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
|
144
144
|
|
145
|
+
# rubocop:disable Metrics/AbcSize
|
145
146
|
def on_if(node)
|
146
147
|
return if allowed_if_condition?(node)
|
147
148
|
|
@@ -155,9 +156,11 @@ module RuboCop
|
|
155
156
|
removal_ranges = [begin_range(node, body), end_range(node, body)]
|
156
157
|
|
157
158
|
report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
|
159
|
+
corrector.replace(receiver, checked_variable.source) if checked_variable.csend_type?
|
158
160
|
corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
|
159
161
|
end
|
160
162
|
end
|
163
|
+
# rubocop:enable Metrics/AbcSize
|
161
164
|
|
162
165
|
def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
163
166
|
collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
|
@@ -259,8 +262,14 @@ module RuboCop
|
|
259
262
|
end
|
260
263
|
|
261
264
|
def dotless_operator_call?(method_call)
|
265
|
+
return true if dotless_operator_method?(method_call)
|
266
|
+
|
262
267
|
method_call = method_call.parent while method_call.parent.send_type?
|
263
268
|
|
269
|
+
dotless_operator_method?(method_call)
|
270
|
+
end
|
271
|
+
|
272
|
+
def dotless_operator_method?(method_call)
|
264
273
|
return false if method_call.loc.dot
|
265
274
|
|
266
275
|
method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
|
@@ -335,8 +344,16 @@ module RuboCop
|
|
335
344
|
|
336
345
|
def matching_call_nodes?(left, right)
|
337
346
|
return false unless left && right.respond_to?(:call_type?)
|
347
|
+
return false unless left.call_type? && right.call_type?
|
348
|
+
|
349
|
+
# Compare receiver and method name, but ignore the difference between
|
350
|
+
# safe navigation method call (`&.`) and dot method call (`.`).
|
351
|
+
left_receiver, left_method, *left_args = left.children
|
352
|
+
right_receiver, right_method, *right_args = right.children
|
338
353
|
|
339
|
-
|
354
|
+
left_method == right_method &&
|
355
|
+
matching_nodes?(left_receiver, right_receiver) &&
|
356
|
+
left_args == right_args
|
340
357
|
end
|
341
358
|
|
342
359
|
def chain_length(method_chain, method)
|
@@ -100,7 +100,7 @@ module RuboCop
|
|
100
100
|
node.receiver.str_type? &&
|
101
101
|
node.first_argument.str_type? &&
|
102
102
|
node.multiline? &&
|
103
|
-
node.source
|
103
|
+
node.source.match?(/\+\s*\n/)
|
104
104
|
end
|
105
105
|
|
106
106
|
def find_topmost_plus_node(node)
|
@@ -141,22 +141,26 @@ module RuboCop
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def replacement(parts)
|
144
|
-
interpolated_parts = parts.map
|
145
|
-
case part.type
|
146
|
-
when :str
|
147
|
-
adjust_str(part)
|
148
|
-
when :dstr
|
149
|
-
part.children.all?(&:str_type?) ? adjust_str(part) : part.value
|
150
|
-
else
|
151
|
-
"\#{#{part.source}}"
|
152
|
-
end
|
153
|
-
end
|
144
|
+
interpolated_parts = parts.map { |part| adjust_str(part) }
|
154
145
|
|
155
146
|
"\"#{handle_quotes(interpolated_parts).join}\""
|
156
147
|
end
|
157
148
|
|
158
|
-
def adjust_str(
|
159
|
-
|
149
|
+
def adjust_str(part)
|
150
|
+
case part.type
|
151
|
+
when :str
|
152
|
+
if single_quoted?(part)
|
153
|
+
part.value.gsub(/(\\|"|#\{|#@|#\$)/, '\\\\\&')
|
154
|
+
else
|
155
|
+
part.value.inspect[1..-2]
|
156
|
+
end
|
157
|
+
when :dstr, :begin
|
158
|
+
part.children.map do |child|
|
159
|
+
adjust_str(child)
|
160
|
+
end.join
|
161
|
+
else
|
162
|
+
"\#{#{part.source}}"
|
163
|
+
end
|
160
164
|
end
|
161
165
|
|
162
166
|
def handle_quotes(parts)
|
@@ -81,7 +81,7 @@ module RuboCop
|
|
81
81
|
|
82
82
|
content = *sym
|
83
83
|
content = content.map { |c| c.is_a?(AST::Node) ? c.source : c }.join
|
84
|
-
content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s
|
84
|
+
content_without_delimiter_pairs = content.gsub(/(\[[^\s\[\]]*\])|(\([^\s()]*\))/, '')
|
85
85
|
|
86
86
|
content.include?(' ') || DELIMITERS.any? do |delimiter|
|
87
87
|
content_without_delimiter_pairs.include?(delimiter)
|
@@ -238,11 +238,16 @@ module RuboCop
|
|
238
238
|
end
|
239
239
|
|
240
240
|
def process_loop(node)
|
241
|
-
if
|
241
|
+
if node.post_condition_loop?
|
242
242
|
# See the comment at the end of file for this behavior.
|
243
243
|
condition_node, body_node = *node
|
244
244
|
process_node(body_node)
|
245
245
|
process_node(condition_node)
|
246
|
+
elsif node.for_type?
|
247
|
+
# In `for item in items` the rightmost expression is evaluated first.
|
248
|
+
process_node(node.collection)
|
249
|
+
process_node(node.variable)
|
250
|
+
process_node(node.body) if node.body
|
246
251
|
else
|
247
252
|
process_children(node)
|
248
253
|
end
|
@@ -356,17 +361,14 @@ module RuboCop
|
|
356
361
|
end
|
357
362
|
|
358
363
|
def reference_assignments(loop_assignments, loop_node)
|
359
|
-
node = loop_assignments.first.node
|
360
|
-
|
361
364
|
# If inside a branching statement, mark all as referenced.
|
362
365
|
# Otherwise, mark only the last assignment as referenced.
|
363
366
|
# Note that `rescue` must be considered as branching because of
|
364
367
|
# the `retry` keyword.
|
365
|
-
|
366
|
-
|
367
|
-
else
|
368
|
-
loop_assignments.last&.reference!(loop_node)
|
368
|
+
loop_assignments.each do |assignment|
|
369
|
+
assignment.reference!(loop_node) if assignment.node.each_ancestor(*BRANCH_NODES).any?
|
369
370
|
end
|
371
|
+
loop_assignments.last&.reference!(loop_node)
|
370
372
|
end
|
371
373
|
|
372
374
|
def scanned_node?(node)
|
@@ -4,7 +4,7 @@ module RuboCop
|
|
4
4
|
module Formatter
|
5
5
|
# This formatter displays a YAML configuration file where all cops that
|
6
6
|
# detected any offenses are configured to not detect the offense.
|
7
|
-
class DisabledConfigFormatter < BaseFormatter
|
7
|
+
class DisabledConfigFormatter < BaseFormatter # rubocop:disable Metrics/ClassLength
|
8
8
|
include PathUtil
|
9
9
|
|
10
10
|
HEADING = <<~COMMENTS
|
@@ -17,6 +17,22 @@ module RuboCop
|
|
17
17
|
# versions of RuboCop, may require this file to be generated again.
|
18
18
|
COMMENTS
|
19
19
|
|
20
|
+
EXCLUDED_CONFIG_KEYS = %w[
|
21
|
+
AutoCorrect
|
22
|
+
Description
|
23
|
+
Enabled
|
24
|
+
Exclude
|
25
|
+
Include
|
26
|
+
Reference
|
27
|
+
References
|
28
|
+
Safe
|
29
|
+
SafeAutoCorrect
|
30
|
+
StyleGuide
|
31
|
+
VersionAdded
|
32
|
+
VersionChanged
|
33
|
+
VersionRemoved
|
34
|
+
].freeze
|
35
|
+
|
20
36
|
@config_to_allow_offenses = {}
|
21
37
|
@detected_styles = {}
|
22
38
|
|
@@ -163,10 +179,7 @@ module RuboCop
|
|
163
179
|
end
|
164
180
|
|
165
181
|
def cop_config_params(default_cfg, cfg)
|
166
|
-
default_cfg.keys -
|
167
|
-
%w[Description StyleGuide Reference References Enabled Exclude Safe
|
168
|
-
SafeAutoCorrect VersionAdded VersionChanged VersionRemoved] -
|
169
|
-
cfg.keys
|
182
|
+
default_cfg.keys - EXCLUDED_CONFIG_KEYS - cfg.keys
|
170
183
|
end
|
171
184
|
|
172
185
|
def output_cop_param_comments(output_buffer, params, default_cfg)
|
data/lib/rubocop/lsp/routes.rb
CHANGED
@@ -51,7 +51,7 @@ module RuboCop
|
|
51
51
|
capabilities: LanguageServer::Protocol::Interface::ServerCapabilities.new(
|
52
52
|
document_formatting_provider: true,
|
53
53
|
text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
|
54
|
-
change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::
|
54
|
+
change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::INCREMENTAL,
|
55
55
|
open_close: true
|
56
56
|
)
|
57
57
|
)
|
@@ -76,7 +76,12 @@ module RuboCop
|
|
76
76
|
|
77
77
|
handle 'textDocument/didChange' do |request|
|
78
78
|
params = request[:params]
|
79
|
-
|
79
|
+
file_uri = params[:textDocument][:uri]
|
80
|
+
text = @text_cache[file_uri]
|
81
|
+
params[:contentChanges].each do |content|
|
82
|
+
text = change_text(text, content[:text], content[:range])
|
83
|
+
end
|
84
|
+
result = diagnostic(file_uri, text)
|
80
85
|
@server.write(result)
|
81
86
|
end
|
82
87
|
|
@@ -219,6 +224,30 @@ module RuboCop
|
|
219
224
|
}
|
220
225
|
end
|
221
226
|
|
227
|
+
def change_text(orig_text, text, range)
|
228
|
+
return text unless range
|
229
|
+
|
230
|
+
start_pos = text_pos(orig_text, range[:start])
|
231
|
+
end_pos = text_pos(orig_text, range[:end])
|
232
|
+
text_bin = orig_text.b
|
233
|
+
text_bin[start_pos...end_pos] = text.b
|
234
|
+
text_bin.force_encoding(orig_text.encoding)
|
235
|
+
end
|
236
|
+
|
237
|
+
def text_pos(text, range)
|
238
|
+
line = range[:line]
|
239
|
+
char = range[:character]
|
240
|
+
pos = 0
|
241
|
+
text.each_line.with_index do |l, i|
|
242
|
+
if i == line
|
243
|
+
pos += l.encode('utf-16be').b[0, char * 2].encode('utf-8', 'utf-16be').bytesize
|
244
|
+
return pos
|
245
|
+
end
|
246
|
+
pos += l.bytesize
|
247
|
+
end
|
248
|
+
pos
|
249
|
+
end
|
250
|
+
|
222
251
|
def convert_file_uri_to_path(uri)
|
223
252
|
URI.decode_www_form_component(uri.delete_prefix('file://'))
|
224
253
|
end
|
data/lib/rubocop/result_cache.rb
CHANGED
@@ -9,7 +9,7 @@ module RuboCop
|
|
9
9
|
# Provides functionality for caching RuboCop runs.
|
10
10
|
# @api private
|
11
11
|
class ResultCache
|
12
|
-
NON_CHANGING = %i[color format formatters out debug fail_level
|
12
|
+
NON_CHANGING = %i[color format formatters out debug display_time fail_level
|
13
13
|
fix_layout autocorrect safe_autocorrect autocorrect_all
|
14
14
|
cache fail_fast stdin parallel].freeze
|
15
15
|
|
data/lib/rubocop/runner.rb
CHANGED
@@ -273,7 +273,8 @@ module RuboCop
|
|
273
273
|
end
|
274
274
|
|
275
275
|
def do_inspection_loop(file)
|
276
|
-
|
276
|
+
# We can reuse the prism result since the source did not change yet.
|
277
|
+
processed_source = get_processed_source(file, @prism_result)
|
277
278
|
# This variable is 2d array used to track corrected offenses after each
|
278
279
|
# inspection iteration. This is used to output meaningful infinite loop
|
279
280
|
# error message.
|
@@ -295,7 +296,8 @@ module RuboCop
|
|
295
296
|
# loop if we find any.
|
296
297
|
break unless updated_source_file
|
297
298
|
|
298
|
-
|
299
|
+
# Autocorrect has happened, don't use the prism result since it is stale.
|
300
|
+
processed_source = get_processed_source(file, nil)
|
299
301
|
end
|
300
302
|
|
301
303
|
# Return summary of corrected offenses after all iterations
|
@@ -482,7 +484,7 @@ module RuboCop
|
|
482
484
|
end
|
483
485
|
|
484
486
|
# rubocop:disable Metrics/MethodLength
|
485
|
-
def get_processed_source(file)
|
487
|
+
def get_processed_source(file, prism_result)
|
486
488
|
config = @config_store.for_file(file)
|
487
489
|
ruby_version = config.target_ruby_version
|
488
490
|
parser_engine = config.parser_engine
|
@@ -493,7 +495,7 @@ module RuboCop
|
|
493
495
|
ruby_version,
|
494
496
|
file,
|
495
497
|
parser_engine: parser_engine,
|
496
|
-
prism_result:
|
498
|
+
prism_result: prism_result
|
497
499
|
)
|
498
500
|
else
|
499
501
|
begin
|
@@ -42,14 +42,12 @@ module RuboCop
|
|
42
42
|
# Support Windows: Backslashes from command-line -> forward slashes
|
43
43
|
base_dir = base_dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
|
44
44
|
all_files = find_files(base_dir, File::FNM_DOTMATCH)
|
45
|
-
# use file.include? for performance optimization
|
46
|
-
hidden_files = all_files.select { |file| file.include?(HIDDEN_PATH_SUBSTRING) }.sort
|
47
45
|
base_dir_config = @config_store.for(base_dir)
|
48
46
|
|
49
|
-
target_files = if
|
47
|
+
target_files = if hidden_path?(base_dir)
|
50
48
|
all_files.select { |file| ruby_file?(file) }
|
51
49
|
else
|
52
|
-
all_files.select { |file| to_inspect?(file,
|
50
|
+
all_files.select { |file| to_inspect?(file, base_dir_config) }
|
53
51
|
end
|
54
52
|
|
55
53
|
target_files.sort_by!(&order)
|
@@ -74,18 +72,20 @@ module RuboCop
|
|
74
72
|
|
75
73
|
private
|
76
74
|
|
77
|
-
def to_inspect?(file,
|
75
|
+
def to_inspect?(file, base_dir_config)
|
78
76
|
return false if base_dir_config.file_to_exclude?(file)
|
79
|
-
return true if !
|
80
|
-
file <=> hidden_file
|
81
|
-
end && ruby_file?(file)
|
77
|
+
return true if !hidden_path?(file) && ruby_file?(file)
|
82
78
|
|
83
79
|
base_dir_config.file_to_include?(file)
|
84
80
|
end
|
85
81
|
|
82
|
+
def hidden_path?(path)
|
83
|
+
path.include?(HIDDEN_PATH_SUBSTRING)
|
84
|
+
end
|
85
|
+
|
86
86
|
def wanted_dir_patterns(base_dir, exclude_pattern, flags)
|
87
87
|
# Escape glob characters in base_dir to avoid unwanted behavior.
|
88
|
-
base_dir = base_dir.gsub(/[
|
88
|
+
base_dir = base_dir.gsub(/[\\{}\[\]*?]/) do |reserved_glob_character|
|
89
89
|
"\\#{reserved_glob_character}"
|
90
90
|
end
|
91
91
|
|
data/lib/rubocop/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.80.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
- Yuji Nakayama
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2025-
|
12
|
+
date: 2025-09-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -1090,9 +1090,9 @@ licenses:
|
|
1090
1090
|
- MIT
|
1091
1091
|
metadata:
|
1092
1092
|
homepage_uri: https://rubocop.org/
|
1093
|
-
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.
|
1093
|
+
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.80.2
|
1094
1094
|
source_code_uri: https://github.com/rubocop/rubocop/
|
1095
|
-
documentation_uri: https://docs.rubocop.org/rubocop/1.
|
1095
|
+
documentation_uri: https://docs.rubocop.org/rubocop/1.80/
|
1096
1096
|
bug_tracker_uri: https://github.com/rubocop/rubocop/issues
|
1097
1097
|
rubygems_mfa_required: 'true'
|
1098
1098
|
rdoc_options: []
|