rubocop 1.75.8 → 1.81.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +20 -16
- data/config/default.yml +117 -26
- data/config/obsoletion.yml +6 -3
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +18 -3
- data/lib/rubocop/config_loader.rb +4 -39
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
- data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +7 -4
- data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
- data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +4 -1
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/dot_position.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
- data/lib/rubocop/cop/layout/line_length.rb +35 -6
- data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
- data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
- data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
- data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
- data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
- data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
- data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
- data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
- data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +30 -4
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
- data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
- data/lib/rubocop/cop/lint/useless_or.rb +98 -0
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.rb +1 -1
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
- data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/method_name.rb +127 -13
- data/lib/rubocop/cop/naming/predicate_method.rb +319 -0
- data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
- data/lib/rubocop/cop/security/eval.rb +2 -1
- data/lib/rubocop/cop/security/open.rb +1 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
- data/lib/rubocop/cop/style/array_intersect.rb +98 -34
- data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
- data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
- data/lib/rubocop/cop/style/case_like_if.rb +1 -1
- data/lib/rubocop/cop/style/collection_querying.rb +167 -0
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
- data/lib/rubocop/cop/style/dig_chain.rb +1 -1
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
- data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
- data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
- data/lib/rubocop/cop/style/it_assignment.rb +69 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
- data/lib/rubocop/cop/style/map_to_set.rb +1 -3
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
- data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
- data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
- 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_exception.rb +1 -1
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
- data/lib/rubocop/cop/style/redundant_format.rb +18 -3
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +55 -16
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
- data/lib/rubocop/cop/style/redundant_self.rb +8 -5
- data/lib/rubocop/cop/style/safe_navigation.rb +44 -12
- data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -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/style/symbol_proc.rb +1 -1
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/unless_else.rb +10 -9
- data/lib/rubocop/cop/utils/format_string.rb +10 -0
- data/lib/rubocop/cop/variable_force/variable.rb +1 -1
- data/lib/rubocop/cop/variable_force.rb +25 -8
- data/lib/rubocop/cops_documentation_generator.rb +1 -0
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
- data/lib/rubocop/lsp/diagnostic.rb +25 -24
- data/lib/rubocop/lsp/routes.rb +65 -9
- data/lib/rubocop/lsp/runtime.rb +2 -2
- data/lib/rubocop/lsp/server.rb +2 -2
- data/lib/rubocop/lsp/stdin_runner.rb +0 -16
- data/lib/rubocop/pending_cops_reporter.rb +56 -0
- data/lib/rubocop/result_cache.rb +14 -12
- data/lib/rubocop/rspec/expect_offense.rb +9 -3
- data/lib/rubocop/runner.rb +6 -4
- data/lib/rubocop/server/cache.rb +4 -2
- data/lib/rubocop/server/client_command/base.rb +10 -0
- data/lib/rubocop/server/client_command/exec.rb +2 -1
- data/lib/rubocop/server/client_command/start.rb +11 -1
- data/lib/rubocop/target_finder.rb +9 -9
- data/lib/rubocop/target_ruby.rb +10 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +12 -1
- data/lib/ruby_lsp/rubocop/addon.rb +25 -10
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +22 -8
@@ -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
|
|
@@ -49,6 +46,7 @@ module RuboCop
|
|
49
46
|
empty_parentheses?(node) ||
|
50
47
|
first_arg_begins_with_hash_literal?(node) ||
|
51
48
|
rescue?(node) ||
|
49
|
+
in_pattern_matching_in_method_argument?(node) ||
|
52
50
|
allowed_pin_operator?(node) ||
|
53
51
|
allowed_expression?(node)
|
54
52
|
end
|
@@ -122,6 +120,13 @@ module RuboCop
|
|
122
120
|
hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
|
123
121
|
end
|
124
122
|
|
123
|
+
def in_pattern_matching_in_method_argument?(begin_node)
|
124
|
+
return false unless begin_node.parent&.call_type?
|
125
|
+
return false unless (node = begin_node.children.first)
|
126
|
+
|
127
|
+
target_ruby_version <= 2.7 ? node.match_pattern_type? : node.match_pattern_p_type?
|
128
|
+
end
|
129
|
+
|
125
130
|
def method_chain_begins_with_hash_literal(node)
|
126
131
|
return if node.nil?
|
127
132
|
return node if node.hash_type?
|
@@ -134,14 +139,14 @@ module RuboCop
|
|
134
139
|
node = begin_node.children.first
|
135
140
|
|
136
141
|
if (message = find_offense_message(begin_node, node))
|
137
|
-
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node)
|
142
|
+
if node.range_type? && !argument_of_parenthesized_method_call?(begin_node, node)
|
138
143
|
begin_node = begin_node.parent
|
139
144
|
end
|
140
145
|
|
141
146
|
return offense(begin_node, message)
|
142
147
|
end
|
143
148
|
|
144
|
-
check_send(begin_node, node) if node
|
149
|
+
check_send(begin_node, node) if call_node?(node)
|
145
150
|
end
|
146
151
|
|
147
152
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
@@ -156,8 +161,12 @@ module RuboCop
|
|
156
161
|
if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
|
157
162
|
return 'an expression'
|
158
163
|
end
|
164
|
+
if disallowed_one_line_pattern_matching?(begin_node, node)
|
165
|
+
return 'a one-line pattern matching'
|
166
|
+
end
|
159
167
|
return 'an interpolated expression' if interpolation?(begin_node)
|
160
|
-
return 'a method argument' if argument_of_parenthesized_method_call?(begin_node)
|
168
|
+
return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
|
169
|
+
return 'a one-line rescue' if oneline_rescue_parentheses_required?(begin_node, node)
|
161
170
|
|
162
171
|
return if begin_node.chained?
|
163
172
|
|
@@ -180,14 +189,24 @@ module RuboCop
|
|
180
189
|
# @!method interpolation?(node)
|
181
190
|
def_node_matcher :interpolation?, '[^begin ^^dstr]'
|
182
191
|
|
183
|
-
def argument_of_parenthesized_method_call?(begin_node)
|
184
|
-
node
|
185
|
-
|
192
|
+
def argument_of_parenthesized_method_call?(begin_node, node)
|
193
|
+
if node.basic_conditional? || node.rescue_type? || method_call_parentheses_required?(node)
|
194
|
+
return false
|
195
|
+
end
|
186
196
|
return false unless (parent = begin_node.parent)
|
187
197
|
|
188
198
|
parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
|
189
199
|
end
|
190
200
|
|
201
|
+
def oneline_rescue_parentheses_required?(begin_node, node)
|
202
|
+
return false unless node.rescue_type?
|
203
|
+
return false unless (parent = begin_node.parent)
|
204
|
+
return false if parent.if_type? && parent.ternary?
|
205
|
+
return false if parent.conditional? && parent.condition == begin_node
|
206
|
+
|
207
|
+
!parent.type?(:call, :array, :pair)
|
208
|
+
end
|
209
|
+
|
191
210
|
def method_call_parentheses_required?(node)
|
192
211
|
return false unless node.call_type?
|
193
212
|
|
@@ -198,10 +217,16 @@ module RuboCop
|
|
198
217
|
!!config.for_enabled_cop('Style/ParenthesesAroundCondition')['AllowInMultilineConditions']
|
199
218
|
end
|
200
219
|
|
220
|
+
def call_node?(node)
|
221
|
+
node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?)
|
222
|
+
end
|
223
|
+
|
201
224
|
def check_send(begin_node, node)
|
225
|
+
node = node.send_node if node.any_block_type?
|
226
|
+
|
202
227
|
return check_unary(begin_node, node) if node.unary_operation?
|
203
228
|
|
204
|
-
return unless method_call_with_redundant_parentheses?(node)
|
229
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
205
230
|
return if call_chain_starts_with_int?(begin_node, node) ||
|
206
231
|
do_end_block_in_method_chain?(begin_node, node)
|
207
232
|
|
@@ -212,8 +237,7 @@ module RuboCop
|
|
212
237
|
return if begin_node.chained?
|
213
238
|
|
214
239
|
node = node.children.first while suspect_unary?(node)
|
215
|
-
|
216
|
-
return if node.send_type? && !method_call_with_redundant_parentheses?(node)
|
240
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
217
241
|
|
218
242
|
offense(begin_node, 'a unary operation')
|
219
243
|
end
|
@@ -242,6 +266,15 @@ module RuboCop
|
|
242
266
|
end
|
243
267
|
end
|
244
268
|
|
269
|
+
def disallowed_one_line_pattern_matching?(begin_node, node)
|
270
|
+
if (parent = begin_node.parent)
|
271
|
+
return false if parent.any_def_type? && parent.endless?
|
272
|
+
return false if parent.assignment?
|
273
|
+
end
|
274
|
+
|
275
|
+
node.any_match_pattern_type? && node.each_ancestor.none?(&:operator_keyword?)
|
276
|
+
end
|
277
|
+
|
245
278
|
def raised_to_power_negative_numeric?(begin_node, node)
|
246
279
|
return false unless node.numeric_type?
|
247
280
|
|
@@ -266,13 +299,19 @@ module RuboCop
|
|
266
299
|
end
|
267
300
|
end
|
268
301
|
|
269
|
-
def method_call_with_redundant_parentheses?(node)
|
270
|
-
return false unless node.
|
302
|
+
def method_call_with_redundant_parentheses?(begin_node, node)
|
303
|
+
return false unless node.type?(:call, :super, :yield, :defined?)
|
271
304
|
return false if node.prefix_not?
|
305
|
+
return true if singular_parenthesized_parent?(begin_node)
|
306
|
+
|
307
|
+
node.arguments.empty? || parentheses?(node) || square_brackets?(node)
|
308
|
+
end
|
272
309
|
|
273
|
-
|
310
|
+
def singular_parenthesized_parent?(begin_node)
|
311
|
+
return true unless begin_node.parent
|
312
|
+
return false if begin_node.parent.type?(:splat, :kwsplat)
|
274
313
|
|
275
|
-
|
314
|
+
parentheses?(begin_node) && begin_node.parent.children.one?
|
276
315
|
end
|
277
316
|
|
278
317
|
def only_begin_arg?(args)
|
@@ -66,6 +66,7 @@ module RuboCop
|
|
66
66
|
DETERMINISTIC_REGEX.match?(regexp_node.source)
|
67
67
|
end
|
68
68
|
|
69
|
+
# rubocop:disable Metrics/MethodLength
|
69
70
|
def preferred_argument(regexp_node)
|
70
71
|
new_argument = replacement(regexp_node)
|
71
72
|
|
@@ -73,6 +74,8 @@ module RuboCop
|
|
73
74
|
new_argument.gsub!("'", "\\\\'")
|
74
75
|
new_argument.gsub!('\"', '"')
|
75
76
|
quote = "'"
|
77
|
+
elsif new_argument.include?("\\'")
|
78
|
+
quote = "'"
|
76
79
|
elsif new_argument.include?('\'')
|
77
80
|
new_argument.gsub!("'", "\\\\'")
|
78
81
|
quote = "'"
|
@@ -84,6 +87,7 @@ module RuboCop
|
|
84
87
|
|
85
88
|
"#{quote}#{new_argument}#{quote}"
|
86
89
|
end
|
90
|
+
# rubocop:enable Metrics/MethodLength
|
87
91
|
|
88
92
|
def replacement(regexp_node)
|
89
93
|
regexp_content = regexp_node.content
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
41
41
|
ALLOWED_ALWAYS_ESCAPES = " \n[]^\\#".chars.freeze
|
42
42
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES = '-'.chars.freeze
|
43
43
|
ALLOWED_OUTSIDE_CHAR_CLASS_METACHAR_ESCAPES = '.*+?{}()|$'.chars.freeze
|
44
|
+
INTERPOLATION_SIGILS = %w[@ $].freeze
|
44
45
|
|
45
46
|
def on_regexp(node)
|
46
47
|
each_escape(node) do |char, index, within_character_class|
|
@@ -64,6 +65,7 @@ module RuboCop
|
|
64
65
|
# different versions of Ruby so that e.g. /\i/ != /i/
|
65
66
|
return true if /[[:alnum:]]/.match?(char)
|
66
67
|
return true if ALLOWED_ALWAYS_ESCAPES.include?(char) || delimiter?(node, char)
|
68
|
+
return true if requires_escape_to_avoid_interpolation?(node.source[index], char)
|
67
69
|
|
68
70
|
if within_character_class
|
69
71
|
ALLOWED_WITHIN_CHAR_CLASS_METACHAR_ESCAPES.include?(char) &&
|
@@ -95,6 +97,12 @@ module RuboCop
|
|
95
97
|
delimiters.include?(char)
|
96
98
|
end
|
97
99
|
|
100
|
+
def requires_escape_to_avoid_interpolation?(char_before_escape, escaped_char)
|
101
|
+
# Preserve escapes after '#' that would otherwise trigger interpolation:
|
102
|
+
# '#@ivar', '#@@cvar', and '#$gvar'.
|
103
|
+
char_before_escape == '#' && INTERPOLATION_SIGILS.include?(escaped_char)
|
104
|
+
end
|
105
|
+
|
98
106
|
def each_escape(node)
|
99
107
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
100
108
|
yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
|
@@ -67,6 +67,9 @@ module RuboCop
|
|
67
67
|
|
68
68
|
def on_or_asgn(node)
|
69
69
|
allow_self(node.lhs)
|
70
|
+
|
71
|
+
lhs_name = node.lhs.lvasgn_type? ? node.lhs.name : node.lhs
|
72
|
+
add_lhs_to_local_variables_scopes(node.rhs, lhs_name)
|
70
73
|
end
|
71
74
|
alias on_and_asgn on_or_asgn
|
72
75
|
|
@@ -123,11 +126,11 @@ module RuboCop
|
|
123
126
|
def on_if(node)
|
124
127
|
# Allow conditional nodes to use `self` in the condition if that variable
|
125
128
|
# name is used in an `lvasgn` or `masgn` within the `if`.
|
126
|
-
node.
|
127
|
-
if
|
128
|
-
add_lhs_to_local_variables_scopes(node.condition,
|
129
|
-
|
130
|
-
add_masgn_lhs_variables(node.condition,
|
129
|
+
node.each_descendant(:lvasgn, :masgn) do |descendant_node|
|
130
|
+
if descendant_node.lvasgn_type?
|
131
|
+
add_lhs_to_local_variables_scopes(node.condition, descendant_node.lhs)
|
132
|
+
else
|
133
|
+
add_masgn_lhs_variables(node.condition, descendant_node.lhs)
|
131
134
|
end
|
132
135
|
end
|
133
136
|
end
|
@@ -86,6 +86,10 @@ module RuboCop
|
|
86
86
|
# foo.baz = bar if foo
|
87
87
|
# foo.baz + bar if foo
|
88
88
|
# foo.bar > 2 if foo
|
89
|
+
#
|
90
|
+
# foo ? foo[index] : nil # Ignored `foo&.[](index)` due to unclear readability benefit.
|
91
|
+
# foo ? foo[idx] = v : nil # Ignored `foo&.[]=(idx, v)` due to unclear readability benefit.
|
92
|
+
# foo ? foo * 42 : nil # Ignored `foo&.*(42)` due to unclear readability benefit.
|
89
93
|
class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
|
90
94
|
include NilMethods
|
91
95
|
include RangeHelp
|
@@ -138,6 +142,7 @@ module RuboCop
|
|
138
142
|
# @!method strip_begin(node)
|
139
143
|
def_node_matcher :strip_begin, '{ (begin $!begin) $!(begin) }'
|
140
144
|
|
145
|
+
# rubocop:disable Metrics/AbcSize
|
141
146
|
def on_if(node)
|
142
147
|
return if allowed_if_condition?(node)
|
143
148
|
|
@@ -146,13 +151,16 @@ module RuboCop
|
|
146
151
|
|
147
152
|
body = extract_if_body(node)
|
148
153
|
method_call = receiver.parent
|
154
|
+
return if dotless_operator_call?(method_call) || method_call.double_colon?
|
149
155
|
|
150
156
|
removal_ranges = [begin_range(node, body), end_range(node, body)]
|
151
157
|
|
152
158
|
report_offense(node, method_chain, method_call, *removal_ranges) do |corrector|
|
159
|
+
corrector.replace(receiver, checked_variable.source) if checked_variable.csend_type?
|
153
160
|
corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
|
154
161
|
end
|
155
162
|
end
|
163
|
+
# rubocop:enable Metrics/AbcSize
|
156
164
|
|
157
165
|
def on_and(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
158
166
|
collect_and_clauses(node).each do |(lhs, lhs_operator_range), (rhs, _rhs_operator_range)|
|
@@ -181,6 +189,8 @@ module RuboCop
|
|
181
189
|
end
|
182
190
|
end
|
183
191
|
|
192
|
+
private
|
193
|
+
|
184
194
|
def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
|
185
195
|
add_offense(offense_range) do |corrector|
|
186
196
|
next if ignored_node?(node)
|
@@ -198,8 +208,6 @@ module RuboCop
|
|
198
208
|
end
|
199
209
|
end
|
200
210
|
|
201
|
-
private
|
202
|
-
|
203
211
|
def find_method_chain(node)
|
204
212
|
return node unless node&.parent&.call_type?
|
205
213
|
|
@@ -235,7 +243,7 @@ module RuboCop
|
|
235
243
|
return false if !matching_nodes?(lhs_receiver, rhs_receiver) || rhs_receiver.nil?
|
236
244
|
return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
|
237
245
|
return false if chain_length(rhs, rhs_receiver) > max_chain_length
|
238
|
-
return false if unsafe_method_used?(rhs, rhs_receiver.parent)
|
246
|
+
return false if unsafe_method_used?(node, rhs, rhs_receiver.parent)
|
239
247
|
return false if rhs.send_type? && rhs.method?(:empty?)
|
240
248
|
|
241
249
|
true
|
@@ -253,6 +261,20 @@ module RuboCop
|
|
253
261
|
end
|
254
262
|
end
|
255
263
|
|
264
|
+
def dotless_operator_call?(method_call)
|
265
|
+
return true if dotless_operator_method?(method_call)
|
266
|
+
|
267
|
+
method_call = method_call.parent while method_call.parent.send_type?
|
268
|
+
|
269
|
+
dotless_operator_method?(method_call)
|
270
|
+
end
|
271
|
+
|
272
|
+
def dotless_operator_method?(method_call)
|
273
|
+
return false if method_call.loc.dot
|
274
|
+
|
275
|
+
method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
|
276
|
+
end
|
277
|
+
|
256
278
|
def handle_comments(corrector, node, method_call)
|
257
279
|
comments = comments(node)
|
258
280
|
return if comments.empty?
|
@@ -322,8 +344,16 @@ module RuboCop
|
|
322
344
|
|
323
345
|
def matching_call_nodes?(left, right)
|
324
346
|
return false unless left && right.respond_to?(:call_type?)
|
347
|
+
return false unless left.call_type? && right.call_type?
|
325
348
|
|
326
|
-
|
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
|
353
|
+
|
354
|
+
left_method == right_method &&
|
355
|
+
matching_nodes?(left_receiver, right_receiver) &&
|
356
|
+
left_args == right_args
|
327
357
|
end
|
328
358
|
|
329
359
|
def chain_length(method_chain, method)
|
@@ -334,21 +364,24 @@ module RuboCop
|
|
334
364
|
end
|
335
365
|
end
|
336
366
|
|
337
|
-
def unsafe_method_used?(method_chain, method)
|
338
|
-
return true if unsafe_method?(method)
|
367
|
+
def unsafe_method_used?(node, method_chain, method)
|
368
|
+
return true if unsafe_method?(node, method)
|
339
369
|
|
340
370
|
method.each_ancestor(:send).any? do |ancestor|
|
341
371
|
break true unless config.cop_enabled?('Lint/SafeNavigationChain')
|
342
372
|
|
343
|
-
break true if unsafe_method?(ancestor)
|
373
|
+
break true if unsafe_method?(node, ancestor)
|
344
374
|
break true if nil_methods.include?(ancestor.method_name)
|
345
375
|
break false if ancestor == method_chain
|
346
376
|
end
|
347
377
|
end
|
348
378
|
|
349
|
-
def unsafe_method?(send_node)
|
350
|
-
negated?(send_node)
|
351
|
-
|
379
|
+
def unsafe_method?(node, send_node)
|
380
|
+
return true if negated?(send_node)
|
381
|
+
|
382
|
+
return false if node.respond_to?(:ternary?) && node.ternary?
|
383
|
+
|
384
|
+
send_node.assignment? ||
|
352
385
|
(!send_node.dot? && !send_node.safe_navigation?)
|
353
386
|
end
|
354
387
|
|
@@ -377,8 +410,7 @@ module RuboCop
|
|
377
410
|
method_chain)
|
378
411
|
start_method.each_ancestor do |ancestor|
|
379
412
|
break unless %i[send block].include?(ancestor.type)
|
380
|
-
next
|
381
|
-
next if ancestor.safe_navigation?
|
413
|
+
next if !ancestor.send_type? || ancestor.operator_method?
|
382
414
|
|
383
415
|
corrector.insert_before(ancestor.loc.dot, '&')
|
384
416
|
|
@@ -65,7 +65,7 @@ module RuboCop
|
|
65
65
|
return false if target_ruby_version < 3.0
|
66
66
|
return false if disallow_endless_method_style?
|
67
67
|
return false unless body_node
|
68
|
-
return false if body_node.parent.assignment_method? ||
|
68
|
+
return false if body_node.basic_conditional? || body_node.parent.assignment_method? ||
|
69
69
|
NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES.include?(body_node.type)
|
70
70
|
|
71
71
|
!body_node.type?(:begin, :kwbegin)
|
@@ -86,10 +86,10 @@ module RuboCop
|
|
86
86
|
end
|
87
87
|
|
88
88
|
def correct_to_endless(corrector, node)
|
89
|
-
|
89
|
+
receiver = "#{node.receiver.source}." if node.receiver
|
90
90
|
arguments = node.arguments.any? ? node.arguments.source : '()'
|
91
91
|
body_source = method_body_source(node.body)
|
92
|
-
replacement = "def #{
|
92
|
+
replacement = "def #{receiver}#{node.method_name}#{arguments} = #{body_source}"
|
93
93
|
|
94
94
|
corrector.replace(node, replacement)
|
95
95
|
end
|
@@ -130,7 +130,10 @@ module RuboCop
|
|
130
130
|
end
|
131
131
|
|
132
132
|
def require_parentheses?(method_body)
|
133
|
-
|
133
|
+
return false unless method_body.send_type?
|
134
|
+
return false if method_body.arithmetic_operation?
|
135
|
+
|
136
|
+
!method_body.arguments.empty? && !method_body.comparison_method?
|
134
137
|
end
|
135
138
|
|
136
139
|
def disallow_endless_method_style?
|
@@ -115,8 +115,9 @@ module RuboCop
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def correct_node(corrector, node)
|
118
|
-
corrector.replace(node.loc.keyword, 'if') if node.unless?
|
118
|
+
corrector.replace(node.loc.keyword, 'if') if node.unless? && !part_of_ignored_node?(node)
|
119
119
|
corrector.replace(node.condition, chainable_condition(node))
|
120
|
+
ignore_node(node)
|
120
121
|
end
|
121
122
|
|
122
123
|
def correct_for_guard_condition_style(corrector, node, if_branch)
|
@@ -174,6 +175,8 @@ module RuboCop
|
|
174
175
|
|
175
176
|
if parenthesize_method?(condition)
|
176
177
|
parenthesized_method_arguments(condition)
|
178
|
+
elsif condition.and_type?
|
179
|
+
parenthesized_and(condition)
|
177
180
|
else
|
178
181
|
"(#{condition.source})"
|
179
182
|
end
|
@@ -185,12 +188,19 @@ module RuboCop
|
|
185
188
|
end
|
186
189
|
|
187
190
|
def add_parentheses?(node)
|
188
|
-
return true if node.assignment? ||
|
191
|
+
return true if node.assignment? || node.or_type?
|
192
|
+
return true if assignment_in_and?(node)
|
189
193
|
return false unless node.call_type?
|
190
194
|
|
191
195
|
(node.arguments.any? && !node.parenthesized?) || node.prefix_not?
|
192
196
|
end
|
193
197
|
|
198
|
+
def assignment_in_and?(node)
|
199
|
+
return false unless node.and_type?
|
200
|
+
|
201
|
+
node.each_descendant.any?(&:assignment?)
|
202
|
+
end
|
203
|
+
|
194
204
|
def parenthesized_method_arguments(node)
|
195
205
|
method_call = node.source_range.begin.join(node.loc.selector.end).source
|
196
206
|
arguments = node.first_argument.source_range.begin.join(node.source_range.end).source
|
@@ -198,6 +208,26 @@ module RuboCop
|
|
198
208
|
"#{method_call}(#{arguments})"
|
199
209
|
end
|
200
210
|
|
211
|
+
def parenthesized_and(node)
|
212
|
+
# We only need to add parentheses around the last clause if it's an assignment,
|
213
|
+
# because other clauses will be unchanged by merging conditionals.
|
214
|
+
lhs = node.lhs.source
|
215
|
+
rhs = parenthesized_and_clause(node.rhs)
|
216
|
+
operator = range_with_surrounding_space(node.loc.operator, whitespace: true).source
|
217
|
+
|
218
|
+
"#{lhs}#{operator}#{rhs}"
|
219
|
+
end
|
220
|
+
|
221
|
+
def parenthesized_and_clause(node)
|
222
|
+
if node.and_type?
|
223
|
+
parenthesized_and(node)
|
224
|
+
elsif node.assignment?
|
225
|
+
"(#{node.source})"
|
226
|
+
else
|
227
|
+
node.source
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
201
231
|
def allow_modifier?
|
202
232
|
cop_config['AllowModifier']
|
203
233
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
#
|
6
|
+
# Checks for parentheses around stabby lambda arguments.
|
7
7
|
# There are two different styles. Defaults to `require_parentheses`.
|
8
8
|
#
|
9
9
|
# @example EnforcedStyle: require_parentheses (default)
|
@@ -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)
|
@@ -270,7 +270,7 @@ module RuboCop
|
|
270
270
|
end
|
271
271
|
|
272
272
|
def allow_if_method_has_argument?(send_node)
|
273
|
-
!!cop_config.fetch('AllowMethodsWithArguments', false) &&
|
273
|
+
!!cop_config.fetch('AllowMethodsWithArguments', false) && send_node.arguments.any?
|
274
274
|
end
|
275
275
|
|
276
276
|
def allow_comments?
|
@@ -10,6 +10,9 @@ module RuboCop
|
|
10
10
|
# for all parenthesized multi-line method calls with arguments.
|
11
11
|
# * `comma`: Requires a comma after the last argument, but only for
|
12
12
|
# parenthesized method calls where each argument is on its own line.
|
13
|
+
# * `diff_comma`: Requires a comma after the last argument, but only
|
14
|
+
# when that argument is followed by an immediate newline, even if
|
15
|
+
# there is an inline comment on the same line.
|
13
16
|
# * `no_comma`: Requires that there is no comma after the last
|
14
17
|
# argument.
|
15
18
|
#
|
@@ -75,6 +78,48 @@ module RuboCop
|
|
75
78
|
# 2,
|
76
79
|
# )
|
77
80
|
#
|
81
|
+
# @example EnforcedStyleForMultiline: diff_comma
|
82
|
+
# # bad
|
83
|
+
# method(1, 2,)
|
84
|
+
#
|
85
|
+
# # good
|
86
|
+
# method(1, 2)
|
87
|
+
#
|
88
|
+
# # good
|
89
|
+
# method(
|
90
|
+
# 1, 2,
|
91
|
+
# 3,
|
92
|
+
# )
|
93
|
+
#
|
94
|
+
# # good
|
95
|
+
# method(
|
96
|
+
# 1, 2, 3,
|
97
|
+
# )
|
98
|
+
#
|
99
|
+
# # good
|
100
|
+
# method(
|
101
|
+
# 1,
|
102
|
+
# 2,
|
103
|
+
# )
|
104
|
+
#
|
105
|
+
# # bad
|
106
|
+
# method(1, [
|
107
|
+
# 2,
|
108
|
+
# ],)
|
109
|
+
#
|
110
|
+
# # good
|
111
|
+
# method(1, [
|
112
|
+
# 2,
|
113
|
+
# ])
|
114
|
+
#
|
115
|
+
# # bad
|
116
|
+
# object[1, 2,
|
117
|
+
# 3, 4,]
|
118
|
+
#
|
119
|
+
# # good
|
120
|
+
# object[1, 2,
|
121
|
+
# 3, 4]
|
122
|
+
#
|
78
123
|
# @example EnforcedStyleForMultiline: no_comma (default)
|
79
124
|
# # bad
|
80
125
|
# method(1, 2,)
|