rubocop 1.82.0 → 1.84.0

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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +1 -1
  4. data/config/default.yml +44 -0
  5. data/lib/rubocop/cli/command/lsp.rb +1 -1
  6. data/lib/rubocop/cli.rb +2 -1
  7. data/lib/rubocop/comment_config.rb +1 -0
  8. data/lib/rubocop/cop/autocorrect_logic.rb +2 -0
  9. data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
  10. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  11. data/lib/rubocop/cop/correctors/alignment_corrector.rb +20 -2
  12. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  13. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
  15. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
  16. data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
  17. data/lib/rubocop/cop/layout/class_structure.rb +12 -5
  18. data/lib/rubocop/cop/layout/first_argument_indentation.rb +32 -1
  19. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
  20. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
  21. data/lib/rubocop/cop/layout/heredoc_indentation.rb +35 -1
  22. data/lib/rubocop/cop/layout/indentation_width.rb +111 -7
  23. data/lib/rubocop/cop/layout/line_length.rb +6 -2
  24. data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
  25. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  26. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
  27. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
  28. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  29. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  30. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +8 -8
  31. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  32. data/lib/rubocop/cop/lint/duplicate_methods.rb +57 -5
  33. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  34. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  35. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  36. data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
  37. data/lib/rubocop/cop/lint/to_json.rb +12 -16
  38. data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
  39. data/lib/rubocop/cop/lint/useless_or.rb +1 -1
  40. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
  41. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -0
  42. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +4 -4
  43. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  44. data/lib/rubocop/cop/mixin/trailing_comma.rb +5 -1
  45. data/lib/rubocop/cop/naming/predicate_prefix.rb +11 -11
  46. data/lib/rubocop/cop/offense.rb +2 -1
  47. data/lib/rubocop/cop/security/json_load.rb +1 -1
  48. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -2
  49. data/lib/rubocop/cop/style/documentation.rb +6 -6
  50. data/lib/rubocop/cop/style/documentation_method.rb +8 -8
  51. data/lib/rubocop/cop/style/empty_class_definition.rb +144 -0
  52. data/lib/rubocop/cop/style/guard_clause.rb +7 -4
  53. data/lib/rubocop/cop/style/hash_lookup_method.rb +94 -0
  54. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
  55. data/lib/rubocop/cop/style/lambda_call.rb +8 -8
  56. data/lib/rubocop/cop/style/module_member_existence_check.rb +56 -13
  57. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -0
  58. data/lib/rubocop/cop/style/negative_array_index.rb +218 -0
  59. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  60. data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
  61. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  62. data/lib/rubocop/cop/style/reverse_find.rb +51 -0
  63. data/lib/rubocop/cop/team.rb +3 -3
  64. data/lib/rubocop/cop/variable_force/branch.rb +28 -4
  65. data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
  66. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  67. data/lib/rubocop/formatter/tap_formatter.rb +5 -2
  68. data/lib/rubocop/remote_config.rb +5 -2
  69. data/lib/rubocop/result_cache.rb +38 -27
  70. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  71. data/lib/rubocop/rspec/support.rb +1 -0
  72. data/lib/rubocop/runner.rb +4 -0
  73. data/lib/rubocop/target_ruby.rb +3 -1
  74. data/lib/rubocop/version.rb +1 -1
  75. data/lib/rubocop.rb +4 -0
  76. metadata +11 -7
@@ -5,6 +5,8 @@ module RuboCop
5
5
  module Layout
6
6
  # Checks for indentation that doesn't use the specified number of spaces.
7
7
  # The indentation width can be configured using the `Width` setting. The default width is 2.
8
+ # The block body indentation for method chain blocks can be configured using the
9
+ # `EnforcedStyleAlignWith` setting.
8
10
  #
9
11
  # See also the `Layout/IndentationConsistency` cop which is the companion to this one.
10
12
  #
@@ -41,7 +43,22 @@ module RuboCop
41
43
  # end
42
44
  # end
43
45
  # end
46
+ #
47
+ # @example EnforcedStyleAlignWith: start_of_line (default)
48
+ # # good
49
+ # records.uniq { |el| el[:profile_id] }
50
+ # .map do |message|
51
+ # SomeJob.perform_later(message[:id])
52
+ # end
53
+ #
54
+ # @example EnforcedStyleAlignWith: relative_to_receiver
55
+ # # good
56
+ # records.uniq { |el| el[:profile_id] }
57
+ # .map do |message|
58
+ # SomeJob.perform_later(message[:id])
59
+ # end
44
60
  class IndentationWidth < Base # rubocop:disable Metrics/ClassLength
61
+ include ConfigurableEnforcedStyle
45
62
  include EndKeywordAlignment
46
63
  include Alignment
47
64
  include CheckAssignment
@@ -50,7 +67,7 @@ module RuboCop
50
67
  extend AutoCorrector
51
68
 
52
69
  MSG = 'Use %<configured_indentation_width>d (not %<indentation>d) ' \
53
- 'spaces for%<name>s indentation.'
70
+ '%<indentation_type>s for%<name>s indentation.'
54
71
 
55
72
  # @!method access_modifier?(node)
56
73
  def_node_matcher :access_modifier?, <<~PATTERN
@@ -83,9 +100,11 @@ module RuboCop
83
100
 
84
101
  return unless begins_its_line?(end_loc)
85
102
 
86
- check_indentation(end_loc, node.body)
103
+ base_loc = block_body_indentation_base(node, end_loc)
104
+ check_indentation(base_loc, node.body)
87
105
 
88
106
  return unless indented_internal_methods_style?
107
+ return unless contains_access_modifier?(node.body)
89
108
 
90
109
  check_members(end_loc, [node.body])
91
110
  end
@@ -145,7 +164,7 @@ module RuboCop
145
164
  end
146
165
 
147
166
  def on_case_match(case_match)
148
- case_match.each_in_pattern do |in_pattern_node|
167
+ case_match.in_pattern_branches.each do |in_pattern_node|
149
168
  check_indentation(in_pattern_node.loc.keyword, in_pattern_node.body)
150
169
  end
151
170
 
@@ -164,6 +183,8 @@ module RuboCop
164
183
  private
165
184
 
166
185
  def autocorrect(corrector, node)
186
+ return unless node
187
+
167
188
  AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
168
189
  end
169
190
 
@@ -172,7 +193,7 @@ module RuboCop
172
193
 
173
194
  return unless members.any? && members.first.begin_type?
174
195
 
175
- if indentation_consistency_style == 'indented_internal_methods'
196
+ if indented_internal_methods_style?
176
197
  check_members_for_indented_internal_methods_style(members)
177
198
  else
178
199
  check_members_for_normal_style(base, members)
@@ -301,10 +322,32 @@ module RuboCop
301
322
  end
302
323
 
303
324
  def message(configured_indentation_width, indentation, name)
325
+ if using_tabs?
326
+ message_for_tabs(configured_indentation_width, indentation, name)
327
+ else
328
+ message_for_spaces(configured_indentation_width, indentation, name)
329
+ end
330
+ end
331
+
332
+ def message_for_tabs(configured_indentation_width, indentation, name)
333
+ configured_tabs = 1
334
+ actual_tabs = indentation / configured_indentation_width
335
+
336
+ format(
337
+ MSG,
338
+ configured_indentation_width: configured_tabs,
339
+ indentation: actual_tabs,
340
+ indentation_type: 'tabs',
341
+ name: name
342
+ )
343
+ end
344
+
345
+ def message_for_spaces(configured_indentation_width, indentation, name)
304
346
  format(
305
347
  MSG,
306
348
  configured_indentation_width: configured_indentation_width,
307
349
  indentation: indentation,
350
+ indentation_type: 'spaces',
308
351
  name: name
309
352
  )
310
353
  end
@@ -360,7 +403,13 @@ module RuboCop
360
403
  def offending_range(body_node, indentation)
361
404
  expr = body_node.source_range
362
405
  begin_pos = expr.begin_pos
363
- ind = expr.begin_pos - indentation
406
+
407
+ ind = if using_tabs?
408
+ begin_pos - line_indentation(expr).length
409
+ else
410
+ begin_pos - indentation
411
+ end
412
+
364
413
  pos = indentation >= 0 ? ind..begin_pos : begin_pos..ind
365
414
  range_between(pos.begin, pos.end)
366
415
  end
@@ -374,8 +423,47 @@ module RuboCop
374
423
  starting_node.send_type? && starting_node.bare_access_modifier?
375
424
  end
376
425
 
377
- def configured_indentation_width
378
- cop_config['Width']
426
+ def contains_access_modifier?(body_node)
427
+ return false unless body_node.begin_type?
428
+
429
+ body_node.children.any? { |child| child.send_type? && child.bare_access_modifier? }
430
+ end
431
+
432
+ def indentation_style
433
+ config.for_cop('Layout/IndentationStyle')['EnforcedStyle'] || 'spaces'
434
+ end
435
+
436
+ def using_tabs?
437
+ indentation_style == 'tabs'
438
+ end
439
+
440
+ def column_offset_between(base_range, range)
441
+ return super unless using_tabs?
442
+
443
+ base_uses_tabs = line_uses_tabs?(base_range)
444
+ range_uses_tabs = line_uses_tabs?(range)
445
+
446
+ return super unless base_uses_tabs || range_uses_tabs
447
+
448
+ visual_column(base_range) - visual_column(range)
449
+ end
450
+
451
+ def line_indentation(range)
452
+ line = processed_source.lines[range.line - 1]
453
+ line[0...range.column]
454
+ end
455
+
456
+ def line_uses_tabs?(range)
457
+ line_indentation(range).include?("\t")
458
+ end
459
+
460
+ def visual_column(range)
461
+ indentation = line_indentation(range)
462
+
463
+ tab_count = indentation.count("\t")
464
+ space_count = indentation.count(' ')
465
+
466
+ (tab_count * configured_indentation_width) + space_count
379
467
  end
380
468
 
381
469
  def leftmost_modifier_of(node)
@@ -383,6 +471,22 @@ module RuboCop
383
471
 
384
472
  leftmost_modifier_of(node.parent)
385
473
  end
474
+
475
+ def block_body_indentation_base(node, end_loc)
476
+ if style != :relative_to_receiver || !dot_on_new_line?(node)
477
+ end_loc
478
+ else
479
+ node.send_node.loc.dot
480
+ end
481
+ end
482
+
483
+ def dot_on_new_line?(node)
484
+ send_node = node.send_node
485
+ return false unless send_node.loc?(:dot)
486
+
487
+ receiver = send_node.receiver
488
+ receiver && receiver.last_line < send_node.loc.dot.line
489
+ end
386
490
  end
387
491
  end
388
492
  end
@@ -311,6 +311,7 @@ module RuboCop
311
311
  def max
312
312
  cop_config['Max']
313
313
  end
314
+ alias max_line_length max
314
315
 
315
316
  def allow_heredoc?
316
317
  allowed_heredoc
@@ -417,8 +418,11 @@ module RuboCop
417
418
  # The maximum allowed length of a string value is:
418
419
  # `Max` - end delimiter (quote) - continuation characters (space and slash)
419
420
  max_length = max - 3
420
- # If the string doesn't start at the beginning of the line, the max length is offset
421
- max_length -= column_offset_between(node.loc, node.parent.loc) if node.parent
421
+ # If the string is on the same line as its parent, offset by the column difference
422
+ # (Only apply when on same line to avoid negative offsets for multi-line dstr)
423
+ if same_line?(node, node.parent)
424
+ max_length -= column_offset_between(node.loc, node.parent.loc)
425
+ end
422
426
  node.source[0...(max_length)]
423
427
  end
424
428
  end
@@ -27,67 +27,67 @@ module RuboCop
27
27
  # line as the last element of the array.
28
28
  #
29
29
  # @example EnforcedStyle: symmetrical (default)
30
- # # bad
31
- # [ :a,
32
- # :b
33
- # ]
34
- #
35
- # # bad
36
- # [
37
- # :a,
38
- # :b ]
39
- #
40
- # # good
41
- # [ :a,
42
- # :b ]
43
- #
44
- # # good
45
- # [
46
- # :a,
47
- # :b
48
- # ]
30
+ # # bad
31
+ # [ :a,
32
+ # :b
33
+ # ]
34
+ #
35
+ # # bad
36
+ # [
37
+ # :a,
38
+ # :b ]
39
+ #
40
+ # # good
41
+ # [ :a,
42
+ # :b ]
43
+ #
44
+ # # good
45
+ # [
46
+ # :a,
47
+ # :b
48
+ # ]
49
49
  #
50
50
  # @example EnforcedStyle: new_line
51
- # # bad
52
- # [
53
- # :a,
54
- # :b ]
55
- #
56
- # # bad
57
- # [ :a,
58
- # :b ]
59
- #
60
- # # good
61
- # [ :a,
62
- # :b
63
- # ]
64
- #
65
- # # good
66
- # [
67
- # :a,
68
- # :b
69
- # ]
51
+ # # bad
52
+ # [
53
+ # :a,
54
+ # :b ]
55
+ #
56
+ # # bad
57
+ # [ :a,
58
+ # :b ]
59
+ #
60
+ # # good
61
+ # [ :a,
62
+ # :b
63
+ # ]
64
+ #
65
+ # # good
66
+ # [
67
+ # :a,
68
+ # :b
69
+ # ]
70
70
  #
71
71
  # @example EnforcedStyle: same_line
72
- # # bad
73
- # [ :a,
74
- # :b
75
- # ]
76
- #
77
- # # bad
78
- # [
79
- # :a,
80
- # :b
81
- # ]
82
- #
83
- # # good
84
- # [
85
- # :a,
86
- # :b ]
87
- #
88
- # # good
89
- # [ :a,
90
- # :b ]
72
+ # # bad
73
+ # [ :a,
74
+ # :b
75
+ # ]
76
+ #
77
+ # # bad
78
+ # [
79
+ # :a,
80
+ # :b
81
+ # ]
82
+ #
83
+ # # good
84
+ # [
85
+ # :a,
86
+ # :b ]
87
+ #
88
+ # # good
89
+ # [ :a,
90
+ # :b ]
91
91
  class MultilineArrayBraceLayout < Base
92
92
  include MultilineLiteralBraceLayout
93
93
  extend AutoCorrector
@@ -78,6 +78,8 @@ module RuboCop
78
78
  end
79
79
 
80
80
  def line_break_necessary_in_args?(node)
81
+ return false unless max_line_length
82
+
81
83
  needed_length_for_args(node) > max_line_length
82
84
  end
83
85
 
@@ -28,66 +28,66 @@ module RuboCop
28
28
  #
29
29
  # @example EnforcedStyle: symmetrical (default)
30
30
  #
31
- # # bad
32
- # { a: 1,
33
- # b: 2
34
- # }
35
- # # bad
36
- # {
37
- # a: 1,
38
- # b: 2 }
39
- #
40
- # # good
41
- # { a: 1,
42
- # b: 2 }
43
- #
44
- # # good
45
- # {
46
- # a: 1,
47
- # b: 2
48
- # }
31
+ # # bad
32
+ # { a: 1,
33
+ # b: 2
34
+ # }
35
+ # # bad
36
+ # {
37
+ # a: 1,
38
+ # b: 2 }
39
+ #
40
+ # # good
41
+ # { a: 1,
42
+ # b: 2 }
43
+ #
44
+ # # good
45
+ # {
46
+ # a: 1,
47
+ # b: 2
48
+ # }
49
49
  #
50
50
  # @example EnforcedStyle: new_line
51
- # # bad
52
- # {
53
- # a: 1,
54
- # b: 2 }
55
- #
56
- # # bad
57
- # { a: 1,
58
- # b: 2 }
59
- #
60
- # # good
61
- # { a: 1,
62
- # b: 2
63
- # }
64
- #
65
- # # good
66
- # {
67
- # a: 1,
68
- # b: 2
69
- # }
51
+ # # bad
52
+ # {
53
+ # a: 1,
54
+ # b: 2 }
55
+ #
56
+ # # bad
57
+ # { a: 1,
58
+ # b: 2 }
59
+ #
60
+ # # good
61
+ # { a: 1,
62
+ # b: 2
63
+ # }
64
+ #
65
+ # # good
66
+ # {
67
+ # a: 1,
68
+ # b: 2
69
+ # }
70
70
  #
71
71
  # @example EnforcedStyle: same_line
72
- # # bad
73
- # { a: 1,
74
- # b: 2
75
- # }
76
- #
77
- # # bad
78
- # {
79
- # a: 1,
80
- # b: 2
81
- # }
82
- #
83
- # # good
84
- # {
85
- # a: 1,
86
- # b: 2 }
87
- #
88
- # # good
89
- # { a: 1,
90
- # b: 2 }
72
+ # # bad
73
+ # { a: 1,
74
+ # b: 2
75
+ # }
76
+ #
77
+ # # bad
78
+ # {
79
+ # a: 1,
80
+ # b: 2
81
+ # }
82
+ #
83
+ # # good
84
+ # {
85
+ # a: 1,
86
+ # b: 2 }
87
+ #
88
+ # # good
89
+ # { a: 1,
90
+ # b: 2 }
91
91
  class MultilineHashBraceLayout < Base
92
92
  include MultilineLiteralBraceLayout
93
93
  extend AutoCorrector
@@ -84,19 +84,23 @@ module RuboCop
84
84
  end
85
85
  end
86
86
 
87
+ # rubocop:disable Metrics/AbcSize
87
88
  def offending_range(node, lhs, rhs, given_style)
88
89
  return false unless begins_its_line?(rhs)
89
90
  return false if not_for_this_cop?(node)
90
91
 
91
92
  @base = alignment_base(node, rhs, given_style)
92
93
  correct_column = if @base
93
- @base.column + extra_indentation(given_style, node.parent)
94
+ parent = node.parent
95
+ parent = parent.parent if parent&.any_block_type?
96
+ @base.column + extra_indentation(given_style, parent)
94
97
  else
95
98
  indentation(lhs) + correct_indentation(node)
96
99
  end
97
100
  @column_delta = correct_column - rhs.column
98
101
  rhs if @column_delta.nonzero?
99
102
  end
103
+ # rubocop:enable Metrics/AbcSize
100
104
 
101
105
  def extra_indentation(given_style, parent)
102
106
  if given_style == :indented_relative_to_receiver
@@ -23,16 +23,8 @@ module RuboCop
23
23
  cfg['EnforcedStyle'] || 'space'
24
24
  end
25
25
 
26
- def kind(token)
27
- 'comma' if token.comma? && !before_semicolon?(token)
28
- end
29
-
30
- private
31
-
32
- def before_semicolon?(token)
33
- tokens = processed_source.tokens
34
-
35
- tokens[tokens.index(token) + 1].semicolon?
26
+ def kind(token, next_token)
27
+ 'comma' if token.comma? && !next_token.semicolon?
36
28
  end
37
29
  end
38
30
  end
@@ -20,7 +20,7 @@ module RuboCop
20
20
  cfg['EnforcedStyle'] || 'space'
21
21
  end
22
22
 
23
- def kind(token)
23
+ def kind(token, _next_token)
24
24
  'semicolon' if token.semicolon?
25
25
  end
26
26
 
@@ -7,18 +7,18 @@ module RuboCop
7
7
  # parenthesis (`(`) in lambda literals.
8
8
  #
9
9
  # @example EnforcedStyle: require_no_space (default)
10
- # # bad
11
- # a = -> (x, y) { x + y }
10
+ # # bad
11
+ # a = -> (x, y) { x + y }
12
12
  #
13
- # # good
14
- # a = ->(x, y) { x + y }
13
+ # # good
14
+ # a = ->(x, y) { x + y }
15
15
  #
16
16
  # @example EnforcedStyle: require_space
17
- # # bad
18
- # a = ->(x, y) { x + y }
17
+ # # bad
18
+ # a = ->(x, y) { x + y }
19
19
  #
20
- # # good
21
- # a = -> (x, y) { x + y }
20
+ # # good
21
+ # a = -> (x, y) { x + y }
22
22
  class SpaceInLambdaLiteral < Base
23
23
  include ConfigurableEnforcedStyle
24
24
  include RangeHelp
@@ -25,17 +25,17 @@ module RuboCop
25
25
  #
26
26
  # # bad - repeated alternate patterns with the same conditions don't depend on the order
27
27
  # case x
28
- # in foo | bar
28
+ # in 0 | 1
29
29
  # first_method
30
- # in bar | foo
30
+ # in 1 | 0
31
31
  # second_method
32
32
  # end
33
33
  #
34
34
  # # good
35
35
  # case x
36
- # in foo | bar
36
+ # in 0 | 1
37
37
  # first_method
38
- # in bar | baz
38
+ # in 2 | 3
39
39
  # second_method
40
40
  # end
41
41
  #
@@ -56,6 +56,27 @@ module RuboCop
56
56
  # 1
57
57
  # end
58
58
  #
59
+ # # bad
60
+ # class MyClass
61
+ # extend Forwardable
62
+ #
63
+ # # or with: `def_instance_delegator`, `def_delegators`, `def_instance_delegators`
64
+ # def_delegator :delegation_target, :delegated_method_name
65
+ #
66
+ # def delegated_method_name
67
+ # end
68
+ # end
69
+ #
70
+ # # good
71
+ # class MyClass
72
+ # extend Forwardable
73
+ #
74
+ # def_delegator :delegation_target, :delegated_method_name
75
+ #
76
+ # def non_duplicated_delegated_method_name
77
+ # end
78
+ # end
79
+ #
59
80
  # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
60
81
  #
61
82
  # # good
@@ -97,10 +118,11 @@ module RuboCop
97
118
  # delegate :foo, to: :bar
98
119
  # end
99
120
  #
100
- class DuplicateMethods < Base
121
+ class DuplicateMethods < Base # rubocop:disable Metrics/ClassLength
101
122
  MSG = 'Method `%<method>s` is defined at both %<defined>s and %<current>s.'
102
123
  RESTRICT_ON_SEND = %i[alias_method attr_reader attr_writer attr_accessor attr
103
- delegate].freeze
124
+ delegate def_delegator def_instance_delegator def_delegators
125
+ def_instance_delegators].freeze
104
126
 
105
127
  def initialize(config = nil, options = nil)
106
128
  super
@@ -154,23 +176,49 @@ module RuboCop
154
176
  )
155
177
  PATTERN
156
178
 
179
+ # @!method delegator?(node)
180
+ def_node_matcher :delegator?, <<~PATTERN
181
+ (send nil? {:def_delegator :def_instance_delegator}
182
+ {
183
+ {sym str} ({sym str} $_) |
184
+ {sym str} {sym str} ({sym str} $_)
185
+ }
186
+ )
187
+ PATTERN
188
+
189
+ # @!method delegators?(node)
190
+ def_node_matcher :delegators?, <<~PATTERN
191
+ (send nil? {:def_delegators :def_instance_delegators}
192
+ {sym str}
193
+ ({sym str} $_)+
194
+ )
195
+ PATTERN
196
+
157
197
  # @!method sym_name(node)
158
198
  def_node_matcher :sym_name, '(sym $_name)'
159
199
 
160
- def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
200
+ def on_send(node) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
161
201
  name, original_name = alias_method?(node)
162
202
 
163
203
  if name && original_name
164
204
  return if name == original_name
165
- return if node.ancestors.any?(&:if_type?)
205
+ return if inside_condition?(node)
166
206
 
167
207
  found_instance_method(node, name)
168
208
  elsif (attr = node.attribute_accessor?)
169
209
  on_attr(node, *attr)
170
210
  elsif active_support_extensions_enabled? && (names = delegate_method?(node))
171
- return if node.ancestors.any?(&:if_type?)
211
+ return if inside_condition?(node)
172
212
 
173
213
  on_delegate(node, names)
214
+ elsif (name = delegator?(node))
215
+ return if inside_condition?(node)
216
+
217
+ found_instance_method(node, name)
218
+ elsif (names = delegators?(node))
219
+ return if inside_condition?(node)
220
+
221
+ names.each { |name| found_instance_method(node, name) }
174
222
  end
175
223
  end
176
224
 
@@ -190,6 +238,10 @@ module RuboCop
190
238
  found_method(node, "#{enclosing}.#{name}")
191
239
  end
192
240
 
241
+ def inside_condition?(node)
242
+ node.ancestors.any?(&:if_type?)
243
+ end
244
+
193
245
  def message_for_dup(node, method_name, key)
194
246
  format(MSG, method: method_name, defined: source_location(@definitions[key]),
195
247
  current: source_location(node))