rubocop 1.54.2 → 1.55.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 +3 -1
- data/config/default.yml +8 -2
- data/config/obsoletion.yml +5 -0
- data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
- data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
- data/lib/rubocop/cop/layout/redundant_line_break.rb +8 -0
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
- data/lib/rubocop/cop/mixin/string_help.rb +2 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +226 -53
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -1
- data/lib/rubocop/cop/style/redundant_argument.rb +6 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +19 -7
- data/lib/rubocop/cop/style/symbol_array.rb +22 -3
- data/lib/rubocop/formatter/junit_formatter.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +9 -3
- data/lib/rubocop/lsp/runtime.rb +12 -1
- data/lib/rubocop/lsp/server.rb +4 -2
- data/lib/rubocop/result_cache.rb +4 -0
- data/lib/rubocop/target_finder.rb +6 -2
- data/lib/rubocop/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7bfd5d73e8097398116fc389fa55f96eca51b61c95a5e9e4654d7eeebaeef985
|
4
|
+
data.tar.gz: a2cf8206a9896953e85516ebdc00f673a5852eb9613e877fee6b43058f7ef080
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 454b6be8363e0a91226348e2543587d5e6c3503bdaa9b2c0287f1a1e347f9a315c8f8337c5762e59f83f7406a797645ebef7e16a896694da29cb751a658e5579
|
7
|
+
data.tar.gz: 388fe4a1934bf7659148ce32a2837f5ff0822babc1e17abd1a2c4f8138e6a839a7c4f304202e7e712763dbc23fadee4c92412554cbe699a8f81810e602d62f49
|
data/README.md
CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
|
|
53
53
|
in your `Gemfile`:
|
54
54
|
|
55
55
|
```rb
|
56
|
-
gem 'rubocop', '~> 1.
|
56
|
+
gem 'rubocop', '~> 1.55', require: false
|
57
57
|
```
|
58
58
|
|
59
59
|
See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
|
@@ -67,6 +67,8 @@ $ cd my/cool/ruby/project
|
|
67
67
|
$ rubocop
|
68
68
|
```
|
69
69
|
|
70
|
+
You can also use this magic in your favorite editor with RuboCop's [built-in LSP](https://docs.rubocop.org/rubocop/usage/lsp.html).
|
71
|
+
|
70
72
|
## Documentation
|
71
73
|
|
72
74
|
You can read a lot more about RuboCop in its [official docs](https://docs.rubocop.org).
|
data/config/default.yml
CHANGED
@@ -3072,6 +3072,7 @@ Style/ArgumentsForwarding:
|
|
3072
3072
|
StyleGuide: '#arguments-forwarding'
|
3073
3073
|
Enabled: pending
|
3074
3074
|
AllowOnlyRestArgument: true
|
3075
|
+
UseAnonymousForwarding: true
|
3075
3076
|
VersionAdded: '1.1'
|
3076
3077
|
|
3077
3078
|
Style/ArrayCoercion:
|
@@ -3916,8 +3917,9 @@ Style/HashConversion:
|
|
3916
3917
|
Description: 'Avoid Hash[] in favor of ary.to_h or literal hashes.'
|
3917
3918
|
StyleGuide: '#avoid-hash-constructor'
|
3918
3919
|
Enabled: pending
|
3920
|
+
SafeAutoCorrect: false
|
3919
3921
|
VersionAdded: '1.10'
|
3920
|
-
VersionChanged: '1.
|
3922
|
+
VersionChanged: '1.55'
|
3921
3923
|
AllowSplatArgument: true
|
3922
3924
|
|
3923
3925
|
Style/HashEachMethods:
|
@@ -4810,12 +4812,16 @@ Style/RedundantArgument:
|
|
4810
4812
|
Enabled: pending
|
4811
4813
|
Safe: false
|
4812
4814
|
VersionAdded: '1.4'
|
4813
|
-
VersionChanged: '1.
|
4815
|
+
VersionChanged: '1.55'
|
4814
4816
|
Methods:
|
4815
4817
|
# Array#join
|
4816
4818
|
join: ''
|
4817
4819
|
# Array#sum
|
4818
4820
|
sum: 0
|
4821
|
+
# Kernel.#exit
|
4822
|
+
exit: true
|
4823
|
+
# Kernel.#exit!
|
4824
|
+
exit!: false
|
4819
4825
|
# String#split
|
4820
4826
|
split: ' '
|
4821
4827
|
# String#chomp
|
data/config/obsoletion.yml
CHANGED
@@ -224,6 +224,11 @@ changed_parameters:
|
|
224
224
|
- AllowedMethods
|
225
225
|
- AllowedPatterns
|
226
226
|
severity: warning
|
227
|
+
- cops: Style/ArgumentsForwarding
|
228
|
+
parameters: AllowOnlyRestArgument
|
229
|
+
reason: "`AllowOnlyRestArgument` has no effect with TargetRubyVersion >= 3.2."
|
230
|
+
severity: warning
|
231
|
+
minimum_ruby_version: 3.2
|
227
232
|
|
228
233
|
# Enforced styles that have been removed or replaced
|
229
234
|
changed_enforced_styles:
|
@@ -19,7 +19,7 @@ module RuboCop
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def violated?
|
22
|
-
config[cop]&.key?(parameter)
|
22
|
+
applies_to_current_ruby_version? && config[cop]&.key?(parameter)
|
23
23
|
end
|
24
24
|
|
25
25
|
def warning?
|
@@ -28,6 +28,14 @@ module RuboCop
|
|
28
28
|
|
29
29
|
private
|
30
30
|
|
31
|
+
def applies_to_current_ruby_version?
|
32
|
+
minimum_ruby_version = metadata['minimum_ruby_version']
|
33
|
+
|
34
|
+
return true unless minimum_ruby_version
|
35
|
+
|
36
|
+
config.target_ruby_version >= minimum_ruby_version
|
37
|
+
end
|
38
|
+
|
31
39
|
def alternative
|
32
40
|
metadata['alternative']
|
33
41
|
end
|
@@ -48,6 +48,10 @@ module RuboCop
|
|
48
48
|
|
49
49
|
MSG = 'Redundant line break detected.'
|
50
50
|
|
51
|
+
def on_lvasgn(node)
|
52
|
+
super unless end_with_percent_blank_string?(processed_source)
|
53
|
+
end
|
54
|
+
|
51
55
|
def on_send(node)
|
52
56
|
# Include "the whole expression".
|
53
57
|
node = node.parent while node.parent&.send_type? ||
|
@@ -61,6 +65,10 @@ module RuboCop
|
|
61
65
|
|
62
66
|
private
|
63
67
|
|
68
|
+
def end_with_percent_blank_string?(processed_source)
|
69
|
+
processed_source.buffer.source.end_with?("%\n\n")
|
70
|
+
end
|
71
|
+
|
64
72
|
def check_assignment(node, _rhs)
|
65
73
|
return unless offense?(node)
|
66
74
|
|
@@ -68,7 +68,7 @@ module RuboCop
|
|
68
68
|
|
69
69
|
def same_conditions_node_different_branch?(variable, outer_local_variable)
|
70
70
|
variable_node = variable_node(variable)
|
71
|
-
return false unless variable_node
|
71
|
+
return false unless node_or_its_ascendant_conditional?(variable_node)
|
72
72
|
|
73
73
|
outer_local_variable_node =
|
74
74
|
find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
|
@@ -96,6 +96,12 @@ module RuboCop
|
|
96
96
|
|
97
97
|
find_conditional_node_from_ascendant(parent)
|
98
98
|
end
|
99
|
+
|
100
|
+
def node_or_its_ascendant_conditional?(node)
|
101
|
+
return true if node.conditional?
|
102
|
+
|
103
|
+
!!find_conditional_node_from_ascendant(node)
|
104
|
+
end
|
99
105
|
end
|
100
106
|
end
|
101
107
|
end
|
@@ -30,8 +30,8 @@ module RuboCop
|
|
30
30
|
private
|
31
31
|
|
32
32
|
def inside_interpolation?(node)
|
33
|
-
# A :begin node inside a :dstr node is an interpolation.
|
34
|
-
node.ancestors.drop_while { |a| !a.begin_type? }.any?
|
33
|
+
# A :begin node inside a :dstr or :dsym node is an interpolation.
|
34
|
+
node.ancestors.drop_while { |a| !a.begin_type? }.any? { |a| a.dstr_type? || a.dsym_type? }
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
@@ -8,6 +8,12 @@ module RuboCop
|
|
8
8
|
# This cop identifies places where `do_something(*args, &block)`
|
9
9
|
# can be replaced by `do_something(...)`.
|
10
10
|
#
|
11
|
+
# In Ruby 3.2, anonymous args/kwargs forwarding has been added.
|
12
|
+
#
|
13
|
+
# This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
|
14
|
+
# replaced by `use_args(*)`/`use_kwargs(**)`; if desired, this functionality can be disabled
|
15
|
+
# by setting UseAnonymousForwarding: false.
|
16
|
+
#
|
11
17
|
# @example
|
12
18
|
# # bad
|
13
19
|
# def foo(*args, &block)
|
@@ -24,7 +30,27 @@ module RuboCop
|
|
24
30
|
# bar(...)
|
25
31
|
# end
|
26
32
|
#
|
27
|
-
# @example
|
33
|
+
# @example UseAnonymousForwarding: true (default, only relevant for Ruby >= 3.2)
|
34
|
+
# # bad
|
35
|
+
# def foo(*args, **kwargs)
|
36
|
+
# args_only(*args)
|
37
|
+
# kwargs_only(**kwargs)
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# def foo(*, **)
|
42
|
+
# args_only(*)
|
43
|
+
# kwargs_only(**)
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @example UseAnonymousForwarding: false (only relevant for Ruby >= 3.2)
|
47
|
+
# # good
|
48
|
+
# def foo(*args, **kwargs)
|
49
|
+
# args_only(*args)
|
50
|
+
# kwargs_only(**kwargs)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# @example AllowOnlyRestArgument: true (default, only relevant for Ruby < 3.2)
|
28
54
|
# # good
|
29
55
|
# def foo(*args)
|
30
56
|
# bar(*args)
|
@@ -34,7 +60,7 @@ module RuboCop
|
|
34
60
|
# bar(**kwargs)
|
35
61
|
# end
|
36
62
|
#
|
37
|
-
# @example AllowOnlyRestArgument: false
|
63
|
+
# @example AllowOnlyRestArgument: false (only relevant for Ruby < 3.2)
|
38
64
|
# # bad
|
39
65
|
# # The following code can replace the arguments with `...`,
|
40
66
|
# # but it will change the behavior. Because `...` forwards block also.
|
@@ -53,77 +79,133 @@ module RuboCop
|
|
53
79
|
|
54
80
|
minimum_target_ruby_version 2.7
|
55
81
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
PATTERN
|
62
|
-
|
63
|
-
# @!method only_rest_arguments?(node, name)
|
64
|
-
def_node_matcher :only_rest_arguments?, <<~PATTERN
|
65
|
-
{
|
66
|
-
(send _ _ (splat (lvar %1)))
|
67
|
-
(send _ _ (hash (kwsplat (lvar %1))))
|
68
|
-
}
|
69
|
-
PATTERN
|
70
|
-
|
71
|
-
# @!method forwarding_method_arguments?(node, rest_name, block_name, kwargs_name)
|
72
|
-
def_node_matcher :forwarding_method_arguments?, <<~PATTERN
|
73
|
-
{
|
74
|
-
(send _ _
|
75
|
-
(splat (lvar %1))
|
76
|
-
(block-pass {(lvar %2) nil?}))
|
77
|
-
(send _ _
|
78
|
-
(splat (lvar %1))
|
79
|
-
(hash (kwsplat (lvar %3)))
|
80
|
-
(block-pass {(lvar %2) nil?}))
|
81
|
-
}
|
82
|
-
PATTERN
|
82
|
+
FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
|
83
|
+
|
84
|
+
FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
|
85
|
+
ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
|
86
|
+
KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
|
83
87
|
|
84
88
|
def on_def(node)
|
85
89
|
return unless node.body
|
86
|
-
return unless (rest_args_name, args = use_rest_arguments?(node.arguments))
|
87
|
-
return if args.any?(&:default?)
|
88
90
|
|
89
|
-
node.
|
90
|
-
|
91
|
+
forwardable_args = extract_forwardable_args(node.arguments)
|
92
|
+
|
93
|
+
send_classifications = classify_send_nodes(
|
94
|
+
node,
|
95
|
+
node.each_descendant(:send).to_a,
|
96
|
+
non_splat_or_block_pass_lvar_references(node.body),
|
97
|
+
forwardable_args
|
98
|
+
)
|
91
99
|
|
92
|
-
|
93
|
-
all_lvars_as_forwarding_method_arguments?(node, send_node)
|
100
|
+
return if send_classifications.empty?
|
94
101
|
|
95
|
-
|
96
|
-
|
102
|
+
if only_forwards_all?(send_classifications)
|
103
|
+
add_forward_all_offenses(node, send_classifications)
|
104
|
+
elsif target_ruby_version >= 3.2
|
105
|
+
add_post_ruby_32_offenses(node, send_classifications, forwardable_args)
|
97
106
|
end
|
98
107
|
end
|
108
|
+
|
99
109
|
alias on_defs on_def
|
100
110
|
|
101
111
|
private
|
102
112
|
|
103
|
-
def
|
104
|
-
|
105
|
-
|
113
|
+
def extract_forwardable_args(args)
|
114
|
+
[args.find(&:restarg_type?), args.find(&:kwrestarg_type?), args.find(&:blockarg_type?)]
|
115
|
+
end
|
116
|
+
|
117
|
+
def only_forwards_all?(send_classifications)
|
118
|
+
send_classifications.each_value.all? { |c, _, _| c == :all }
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_forward_all_offenses(node, send_classifications)
|
122
|
+
send_classifications.each_key do |send_node|
|
123
|
+
register_forward_all_offense_on_forwarding_method(send_node)
|
124
|
+
end
|
125
|
+
|
126
|
+
register_forward_all_offense_on_method_def(node)
|
127
|
+
end
|
128
|
+
|
129
|
+
def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
|
130
|
+
return unless use_anonymous_forwarding?
|
131
|
+
|
132
|
+
rest_arg, kwrest_arg, _block_arg = *forwardable_args
|
133
|
+
|
134
|
+
send_classifications.each do |send_node, (_c, forward_rest, forward_kwrest)|
|
135
|
+
if forward_rest
|
136
|
+
register_forward_args_offense(def_node.arguments, rest_arg)
|
137
|
+
register_forward_args_offense(send_node, forward_rest)
|
138
|
+
end
|
106
139
|
|
107
|
-
|
140
|
+
if forward_kwrest
|
141
|
+
register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
|
142
|
+
register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
|
143
|
+
end
|
144
|
+
end
|
108
145
|
end
|
109
146
|
|
110
|
-
def
|
111
|
-
|
147
|
+
def non_splat_or_block_pass_lvar_references(body)
|
148
|
+
body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
|
149
|
+
parent = lvar.parent
|
150
|
+
|
151
|
+
next if lvar.lvar_type? && FORWARDING_LVAR_TYPES.include?(parent.type)
|
112
152
|
|
113
|
-
|
153
|
+
lvar.children.first
|
154
|
+
end.uniq
|
114
155
|
end
|
115
156
|
|
116
|
-
def
|
117
|
-
|
157
|
+
def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
|
158
|
+
send_nodes.to_h do |send_node|
|
159
|
+
classification_and_forwards = classification_and_forwards(
|
160
|
+
def_node,
|
161
|
+
send_node,
|
162
|
+
referenced_lvars,
|
163
|
+
forwardable_args
|
164
|
+
)
|
165
|
+
|
166
|
+
[send_node, classification_and_forwards]
|
167
|
+
end.compact
|
168
|
+
end
|
118
169
|
|
119
|
-
|
120
|
-
|
170
|
+
def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
|
171
|
+
classifier = SendNodeClassifier.new(
|
172
|
+
def_node,
|
173
|
+
send_node,
|
174
|
+
referenced_lvars,
|
175
|
+
forwardable_args,
|
176
|
+
target_ruby_version: target_ruby_version,
|
177
|
+
allow_only_rest_arguments: allow_only_rest_arguments?
|
178
|
+
)
|
121
179
|
|
122
|
-
|
180
|
+
classification = classifier.classification
|
181
|
+
|
182
|
+
return unless classification
|
183
|
+
|
184
|
+
[classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
|
123
185
|
end
|
124
186
|
|
125
|
-
def
|
126
|
-
add_offense(
|
187
|
+
def register_forward_args_offense(def_arguments_or_send, rest_arg_or_splat)
|
188
|
+
add_offense(rest_arg_or_splat, message: ARGS_MSG) do |corrector|
|
189
|
+
unless parentheses?(def_arguments_or_send)
|
190
|
+
add_parentheses(def_arguments_or_send, corrector)
|
191
|
+
end
|
192
|
+
|
193
|
+
corrector.replace(rest_arg_or_splat, '*')
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def register_forward_kwargs_offense(add_parens, def_arguments_or_send, kwrest_arg_or_splat)
|
198
|
+
add_offense(kwrest_arg_or_splat, message: KWARGS_MSG) do |corrector|
|
199
|
+
if add_parens && !parentheses?(def_arguments_or_send)
|
200
|
+
add_parentheses(def_arguments_or_send, corrector)
|
201
|
+
end
|
202
|
+
|
203
|
+
corrector.replace(kwrest_arg_or_splat, '**')
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def register_forward_all_offense_on_forwarding_method(forwarding_method)
|
208
|
+
add_offense(arguments_range(forwarding_method), message: FORWARDING_MSG) do |corrector|
|
127
209
|
begin_pos = forwarding_method.loc.selector&.end_pos || forwarding_method.loc.dot.end_pos
|
128
210
|
range = range_between(begin_pos, forwarding_method.source_range.end_pos)
|
129
211
|
|
@@ -131,8 +213,8 @@ module RuboCop
|
|
131
213
|
end
|
132
214
|
end
|
133
215
|
|
134
|
-
def
|
135
|
-
add_offense(arguments_range(method_definition)) do |corrector|
|
216
|
+
def register_forward_all_offense_on_method_def(method_definition)
|
217
|
+
add_offense(arguments_range(method_definition), message: FORWARDING_MSG) do |corrector|
|
136
218
|
arguments_range = range_with_surrounding_space(
|
137
219
|
method_definition.arguments.source_range, side: :left
|
138
220
|
)
|
@@ -149,6 +231,97 @@ module RuboCop
|
|
149
231
|
def allow_only_rest_arguments?
|
150
232
|
cop_config.fetch('AllowOnlyRestArgument', true)
|
151
233
|
end
|
234
|
+
|
235
|
+
def use_anonymous_forwarding?
|
236
|
+
cop_config.fetch('UseAnonymousForwarding', false)
|
237
|
+
end
|
238
|
+
|
239
|
+
# Classifies send nodes for possible rest/kwrest/all (including block) forwarding.
|
240
|
+
class SendNodeClassifier
|
241
|
+
extend NodePattern::Macros
|
242
|
+
|
243
|
+
# @!method find_forwarded_rest_arg(node, rest_name)
|
244
|
+
def_node_search :find_forwarded_rest_arg, '(splat (lvar %1))'
|
245
|
+
|
246
|
+
# @!method find_forwarded_kwrest_arg(node, kwrest_name)
|
247
|
+
def_node_search :find_forwarded_kwrest_arg, '(kwsplat (lvar %1))'
|
248
|
+
|
249
|
+
# @!method find_forwarded_block_arg(node, block_name)
|
250
|
+
def_node_search :find_forwarded_block_arg, '(block_pass {(lvar %1) nil?})'
|
251
|
+
|
252
|
+
def initialize(def_node, send_node, referenced_lvars, forwardable_args, **config)
|
253
|
+
@def_node = def_node
|
254
|
+
@send_node = send_node
|
255
|
+
@referenced_lvars = referenced_lvars
|
256
|
+
@rest_arg, @kwrest_arg, @block_arg = *forwardable_args
|
257
|
+
@rest_arg_name, @kwrest_arg_name, @block_arg_name =
|
258
|
+
*forwardable_args.map { |a| a&.name }
|
259
|
+
@config = config
|
260
|
+
end
|
261
|
+
|
262
|
+
def forwarded_rest_arg
|
263
|
+
return nil if referenced_rest_arg?
|
264
|
+
|
265
|
+
find_forwarded_rest_arg(@send_node, @rest_arg_name).first
|
266
|
+
end
|
267
|
+
|
268
|
+
def forwarded_kwrest_arg
|
269
|
+
return nil if referenced_kwrest_arg?
|
270
|
+
|
271
|
+
find_forwarded_kwrest_arg(@send_node, @kwrest_arg_name).first
|
272
|
+
end
|
273
|
+
|
274
|
+
def forwarded_block_arg
|
275
|
+
return nil if referenced_block_arg?
|
276
|
+
|
277
|
+
find_forwarded_block_arg(@send_node, @block_arg_name).first
|
278
|
+
end
|
279
|
+
|
280
|
+
def classification
|
281
|
+
return nil unless forwarded_rest_arg || forwarded_kwrest_arg
|
282
|
+
|
283
|
+
if referenced_none? && (forwarded_exactly_all? || pre_ruby_32_allow_forward_all?)
|
284
|
+
:all
|
285
|
+
elsif target_ruby_version >= 3.2
|
286
|
+
:rest_or_kwrest
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
private
|
291
|
+
|
292
|
+
def referenced_rest_arg?
|
293
|
+
@referenced_lvars.include?(@rest_arg_name)
|
294
|
+
end
|
295
|
+
|
296
|
+
def referenced_kwrest_arg?
|
297
|
+
@referenced_lvars.include?(@kwrest_arg_name)
|
298
|
+
end
|
299
|
+
|
300
|
+
def referenced_block_arg?
|
301
|
+
@referenced_lvars.include?(@block_arg_name)
|
302
|
+
end
|
303
|
+
|
304
|
+
def referenced_none?
|
305
|
+
!(referenced_rest_arg? || referenced_kwrest_arg? || referenced_block_arg?)
|
306
|
+
end
|
307
|
+
|
308
|
+
def forwarded_exactly_all?
|
309
|
+
@send_node.arguments.size == 3 &&
|
310
|
+
forwarded_rest_arg &&
|
311
|
+
forwarded_kwrest_arg &&
|
312
|
+
forwarded_block_arg
|
313
|
+
end
|
314
|
+
|
315
|
+
def target_ruby_version
|
316
|
+
@config.fetch(:target_ruby_version)
|
317
|
+
end
|
318
|
+
|
319
|
+
def pre_ruby_32_allow_forward_all?
|
320
|
+
target_ruby_version < 3.2 &&
|
321
|
+
@def_node.arguments.none?(&:default?) &&
|
322
|
+
(@block_arg ? forwarded_block_arg : !@config.fetch(:allow_only_rest_arguments))
|
323
|
+
end
|
324
|
+
end
|
152
325
|
end
|
153
326
|
end
|
154
327
|
end
|
@@ -67,6 +67,7 @@ module RuboCop
|
|
67
67
|
return unless node.parent&.begin_type?
|
68
68
|
return unless collection_looping_method?(node)
|
69
69
|
return unless same_collection_looping_block?(node, node.left_sibling)
|
70
|
+
return unless node.body && node.left_sibling.body
|
70
71
|
|
71
72
|
add_offense(node) do |corrector|
|
72
73
|
combine_with_left_sibling(corrector, node)
|
@@ -10,6 +10,16 @@ module RuboCop
|
|
10
10
|
# `Hash[*ary]` can be replaced with `ary.each_slice(2).to_h` but it will be complicated.
|
11
11
|
# So, `AllowSplatArgument` option is true by default to allow splat argument for simple code.
|
12
12
|
#
|
13
|
+
# @safety
|
14
|
+
# This cop's autocorrection is unsafe because `ArgumentError` occurs
|
15
|
+
# if the number of elements is odd:
|
16
|
+
#
|
17
|
+
# [source,ruby]
|
18
|
+
# ----
|
19
|
+
# Hash[[[1, 2], [3]]] #=> {1=>2, 3=>nil}
|
20
|
+
# [[1, 2], [5]].to_h #=> wrong array length at 1 (expected 2, was 1) (ArgumentError)
|
21
|
+
# ----
|
22
|
+
#
|
13
23
|
# @example
|
14
24
|
# # bad
|
15
25
|
# Hash[ary]
|
@@ -124,9 +124,10 @@ module RuboCop
|
|
124
124
|
node.parent&.class_type? && node.parent&.single_line?
|
125
125
|
end
|
126
126
|
|
127
|
-
def call_with_ambiguous_arguments?(node)
|
127
|
+
def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
|
128
128
|
call_with_braced_block?(node) ||
|
129
129
|
call_as_argument_or_chain?(node) ||
|
130
|
+
call_in_match_pattern?(node) ||
|
130
131
|
hash_literal_in_arguments?(node) ||
|
131
132
|
node.descendants.any? do |n|
|
132
133
|
n.forwarded_args_type? || ambiguous_literal?(n) || logical_operator?(n) ||
|
@@ -144,6 +145,10 @@ module RuboCop
|
|
144
145
|
node.parent.csend_type? || node.parent.super_type? || node.parent.yield_type?)
|
145
146
|
end
|
146
147
|
|
148
|
+
def call_in_match_pattern?(node)
|
149
|
+
node.parent&.match_pattern_type?
|
150
|
+
end
|
151
|
+
|
147
152
|
def hash_literal_in_arguments?(node)
|
148
153
|
node.arguments.any? do |n|
|
149
154
|
hash_literal?(n) ||
|
@@ -35,6 +35,8 @@ module RuboCop
|
|
35
35
|
# array.join('')
|
36
36
|
# [1, 2, 3].join("")
|
37
37
|
# array.sum(0)
|
38
|
+
# exit(true)
|
39
|
+
# exit!(false)
|
38
40
|
# string.split(" ")
|
39
41
|
# "first\nsecond".split(" ")
|
40
42
|
# string.chomp("\n")
|
@@ -45,6 +47,8 @@ module RuboCop
|
|
45
47
|
# array.join
|
46
48
|
# [1, 2, 3].join
|
47
49
|
# array.sum
|
50
|
+
# exit
|
51
|
+
# exit!
|
48
52
|
# string.split
|
49
53
|
# "first second".split
|
50
54
|
# string.chomp
|
@@ -55,9 +59,10 @@ module RuboCop
|
|
55
59
|
extend AutoCorrector
|
56
60
|
|
57
61
|
MSG = 'Argument %<arg>s is redundant because it is implied by default.'
|
62
|
+
NO_RECEIVER_METHODS = %i[exit exit!].freeze
|
58
63
|
|
59
64
|
def on_send(node)
|
60
|
-
return if node.receiver.nil?
|
65
|
+
return if !NO_RECEIVER_METHODS.include?(node.method_name) && node.receiver.nil?
|
61
66
|
return if node.arguments.count != 1
|
62
67
|
return unless redundant_argument?(node)
|
63
68
|
|
@@ -52,7 +52,7 @@ module RuboCop
|
|
52
52
|
include AllowedMethods
|
53
53
|
include AllowedPattern
|
54
54
|
|
55
|
-
MSG = '
|
55
|
+
MSG = 'Return `false` instead of `nil` in predicate methods.'
|
56
56
|
|
57
57
|
# @!method return_nil?(node)
|
58
58
|
def_node_matcher :return_nil?, <<~PATTERN
|
@@ -65,16 +65,28 @@ module RuboCop
|
|
65
65
|
return unless (body = node.body)
|
66
66
|
|
67
67
|
body.each_descendant(:return) do |return_node|
|
68
|
-
|
68
|
+
register_offense(return_node, 'return false') if return_nil?(return_node)
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
+
return unless (nil_node = nil_node_at_the_end_of_method_body(body))
|
71
72
|
|
72
|
-
|
73
|
-
corrector.replace(return_node, 'return false')
|
74
|
-
end
|
75
|
-
end
|
73
|
+
register_offense(nil_node, 'false')
|
76
74
|
end
|
77
75
|
alias on_defs on_def
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def nil_node_at_the_end_of_method_body(body)
|
80
|
+
return unless (last_child = body.children.last)
|
81
|
+
|
82
|
+
last_child if last_child.is_a?(AST::Node) && last_child.nil_type?
|
83
|
+
end
|
84
|
+
|
85
|
+
def register_offense(offense_node, replacement)
|
86
|
+
add_offense(offense_node) do |corrector|
|
87
|
+
corrector.replace(offense_node, replacement)
|
88
|
+
end
|
89
|
+
end
|
78
90
|
end
|
79
91
|
end
|
80
92
|
end
|
@@ -22,6 +22,15 @@ module RuboCop
|
|
22
22
|
# # bad
|
23
23
|
# [:foo, :bar, :baz]
|
24
24
|
#
|
25
|
+
# # bad (contains spaces)
|
26
|
+
# %i[foo\ bar baz\ quux]
|
27
|
+
#
|
28
|
+
# # bad (contains [] with spaces)
|
29
|
+
# %i[foo \[ \]]
|
30
|
+
#
|
31
|
+
# # bad (contains () with spaces)
|
32
|
+
# %i(foo \( \))
|
33
|
+
#
|
25
34
|
# @example EnforcedStyle: brackets
|
26
35
|
# # good
|
27
36
|
# [:foo, :bar, :baz]
|
@@ -40,6 +49,7 @@ module RuboCop
|
|
40
49
|
|
41
50
|
PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
|
42
51
|
ARRAY_MSG = 'Use %<prefer>s for an array of symbols.'
|
52
|
+
DELIMITERS = ['[', ']', '(', ')'].freeze
|
43
53
|
|
44
54
|
class << self
|
45
55
|
attr_accessor :largest_brackets
|
@@ -47,7 +57,7 @@ module RuboCop
|
|
47
57
|
|
48
58
|
def on_array(node)
|
49
59
|
if bracketed_array_of?(:sym, node)
|
50
|
-
return if
|
60
|
+
return if complex_content?(node)
|
51
61
|
|
52
62
|
check_bracketed_array(node, 'i')
|
53
63
|
elsif node.percent_literal?(:symbol)
|
@@ -57,13 +67,22 @@ module RuboCop
|
|
57
67
|
|
58
68
|
private
|
59
69
|
|
60
|
-
def
|
70
|
+
def complex_content?(node)
|
61
71
|
node.children.any? do |sym|
|
62
72
|
content, = *sym
|
63
|
-
content.to_s
|
73
|
+
content = content.to_s
|
74
|
+
content_without_delimiter_pairs = content.gsub(/(\[\])|(\(\))/, '')
|
75
|
+
|
76
|
+
content.include?(' ') || DELIMITERS.any? do |delimiter|
|
77
|
+
content_without_delimiter_pairs.include?(delimiter)
|
78
|
+
end
|
64
79
|
end
|
65
80
|
end
|
66
81
|
|
82
|
+
def invalid_percent_array_contents?(node)
|
83
|
+
complex_content?(node)
|
84
|
+
end
|
85
|
+
|
67
86
|
def build_bracketed_array(node)
|
68
87
|
return '[]' if node.children.empty?
|
69
88
|
|
@@ -63,7 +63,7 @@ module RuboCop
|
|
63
63
|
|
64
64
|
def classname_attribute_value(file)
|
65
65
|
@classname_attribute_value_cache ||= Hash.new do |hash, key|
|
66
|
-
hash[key] = key.
|
66
|
+
hash[key] = key.delete_suffix('.rb').gsub("#{Dir.pwd}/", '').tr('/', '.')
|
67
67
|
end
|
68
68
|
@classname_attribute_value_cache[file]
|
69
69
|
end
|
data/lib/rubocop/lsp/routes.rb
CHANGED
@@ -36,7 +36,9 @@ module RuboCop
|
|
36
36
|
end
|
37
37
|
|
38
38
|
handle 'initialize' do |request|
|
39
|
-
|
39
|
+
initialization_options = extract_initialization_options_from(request)
|
40
|
+
|
41
|
+
@server.configure(initialization_options)
|
40
42
|
|
41
43
|
@server.write(
|
42
44
|
id: request[:id],
|
@@ -164,10 +166,14 @@ module RuboCop
|
|
164
166
|
|
165
167
|
private
|
166
168
|
|
167
|
-
def
|
169
|
+
def extract_initialization_options_from(request)
|
168
170
|
safe_autocorrect = request.dig(:params, :initializationOptions, :safeAutocorrect)
|
169
171
|
|
170
|
-
|
172
|
+
{
|
173
|
+
safe_autocorrect: safe_autocorrect.nil? || safe_autocorrect == true,
|
174
|
+
lint_mode: request.dig(:params, :initializationOptions, :lintMode) == true,
|
175
|
+
layout_mode: request.dig(:params, :initializationOptions, :layoutMode) == true
|
176
|
+
}
|
171
177
|
end
|
172
178
|
|
173
179
|
def format_file(file_uri)
|
data/lib/rubocop/lsp/runtime.rb
CHANGED
@@ -14,12 +14,14 @@ module RuboCop
|
|
14
14
|
# Runtime for Language Server Protocol of RuboCop.
|
15
15
|
# @api private
|
16
16
|
class Runtime
|
17
|
-
attr_writer :safe_autocorrect
|
17
|
+
attr_writer :safe_autocorrect, :lint_mode, :layout_mode
|
18
18
|
|
19
19
|
def initialize(config_store)
|
20
20
|
@config_store = config_store
|
21
21
|
@logged_paths = []
|
22
22
|
@safe_autocorrect = true
|
23
|
+
@lint_mode = false
|
24
|
+
@layout_mode = false
|
23
25
|
end
|
24
26
|
|
25
27
|
# This abuses the `--stdin` option of rubocop and reads the formatted text
|
@@ -37,6 +39,7 @@ module RuboCop
|
|
37
39
|
formatting_options = {
|
38
40
|
stdin: text, force_exclusion: true, autocorrect: true, safe_autocorrect: @safe_autocorrect
|
39
41
|
}
|
42
|
+
formatting_options[:only] = config_only_options if @lint_mode || @layout_mode
|
40
43
|
|
41
44
|
redirect_stdout { run_rubocop(formatting_options, path) }
|
42
45
|
|
@@ -47,6 +50,7 @@ module RuboCop
|
|
47
50
|
diagnostic_options = {
|
48
51
|
stdin: text, force_exclusion: true, formatters: ['json'], format: 'json'
|
49
52
|
}
|
53
|
+
diagnostic_options[:only] = config_only_options if @lint_mode || @layout_mode
|
50
54
|
|
51
55
|
json = redirect_stdout { run_rubocop(diagnostic_options, path) }
|
52
56
|
results = JSON.parse(json, symbolize_names: true)
|
@@ -64,6 +68,13 @@ module RuboCop
|
|
64
68
|
|
65
69
|
private
|
66
70
|
|
71
|
+
def config_only_options
|
72
|
+
only_options = []
|
73
|
+
only_options << 'Lint' if @lint_mode
|
74
|
+
only_options << 'Layout' if @layout_mode
|
75
|
+
only_options
|
76
|
+
end
|
77
|
+
|
67
78
|
def redirect_stdout(&block)
|
68
79
|
stdout = StringIO.new
|
69
80
|
|
data/lib/rubocop/lsp/server.rb
CHANGED
@@ -53,8 +53,10 @@ module RuboCop
|
|
53
53
|
@runtime.offenses(path, text)
|
54
54
|
end
|
55
55
|
|
56
|
-
def configure(
|
57
|
-
@runtime.safe_autocorrect = safe_autocorrect
|
56
|
+
def configure(options)
|
57
|
+
@runtime.safe_autocorrect = options[:safe_autocorrect]
|
58
|
+
@runtime.lint_mode = options[:lint_mode]
|
59
|
+
@runtime.layout_mode = options[:layout_mode]
|
58
60
|
end
|
59
61
|
|
60
62
|
def stop(&block)
|
data/lib/rubocop/result_cache.rb
CHANGED
@@ -202,6 +202,10 @@ module RuboCop
|
|
202
202
|
lib_root = File.join(File.dirname(__FILE__), '..')
|
203
203
|
exe_root = File.join(lib_root, '..', 'exe')
|
204
204
|
|
205
|
+
# Make sure to use an absolute path to prevent errors on Windows
|
206
|
+
# when traversing the relative paths with symlinks.
|
207
|
+
exe_root = File.absolute_path(exe_root)
|
208
|
+
|
205
209
|
# These are all the files we have `require`d plus everything in the
|
206
210
|
# exe directory. A change to any of them could affect the cop output
|
207
211
|
# so we include them in the cache hash.
|
@@ -94,8 +94,12 @@ module RuboCop
|
|
94
94
|
end
|
95
95
|
|
96
96
|
def wanted_dir_patterns(base_dir, exclude_pattern, flags)
|
97
|
-
base_dir
|
98
|
-
|
97
|
+
# Escape glob characters in base_dir to avoid unwanted behavior.
|
98
|
+
base_dir = base_dir.gsub(/[\\\{\}\[\]\*\?]/) do |reserved_glob_character|
|
99
|
+
"\\#{reserved_glob_character}"
|
100
|
+
end
|
101
|
+
|
102
|
+
dirs = Dir.glob(File.join(base_dir, '*/'), flags)
|
99
103
|
.reject do |dir|
|
100
104
|
next true if dir.end_with?('/./', '/../')
|
101
105
|
next true if File.fnmatch?(exclude_pattern, dir, flags)
|
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.55.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2023-07-
|
13
|
+
date: 2023-07-25 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
@@ -134,7 +134,7 @@ dependencies:
|
|
134
134
|
requirements:
|
135
135
|
- - ">="
|
136
136
|
- !ruby/object:Gem::Version
|
137
|
-
version: 1.28.
|
137
|
+
version: 1.28.1
|
138
138
|
- - "<"
|
139
139
|
- !ruby/object:Gem::Version
|
140
140
|
version: '2.0'
|
@@ -144,7 +144,7 @@ dependencies:
|
|
144
144
|
requirements:
|
145
145
|
- - ">="
|
146
146
|
- !ruby/object:Gem::Version
|
147
|
-
version: 1.28.
|
147
|
+
version: 1.28.1
|
148
148
|
- - "<"
|
149
149
|
- !ruby/object:Gem::Version
|
150
150
|
version: '2.0'
|
@@ -1023,7 +1023,7 @@ metadata:
|
|
1023
1023
|
homepage_uri: https://rubocop.org/
|
1024
1024
|
changelog_uri: https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md
|
1025
1025
|
source_code_uri: https://github.com/rubocop/rubocop/
|
1026
|
-
documentation_uri: https://docs.rubocop.org/rubocop/1.
|
1026
|
+
documentation_uri: https://docs.rubocop.org/rubocop/1.55/
|
1027
1027
|
bug_tracker_uri: https://github.com/rubocop/rubocop/issues
|
1028
1028
|
rubygems_mfa_required: 'true'
|
1029
1029
|
post_install_message:
|