rubocop 1.92.0 → 1.92.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/default.yml +1 -0
- data/lib/rubocop/config.rb +0 -4
- data/lib/rubocop/config_obsoletion.rb +9 -0
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +16 -1
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/class_structure.rb +21 -7
- data/lib/rubocop/cop/layout/dot_position.rb +25 -15
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +11 -7
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
- data/lib/rubocop/cop/layout/end_alignment.rb +5 -15
- data/lib/rubocop/cop/layout/extra_spacing.rb +18 -7
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +26 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +11 -5
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +5 -3
- data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
- data/lib/rubocop/cop/layout/line_length.rb +127 -11
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +6 -4
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +3 -3
- data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +12 -0
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +2 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -8
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
- data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -6
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +3 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +36 -0
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +26 -5
- data/lib/rubocop/cop/mixin/statement_modifier.rb +17 -3
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +30 -2
- data/lib/rubocop/cop/style/and_or.rb +34 -11
- data/lib/rubocop/cop/style/arguments_forwarding.rb +28 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +6 -0
- data/lib/rubocop/cop/style/case_equality.rb +14 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +6 -3
- data/lib/rubocop/cop/style/class_methods_definitions.rb +7 -0
- data/lib/rubocop/cop/style/collection_querying.rb +79 -46
- data/lib/rubocop/cop/style/combinable_loops.rb +4 -0
- data/lib/rubocop/cop/style/concat_array_literals.rb +13 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
- data/lib/rubocop/cop/style/endless_method.rb +38 -6
- data/lib/rubocop/cop/style/float_division.rb +16 -1
- data/lib/rubocop/cop/style/for.rb +14 -1
- data/lib/rubocop/cop/style/guard_clause.rb +22 -21
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
- data/lib/rubocop/cop/style/hash_lookup_method.rb +10 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +19 -12
- data/lib/rubocop/cop/style/it_block_parameter.rb +63 -2
- data/lib/rubocop/cop/style/lambda.rb +16 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +36 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -0
- data/lib/rubocop/cop/style/method_def_parentheses.rb +9 -1
- data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
- data/lib/rubocop/cop/style/mutable_constant.rb +25 -7
- data/lib/rubocop/cop/style/numeric_predicate.rb +7 -0
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
- data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
- data/lib/rubocop/cop/style/raise_args.rb +80 -11
- data/lib/rubocop/cop/style/reduce_to_hash.rb +32 -34
- data/lib/rubocop/cop/style/redundant_begin.rb +26 -3
- data/lib/rubocop/cop/style/redundant_exception.rb +13 -9
- data/lib/rubocop/cop/style/redundant_freeze.rb +1 -2
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
- data/lib/rubocop/cop/style/redundant_self.rb +9 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
- data/lib/rubocop/cop/style/require_order.rb +16 -8
- data/lib/rubocop/cop/style/rescue_modifier.rb +6 -0
- data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +26 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +12 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
- data/lib/rubocop/lsp/routes.rb +23 -23
- data/lib/rubocop/plugin/configuration_integrator.rb +24 -3
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/server/cache.rb +4 -4
- data/lib/rubocop/target_ruby.rb +21 -9
- data/lib/rubocop/version.rb +1 -1
- metadata +2 -2
|
@@ -85,7 +85,7 @@ module RuboCop
|
|
|
85
85
|
/xi.freeze
|
|
86
86
|
|
|
87
87
|
def self.autocorrect_incompatible_with
|
|
88
|
-
[Style::MethodCallWithArgsParentheses]
|
|
88
|
+
[Style::MethodCallWithArgsParentheses, Style::MethodDefParentheses]
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def on_block(node)
|
|
@@ -122,8 +122,10 @@ module RuboCop
|
|
|
122
122
|
def on_new_investigation
|
|
123
123
|
@breakable_range_by_line_index = {}
|
|
124
124
|
@breakable_string_delimiters = {}
|
|
125
|
+
@breakable_strings = {}
|
|
125
126
|
@endless_methods_by_line = {}
|
|
126
127
|
@heredocs = nil
|
|
128
|
+
@heredoc_openers = nil
|
|
127
129
|
|
|
128
130
|
return unless processed_source.raw_source.include?(';')
|
|
129
131
|
|
|
@@ -207,37 +209,69 @@ module RuboCop
|
|
|
207
209
|
def check_for_breakable_node(node)
|
|
208
210
|
breakable_node = extract_breakable_node(node, max)
|
|
209
211
|
return if breakable_node.nil?
|
|
212
|
+
return if compact_nested_array_break?(node, breakable_node)
|
|
210
213
|
|
|
211
214
|
line_index = breakable_node.first_line - 1
|
|
212
215
|
range = breakable_node.source_range
|
|
213
216
|
|
|
214
217
|
existing = breakable_range_by_line_index[line_index]
|
|
215
|
-
return if existing
|
|
218
|
+
return if existing || after_heredoc_opener?(range)
|
|
216
219
|
|
|
217
220
|
breakable_range_by_line_index[line_index] = range
|
|
218
221
|
end
|
|
219
222
|
|
|
223
|
+
def after_heredoc_opener?(range)
|
|
224
|
+
heredoc_openers.any? do |opener|
|
|
225
|
+
opener.line == range.line && opener.begin_pos < range.begin_pos
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def heredoc_openers
|
|
230
|
+
ast = processed_source.ast
|
|
231
|
+
return [] if ast.nil?
|
|
232
|
+
|
|
233
|
+
@heredoc_openers ||= ast.each_node(:any_str).select(&:heredoc?).map(&:source_range)
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def compact_nested_array_break?(node, breakable_node)
|
|
237
|
+
return false if !node.array_type? || !node.square_brackets?
|
|
238
|
+
return false unless breakable_node.equal?(node.children.first)
|
|
239
|
+
return false unless breakable_node.source.start_with?('[')
|
|
240
|
+
|
|
241
|
+
compact_array_brackets_enforced?
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def compact_array_brackets_enforced?
|
|
245
|
+
return false unless config.cop_enabled?('Layout/SpaceInsideArrayLiteralBrackets')
|
|
246
|
+
|
|
247
|
+
config.for_cop('Layout/SpaceInsideArrayLiteralBrackets')['EnforcedStyle'] == 'compact'
|
|
248
|
+
end
|
|
249
|
+
|
|
220
250
|
def check_for_breakable_semicolons(processed_source)
|
|
221
251
|
tokens = processed_source.tokens.select { |t| t.type == :tSEMI }
|
|
222
252
|
tokens.reverse_each do |token|
|
|
223
253
|
range = breakable_range_after_semicolon(token)
|
|
224
|
-
|
|
254
|
+
next if range.nil? || after_heredoc_opener?(range)
|
|
255
|
+
|
|
256
|
+
breakable_range_by_line_index[range.line - 1] = range
|
|
225
257
|
end
|
|
226
258
|
end
|
|
227
259
|
|
|
228
260
|
def check_for_breakable_block(block_node)
|
|
229
261
|
return unless block_node.single_line?
|
|
230
|
-
return if receiver_contains_heredoc?(block_node)
|
|
231
262
|
|
|
232
263
|
line_index = block_node.loc.line - 1
|
|
233
264
|
return if breakable_string_delimiters[line_index]
|
|
234
265
|
|
|
235
266
|
range = breakable_block_range(block_node)
|
|
236
267
|
pos = range.begin_pos + 1
|
|
268
|
+
range = range_between(pos, pos + 1)
|
|
269
|
+
return if after_heredoc_opener?(range)
|
|
237
270
|
|
|
238
|
-
breakable_range_by_line_index[line_index] =
|
|
271
|
+
breakable_range_by_line_index[line_index] = range
|
|
239
272
|
end
|
|
240
273
|
|
|
274
|
+
# rubocop:disable-next Metrics/AbcSize
|
|
241
275
|
def check_for_breakable_str(node)
|
|
242
276
|
line_index = node.loc.line - 1
|
|
243
277
|
return if breakable_range_by_line_index[line_index]
|
|
@@ -248,6 +282,7 @@ module RuboCop
|
|
|
248
282
|
|
|
249
283
|
breakable_range_by_line_index[line_index] = range_between(pos, pos + 1)
|
|
250
284
|
breakable_string_delimiters[line_index] = delimiter
|
|
285
|
+
breakable_strings[line_index] = node
|
|
251
286
|
end
|
|
252
287
|
|
|
253
288
|
def check_for_breakable_dstr(node) # rubocop:disable Metrics/AbcSize
|
|
@@ -271,7 +306,8 @@ module RuboCop
|
|
|
271
306
|
!node.heredoc? &&
|
|
272
307
|
# TODO: strings inside hashes, kwargs and arrays are currently ignored,
|
|
273
308
|
# but could be considered in the future
|
|
274
|
-
!node.parent&.type?(:pair, :kwoptarg, :array)
|
|
309
|
+
!node.parent&.type?(:pair, :kwoptarg, :array) &&
|
|
310
|
+
!after_heredoc_opener?(node.source_range)
|
|
275
311
|
end
|
|
276
312
|
|
|
277
313
|
def breakable_block_range(block_node)
|
|
@@ -301,7 +337,32 @@ module RuboCop
|
|
|
301
337
|
return unless (pos = breakable_string_range(node))
|
|
302
338
|
|
|
303
339
|
content_begin_pos = node.loc?(:begin) ? node.loc.begin.end_pos : source_range.begin_pos
|
|
304
|
-
|
|
340
|
+
if blank_left_part?(node, content_begin_pos, pos)
|
|
341
|
+
pos = breakable_string_range_at_limit(source_range)
|
|
342
|
+
end
|
|
343
|
+
pos.end_pos if pos && leaves_two_parts?(node, content_begin_pos, pos)
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def leaves_two_parts?(node, content_begin_pos, pos)
|
|
347
|
+
pos.end_pos > content_begin_pos && !blank_rest?(node, pos.end_pos)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
def blank_left_part?(node, content_begin_pos, pos)
|
|
351
|
+
return false unless pos.end_pos > content_begin_pos
|
|
352
|
+
return false unless starts_after_quote?(node)
|
|
353
|
+
|
|
354
|
+
blank_between?(content_begin_pos, pos.end_pos)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def starts_after_quote?(node)
|
|
358
|
+
return true if node.loc?(:begin)
|
|
359
|
+
|
|
360
|
+
quote = node.parent&.loc&.begin if node.parent&.dstr_type?
|
|
361
|
+
quote ? quote.end_pos == node.source_range.begin_pos : false
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def blank_between?(begin_pos, end_pos)
|
|
365
|
+
range_between(begin_pos, end_pos).source.strip.empty?
|
|
305
366
|
end
|
|
306
367
|
|
|
307
368
|
# Locate where to break a string that is too long, ensuring that escape characters
|
|
@@ -325,11 +386,17 @@ module RuboCop
|
|
|
325
386
|
|
|
326
387
|
def breakable_space_position(node, substr)
|
|
327
388
|
limit = string_content_length(node) - 1
|
|
328
|
-
space_pos = substr.
|
|
329
|
-
space_pos =
|
|
389
|
+
space_pos = space_before_word(node, substr.length - 1)
|
|
390
|
+
space_pos = space_before_word(node, limit - 1) if space_pos && space_pos + 1 > limit
|
|
330
391
|
space_pos
|
|
331
392
|
end
|
|
332
393
|
|
|
394
|
+
def space_before_word(node, max_pos)
|
|
395
|
+
return if max_pos.negative?
|
|
396
|
+
|
|
397
|
+
node.source.rindex(/\s(?=\S)/, max_pos)
|
|
398
|
+
end
|
|
399
|
+
|
|
333
400
|
def escape_sequence_start(text)
|
|
334
401
|
text.match(ESCAPE_SEQUENCE_TAIL)&.begin(1)
|
|
335
402
|
end
|
|
@@ -339,10 +406,25 @@ module RuboCop
|
|
|
339
406
|
return if adjustment.abs > source_range.size
|
|
340
407
|
|
|
341
408
|
cut = source_range.adjust(end_pos: adjustment)
|
|
409
|
+
cut = cut.resize(size_before_whitespace_run(source_range.source, cut.size))
|
|
342
410
|
escape_pos = escape_sequence_start(cut.source)
|
|
411
|
+
|
|
343
412
|
escape_pos ? source_range.resize(escape_pos) : cut
|
|
344
413
|
end
|
|
345
414
|
|
|
415
|
+
def size_before_whitespace_run(text, size)
|
|
416
|
+
return size if size.zero?
|
|
417
|
+
return size unless text[size]&.match?(/\s/)
|
|
418
|
+
|
|
419
|
+
text.rindex(/\S/, size - 1) || size
|
|
420
|
+
end
|
|
421
|
+
|
|
422
|
+
def blank_rest?(node, break_pos)
|
|
423
|
+
offset = break_pos - node.source_range.begin_pos
|
|
424
|
+
|
|
425
|
+
node.source[offset...string_content_length(node)].strip.empty?
|
|
426
|
+
end
|
|
427
|
+
|
|
346
428
|
def string_content_length(node)
|
|
347
429
|
content_end = node.loc?(:end) ? node.loc.end.begin_pos : node.source_range.end_pos
|
|
348
430
|
|
|
@@ -360,7 +442,9 @@ module RuboCop
|
|
|
360
442
|
def opens_string?(begin_node)
|
|
361
443
|
quote = begin_node.parent.loc.begin
|
|
362
444
|
|
|
363
|
-
|
|
445
|
+
return false unless quote
|
|
446
|
+
|
|
447
|
+
blank_between?(quote.end_pos, begin_node.source_range.begin_pos)
|
|
364
448
|
end
|
|
365
449
|
|
|
366
450
|
def breakable_range_by_line_index
|
|
@@ -375,6 +459,10 @@ module RuboCop
|
|
|
375
459
|
@breakable_string_delimiters ||= {}
|
|
376
460
|
end
|
|
377
461
|
|
|
462
|
+
def breakable_strings
|
|
463
|
+
@breakable_strings ||= {}
|
|
464
|
+
end
|
|
465
|
+
|
|
378
466
|
def heredocs
|
|
379
467
|
@heredocs ||= extract_heredocs(processed_source.ast)
|
|
380
468
|
end
|
|
@@ -431,8 +519,36 @@ module RuboCop
|
|
|
431
519
|
"\n"
|
|
432
520
|
end
|
|
433
521
|
|
|
434
|
-
|
|
522
|
+
break_ranges(line_index).each do |range|
|
|
523
|
+
corrector.insert_before(range, insertion)
|
|
524
|
+
end
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
|
|
528
|
+
def break_ranges(line_index)
|
|
529
|
+
return [] if breakable_range.nil?
|
|
530
|
+
return [breakable_range] unless (node = breakable_strings[line_index])&.loc?(:end)
|
|
531
|
+
|
|
532
|
+
string_break_ranges(node, breakable_string_delimiters[line_index])
|
|
533
|
+
end
|
|
534
|
+
|
|
535
|
+
def string_break_ranges(node, delimiter)
|
|
536
|
+
ranges = [breakable_range]
|
|
537
|
+
pos = breakable_range.begin_pos
|
|
538
|
+
|
|
539
|
+
while (pos = next_string_break_position(node, pos, delimiter))
|
|
540
|
+
ranges << range_between(pos, pos + 1)
|
|
435
541
|
end
|
|
542
|
+
|
|
543
|
+
ranges
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
def next_string_break_position(node, pos, delimiter)
|
|
547
|
+
rest = parse("#{delimiter}#{node.source_range.with(begin_pos: pos).source}").ast
|
|
548
|
+
return unless rest&.str_type?
|
|
549
|
+
return unless (rest_pos = breakable_string_position(rest))
|
|
550
|
+
|
|
551
|
+
pos + rest_pos - delimiter.length
|
|
436
552
|
end
|
|
437
553
|
|
|
438
554
|
def excess_range(uri_range, line, line_index)
|
|
@@ -106,11 +106,13 @@ module RuboCop
|
|
|
106
106
|
ALWAYS_SAME_LINE_MESSAGE = 'Closing hash brace must be on the same ' \
|
|
107
107
|
'line as the last hash element.'
|
|
108
108
|
|
|
109
|
-
# Moving the closing brace
|
|
110
|
-
# `
|
|
111
|
-
# the brace
|
|
109
|
+
# Moving the closing brace after a method call value while
|
|
110
|
+
# `Layout/MultilineMethodCallBraceLayout` moves that call's closing parenthesis
|
|
111
|
+
# before a comment in the same pass places the brace after the comment.
|
|
112
|
+
# Moving it while `Style/HashAsLastArrayItem` or `Style/RedundantDoubleSplatHashBraces`
|
|
113
|
+
# removes the braces in the same pass leaves the brace behind.
|
|
112
114
|
def self.autocorrect_incompatible_with
|
|
113
|
-
[Style::HashAsLastArrayItem, Style::RedundantDoubleSplatHashBraces]
|
|
115
|
+
[Layout::MultilineMethodCallBraceLayout, Style::HashAsLastArrayItem, Style::RedundantDoubleSplatHashBraces]
|
|
114
116
|
end
|
|
115
117
|
|
|
116
118
|
def on_hash(node)
|
|
@@ -102,7 +102,7 @@ module RuboCop
|
|
|
102
102
|
def autocorrect(corrector, node)
|
|
103
103
|
if @send_node.block_node
|
|
104
104
|
correct_selector_only(corrector, node)
|
|
105
|
-
correct_block(corrector, @send_node.block_node)
|
|
105
|
+
correct_block(corrector, @send_node.block_node, node.first_line)
|
|
106
106
|
else
|
|
107
107
|
AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
|
|
108
108
|
end
|
|
@@ -114,10 +114,21 @@ module RuboCop
|
|
|
114
114
|
AlignmentCorrector.correct(corrector, processed_source, selector_range, @column_delta)
|
|
115
115
|
end
|
|
116
116
|
|
|
117
|
-
def correct_block(corrector, block_node)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
117
|
+
def correct_block(corrector, block_node, selector_line)
|
|
118
|
+
lines_to_shift = block_lines(block_node) - [selector_line]
|
|
119
|
+
|
|
120
|
+
lines_to_shift.each do |line|
|
|
121
|
+
line_range = processed_source.buffer.line_range(line)
|
|
122
|
+
|
|
123
|
+
AlignmentCorrector.correct(corrector, processed_source, line_range, @column_delta)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def block_lines(block_node)
|
|
128
|
+
body = block_node.body
|
|
129
|
+
lines = body ? (body.first_line..body.last_line).to_a : []
|
|
130
|
+
|
|
131
|
+
(lines << block_node.loc.end.line).uniq
|
|
121
132
|
end
|
|
122
133
|
|
|
123
134
|
def relevant_node?(send_node)
|
|
@@ -115,7 +115,7 @@ module RuboCop
|
|
|
115
115
|
node.loc.name
|
|
116
116
|
when :sclass
|
|
117
117
|
node.identifier.source_range
|
|
118
|
-
when :masgn
|
|
118
|
+
when :masgn, :op_asgn, :or_asgn, :and_asgn
|
|
119
119
|
node.lhs.source_range
|
|
120
120
|
else
|
|
121
121
|
# It is a wrapper with receiver of object attribute or access modifier.
|
|
@@ -25,10 +25,10 @@ module RuboCop
|
|
|
25
25
|
|
|
26
26
|
MSG = 'Put method call on a separate line if chained to a single line block.'
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
#
|
|
28
|
+
# These cops rewrite a method chained to the block in the same pass,
|
|
29
|
+
# which must not start a line of its own.
|
|
30
30
|
def self.autocorrect_incompatible_with
|
|
31
|
-
[Style::FloatDivision, Style::MapToHash, Style::NilComparison]
|
|
31
|
+
[Style::FloatDivision, Style::MapToHash, Style::MapToSet, Style::NilComparison, Style::RedundantSort]
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def on_send(node)
|
|
@@ -245,20 +245,30 @@ module RuboCop
|
|
|
245
245
|
return if with_space.is?(operator.source)
|
|
246
246
|
|
|
247
247
|
"Space around operator `#{operator.source}` detected."
|
|
248
|
-
elsif
|
|
248
|
+
elsif missing_surrounding_space?(with_space)
|
|
249
249
|
"Surrounding space missing for operator `#{operator.source}`."
|
|
250
|
-
elsif excess_leading_space?(type, operator, with_space) ||
|
|
250
|
+
elsif excess_leading_space?(type, operator, with_space, right_operand) ||
|
|
251
251
|
excess_trailing_space?(right_operand.source_range, with_space)
|
|
252
252
|
"Operator `#{operator.source}` should be surrounded " \
|
|
253
253
|
'by a single space.'
|
|
254
254
|
end
|
|
255
255
|
end
|
|
256
256
|
|
|
257
|
-
def
|
|
257
|
+
def missing_surrounding_space?(with_space)
|
|
258
|
+
return false if /\A\s.*\s\z/m.match?(with_space.source)
|
|
259
|
+
|
|
260
|
+
!with_space.source.start_with?(/\s/) || !line_continuation_after?(with_space)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def line_continuation_after?(range)
|
|
264
|
+
processed_source.buffer.source[range.end_pos, 2] == "\\\n"
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def excess_leading_space?(type, operator, with_space, right_operand)
|
|
258
268
|
return false unless allow_for_alignment?
|
|
259
269
|
return false unless with_space.source.start_with?(EXCESSIVE_SPACE)
|
|
260
270
|
|
|
261
|
-
return !
|
|
271
|
+
return !aligned?(type, operator, right_operand) unless grouped_alignment_type?(type)
|
|
262
272
|
|
|
263
273
|
token = Token.new(operator, nil, operator.source)
|
|
264
274
|
align_preceding = aligned_with_preceding_equals_operator(token)
|
|
@@ -269,6 +279,23 @@ module RuboCop
|
|
|
269
279
|
aligned_with_subsequent_equals_operator(token) != :yes
|
|
270
280
|
end
|
|
271
281
|
|
|
282
|
+
def aligned?(type, operator, right_operand)
|
|
283
|
+
return true if aligned_with_operator?(operator)
|
|
284
|
+
|
|
285
|
+
type == :pair && aligned_with_another_hash_rocket?(right_operand)
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def aligned_with_another_hash_rocket?(pair)
|
|
289
|
+
hash_rocket_columns(pair.parent)[pair.loc.operator.column] > 1
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def hash_rocket_columns(hash_node)
|
|
293
|
+
@hash_rocket_columns ||= {}.compare_by_identity
|
|
294
|
+
@hash_rocket_columns[hash_node] ||= hash_node.pairs.select(&:hash_rocket?).map do |pair|
|
|
295
|
+
pair.loc.operator.column
|
|
296
|
+
end.tally
|
|
297
|
+
end
|
|
298
|
+
|
|
272
299
|
def grouped_alignment_type?(type)
|
|
273
300
|
type == :assignment || (type == :special_asgn && force_equal_sign_alignment?)
|
|
274
301
|
end
|
|
@@ -136,11 +136,23 @@ module RuboCop
|
|
|
136
136
|
|
|
137
137
|
def autocorrect(corrector, range)
|
|
138
138
|
case range.source
|
|
139
|
+
when /\n/ then remove_line_continuation(corrector, range)
|
|
139
140
|
when /\s/ then corrector.remove(range)
|
|
140
141
|
else corrector.insert_before(range, ' ')
|
|
141
142
|
end
|
|
142
143
|
end
|
|
143
144
|
|
|
145
|
+
# When a heredoc body sits between the `\` and the brace, the newline before
|
|
146
|
+
# the brace ends the heredoc, and removing it would break the terminator.
|
|
147
|
+
def remove_line_continuation(corrector, range)
|
|
148
|
+
source = processed_source.buffer.source
|
|
149
|
+
return unless source[range.begin_pos - 1] == '\\'
|
|
150
|
+
|
|
151
|
+
begin_pos = range.begin_pos
|
|
152
|
+
begin_pos -= 1 while source[begin_pos - 1].match?(/[ \t\\]/)
|
|
153
|
+
corrector.remove(range_between(begin_pos, range.end_pos))
|
|
154
|
+
end
|
|
155
|
+
|
|
144
156
|
def style_for_empty_braces
|
|
145
157
|
case cop_config['EnforcedStyleForEmptyBraces']
|
|
146
158
|
when 'space' then :space
|
|
@@ -18,6 +18,7 @@ module RuboCop
|
|
|
18
18
|
# # good
|
|
19
19
|
# 'a'..'z'
|
|
20
20
|
class SpaceInsideRangeLiteral < Base
|
|
21
|
+
include RangeHelp
|
|
21
22
|
extend AutoCorrector
|
|
22
23
|
|
|
23
24
|
MSG = 'Space inside range literal.'
|
|
@@ -33,21 +34,38 @@ module RuboCop
|
|
|
33
34
|
private
|
|
34
35
|
|
|
35
36
|
def check(node)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# account for multiline range literals
|
|
41
|
-
expression.sub!(/#{escaped_op}\n\s*/, op)
|
|
42
|
-
|
|
43
|
-
return unless /(\s#{escaped_op})|(#{escaped_op}\s)/.match?(expression)
|
|
37
|
+
operator = node.loc.operator
|
|
38
|
+
spaces = [space_before(node, operator), space_after(node, operator)].compact
|
|
39
|
+
return if spaces.all? { |space| line_break_after_operator?(space, operator) }
|
|
44
40
|
|
|
45
41
|
add_offense(node) do |corrector|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
spaces.each do |space|
|
|
43
|
+
corrector.remove(space)
|
|
44
|
+
end
|
|
49
45
|
end
|
|
50
46
|
end
|
|
47
|
+
|
|
48
|
+
def space_before(node, operator)
|
|
49
|
+
return unless node.begin
|
|
50
|
+
|
|
51
|
+
whitespace_range(node.begin.source_range.end_pos, operator.begin_pos)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def space_after(node, operator)
|
|
55
|
+
return unless node.end
|
|
56
|
+
|
|
57
|
+
whitespace_range(operator.end_pos, node.end.source_range.begin_pos)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def line_break_after_operator?(space, operator)
|
|
61
|
+
space.begin_pos == operator.end_pos && space.source.start_with?("\n")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def whitespace_range(begin_pos, end_pos)
|
|
65
|
+
range = range_between(begin_pos, end_pos)
|
|
66
|
+
|
|
67
|
+
range if range.source.match?(/\A\s+\z/)
|
|
68
|
+
end
|
|
51
69
|
end
|
|
52
70
|
end
|
|
53
71
|
end
|
|
@@ -108,14 +108,9 @@ module RuboCop
|
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def left_ref_bracket(node, tokens)
|
|
111
|
-
|
|
112
|
-
previous_token = previous_token(current_token)
|
|
111
|
+
bracket_pos = node.loc.selector.begin_pos
|
|
113
112
|
|
|
114
|
-
|
|
115
|
-
tokens.find(&:left_ref_bracket?)
|
|
116
|
-
else
|
|
117
|
-
current_token
|
|
118
|
-
end
|
|
113
|
+
tokens.find { |token| token.left_ref_bracket? && token.begin_pos == bracket_pos }
|
|
119
114
|
end
|
|
120
115
|
|
|
121
116
|
def closing_bracket(tokens, opening_bracket)
|
|
@@ -129,11 +124,6 @@ module RuboCop
|
|
|
129
124
|
end
|
|
130
125
|
end
|
|
131
126
|
|
|
132
|
-
def previous_token(current_token)
|
|
133
|
-
index = processed_source.tokens.index(current_token)
|
|
134
|
-
index.nil? || index.zero? ? nil : processed_source.tokens[index - 1]
|
|
135
|
-
end
|
|
136
|
-
|
|
137
127
|
def empty_config
|
|
138
128
|
cop_config['EnforcedStyleForEmptyBrackets']
|
|
139
129
|
end
|
|
@@ -55,7 +55,8 @@ module RuboCop
|
|
|
55
55
|
return node if first_argument_is_regexp?(node) || !node.parent
|
|
56
56
|
|
|
57
57
|
if (node.parent.send_type? && node.receiver) ||
|
|
58
|
-
method_chain_to_regexp_receiver?(node, regexp_receiver)
|
|
58
|
+
method_chain_to_regexp_receiver?(node, regexp_receiver) ||
|
|
59
|
+
part_of_hash_argument?(node)
|
|
59
60
|
node = find_offense_node(node.parent, regexp_receiver)
|
|
60
61
|
end
|
|
61
62
|
|
|
@@ -72,6 +73,10 @@ module RuboCop
|
|
|
72
73
|
|
|
73
74
|
parent.parent && parent_receiver.receiver == regexp_receiver
|
|
74
75
|
end
|
|
76
|
+
|
|
77
|
+
def part_of_hash_argument?(node)
|
|
78
|
+
node.pair_type? || node.parent.pair_type? || (node.hash_type? && !node.braces?)
|
|
79
|
+
end
|
|
75
80
|
end
|
|
76
81
|
end
|
|
77
82
|
end
|
|
@@ -34,6 +34,10 @@ module RuboCop
|
|
|
34
34
|
|
|
35
35
|
MSG = 'Put a method call with a HEREDOC receiver on the same line as the HEREDOC opening.'
|
|
36
36
|
|
|
37
|
+
def self.autocorrect_incompatible_with
|
|
38
|
+
[Style::MethodCallWithArgsParentheses]
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
def on_send(node)
|
|
38
42
|
heredoc = heredoc_node_descendent_receiver(node)
|
|
39
43
|
return unless heredoc
|
|
@@ -41,26 +41,22 @@ module RuboCop
|
|
|
41
41
|
|
|
42
42
|
private
|
|
43
43
|
|
|
44
|
-
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
45
44
|
def check(node)
|
|
46
45
|
return if node.parent&.regexp_type?
|
|
47
46
|
return unless /(?<!\\)#\{.*\}/.match?(node.source)
|
|
48
47
|
return if heredoc?(node)
|
|
49
|
-
return unless node
|
|
48
|
+
return unless single_quoted?(node)
|
|
50
49
|
return unless valid_syntax?(node)
|
|
51
50
|
|
|
52
51
|
add_offense(node) { |corrector| autocorrect(corrector, node) }
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
else
|
|
59
|
-
['"', '"']
|
|
60
|
-
end
|
|
54
|
+
def single_quoted?(node)
|
|
55
|
+
node.loc.begin&.is?("'") && node.loc.end
|
|
56
|
+
end
|
|
61
57
|
|
|
62
|
-
|
|
63
|
-
corrector.replace(node
|
|
58
|
+
def autocorrect(corrector, node)
|
|
59
|
+
corrector.replace(node, interpolated_source(node))
|
|
64
60
|
end
|
|
65
61
|
|
|
66
62
|
def heredoc?(node)
|
|
@@ -68,15 +64,22 @@ module RuboCop
|
|
|
68
64
|
end
|
|
69
65
|
|
|
70
66
|
def valid_syntax?(node)
|
|
71
|
-
|
|
72
|
-
node.source.sub(/\A'/, '%{').sub(/'\z/, '}')
|
|
73
|
-
else
|
|
74
|
-
node.source.gsub(/\A'|'\z/, '"')
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
processed_source = parse(double_quoted_string)
|
|
67
|
+
processed_source = parse(interpolated_source(node))
|
|
78
68
|
processed_source.valid_syntax? && processed_source.ast.dstr_type?
|
|
79
69
|
end
|
|
70
|
+
|
|
71
|
+
def interpolated_source(node)
|
|
72
|
+
content = node.source[1...-1]
|
|
73
|
+
return %("#{content}") unless content.include?('"')
|
|
74
|
+
return "%{#{content}}" unless continued_string_part?(node)
|
|
75
|
+
|
|
76
|
+
%("#{content.gsub(/\\.|"/) { |m| m == '"' ? '\"' : m }}")
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# A percent literal right after another string part is parsed as the `%` operator.
|
|
80
|
+
def continued_string_part?(node)
|
|
81
|
+
node.parent&.dstr_type? && node.left_sibling
|
|
82
|
+
end
|
|
80
83
|
end
|
|
81
84
|
end
|
|
82
85
|
end
|
|
@@ -40,6 +40,7 @@ module RuboCop
|
|
|
40
40
|
# NestedPercentLiteral violation to be emitted.
|
|
41
41
|
PERCENT_LITERAL_TYPES = PreferredDelimiters::PERCENT_LITERAL_TYPES
|
|
42
42
|
REGEXES = PERCENT_LITERAL_TYPES.map { |percent_literal| /\A#{percent_literal}\W/ }.freeze
|
|
43
|
+
FORMAT_SEQUENCE = /\A#{RuboCop::Cop::Utils::FormatString::SEQUENCE}\z/.freeze
|
|
43
44
|
|
|
44
45
|
def on_array(node)
|
|
45
46
|
process(node, *PERCENT_LITERAL_TYPES)
|
|
@@ -54,6 +55,8 @@ module RuboCop
|
|
|
54
55
|
def contains_percent_literals?(node)
|
|
55
56
|
node.each_child_node.any? do |child|
|
|
56
57
|
literal = child.children.first.to_s.scrub
|
|
58
|
+
next false if literal.match?(FORMAT_SEQUENCE)
|
|
59
|
+
|
|
57
60
|
REGEXES.any? { |regex| literal.match?(regex) }
|
|
58
61
|
end
|
|
59
62
|
end
|
|
@@ -140,6 +140,8 @@ module RuboCop
|
|
|
140
140
|
corrected_method: correct_sym_method(to_method)
|
|
141
141
|
)
|
|
142
142
|
add_offense(node, message: message) do |corrector|
|
|
143
|
+
next if part_of_ignored_node?(node)
|
|
144
|
+
|
|
143
145
|
remove_parentheses(corrector, node) if node.parenthesized?
|
|
144
146
|
|
|
145
147
|
corrector.replace(sym_node, correct_sym_method(to_method))
|
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
|
57
57
|
node = node.children.first while node&.begin_type? && node.children.one?
|
|
58
58
|
return false unless node
|
|
59
59
|
|
|
60
|
-
keyword_operator?(node) || modifier_expression?(node)
|
|
60
|
+
keyword_operator?(node) || modifier_expression?(node) || command_call_operand?(node)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
def keyword_operator?(node)
|
|
@@ -69,6 +69,27 @@ module RuboCop
|
|
|
69
69
|
node.type?(:if, :while, :until) && node.modifier_form?
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
+
def command_call_operand?(node)
|
|
73
|
+
if node.assignment? || (node.call_type? && node.assignment_method?)
|
|
74
|
+
command_call_value?(node.children.last)
|
|
75
|
+
elsif node.send_type? && node.prefix_bang?
|
|
76
|
+
command_call_value?(node.receiver)
|
|
77
|
+
else
|
|
78
|
+
false
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def command_call_value?(node)
|
|
83
|
+
command_call?(node) || command_call_operand?(node)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def command_call?(node)
|
|
87
|
+
node = node.send_node if node.any_block_type?
|
|
88
|
+
return false unless node.type?(:call, :super, :yield)
|
|
89
|
+
|
|
90
|
+
node.arguments? && !node.parenthesized? && !node.operator_method?
|
|
91
|
+
end
|
|
92
|
+
|
|
72
93
|
def chained_calls?(node)
|
|
73
94
|
first_argument = node.first_argument
|
|
74
95
|
first_argument.call_type? && (node.children.last&.children&.count || 0) > 1
|