rubocop 1.60.0 → 1.61.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.

Potentially problematic release.


This version of rubocop might be problematic. Click here for more details.

Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/assets/output.css.erb +159 -0
  4. data/assets/output.html.erb +1 -160
  5. data/config/default.yml +35 -12
  6. data/lib/rubocop/cli/command/lsp.rb +2 -2
  7. data/lib/rubocop/cli.rb +6 -1
  8. data/lib/rubocop/config_finder.rb +12 -2
  9. data/lib/rubocop/config_validator.rb +14 -5
  10. data/lib/rubocop/cop/autocorrect_logic.rb +6 -1
  11. data/lib/rubocop/cop/base.rb +12 -7
  12. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +4 -8
  13. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +5 -13
  14. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +2 -0
  15. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -4
  16. data/lib/rubocop/cop/internal_affairs/method_name_end_with.rb +8 -6
  17. data/lib/rubocop/cop/internal_affairs/redundant_expect_offense_arguments.rb +34 -0
  18. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  19. data/lib/rubocop/cop/layout/end_alignment.rb +3 -1
  20. data/lib/rubocop/cop/layout/redundant_line_break.rb +7 -1
  21. data/lib/rubocop/cop/layout/space_before_block_braces.rb +19 -10
  22. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  23. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -9
  24. data/lib/rubocop/cop/lint/rescue_type.rb +1 -3
  25. data/lib/rubocop/cop/lint/script_permission.rb +3 -3
  26. data/lib/rubocop/cop/lint/syntax.rb +1 -1
  27. data/lib/rubocop/cop/lint/void.rb +6 -1
  28. data/lib/rubocop/cop/naming/predicate_name.rb +2 -2
  29. data/lib/rubocop/cop/registry.rb +5 -5
  30. data/lib/rubocop/cop/style/arguments_forwarding.rb +69 -13
  31. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  32. data/lib/rubocop/cop/style/collection_compact.rb +11 -2
  33. data/lib/rubocop/cop/style/commented_keyword.rb +5 -2
  34. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -5
  35. data/lib/rubocop/cop/style/hash_each_methods.rb +26 -5
  36. data/lib/rubocop/cop/style/hash_syntax.rb +6 -2
  37. data/lib/rubocop/cop/style/inverse_methods.rb +8 -8
  38. data/lib/rubocop/cop/style/invertible_unless_condition.rb +10 -5
  39. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +5 -8
  40. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +1 -1
  41. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +4 -0
  42. data/lib/rubocop/cop/style/object_then.rb +5 -3
  43. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
  44. data/lib/rubocop/cop/style/raise_args.rb +3 -0
  45. data/lib/rubocop/cop/style/redundant_argument.rb +2 -2
  46. data/lib/rubocop/cop/style/redundant_assignment.rb +10 -2
  47. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -5
  48. data/lib/rubocop/cop/style/redundant_parentheses.rb +8 -1
  49. data/lib/rubocop/cop/style/redundant_return.rb +6 -0
  50. data/lib/rubocop/cop/style/sample.rb +1 -3
  51. data/lib/rubocop/cops_documentation_generator.rb +4 -2
  52. data/lib/rubocop/formatter/html_formatter.rb +30 -10
  53. data/lib/rubocop/formatter/offense_count_formatter.rb +12 -2
  54. data/lib/rubocop/lsp/logger.rb +1 -1
  55. data/lib/rubocop/lsp/routes.rb +1 -1
  56. data/lib/rubocop/lsp/runtime.rb +1 -1
  57. data/lib/rubocop/lsp/server.rb +5 -2
  58. data/lib/rubocop/lsp/severity.rb +1 -1
  59. data/lib/rubocop/lsp.rb +29 -0
  60. data/lib/rubocop/options.rb +11 -0
  61. data/lib/rubocop/path_util.rb +6 -2
  62. data/lib/rubocop/rspec/shared_contexts.rb +10 -6
  63. data/lib/rubocop/rspec/support.rb +1 -1
  64. data/lib/rubocop/server/cache.rb +1 -1
  65. data/lib/rubocop/server/client_command/exec.rb +0 -1
  66. data/lib/rubocop/server/server_command/exec.rb +0 -1
  67. data/lib/rubocop/target_ruby.rb +79 -79
  68. data/lib/rubocop/version.rb +1 -1
  69. metadata +11 -8
@@ -34,18 +34,14 @@ module RuboCop
34
34
  end
35
35
 
36
36
  def offending_range
37
+ begin_range = block_node.source_range.begin
38
+
37
39
  if block_node.arguments?
38
- replacement_range(argument_node.source_range.end_pos)
40
+ begin_range.join(argument_node.source_range.end)
39
41
  else
40
- replacement_range(block_node.loc.begin.end_pos)
42
+ begin_range.join(block_node.loc.begin.end)
41
43
  end
42
44
  end
43
-
44
- def replacement_range(end_pos)
45
- Parser::Source::Range.new(block_node.source_range.source_buffer,
46
- block_node.source_range.begin_pos,
47
- end_pos)
48
- end
49
45
  end
50
46
  end
51
47
  end
@@ -15,6 +15,8 @@ module RuboCop
15
15
  end
16
16
 
17
17
  def call(corrector)
18
+ offending_range = for_node.source_range.begin.join(end_range)
19
+
18
20
  corrector.replace(offending_range, correction)
19
21
  end
20
22
 
@@ -40,11 +42,11 @@ module RuboCop
40
42
  collection_node.range_type? || collection_node.or_type? || collection_node.and_type?
41
43
  end
42
44
 
43
- def end_position
45
+ def end_range
44
46
  if for_node.do?
45
- keyword_begin.end_pos
47
+ keyword_begin.end
46
48
  else
47
- collection_end.end_pos
49
+ collection_end.end
48
50
  end
49
51
  end
50
52
 
@@ -59,16 +61,6 @@ module RuboCop
59
61
  collection_node.source_range
60
62
  end
61
63
  end
62
-
63
- def offending_range
64
- replacement_range(end_position)
65
- end
66
-
67
- def replacement_range(end_pos)
68
- Parser::Source::Range.new(for_node.source_range.source_buffer,
69
- for_node.source_range.begin_pos,
70
- end_pos)
71
- end
72
64
  end
73
65
  end
74
66
  end
@@ -110,6 +110,8 @@ module RuboCop
110
110
  end
111
111
  end
112
112
 
113
+ return unless required_ruby_version
114
+
113
115
  required_ruby_version.str_content.scan(/\d/).first(2).join('.')
114
116
  end
115
117
 
@@ -63,10 +63,10 @@ module RuboCop
63
63
  expect_correction: EXPECT_CORRECTION_DESCRIPTION_MAPPING
64
64
  }.freeze
65
65
 
66
- # @!method offense_example?(node)
67
- def_node_matcher :offense_example?, <<~PATTERN
66
+ # @!method offense_example(node)
67
+ def_node_matcher :offense_example, <<~PATTERN
68
68
  (block
69
- (send _ {:it :specify} $_description)
69
+ (send _ {:it :specify} $...)
70
70
  _args
71
71
  `(send nil? %RESTRICT_ON_SEND ...)
72
72
  )
@@ -74,7 +74,7 @@ module RuboCop
74
74
 
75
75
  def on_send(node)
76
76
  parent = node.each_ancestor(:block).first
77
- return unless parent && (current_description = offense_example?(parent))
77
+ return unless parent && (current_description = offense_example(parent)&.first)
78
78
 
79
79
  method_name = node.method_name
80
80
  message = format(MSG, method_name: method_name)
@@ -30,6 +30,7 @@ module RuboCop
30
30
  extend AutoCorrector
31
31
 
32
32
  MSG = 'Use `%<method_name>s` instead of `%<method_suffix>s`.'
33
+ RESTRICT_ON_SEND = %i[end_with?].freeze
33
34
  SUGGEST_METHOD_FOR_SUFFIX = {
34
35
  '=' => 'assignment_method?',
35
36
  '!' => 'bang_method?',
@@ -51,14 +52,15 @@ module RuboCop
51
52
 
52
53
  def on_send(node)
53
54
  method_name_end_with?(node) do |method_name_node, end_with_arg|
55
+ next unless method_name_node.receiver
56
+
57
+ preferred_method = SUGGEST_METHOD_FOR_SUFFIX[end_with_arg.value]
54
58
  range = range(method_name_node, node)
55
- message = format(
56
- MSG,
57
- method_name: SUGGEST_METHOD_FOR_SUFFIX[end_with_arg.value],
58
- method_suffix: range.source
59
- )
59
+ message = format(MSG, method_name: preferred_method, method_suffix: range.source)
60
60
 
61
- add_offense(range, message: message)
61
+ add_offense(range, message: message) do |corrector|
62
+ corrector.replace(range, preferred_method)
63
+ end
62
64
  end
63
65
  end
64
66
  alias on_csend on_send
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks for redundant arguments of `RuboCop::RSpec::ExpectOffense`'s methods.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # expect_no_offenses('code', keyword: keyword)
12
+ #
13
+ # # good
14
+ # expect_no_offenses('code')
15
+ #
16
+ class RedundantExpectOffenseArguments < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Remove the redundant arguments.'
20
+ RESTRICT_ON_SEND = %i[expect_no_offenses].freeze
21
+
22
+ def on_send(node)
23
+ return if node.arguments.one? || !node.arguments[1]&.hash_type?
24
+
25
+ range = node.first_argument.source_range.end.join(node.last_argument.source_range.end)
26
+
27
+ add_offense(range) do |corrector|
28
+ corrector.remove(range)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -20,6 +20,7 @@ require_relative 'internal_affairs/offense_location_keyword'
20
20
  require_relative 'internal_affairs/processed_source_buffer_name'
21
21
  require_relative 'internal_affairs/redundant_context_config_parameter'
22
22
  require_relative 'internal_affairs/redundant_described_class_as_subject'
23
+ require_relative 'internal_affairs/redundant_expect_offense_arguments'
23
24
  require_relative 'internal_affairs/redundant_let_rubocop_config_new'
24
25
  require_relative 'internal_affairs/redundant_location_argument'
25
26
  require_relative 'internal_affairs/redundant_message_argument'
@@ -20,7 +20,9 @@ module RuboCop
20
20
  # This `Layout/EndAlignment` cop aligns with keywords (e.g. `if`, `while`, `case`)
21
21
  # by default. On the other hand, `Layout/BeginEndAlignment` cop aligns with
22
22
  # `EnforcedStyleAlignWith: start_of_line` by default due to `||= begin` tends
23
- # to align with the start of the line. These style can be configured by each cop.
23
+ # to align with the start of the line. `Layout/DefEndAlignment` cop also aligns with
24
+ # `EnforcedStyleAlignWith: start_of_line` by default.
25
+ # These style can be configured by each cop.
24
26
  #
25
27
  # @example EnforcedStyleAlignWith: keyword (default)
26
28
  # # bad
@@ -85,7 +85,13 @@ module RuboCop
85
85
 
86
86
  def offense?(node)
87
87
  node.multiline? && !too_long?(node) && suitable_as_single_line?(node) &&
88
- !configured_to_not_be_inspected?(node)
88
+ !index_access_call_chained?(node) && !configured_to_not_be_inspected?(node)
89
+ end
90
+
91
+ def index_access_call_chained?(node)
92
+ return false unless node.send_type? && node.method?(:[])
93
+
94
+ node.children.first.send_type? && node.children.first.method?(:[])
89
95
  end
90
96
 
91
97
  def configured_to_not_be_inspected?(node)
@@ -81,19 +81,28 @@ module RuboCop
81
81
  private
82
82
 
83
83
  def check_empty(left_brace, space_plus_brace, used_style)
84
- return if style_for_empty_braces == used_style
85
-
86
- config_to_allow_offenses['EnforcedStyleForEmptyBraces'] = used_style.to_s
84
+ if style_for_empty_braces == used_style
85
+ handle_different_styles_for_empty_braces(used_style)
86
+ return
87
+ elsif !config_to_allow_offenses.key?('Enabled')
88
+ config_to_allow_offenses['EnforcedStyleForEmptyBraces'] = used_style.to_s
89
+ end
87
90
 
88
91
  if style_for_empty_braces == :space
89
- add_offense(left_brace, message: MISSING_MSG) do |corrector|
90
- autocorrect(corrector, left_brace)
91
- end
92
+ range = left_brace
93
+ msg = MISSING_MSG
92
94
  else
93
- space = range_between(space_plus_brace.begin_pos, left_brace.begin_pos)
94
- add_offense(space, message: DETECTED_MSG) do |corrector|
95
- autocorrect(corrector, space)
96
- end
95
+ range = range_between(space_plus_brace.begin_pos, left_brace.begin_pos)
96
+ msg = DETECTED_MSG
97
+ end
98
+ add_offense(range, message: msg) { |corrector| autocorrect(corrector, range) }
99
+ end
100
+
101
+ def handle_different_styles_for_empty_braces(used_style)
102
+ if config_to_allow_offenses['EnforcedStyleForEmptyBraces'] &&
103
+ config_to_allow_offenses['EnforcedStyleForEmptyBraces'].to_sym != used_style
104
+ config_to_allow_offenses.clear
105
+ config_to_allow_offenses['Enabled'] = false
97
106
  end
98
107
  end
99
108
 
@@ -104,7 +104,7 @@ module RuboCop
104
104
  def correct_other_branches(corrector, node)
105
105
  return unless require_other_branches_correction?(node)
106
106
 
107
- if node.else_branch&.if_type?
107
+ if node.else_branch&.if_type? && !node.else_branch.modifier_form?
108
108
  # Replace an orphaned `elsif` with `if`
109
109
  corrector.replace(node.else_branch.loc.keyword, 'if')
110
110
  else
@@ -5,7 +5,8 @@ module RuboCop
5
5
  module Lint
6
6
  # Checks for redundant safe navigation calls.
7
7
  # Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
8
- # and an offense is not detected when the receiver is a snake case constant.
8
+ # and an offense is not detected when the receiver is a constant. The detection also applies
9
+ # to literal receivers, except for `nil`.
9
10
  #
10
11
  # For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
11
12
  # and `equal?` methods are checked by default.
@@ -76,10 +77,9 @@ module RuboCop
76
77
  #
77
78
  class RedundantSafeNavigation < Base
78
79
  include AllowedMethods
79
- include RangeHelp
80
80
  extend AutoCorrector
81
81
 
82
- MSG = 'Redundant safe navigation detected.'
82
+ MSG = 'Redundant safe navigation detected, use `.` instead.'
83
83
  MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
84
84
 
85
85
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
@@ -105,24 +105,23 @@ module RuboCop
105
105
 
106
106
  # rubocop:disable Metrics/AbcSize
107
107
  def on_csend(node)
108
- unless node.receiver.const_type? && !node.receiver.source.match?(SNAKE_CASE)
108
+ unless assume_receiver_instance_exists?(node.receiver)
109
109
  return unless check?(node) && allowed_method?(node.method_name)
110
110
  return if respond_to_nil_specific_method?(node)
111
111
  end
112
112
 
113
- range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
114
- add_offense(range) { |corrector| corrector.replace(node.loc.dot, '.') }
113
+ range = node.loc.dot
114
+ add_offense(range) { |corrector| corrector.replace(range, '.') }
115
115
  end
116
116
 
117
117
  def on_or(node)
118
118
  conversion_with_default?(node) do |send_node|
119
- range = range_between(send_node.loc.dot.begin_pos, node.source_range.end_pos)
119
+ range = send_node.loc.dot.begin.join(node.source_range.end)
120
120
 
121
121
  add_offense(range, message: MSG_LITERAL) do |corrector|
122
122
  corrector.replace(send_node.loc.dot, '.')
123
123
 
124
- range_with_default = range_between(node.lhs.source_range.end.begin_pos,
125
- node.source_range.end.end_pos)
124
+ range_with_default = node.lhs.source_range.end.begin.join(node.source_range.end)
126
125
  corrector.remove(range_with_default)
127
126
  end
128
127
  end
@@ -131,6 +130,12 @@ module RuboCop
131
130
 
132
131
  private
133
132
 
133
+ def assume_receiver_instance_exists?(receiver)
134
+ return true if receiver.const_type? && !receiver.source.match?(SNAKE_CASE)
135
+
136
+ receiver.literal? && !receiver.nil_type?
137
+ end
138
+
134
139
  def check?(node)
135
140
  parent = node.parent
136
141
  return false unless parent
@@ -59,9 +59,7 @@ module RuboCop
59
59
 
60
60
  def autocorrect(corrector, node)
61
61
  rescued, _, _body = *node
62
- range = Parser::Source::Range.new(node.source_range.source_buffer,
63
- node.loc.keyword.end_pos,
64
- rescued.source_range.end_pos)
62
+ range = node.loc.keyword.end.join(rescued.source_range.end)
65
63
 
66
64
  corrector.replace(range, correction(*rescued))
67
65
  end
@@ -46,14 +46,14 @@ module RuboCop
46
46
  message = format_message_from(processed_source)
47
47
 
48
48
  add_offense(comment, message: message) do
49
- autocorrect(comment) if autocorrect_requested?
49
+ autocorrect if autocorrect_requested?
50
50
  end
51
51
  end
52
52
 
53
53
  private
54
54
 
55
- def autocorrect(comment)
56
- FileUtils.chmod('+x', comment.source_range.source_buffer.name)
55
+ def autocorrect
56
+ FileUtils.chmod('+x', processed_source.file_path)
57
57
  end
58
58
 
59
59
  def executable?(processed_source)
@@ -17,7 +17,7 @@ module RuboCop
17
17
  private
18
18
 
19
19
  def add_offense_from_diagnostic(diagnostic, ruby_version)
20
- message = if lsp_mode?
20
+ message = if LSP.enabled?
21
21
  diagnostic.message
22
22
  else
23
23
  "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
@@ -100,7 +100,12 @@ module RuboCop
100
100
  expressions = *node
101
101
  expressions.pop unless in_void_context?(node)
102
102
  expressions.each do |expr|
103
- check_void_op(expr)
103
+ check_void_op(expr) do
104
+ block_node = node.each_ancestor(:block).first
105
+
106
+ block_node&.method?(:each)
107
+ end
108
+
104
109
  check_expression(expr)
105
110
  end
106
111
  end
@@ -99,7 +99,7 @@ module RuboCop
99
99
  !(method_name.start_with?(prefix) && # cheap check to avoid allocating Regexp
100
100
  method_name.match?(/^#{prefix}[^0-9]/)) ||
101
101
  method_name == expected_name(method_name, prefix) ||
102
- method_name.end_with?('=') || # rubocop:todo InternalAffairs/MethodNameEndWith
102
+ method_name.end_with?('=') ||
103
103
  allowed_method?(method_name)
104
104
  end
105
105
 
@@ -109,7 +109,7 @@ module RuboCop
109
109
  else
110
110
  method_name.dup
111
111
  end
112
- new_name << '?' unless method_name.end_with?('?') # rubocop:todo InternalAffairs/MethodNameEndWith
112
+ new_name << '?' unless method_name.end_with?('?')
113
113
  new_name
114
114
  end
115
115
 
@@ -240,12 +240,12 @@ module RuboCop
240
240
  self
241
241
  end
242
242
 
243
- def select(...)
244
- cops.select(...)
243
+ def select(&block)
244
+ cops.select(&block)
245
245
  end
246
246
 
247
- def each(...)
248
- cops.each(...)
247
+ def each(&block)
248
+ cops.each(&block)
249
249
  end
250
250
 
251
251
  # @param [String] cop_name
@@ -300,7 +300,7 @@ module RuboCop
300
300
  unless given_badge.match?(real_badge)
301
301
  path = PathUtil.smart_path(source_path)
302
302
  warn "#{path}: #{given_badge} has the wrong namespace - " \
303
- "should be #{real_badge.department}"
303
+ "replace it with #{given_badge.with_department(real_badge.department)}"
304
304
  end
305
305
 
306
306
  real_badge.to_s
@@ -8,6 +8,12 @@ module RuboCop
8
8
  # This cop identifies places where `do_something(*args, &block)`
9
9
  # can be replaced by `do_something(...)`.
10
10
  #
11
+ # In Ruby 3.1, anonymous block forwarding has been added.
12
+ #
13
+ # This cop identifies places where `do_something(&block)` can be replaced
14
+ # by `do_something(&)`; if desired, this functionality can be disabled
15
+ # by setting `UseAnonymousForwarding: false`.
16
+ #
11
17
  # In Ruby 3.2, anonymous args/kwargs forwarding has been added.
12
18
  #
13
19
  # This cop also identifies places where `use_args(*args)`/`use_kwargs(**kwargs)` can be
@@ -41,22 +47,25 @@ module RuboCop
41
47
  #
42
48
  # @example UseAnonymousForwarding: true (default, only relevant for Ruby >= 3.2)
43
49
  # # bad
44
- # def foo(*args, **kwargs)
50
+ # def foo(*args, **kwargs, &block)
45
51
  # args_only(*args)
46
52
  # kwargs_only(**kwargs)
53
+ # block_only(&block)
47
54
  # end
48
55
  #
49
56
  # # good
50
- # def foo(*, **)
57
+ # def foo(*, **, &)
51
58
  # args_only(*)
52
59
  # kwargs_only(**)
60
+ # block_only(&)
53
61
  # end
54
62
  #
55
63
  # @example UseAnonymousForwarding: false (only relevant for Ruby >= 3.2)
56
64
  # # good
57
- # def foo(*args, **kwargs)
65
+ # def foo(*args, **kwargs, &block)
58
66
  # args_only(*args)
59
67
  # kwargs_only(**kwargs)
68
+ # block_only(&block)
60
69
  # end
61
70
  #
62
71
  # @example AllowOnlyRestArgument: true (default, only relevant for Ruby < 3.2)
@@ -104,7 +113,7 @@ module RuboCop
104
113
  # end
105
114
  #
106
115
  # @example RedundantBlockArgumentNames: ['blk', 'block', 'proc'] (default)
107
- # # bad
116
+ # # bad - But it is good with `EnforcedStyle: explicit` set for `Naming/BlockForwarding`.
108
117
  # def foo(&block)
109
118
  # bar(&block)
110
119
  # end
@@ -126,6 +135,7 @@ module RuboCop
126
135
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
127
136
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
128
137
  KWARGS_MSG = 'Use anonymous keyword arguments forwarding (`**`).'
138
+ BLOCK_MSG = 'Use anonymous block arguments forwarding (`&`).'
129
139
 
130
140
  def self.autocorrect_incompatible_with
131
141
  [Naming::BlockForwarding]
@@ -171,21 +181,40 @@ module RuboCop
171
181
  send_classifications.all? { |_, c, _, _| c == :all }
172
182
  end
173
183
 
184
+ # rubocop:disable Metrics/MethodLength
174
185
  def add_forward_all_offenses(node, send_classifications, forwardable_args)
175
- send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
176
- register_forward_all_offense(send_node, send_node, forward_rest)
186
+ _rest_arg, _kwrest_arg, block_arg = *forwardable_args
187
+ registered_block_arg_offense = false
188
+
189
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
190
+ if !forward_rest && !forward_kwrest
191
+ # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
192
+ # in Ruby 3.3.0.
193
+ if outside_block?(forward_block_arg)
194
+ register_forward_block_arg_offense(!forward_rest, node.arguments, block_arg)
195
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
196
+ end
197
+ registered_block_arg_offense = true
198
+ break
199
+ else
200
+ register_forward_all_offense(send_node, send_node, forward_rest)
201
+ end
177
202
  end
178
203
 
204
+ return if registered_block_arg_offense
205
+
179
206
  rest_arg, _kwrest_arg, _block_arg = *forwardable_args
180
207
  register_forward_all_offense(node, node.arguments, rest_arg)
181
208
  end
209
+ # rubocop:enable Metrics/MethodLength
182
210
 
211
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
183
212
  def add_post_ruby_32_offenses(def_node, send_classifications, forwardable_args)
184
213
  return unless use_anonymous_forwarding?
185
214
 
186
- rest_arg, kwrest_arg, _block_arg = *forwardable_args
215
+ rest_arg, kwrest_arg, block_arg = *forwardable_args
187
216
 
188
- send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
217
+ send_classifications.each do |send_node, _c, forward_rest, forward_kwrest, forward_block_arg| # rubocop:disable Layout/LineLength
189
218
  if outside_block?(forward_rest)
190
219
  register_forward_args_offense(def_node.arguments, rest_arg)
191
220
  register_forward_args_offense(send_node, forward_rest)
@@ -195,8 +224,16 @@ module RuboCop
195
224
  register_forward_kwargs_offense(!forward_rest, def_node.arguments, kwrest_arg)
196
225
  register_forward_kwargs_offense(!forward_rest, send_node, forward_kwrest)
197
226
  end
227
+
228
+ # Prevents `anonymous block parameter is also used within block (SyntaxError)` occurs
229
+ # in Ruby 3.3.0.
230
+ if outside_block?(forward_block_arg)
231
+ register_forward_block_arg_offense(!forward_rest, def_node.arguments, block_arg)
232
+ register_forward_block_arg_offense(!forward_rest, send_node, forward_block_arg)
233
+ end
198
234
  end
199
235
  end
236
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength
200
237
 
201
238
  def non_splat_or_block_pass_lvar_references(body)
202
239
  body.each_descendant(:lvar, :lvasgn).filter_map do |lvar|
@@ -225,10 +262,7 @@ module RuboCop
225
262
 
226
263
  def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
227
264
  classifier = SendNodeClassifier.new(
228
- def_node,
229
- send_node,
230
- referenced_lvars,
231
- forwardable_args,
265
+ def_node, send_node, referenced_lvars, forwardable_args,
232
266
  target_ruby_version: target_ruby_version,
233
267
  allow_only_rest_arguments: allow_only_rest_arguments?
234
268
  )
@@ -237,7 +271,12 @@ module RuboCop
237
271
 
238
272
  return unless classification
239
273
 
240
- [classification, classifier.forwarded_rest_arg, classifier.forwarded_kwrest_arg]
274
+ [
275
+ classification,
276
+ classifier.forwarded_rest_arg,
277
+ classifier.forwarded_kwrest_arg,
278
+ classifier.forwarded_block_arg
279
+ ]
241
280
  end
242
281
 
243
282
  def redundant_named_arg(arg, config_name, keyword)
@@ -272,6 +311,16 @@ module RuboCop
272
311
  end
273
312
  end
274
313
 
314
+ def register_forward_block_arg_offense(add_parens, def_arguments_or_send, block_arg)
315
+ return if target_ruby_version <= 3.0 || block_arg.source == '&' || explicit_block_name?
316
+
317
+ add_offense(block_arg, message: BLOCK_MSG) do |corrector|
318
+ add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
319
+
320
+ corrector.replace(block_arg, '&')
321
+ end
322
+ end
323
+
275
324
  def register_forward_all_offense(def_or_send, send_or_arguments, rest_or_splat)
276
325
  arg_range = arguments_range(def_or_send, rest_or_splat)
277
326
 
@@ -441,6 +490,13 @@ module RuboCop
441
490
  (@kwrest_arg_name && !forwarded_kwrest_arg)
442
491
  end
443
492
  end
493
+
494
+ def explicit_block_name?
495
+ block_forwarding_config = config.for_cop('Naming/BlockForwarding')
496
+ return false unless block_forwarding_config['Enabled']
497
+
498
+ block_forwarding_config['EnforcedStyle'] == 'explicit'
499
+ end
444
500
  end
445
501
  end
446
502
  end
@@ -230,7 +230,7 @@ module RuboCop
230
230
 
231
231
  def branch_conditions(node)
232
232
  conditions = []
233
- while node&.if_type?
233
+ while node&.if_type? && !node.ternary?
234
234
  conditions << node.condition
235
235
  node = node.else_branch
236
236
  end
@@ -23,6 +23,8 @@ module RuboCop
23
23
  # array.reject { |e| e.nil? }
24
24
  # array.delete_if { |e| e.nil? }
25
25
  # array.select { |e| !e.nil? }
26
+ # array.grep_v(nil)
27
+ # array.grep_v(NilClass)
26
28
  #
27
29
  # # good
28
30
  # array.compact
@@ -46,7 +48,7 @@ module RuboCop
46
48
  extend TargetRubyVersion
47
49
 
48
50
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
49
- RESTRICT_ON_SEND = %i[reject delete_if reject! select select!].freeze
51
+ RESTRICT_ON_SEND = %i[reject delete_if reject! select select! grep_v].freeze
50
52
  TO_ENUM_METHODS = %i[to_enum lazy].freeze
51
53
 
52
54
  minimum_target_ruby_version 2.4
@@ -79,6 +81,11 @@ module RuboCop
79
81
  $(lvar _) :nil?) :!))
80
82
  PATTERN
81
83
 
84
+ # @!method grep_v_with_nil?(node)
85
+ def_node_matcher :grep_v_with_nil?, <<~PATTERN
86
+ (send _ :grep_v {(nil) (const {nil? cbase} :NilClass)})
87
+ PATTERN
88
+
82
89
  def on_send(node)
83
90
  return unless (range = offense_range(node))
84
91
  return if allowed_receiver?(node.receiver)
@@ -95,8 +102,9 @@ module RuboCop
95
102
 
96
103
  private
97
104
 
105
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
98
106
  def offense_range(node)
99
- if reject_method_with_block_pass?(node)
107
+ if reject_method_with_block_pass?(node) || grep_v_with_nil?(node)
100
108
  range(node, node)
101
109
  else
102
110
  block_node = node.parent
@@ -110,6 +118,7 @@ module RuboCop
110
118
  range(node, block_node)
111
119
  end
112
120
  end
121
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
113
122
 
114
123
  def to_enum_method?(node)
115
124
  return false unless node.receiver.send_type?