rubocop 1.79.2 → 1.81.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/config/default.yml +14 -2
- data/exe/rubocop +1 -8
- data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
- data/lib/rubocop/cli.rb +6 -2
- data/lib/rubocop/config_loader.rb +3 -1
- data/lib/rubocop/config_store.rb +5 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
- 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/internal_affairs/node_pattern_groups.rb +3 -1
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/class_structure.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 +1 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +0 -5
- data/lib/rubocop/cop/layout/line_length.rb +9 -1
- 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/trailing_whitespace.rb +1 -1
- data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +13 -7
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
- data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
- data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
- data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
- data/lib/rubocop/cop/lint/self_assignment.rb +6 -5
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
- data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
- data/lib/rubocop/cop/lint/void.rb +7 -0
- data/lib/rubocop/cop/message_annotator.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/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/naming/method_name.rb +1 -1
- data/lib/rubocop/cop/naming/predicate_method.rb +15 -2
- data/lib/rubocop/cop/security/json_load.rb +33 -11
- data/lib/rubocop/cop/style/array_intersect.rb +46 -12
- 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/conditional_assignment.rb +7 -3
- data/lib/rubocop/cop/style/double_negation.rb +1 -1
- data/lib/rubocop/cop/style/endless_method.rb +15 -2
- data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
- data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
- data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
- 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_format.rb +26 -5
- data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +14 -11
- 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/safe_navigation.rb +18 -1
- data/lib/rubocop/cop/style/semicolon.rb +20 -5
- 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/trailing_comma_in_arguments.rb +45 -0
- 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 +9 -7
- data/lib/rubocop/cops_documentation_generator.rb +4 -4
- data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
- data/lib/rubocop/lsp/diagnostic.rb +21 -20
- data/lib/rubocop/lsp/routes.rb +62 -6
- 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/result_cache.rb +1 -1
- data/lib/rubocop/runner.rb +6 -4
- 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 +1 -0
- data/lib/ruby_lsp/rubocop/addon.rb +23 -8
- data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
- metadata +7 -6
|
@@ -55,19 +55,21 @@ module RuboCop
|
|
|
55
55
|
include OnNormalIfUnless
|
|
56
56
|
extend AutoCorrector
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
MSG_SUFFIX = 'over single-line `%<keyword>s/then/else/end` constructs.'
|
|
59
|
+
MSG_TERNARY = "Favor the ternary operator (`?:`) #{MSG_SUFFIX}"
|
|
60
|
+
MSG_MULTILINE = "Favor multi-line `%<keyword>s` #{MSG_SUFFIX}"
|
|
60
61
|
|
|
61
62
|
def on_normal_if_unless(node)
|
|
62
63
|
return unless node.single_line?
|
|
63
64
|
return unless node.else_branch
|
|
64
65
|
return if node.elsif? || node.if_branch&.begin_type?
|
|
65
66
|
|
|
66
|
-
|
|
67
|
-
|
|
67
|
+
multiline = multiline?(node)
|
|
68
|
+
|
|
69
|
+
add_offense(node, message: message(node, multiline)) do |corrector|
|
|
68
70
|
next if part_of_ignored_node?(node)
|
|
69
71
|
|
|
70
|
-
autocorrect(corrector, node)
|
|
72
|
+
autocorrect(corrector, node, multiline)
|
|
71
73
|
|
|
72
74
|
ignore_node(node)
|
|
73
75
|
end
|
|
@@ -75,12 +77,18 @@ module RuboCop
|
|
|
75
77
|
|
|
76
78
|
private
|
|
77
79
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
+
def multiline?(node)
|
|
81
|
+
always_multiline? || cannot_replace_to_ternary?(node)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def message(node, multiline)
|
|
85
|
+
template = multiline ? MSG_MULTILINE : MSG_TERNARY
|
|
86
|
+
|
|
87
|
+
format(template, keyword: node.keyword)
|
|
80
88
|
end
|
|
81
89
|
|
|
82
|
-
def autocorrect(corrector, node)
|
|
83
|
-
if
|
|
90
|
+
def autocorrect(corrector, node, multiline)
|
|
91
|
+
if multiline
|
|
84
92
|
IfThenCorrector.new(node, indentation: configured_indentation_width).call(corrector)
|
|
85
93
|
else
|
|
86
94
|
corrector.replace(node, ternary_correction(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
|
|
@@ -89,7 +89,7 @@ module RuboCop
|
|
|
89
89
|
|
|
90
90
|
def on_send(node)
|
|
91
91
|
format_without_additional_args?(node) do |value|
|
|
92
|
-
replacement = value.source
|
|
92
|
+
replacement = escape_control_chars(value.source)
|
|
93
93
|
|
|
94
94
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
|
95
95
|
corrector.replace(node, replacement)
|
|
@@ -134,6 +134,7 @@ module RuboCop
|
|
|
134
134
|
end
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
137
138
|
def all_fields_literal?(string, arguments)
|
|
138
139
|
count = 0
|
|
139
140
|
sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
|
|
@@ -141,29 +142,44 @@ module RuboCop
|
|
|
141
142
|
|
|
142
143
|
sequences.each do |sequence|
|
|
143
144
|
next if sequence.percent?
|
|
145
|
+
next if unknown_variable_width?(sequence, arguments)
|
|
144
146
|
|
|
145
147
|
hash = arguments.detect(&:hash_type?)
|
|
146
148
|
next unless (argument = find_argument(sequence, arguments, hash))
|
|
147
149
|
next unless matching_argument?(sequence, argument)
|
|
150
|
+
next if (sequence.width || sequence.precision) && argument.dstr_type?
|
|
148
151
|
|
|
149
152
|
count += 1
|
|
150
153
|
end
|
|
151
154
|
|
|
152
155
|
sequences.size == count
|
|
153
156
|
end
|
|
157
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
154
158
|
|
|
159
|
+
# If the sequence has a variable (`*`) width, it cannot be autocorrected
|
|
160
|
+
# if the width is not given as a numeric literal argument
|
|
161
|
+
def unknown_variable_width?(sequence, arguments)
|
|
162
|
+
return false unless sequence.variable_width?
|
|
163
|
+
|
|
164
|
+
argument = arguments[sequence.variable_width_argument_number - 1]
|
|
165
|
+
!numeric?(argument)
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# rubocop:disable Metrics/AbcSize
|
|
155
169
|
def find_argument(sequence, arguments, hash)
|
|
156
170
|
if hash && (sequence.annotated? || sequence.template?)
|
|
157
171
|
find_hash_value_node(hash, sequence.name.to_sym).first
|
|
172
|
+
elsif sequence.variable_width?
|
|
173
|
+
# If the specifier contains `*`, the argument for the width can be ignored.
|
|
174
|
+
arguments.delete_at(sequence.variable_width_argument_number - 1)
|
|
175
|
+
arguments.shift
|
|
158
176
|
elsif sequence.arg_number
|
|
159
177
|
arguments[sequence.arg_number.to_i - 1]
|
|
160
178
|
else
|
|
161
|
-
# If the specifier contains `*`, the following arguments will be used
|
|
162
|
-
# to specify the width and can be ignored.
|
|
163
|
-
(sequence.arity - 1).times { arguments.shift }
|
|
164
179
|
arguments.shift
|
|
165
180
|
end
|
|
166
181
|
end
|
|
182
|
+
# rubocop:enable Metrics/AbcSize
|
|
167
183
|
|
|
168
184
|
def matching_argument?(sequence, argument)
|
|
169
185
|
# Template specifiers don't give a type, any acceptable literal type is ok.
|
|
@@ -214,7 +230,12 @@ module RuboCop
|
|
|
214
230
|
end
|
|
215
231
|
end
|
|
216
232
|
|
|
217
|
-
"#{start_delimiter}#{string}#{end_delimiter}"
|
|
233
|
+
"#{start_delimiter}#{escape_control_chars(string)}#{end_delimiter}"
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Escape any control characters in the string (eg. `\t` or `\n` become `\\t` or `\\n`)
|
|
237
|
+
def escape_control_chars(string)
|
|
238
|
+
string.gsub(/\p{Cc}/) { |s| s.dump[1..-2] }
|
|
218
239
|
end
|
|
219
240
|
|
|
220
241
|
def argument_values(arguments)
|
|
@@ -49,9 +49,10 @@ module RuboCop
|
|
|
49
49
|
def on_dstr(node)
|
|
50
50
|
return unless single_interpolation?(node)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
embedded_node = node.children.first
|
|
53
|
+
return if use_match_pattern?(embedded_node)
|
|
54
54
|
|
|
55
|
+
add_offense(node) do |corrector|
|
|
55
56
|
if variable_interpolation?(embedded_node)
|
|
56
57
|
autocorrect_variable_interpolation(corrector, embedded_node, node)
|
|
57
58
|
elsif single_variable_interpolation?(embedded_node)
|
|
@@ -71,6 +72,14 @@ module RuboCop
|
|
|
71
72
|
!embedded_in_percent_array?(node)
|
|
72
73
|
end
|
|
73
74
|
|
|
75
|
+
def use_match_pattern?(node)
|
|
76
|
+
return false if target_ruby_version <= 2.7
|
|
77
|
+
|
|
78
|
+
node.children.any? do |child|
|
|
79
|
+
child.respond_to?(:match_pattern_type?) && child.match_pattern_type?
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
def single_variable_interpolation?(node)
|
|
75
84
|
return false unless node.children.one?
|
|
76
85
|
|
|
@@ -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
|
|
|
@@ -205,6 +202,7 @@ module RuboCop
|
|
|
205
202
|
return false unless node.rescue_type?
|
|
206
203
|
return false unless (parent = begin_node.parent)
|
|
207
204
|
return false if parent.if_type? && parent.ternary?
|
|
205
|
+
return false if parent.conditional? && parent.condition == begin_node
|
|
208
206
|
|
|
209
207
|
!parent.type?(:call, :array, :pair)
|
|
210
208
|
end
|
|
@@ -220,7 +218,7 @@ module RuboCop
|
|
|
220
218
|
end
|
|
221
219
|
|
|
222
220
|
def call_node?(node)
|
|
223
|
-
node.call_type? || (node.any_block_type? && !node.lambda_or_proc?)
|
|
221
|
+
node.call_type? || (node.any_block_type? && node.braces? && !node.lambda_or_proc?)
|
|
224
222
|
end
|
|
225
223
|
|
|
226
224
|
def check_send(begin_node, node)
|
|
@@ -228,7 +226,7 @@ module RuboCop
|
|
|
228
226
|
|
|
229
227
|
return check_unary(begin_node, node) if node.unary_operation?
|
|
230
228
|
|
|
231
|
-
return unless method_call_with_redundant_parentheses?(node)
|
|
229
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
|
232
230
|
return if call_chain_starts_with_int?(begin_node, node) ||
|
|
233
231
|
do_end_block_in_method_chain?(begin_node, node)
|
|
234
232
|
|
|
@@ -239,8 +237,7 @@ module RuboCop
|
|
|
239
237
|
return if begin_node.chained?
|
|
240
238
|
|
|
241
239
|
node = node.children.first while suspect_unary?(node)
|
|
242
|
-
|
|
243
|
-
return if node.send_type? && !method_call_with_redundant_parentheses?(node)
|
|
240
|
+
return unless method_call_with_redundant_parentheses?(begin_node, node)
|
|
244
241
|
|
|
245
242
|
offense(begin_node, 'a unary operation')
|
|
246
243
|
end
|
|
@@ -302,13 +299,19 @@ module RuboCop
|
|
|
302
299
|
end
|
|
303
300
|
end
|
|
304
301
|
|
|
305
|
-
def method_call_with_redundant_parentheses?(node)
|
|
306
|
-
return false unless node.
|
|
302
|
+
def method_call_with_redundant_parentheses?(begin_node, node)
|
|
303
|
+
return false unless node.type?(:call, :super, :yield, :defined?)
|
|
307
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
|
|
308
309
|
|
|
309
|
-
|
|
310
|
+
def singular_parenthesized_parent?(begin_node)
|
|
311
|
+
return true unless begin_node.parent
|
|
312
|
+
return false if begin_node.parent.type?(:splat, :kwsplat)
|
|
310
313
|
|
|
311
|
-
|
|
314
|
+
parentheses?(begin_node) && begin_node.parent.children.one?
|
|
312
315
|
end
|
|
313
316
|
|
|
314
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
|
|
@@ -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)
|
|
@@ -119,6 +119,7 @@ module RuboCop
|
|
|
119
119
|
tokens[1]&.type == :tSTRING_DBEG && tokens[2]&.semicolon?
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
+
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
122
123
|
def register_semicolon(line, column, after_expression, token_before_semicolon = nil)
|
|
123
124
|
range = source_range(processed_source.buffer, line, column)
|
|
124
125
|
|
|
@@ -130,14 +131,19 @@ module RuboCop
|
|
|
130
131
|
# without parentheses.
|
|
131
132
|
# See: https://github.com/rubocop/rubocop/issues/10791
|
|
132
133
|
if token_before_semicolon&.regexp_dots?
|
|
133
|
-
|
|
134
|
-
|
|
134
|
+
node = find_node(range_nodes, token_before_semicolon)
|
|
135
|
+
elsif token_before_semicolon&.type == :tLABEL
|
|
136
|
+
node = find_node(value_omission_pair_nodes, token_before_semicolon).parent
|
|
137
|
+
space = node.parent.loc.selector.end.join(node.source_range.begin)
|
|
138
|
+
corrector.remove(space)
|
|
135
139
|
end
|
|
136
140
|
|
|
141
|
+
corrector.wrap(node, '(', ')') if node
|
|
137
142
|
corrector.remove(range)
|
|
138
143
|
end
|
|
139
144
|
end
|
|
140
145
|
end
|
|
146
|
+
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
141
147
|
|
|
142
148
|
def expressions_per_line(exprs)
|
|
143
149
|
# create a map matching lines to the number of expressions on them
|
|
@@ -153,9 +159,9 @@ module RuboCop
|
|
|
153
159
|
end
|
|
154
160
|
end
|
|
155
161
|
|
|
156
|
-
def
|
|
157
|
-
|
|
158
|
-
|
|
162
|
+
def find_node(nodes, token_before_semicolon)
|
|
163
|
+
nodes.detect do |node|
|
|
164
|
+
node.source_range.overlaps?(token_before_semicolon.pos)
|
|
159
165
|
end
|
|
160
166
|
end
|
|
161
167
|
|
|
@@ -166,6 +172,15 @@ module RuboCop
|
|
|
166
172
|
@range_nodes = ast.range_type? ? [ast] : []
|
|
167
173
|
@range_nodes.concat(ast.each_descendant(:range).to_a)
|
|
168
174
|
end
|
|
175
|
+
|
|
176
|
+
def value_omission_pair_nodes
|
|
177
|
+
if instance_variable_defined?(:@value_omission_pair_nodes)
|
|
178
|
+
return @value_omission_pair_nodes
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
ast = processed_source.ast
|
|
182
|
+
@value_omission_pair_nodes = ast.each_descendant(:pair).to_a.select(&:value_omission?)
|
|
183
|
+
end
|
|
169
184
|
end
|
|
170
185
|
end
|
|
171
186
|
end
|
|
@@ -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)
|
|
@@ -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,)
|
|
@@ -20,7 +20,6 @@ module RuboCop
|
|
|
20
20
|
# # do a different thing...
|
|
21
21
|
# end
|
|
22
22
|
class UnlessElse < Base
|
|
23
|
-
include RangeHelp
|
|
24
23
|
extend AutoCorrector
|
|
25
24
|
|
|
26
25
|
MSG = 'Do not use `unless` with `else`. Rewrite these with the positive case first.'
|
|
@@ -29,25 +28,27 @@ module RuboCop
|
|
|
29
28
|
return unless node.unless? && node.else?
|
|
30
29
|
|
|
31
30
|
add_offense(node) do |corrector|
|
|
32
|
-
body_range = range_between_condition_and_else(node, node.condition)
|
|
33
|
-
else_range = range_between_else_and_end(node)
|
|
34
|
-
|
|
35
31
|
next if part_of_ignored_node?(node)
|
|
36
32
|
|
|
37
33
|
corrector.replace(node.loc.keyword, 'if')
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
|
|
35
|
+
body_range = range_between_condition_and_else(node)
|
|
36
|
+
else_range = range_between_else_and_end(node)
|
|
37
|
+
|
|
38
|
+
corrector.swap(body_range, else_range)
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
ignore_node(node)
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
def range_between_condition_and_else(node
|
|
46
|
-
|
|
44
|
+
def range_between_condition_and_else(node)
|
|
45
|
+
range = node.loc.begin ? node.loc.begin.end : node.condition.source_range
|
|
46
|
+
|
|
47
|
+
range.end.join(node.loc.else.begin)
|
|
47
48
|
end
|
|
48
49
|
|
|
49
50
|
def range_between_else_and_end(node)
|
|
50
|
-
|
|
51
|
+
node.loc.else.end.join(node.loc.end.begin)
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
54
|
end
|
|
@@ -71,6 +71,16 @@ module RuboCop
|
|
|
71
71
|
name && @source.include?('{')
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
def variable_width?
|
|
75
|
+
!!width&.start_with?('*')
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def variable_width_argument_number
|
|
79
|
+
return unless variable_width?
|
|
80
|
+
|
|
81
|
+
width == '*' ? 1 : width.match(DIGIT_DOLLAR)['arg_number'].to_i
|
|
82
|
+
end
|
|
83
|
+
|
|
74
84
|
# Number of arguments required for the format sequence
|
|
75
85
|
def arity
|
|
76
86
|
@source.scan('*').count + 1
|