rubocop 1.74.0 → 1.75.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +32 -6
  4. data/config/obsoletion.yml +3 -1
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +35 -6
  7. data/lib/rubocop/config_loader.rb +4 -0
  8. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  9. data/lib/rubocop/config_obsoletion.rb +46 -2
  10. data/lib/rubocop/config_validator.rb +1 -0
  11. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -1
  12. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  13. data/lib/rubocop/cop/layout/block_alignment.rb +1 -0
  14. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  15. data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
  16. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  18. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
  19. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  20. data/lib/rubocop/cop/layout/line_length.rb +5 -1
  21. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
  22. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  23. data/lib/rubocop/cop/layout/redundant_line_break.rb +9 -5
  24. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  25. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  26. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
  27. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
  28. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  29. data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
  30. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  31. data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
  32. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
  33. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
  34. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
  35. data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
  36. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -0
  37. data/lib/rubocop/cop/lint/void.rb +1 -0
  38. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  39. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  40. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  41. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  42. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  43. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
  44. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
  45. data/lib/rubocop/cop/naming/method_name.rb +64 -8
  46. data/lib/rubocop/cop/naming/variable_name.rb +6 -19
  47. data/lib/rubocop/cop/registry.rb +9 -6
  48. data/lib/rubocop/cop/style/array_intersect.rb +39 -28
  49. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  50. data/lib/rubocop/cop/style/collection_methods.rb +1 -0
  51. data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
  52. data/lib/rubocop/cop/style/for.rb +1 -0
  53. data/lib/rubocop/cop/style/guard_clause.rb +2 -1
  54. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -2
  55. data/lib/rubocop/cop/style/hash_fetch_chain.rb +105 -0
  56. data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
  57. data/lib/rubocop/cop/style/inverse_methods.rb +1 -0
  58. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  59. data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
  60. data/lib/rubocop/cop/style/it_block_parameter.rb +100 -0
  61. data/lib/rubocop/cop/style/lambda.rb +1 -0
  62. data/lib/rubocop/cop/style/map_into_array.rb +1 -0
  63. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -0
  64. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -0
  65. data/lib/rubocop/cop/style/next.rb +44 -0
  66. data/lib/rubocop/cop/style/object_then.rb +1 -0
  67. data/lib/rubocop/cop/style/proc.rb +1 -0
  68. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  69. data/lib/rubocop/cop/style/redundant_format.rb +10 -3
  70. data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -1
  71. data/lib/rubocop/cop/style/redundant_self.rb +1 -0
  72. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  73. data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
  74. data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
  75. data/lib/rubocop/cop/style/sole_nested_conditional.rb +41 -100
  76. data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
  77. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -0
  78. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  79. data/lib/rubocop/cop/variable_force/variable.rb +1 -6
  80. data/lib/rubocop/cop/variable_force.rb +1 -1
  81. data/lib/rubocop/lsp/runtime.rb +4 -4
  82. data/lib/rubocop/lsp/stdin_runner.rb +3 -1
  83. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  84. data/lib/rubocop/rspec/shared_contexts.rb +20 -0
  85. data/lib/rubocop/rspec/support.rb +2 -0
  86. data/lib/rubocop/runner.rb +5 -1
  87. data/lib/rubocop/target_ruby.rb +1 -1
  88. data/lib/rubocop/version.rb +14 -7
  89. data/lib/rubocop.rb +4 -0
  90. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +20 -2
  91. metadata +10 -6
@@ -74,6 +74,7 @@ module RuboCop
74
74
  check_unused_block_args(node, key, value)
75
75
  end
76
76
  alias on_numblock on_block
77
+ alias on_itblock on_block
77
78
 
78
79
  # rubocop:disable Metrics/AbcSize
79
80
  def check_unused_block_args(node, key, value)
@@ -128,8 +129,8 @@ module RuboCop
128
129
  lvar_sources = node.body.each_descendant(:lvar).map(&:source)
129
130
 
130
131
  if block_arg.mlhs_type?
131
- block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
132
- lvar_sources.none?(block_arg.source.delete_prefix('*'))
132
+ block_arg.each_descendant(:arg, :restarg).all? do |descendant|
133
+ lvar_sources.none?(descendant.source.delete_prefix('*'))
133
134
  end
134
135
  else
135
136
  lvar_sources.none?(block_arg.source.delete_prefix('*'))
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Use `Hash#dig` instead of chaining potentially null `fetch` calls.
7
+ #
8
+ # When `fetch(identifier, nil)` calls are chained on a hash, the expectation
9
+ # is that each step in the chain returns either `nil` or another hash,
10
+ # and in both cases, these can be simplified with a single call to `dig` with
11
+ # multiple arguments.
12
+ #
13
+ # If the 2nd parameter is `{}` or `Hash.new`, an offense will also be registered,
14
+ # as long as the final call in the chain is a nil value. If a non-nil value is given,
15
+ # the chain will not be registered as an offense, as the default value cannot be safely
16
+ # given with `dig`.
17
+ #
18
+ # NOTE: See `Style/DigChain` for replacing chains of `dig` calls with
19
+ # a single method call.
20
+ #
21
+ # @safety
22
+ # This cop is unsafe because it cannot be guaranteed that the receiver
23
+ # is a `Hash` or that `fetch` or `dig` have the expected standard implementation.
24
+ #
25
+ # @example
26
+ # # bad
27
+ # hash.fetch('foo', nil)&.fetch('bar', nil)
28
+ #
29
+ # # bad
30
+ # # earlier members of the chain can return `{}` as long as the final `fetch`
31
+ # # has `nil` as a default value
32
+ # hash.fetch('foo', {}).fetch('bar', nil)
33
+ #
34
+ # # good
35
+ # hash.dig('foo', 'bar')
36
+ #
37
+ # # ok - not handled by the cop since the final `fetch` value is non-nil
38
+ # hash.fetch('foo', {}).fetch('bar', {})
39
+ #
40
+ class HashFetchChain < Base
41
+ extend AutoCorrector
42
+ extend TargetRubyVersion
43
+ include IgnoredNode
44
+
45
+ MSG = 'Use `%<replacement>s` instead.'
46
+ RESTRICT_ON_SEND = %i[fetch].freeze
47
+
48
+ minimum_target_ruby_version 2.3
49
+
50
+ # @!method diggable?(node)
51
+ def_node_matcher :diggable?, <<~PATTERN
52
+ (call _ :fetch $_arg {nil (hash) (send (const {nil? cbase} :Hash) :new)})
53
+ PATTERN
54
+
55
+ def on_send(node)
56
+ return if ignored_node?(node)
57
+ return if last_fetch_non_nil?(node)
58
+
59
+ last_replaceable_node, arguments = inspect_chain(node)
60
+ return unless last_replaceable_node
61
+ return unless arguments.size > 1
62
+
63
+ range = last_replaceable_node.selector.join(node.loc.end)
64
+ replacement = replacement(arguments)
65
+ message = format(MSG, replacement: replacement)
66
+
67
+ add_offense(range, message: message) do |corrector|
68
+ corrector.replace(range, replacement)
69
+ end
70
+ end
71
+ alias on_csend on_send
72
+
73
+ private
74
+
75
+ def last_fetch_non_nil?(node)
76
+ # When chaining `fetch` methods, `fetch(x, {})` is acceptable within
77
+ # the chain, as long as the last method in the chain has a `nil`
78
+ # default value.
79
+
80
+ return false unless node.method?(:fetch)
81
+
82
+ !node.last_argument&.nil_type?
83
+ end
84
+
85
+ def inspect_chain(node)
86
+ arguments = []
87
+
88
+ while (arg = diggable?(node))
89
+ arguments.unshift(arg)
90
+ ignore_node(node)
91
+ last_replaceable_node = node
92
+ node = node.receiver
93
+ end
94
+
95
+ [last_replaceable_node, arguments]
96
+ end
97
+
98
+ def replacement(arguments)
99
+ values = arguments.map(&:source).join(', ')
100
+ "dig(#{values})"
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
@@ -97,34 +97,31 @@ module RuboCop
97
97
  else
98
98
  correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
99
99
  end
100
-
101
- corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
102
- return unless (if_branch = node.if_branch)
103
-
104
- range = range_by_whole_lines(if_branch.source_range, include_final_newline: true)
105
- corrector.remove(range)
106
100
  end
107
101
 
108
102
  def correct_to_elsif_from_modifier_form(corrector, node)
109
- corrector.replace(node.parent.loc.else, <<~RUBY.chop)
110
- elsif #{node.condition.source}
111
- #{indent(node.if_branch)}#{node.if_branch.source}
112
- end
113
- RUBY
103
+ corrector.replace(node.parent.loc.else, "elsif #{node.condition.source}")
104
+
105
+ condition_range = range_between(
106
+ node.if_branch.source_range.end_pos, node.condition.source_range.end_pos
107
+ )
108
+ corrector.remove(condition_range)
114
109
  end
115
110
 
116
- def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
111
+ def correct_to_elsif_from_if_inside_else_form(corrector, node, condition) # rubocop:disable Metrics/AbcSize
117
112
  corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
118
113
 
119
114
  if_condition_range = if_condition_range(node, condition)
120
115
 
121
116
  if (if_branch = node.if_branch)
122
- corrector.replace(if_condition_range, if_branch.source)
117
+ corrector.replace(if_condition_range, range_with_comments(if_branch).source)
118
+ corrector.remove(range_with_comments_and_lines(if_branch))
123
119
  else
124
120
  corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
125
121
  end
126
122
 
127
123
  corrector.remove(condition)
124
+ corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
128
125
  end
129
126
 
130
127
  def then?(node)
@@ -106,6 +106,7 @@ module RuboCop
106
106
  end
107
107
 
108
108
  alias on_numblock on_block
109
+ alias on_itblock on_block
109
110
 
110
111
  private
111
112
 
@@ -89,8 +89,8 @@ module RuboCop
89
89
 
90
90
  def inheritance_check?(node)
91
91
  argument = node.first_argument
92
- node.method?(:<) &&
93
- (argument.const_type? && argument.short_name.to_s.upcase != argument.short_name.to_s)
92
+ node.method?(:<) && argument.const_type? &&
93
+ argument.short_name.to_s.upcase != argument.short_name.to_s
94
94
  end
95
95
 
96
96
  def preferred_condition(node)
@@ -32,7 +32,7 @@ module RuboCop
32
32
 
33
33
  # To try to avoid doing two regex checks on every string,
34
34
  # shortcut out if the string does not look like an IP address
35
- return false unless could_be_ip?(contents)
35
+ return false unless potential_ip?(contents)
36
36
 
37
37
  ::Resolv::IPv4::Regex.match?(contents) || ::Resolv::IPv6::Regex.match?(contents)
38
38
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  Array(allowed_addresses).map(&:downcase)
53
53
  end
54
54
 
55
- def could_be_ip?(str)
55
+ def potential_ip?(str)
56
56
  # If the string is too long, it can't be an IP
57
57
  return false if too_long?(str)
58
58
 
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for blocks with one argument where `it` block parameter can be used.
7
+ #
8
+ # It provides three `EnforcedStyle` options:
9
+ #
10
+ # 1. `allow_named_parameter` (default) ... Detects only numbered block parameters.
11
+ # 2. `always` ... Always uses the `it` block parameter.
12
+ # 3. `disallow` ... Disallows the `it` block parameter.
13
+ #
14
+ # A single numbered parameter is detected when `allow_named_parameter` or `always`.
15
+ #
16
+ # @example EnforcedStyle: allow_named_parameter (default)
17
+ # # bad
18
+ # block { do_something(_1) }
19
+ #
20
+ # # good
21
+ # block { do_something(it) }
22
+ # block { |named_param| do_something(named_param) }
23
+ #
24
+ # @example EnforcedStyle: always
25
+ # # bad
26
+ # block { do_something(_1) }
27
+ # block { |named_param| do_something(named_param) }
28
+ #
29
+ # # good
30
+ # block { do_something(it) }
31
+ #
32
+ # @example EnforcedStyle: disallow
33
+ # # bad
34
+ # block { do_something(it) }
35
+ #
36
+ # # good
37
+ # block { do_something(_1) }
38
+ # block { |named_param| do_something(named_param) }
39
+ #
40
+ class ItBlockParameter < Base
41
+ include ConfigurableEnforcedStyle
42
+ extend TargetRubyVersion
43
+ extend AutoCorrector
44
+
45
+ MSG_USE_IT_BLOCK_PARAMETER = 'Use `it` block parameter.'
46
+ MSG_AVOID_IT_BLOCK_PARAMETER = 'Avoid using `it` block parameter.'
47
+
48
+ minimum_target_ruby_version 3.4
49
+
50
+ def on_block(node)
51
+ return unless style == :always
52
+ return unless node.arguments.one?
53
+
54
+ # `restarg`, `kwrestarg`, `blockarg` nodes can return early.
55
+ return unless node.first_argument.arg_type?
56
+
57
+ variables = find_block_variables(node, node.first_argument.source)
58
+
59
+ variables.each do |variable|
60
+ add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
61
+ corrector.remove(node.arguments)
62
+ corrector.replace(variable, 'it')
63
+ end
64
+ end
65
+ end
66
+
67
+ def on_numblock(node)
68
+ return if style == :disallow
69
+ return unless node.children[1] == 1
70
+
71
+ variables = find_block_variables(node, '_1')
72
+
73
+ variables.each do |variable|
74
+ add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
75
+ corrector.replace(variable, 'it')
76
+ end
77
+ end
78
+ end
79
+
80
+ def on_itblock(node)
81
+ return unless style == :disallow
82
+
83
+ variables = find_block_variables(node, 'it')
84
+
85
+ variables.each do |variable|
86
+ add_offense(variable, message: MSG_AVOID_IT_BLOCK_PARAMETER)
87
+ end
88
+ end
89
+
90
+ private
91
+
92
+ def find_block_variables(node, block_argument_name)
93
+ node.each_descendant(:lvar).select do |descendant|
94
+ descendant.source == block_argument_name
95
+ end
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -77,6 +77,7 @@ module RuboCop
77
77
  end
78
78
  end
79
79
  alias on_numblock on_block
80
+ alias on_itblock on_block
80
81
 
81
82
  private
82
83
 
@@ -127,6 +127,7 @@ module RuboCop
127
127
  end
128
128
 
129
129
  alias on_numblock on_block
130
+ alias on_itblock on_block
130
131
 
131
132
  private
132
133
 
@@ -36,6 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  alias on_numblock on_block
39
+ alias on_itblock on_block
39
40
 
40
41
  def on_send(node)
41
42
  return if ignored_node?(node)
@@ -44,6 +44,7 @@ module RuboCop
44
44
  end
45
45
 
46
46
  alias on_numblock on_block
47
+ alias on_itblock on_block
47
48
  end
48
49
  end
49
50
  end
@@ -46,6 +46,38 @@ module RuboCop
46
46
  # next unless a == 1
47
47
  # puts a
48
48
  # end
49
+ #
50
+ # @example AllowConsecutiveConditionals: false (default)
51
+ # # bad
52
+ # [1, 2].each do |a|
53
+ # if a == 1
54
+ # puts a
55
+ # end
56
+ # if a == 2
57
+ # puts a
58
+ # end
59
+ # end
60
+ #
61
+ # # good
62
+ # [1, 2].each do |a|
63
+ # if a == 1
64
+ # puts a
65
+ # end
66
+ # next unless a == 2
67
+ # puts a
68
+ # end
69
+ #
70
+ # @example AllowConsecutiveConditionals: true
71
+ # # good
72
+ # [1, 2].each do |a|
73
+ # if a == 1
74
+ # puts a
75
+ # end
76
+ # if a == 2
77
+ # puts a
78
+ # end
79
+ # end
80
+ #
49
81
  class Next < Base
50
82
  include ConfigurableEnforcedStyle
51
83
  include MinBodyLength
@@ -72,6 +104,7 @@ module RuboCop
72
104
  end
73
105
 
74
106
  alias on_numblock on_block
107
+ alias on_itblock on_block
75
108
 
76
109
  def on_while(node)
77
110
  check(node)
@@ -86,6 +119,9 @@ module RuboCop
86
119
 
87
120
  offending_node = offense_node(node.body)
88
121
 
122
+ return if allowed_consecutive_conditionals? &&
123
+ consecutive_conditionals?(offending_node)
124
+
89
125
  add_offense(offense_location(offending_node)) do |corrector|
90
126
  if offending_node.modifier_form?
91
127
  autocorrect_modifier(corrector, offending_node)
@@ -227,6 +263,14 @@ module RuboCop
227
263
 
228
264
  corrector.remove_leading(buffer.line_range(lineno), adjustment) if adjustment.positive?
229
265
  end
266
+
267
+ def consecutive_conditionals?(if_node)
268
+ if_node.parent&.begin_type? && if_node.left_sibling&.if_type?
269
+ end
270
+
271
+ def allowed_consecutive_conditionals?
272
+ cop_config.fetch('AllowConsecutiveConditionals', false)
273
+ end
230
274
  end
231
275
  end
232
276
  end
@@ -38,6 +38,7 @@ module RuboCop
38
38
  check_method_node(node.send_node)
39
39
  end
40
40
  alias on_numblock on_block
41
+ alias on_itblock on_block
41
42
 
42
43
  def on_send(node)
43
44
  return unless node.arguments.one? && node.first_argument.block_pass_type?
@@ -30,6 +30,7 @@ module RuboCop
30
30
  end
31
31
 
32
32
  alias on_numblock on_block
33
+ alias on_itblock on_block
33
34
  end
34
35
  end
35
36
  end
@@ -95,6 +95,7 @@ module RuboCop
95
95
  end
96
96
 
97
97
  alias on_numblock on_block
98
+ alias on_itblock on_block
98
99
 
99
100
  def on_kwbegin(node)
100
101
  return unless (target_node = offensive_kwbegins(node).to_a.last)
@@ -5,8 +5,8 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for calls to `Kernel#format` or `Kernel#sprintf` that are redundant.
7
7
  #
8
- # Calling `format` with only a single string argument is redundant, as it can be
9
- # replaced by the string itself.
8
+ # Calling `format` with only a single string or constant argument is redundant,
9
+ # as it can be replaced by the string or constant itself.
10
10
  #
11
11
  # Also looks for `format` calls where the arguments are literals that can be
12
12
  # inlined into a string easily. This applies to the `%s`, `%d`, `%i`, `%u`, and
@@ -38,6 +38,13 @@ module RuboCop
38
38
  # 'the quick brown fox jumps over the lazy dog.'
39
39
  #
40
40
  # # bad
41
+ # format(MESSAGE)
42
+ # sprintf(MESSAGE)
43
+ #
44
+ # # good
45
+ # MESSAGE
46
+ #
47
+ # # bad
41
48
  # format('%s %s', 'foo', 'bar')
42
49
  # sprintf('%s %s', 'foo', 'bar')
43
50
  #
@@ -54,7 +61,7 @@ module RuboCop
54
61
 
55
62
  # @!method format_without_additional_args?(node)
56
63
  def_node_matcher :format_without_additional_args?, <<~PATTERN
57
- (send {(const {nil? cbase} :Kernel) nil?} %RESTRICT_ON_SEND ${str dstr})
64
+ (send {(const {nil? cbase} :Kernel) nil?} %RESTRICT_ON_SEND ${str dstr const})
58
65
  PATTERN
59
66
 
60
67
  # @!method rational_number?(node)
@@ -17,7 +17,7 @@ module RuboCop
17
17
  include Parentheses
18
18
  extend AutoCorrector
19
19
 
20
- ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
20
+ ALLOWED_NODE_TYPES = %i[or send splat kwsplat].freeze
21
21
 
22
22
  # @!method square_brackets?(node)
23
23
  def_node_matcher :square_brackets?, <<~PATTERN
@@ -162,6 +162,7 @@ module RuboCop
162
162
  return if node.semantic_operator? && begin_node.parent
163
163
  return if node.multiline? && allow_in_multiline_conditions?
164
164
  return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
165
+ return if !node.and_type? && begin_node.parent&.and_type?
165
166
  return if begin_node.parent&.if_type? && begin_node.parent.ternary?
166
167
 
167
168
  'a logical expression'
@@ -118,6 +118,7 @@ module RuboCop
118
118
  end
119
119
 
120
120
  alias on_numblock on_block
121
+ alias on_itblock on_block
121
122
 
122
123
  def on_if(node)
123
124
  # Allow conditional nodes to use `self` in the condition if that variable
@@ -21,6 +21,7 @@ module RuboCop
21
21
 
22
22
  MSG_BLOCK = 'Use `sort` instead of `sort_by { |%<var>s| %<var>s }`.'
23
23
  MSG_NUMBLOCK = 'Use `sort` instead of `sort_by { _1 }`.'
24
+ MSG_ITBLOCK = 'Use `sort` instead of `sort_by { it }`.'
24
25
 
25
26
  def on_block(node)
26
27
  redundant_sort_by_block(node) do |send, var_name|
@@ -36,7 +37,17 @@ module RuboCop
36
37
  redundant_sort_by_numblock(node) do |send|
37
38
  range = sort_by_range(send, node)
38
39
 
39
- add_offense(range, message: format(MSG_NUMBLOCK)) do |corrector|
40
+ add_offense(range, message: MSG_NUMBLOCK) do |corrector|
41
+ corrector.replace(range, 'sort')
42
+ end
43
+ end
44
+ end
45
+
46
+ def on_itblock(node)
47
+ redundant_sort_by_itblock(node) do |send|
48
+ range = sort_by_range(send, node)
49
+
50
+ add_offense(range, message: MSG_ITBLOCK) do |corrector|
40
51
  corrector.replace(range, 'sort')
41
52
  end
42
53
  end
@@ -54,6 +65,11 @@ module RuboCop
54
65
  (numblock $(call _ :sort_by) 1 (lvar :_1))
55
66
  PATTERN
56
67
 
68
+ # @!method redundant_sort_by_itblock(node)
69
+ def_node_matcher :redundant_sort_by_itblock, <<~PATTERN
70
+ (itblock $(call _ :sort_by) _ (lvar :it))
71
+ PATTERN
72
+
57
73
  def sort_by_range(send, node)
58
74
  range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
59
75
  end
@@ -59,6 +59,7 @@ module RuboCop
59
59
  {
60
60
  (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
61
61
  (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
62
+ (itblock call $_ ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
62
63
  }
63
64
  PATTERN
64
65
 
@@ -137,6 +138,7 @@ module RuboCop
137
138
  return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
138
139
 
139
140
  block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
141
+
140
142
  return unless calls_lvar?(regexp_method_send_node, block_arg_name)
141
143
 
142
144
  regexp_method_send_node
@@ -150,7 +152,8 @@ module RuboCop
150
152
  return node.child_nodes.first if node.match_with_lvasgn_type?
151
153
 
152
154
  if node.receiver.lvar_type? &&
153
- (block.numblock_type? || node.receiver.source == block.first_argument.source)
155
+ (block.type?(:numblock, :itblock) ||
156
+ node.receiver.source == block.first_argument.source)
154
157
  node.first_argument
155
158
  elsif node.first_argument.lvar_type?
156
159
  node.receiver
@@ -56,11 +56,13 @@ module RuboCop
56
56
  end
57
57
  # rubocop:enable Metrics/AbcSize
58
58
  alias on_numblock on_block
59
+ alias on_itblock on_block
59
60
 
60
61
  private
61
62
 
62
63
  def do_line(node)
63
- if node.numblock_type? || node.arguments.children.empty? || node.send_node.lambda_literal?
64
+ if node.type?(:numblock, :itblock) ||
65
+ node.arguments.children.empty? || node.send_node.lambda_literal?
64
66
  node.loc.begin
65
67
  else
66
68
  node.arguments