rubocop 1.77.0 → 1.79.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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +36 -20
  4. data/lib/rubocop/cli.rb +12 -1
  5. data/lib/rubocop/config_loader.rb +1 -38
  6. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  7. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  8. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  9. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
  10. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  11. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  12. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  13. data/lib/rubocop/cop/lint/literal_as_condition.rb +3 -1
  14. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  15. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  16. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  17. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  18. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  19. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  20. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  21. data/lib/rubocop/cop/naming/method_name.rb +102 -13
  22. data/lib/rubocop/cop/naming/predicate_method.rb +27 -2
  23. data/lib/rubocop/cop/security/eval.rb +2 -1
  24. data/lib/rubocop/cop/security/open.rb +1 -0
  25. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  26. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  27. data/lib/rubocop/cop/style/array_intersect.rb +51 -23
  28. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  29. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  30. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  31. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  32. data/lib/rubocop/cop/style/hash_conversion.rb +8 -9
  33. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  34. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  35. data/lib/rubocop/cop/style/it_block_parameter.rb +3 -1
  36. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  37. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  38. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -0
  39. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  40. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  41. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  42. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -0
  43. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  44. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  45. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  46. data/lib/rubocop/cop/variable_force.rb +16 -7
  47. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  48. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  49. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  50. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  51. data/lib/rubocop/server/cache.rb +4 -2
  52. data/lib/rubocop/server/client_command/base.rb +10 -0
  53. data/lib/rubocop/server/client_command/exec.rb +2 -1
  54. data/lib/rubocop/server/client_command/start.rb +11 -1
  55. data/lib/rubocop/version.rb +1 -1
  56. data/lib/rubocop.rb +3 -0
  57. metadata +23 -6
@@ -5,12 +5,13 @@ module RuboCop
5
5
  module Style
6
6
  # In Ruby 3.1, `Array#intersect?` has been added.
7
7
  #
8
- # This cop identifies places where `(array1 & array2).any?`
9
- # or `(array1.intersection(array2)).any?` can be replaced by
10
- # `array1.intersect?(array2)`.
8
+ # This cop identifies places where:
9
+ # * `(array1 & array2).any?`
10
+ # * `(array1.intersection(array2)).any?`
11
+ # * `array1.any? { |elem| array2.member?(elem) }`
12
+ # can be replaced with `array1.intersect?(array2)`.
11
13
  #
12
- # The `array1.intersect?(array2)` method is faster than
13
- # `(array1 & array2).any?` and is more readable.
14
+ # `array1.intersect?(array2)` is faster and more readable.
14
15
  #
15
16
  # In cases like the following, compatibility is not ensured,
16
17
  # so it will not be detected when using block argument.
@@ -40,6 +41,10 @@ module RuboCop
40
41
  # array1.intersection(array2).empty?
41
42
  # array1.intersection(array2).none?
42
43
  #
44
+ # # bad
45
+ # array1.any? { |elem| array2.member?(elem) }
46
+ # array1.none? { |elem| array2.member?(elem) }
47
+ #
43
48
  # # good
44
49
  # array1.intersect?(array2)
45
50
  # !array1.intersect?(array2)
@@ -77,8 +82,26 @@ module RuboCop
77
82
  )
78
83
  PATTERN
79
84
 
80
- MSG = 'Use `%<negated>s%<receiver>s%<dot>sintersect?(%<argument>s)` ' \
81
- 'instead of `%<existing>s`.'
85
+ # @!method any_none_block_intersection(node)
86
+ def_node_matcher :any_none_block_intersection, <<~PATTERN
87
+ {
88
+ (block
89
+ (call $_receiver ${:any? :none?})
90
+ (args (arg _key))
91
+ (send $_argument :member? (lvar _key))
92
+ )
93
+ (numblock
94
+ (call $_receiver ${:any? :none?}) 1
95
+ (send $_argument :member? (lvar :_1))
96
+ )
97
+ (itblock
98
+ (call $_receiver ${:any? :none?}) :it
99
+ (send $_argument :member? (lvar :it))
100
+ )
101
+ }
102
+ PATTERN
103
+
104
+ MSG = 'Use `%<replacement>s` instead of `%<existing>s`.'
82
105
  STRAIGHT_METHODS = %i[present? any?].freeze
83
106
  NEGATED_METHODS = %i[blank? empty? none?].freeze
84
107
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
@@ -88,16 +111,25 @@ module RuboCop
88
111
  return unless (receiver, argument, method_name = bad_intersection?(node))
89
112
 
90
113
  dot = node.loc.dot.source
91
- message = message(receiver.source, argument.source, method_name, dot, node.source)
114
+ bang = straight?(method_name) ? '' : '!'
115
+ replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
92
116
 
93
- add_offense(node, message: message) do |corrector|
94
- bang = straight?(method_name) ? '' : '!'
95
-
96
- corrector.replace(node, "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})")
97
- end
117
+ register_offense(node, replacement)
98
118
  end
99
119
  alias on_csend on_send
100
120
 
121
+ def on_block(node)
122
+ return unless (receiver, method_name, argument = any_none_block_intersection(node))
123
+
124
+ dot = node.send_node.loc.dot.source
125
+ bang = method_name == :any? ? '' : '!'
126
+ replacement = "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})"
127
+
128
+ register_offense(node, replacement)
129
+ end
130
+ alias on_numblock on_block
131
+ alias on_itblock on_block
132
+
101
133
  private
102
134
 
103
135
  def bad_intersection?(node)
@@ -114,16 +146,12 @@ module RuboCop
114
146
  STRAIGHT_METHODS.include?(method_name.to_sym)
115
147
  end
116
148
 
117
- def message(receiver, argument, method_name, dot, existing)
118
- negated = straight?(method_name) ? '' : '!'
119
- format(
120
- MSG,
121
- negated: negated,
122
- receiver: receiver,
123
- argument: argument,
124
- dot: dot,
125
- existing: existing
126
- )
149
+ def register_offense(node, replacement)
150
+ message = format(MSG, replacement: replacement, existing: node.source)
151
+
152
+ add_offense(node, message: message) do |corrector|
153
+ corrector.replace(node, replacement)
154
+ end
127
155
  end
128
156
  end
129
157
  end
@@ -4,7 +4,7 @@
4
4
  module RuboCop
5
5
  module Cop
6
6
  module Style
7
- # Check for uses of braces or do/end around single line or
7
+ # Checks for uses of braces or do/end around single line or
8
8
  # multi-line blocks.
9
9
  #
10
10
  # Methods that can be either procedural or functional and cannot be
@@ -111,7 +111,7 @@ module RuboCop
111
111
  end
112
112
  end
113
113
 
114
- # Check for `if` and `case` statements where each branch is used for
114
+ # Checks for `if` and `case` statements where each branch is used for
115
115
  # both the assignment and comparison of the same variable
116
116
  # when using the return of the condition can be used instead.
117
117
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for chained `dig` calls that can be collapsed into a single `dig`.
6
+ # Checks for chained `dig` calls that can be collapsed into a single `dig`.
7
7
  #
8
8
  # @safety
9
9
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -59,6 +59,7 @@ module RuboCop
59
59
  #
60
60
  class ExponentialNotation < Base
61
61
  include ConfigurableEnforcedStyle
62
+
62
63
  MESSAGES = {
63
64
  scientific: 'Use a mantissa >= 1 and < 10.',
64
65
  engineering: 'Use an exponent divisible by 3 and a mantissa >= 0.1 and < 1000.',
@@ -44,10 +44,10 @@ module RuboCop
44
44
  class HashConversion < Base
45
45
  extend AutoCorrector
46
46
 
47
- MSG_TO_H = 'Prefer ary.to_h to Hash[ary].'
48
- MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to Hash[arg1, arg2, ...].'
49
- MSG_LITERAL_HASH_ARG = 'Prefer literal hash to Hash[key: value, ...].'
50
- MSG_SPLAT = 'Prefer array_of_pairs.to_h to Hash[*array].'
47
+ MSG_TO_H = 'Prefer `ary.to_h` to `Hash[ary]`.'
48
+ MSG_LITERAL_MULTI_ARG = 'Prefer literal hash to `Hash[arg1, arg2, ...]`.'
49
+ MSG_LITERAL_HASH_ARG = 'Prefer literal hash to `Hash[key: value, ...]`.'
50
+ MSG_SPLAT = 'Prefer `array_of_pairs.to_h` to `Hash[*array]`.'
51
51
  RESTRICT_ON_SEND = %i[[]].freeze
52
52
 
53
53
  # @!method hash_from_array?(node)
@@ -64,11 +64,11 @@ module RuboCop
64
64
  # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
65
65
  # ...but don't suggest correction if there is odd number of them (it is a bug)
66
66
  node.arguments.one? ? single_argument(node) : multi_argument(node)
67
+ ignore_node(node)
67
68
  end
68
69
 
69
70
  private
70
71
 
71
- # rubocop:disable Metrics/MethodLength
72
72
  def single_argument(node)
73
73
  first_argument = node.first_argument
74
74
  if first_argument.hash_type?
@@ -83,11 +83,8 @@ module RuboCop
83
83
  replacement = "(#{replacement})" if requires_parens?(first_argument)
84
84
  corrector.replace(node, "#{replacement}.to_h")
85
85
  end
86
-
87
- ignore_node(node)
88
86
  end
89
87
  end
90
- # rubocop:enable Metrics/MethodLength
91
88
 
92
89
  def use_zip_method_without_argument?(first_argument)
93
90
  return false unless first_argument&.send_type?
@@ -131,7 +128,9 @@ module RuboCop
131
128
  corrector.replace(node, args_to_hash(node.arguments))
132
129
 
133
130
  parent = node.parent
134
- add_parentheses(parent, corrector) if parent&.send_type? && !parent.parenthesized?
131
+ if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
132
+ add_parentheses(parent, corrector)
133
+ end
135
134
  end
136
135
  end
137
136
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for usages of not (`not` or `!`) called on a method
6
+ # Checks for usages of not (`not` or `!`) called on a method
7
7
  # when an inverse of that method can be used instead.
8
8
  #
9
9
  # Methods that can be inverted by a not (`not` or `!`) should be defined
@@ -3,33 +3,90 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for assignments to a local `it` variable inside a block
6
+ # Checks for local variables and method parameters named `it`,
7
7
  # where `it` can refer to the first anonymous parameter as of Ruby 3.4.
8
+ # Use a meaningful variable name instead.
8
9
  #
9
- # Although Ruby allows reassigning `it` in these cases, it could
10
+ # NOTE: Although Ruby allows reassigning `it` in these cases, it could
10
11
  # cause confusion if `it` is used as a block parameter elsewhere.
11
- # For consistency, this also applies to numblocks and blocks with
12
- # parameters, even though `it` cannot be used in those cases.
13
12
  #
14
13
  # @example
15
14
  # # bad
16
- # foo { it = 5 }
17
- # foo { |bar| it = bar }
18
- # foo { it = _2 }
15
+ # it = 5
19
16
  #
20
- # # good - use a different variable name
21
- # foo { var = 5 }
22
- # foo { |bar| var = bar }
23
- # foo { bar = _2 }
17
+ # # good
18
+ # var = 5
19
+ #
20
+ # # bad
21
+ # def foo(it)
22
+ # end
23
+ #
24
+ # # good
25
+ # def foo(arg)
26
+ # end
27
+ #
28
+ # # bad
29
+ # def foo(it = 5)
30
+ # end
31
+ #
32
+ # # good
33
+ # def foo(arg = 5)
34
+ # end
35
+ #
36
+ # # bad
37
+ # def foo(*it)
38
+ # end
39
+ #
40
+ # # good
41
+ # def foo(*args)
42
+ # end
43
+ #
44
+ # # bad
45
+ # def foo(it:)
46
+ # end
47
+ #
48
+ # # good
49
+ # def foo(arg:)
50
+ # end
51
+ #
52
+ # # bad
53
+ # def foo(it: 5)
54
+ # end
55
+ #
56
+ # # good
57
+ # def foo(arg: 5)
58
+ # end
59
+ #
60
+ # # bad
61
+ # def foo(**it)
62
+ # end
63
+ #
64
+ # # good
65
+ # def foo(**kwargs)
66
+ # end
67
+ #
68
+ # # bad
69
+ # def foo(&it)
70
+ # end
71
+ #
72
+ # # good
73
+ # def foo(&block)
74
+ # end
24
75
  class ItAssignment < Base
25
76
  MSG = '`it` is the default block parameter; consider another name.'
26
77
 
27
78
  def on_lvasgn(node)
28
79
  return unless node.name == :it
29
- return unless node.each_ancestor(:any_block).any?
30
80
 
31
81
  add_offense(node.loc.name)
32
82
  end
83
+ alias on_arg on_lvasgn
84
+ alias on_optarg on_lvasgn
85
+ alias on_restarg on_lvasgn
86
+ alias on_blockarg on_lvasgn
87
+ alias on_kwarg on_lvasgn
88
+ alias on_kwoptarg on_lvasgn
89
+ alias on_kwrestarg on_lvasgn
33
90
  end
34
91
  end
35
92
  end
@@ -109,7 +109,9 @@ module RuboCop
109
109
  private
110
110
 
111
111
  def find_block_variables(node, block_argument_name)
112
- node.each_descendant(:lvar).select do |descendant|
112
+ return [] unless node.body
113
+
114
+ node.body.each_descendant(:lvar).select do |descendant|
113
115
  descendant.source == block_argument_name
114
116
  end
115
117
  end
@@ -222,11 +222,9 @@ module RuboCop
222
222
  end
223
223
 
224
224
  def unary_literal?(node)
225
- # NOTE: should be removed after releasing https://github.com/rubocop/rubocop-ast/pull/379
226
- return node.source.match?(/\A[+-]/) if node.complex_type?
225
+ return true if node.numeric_type? && node.sign?
227
226
 
228
- (node.numeric_type? && node.sign?) ||
229
- (node.parent&.send_type? && node.parent.unary_operation?)
227
+ node.parent&.send_type? && node.parent.unary_operation?
230
228
  end
231
229
 
232
230
  def assigned_before?(node, target)
@@ -132,6 +132,22 @@ module RuboCop
132
132
  # bar :baz
133
133
  # end
134
134
  #
135
+ # @example AllowedMethods: ["puts", "print"]
136
+ #
137
+ # # good
138
+ # puts "Hello world"
139
+ # print "Hello world"
140
+ # # still enforces parentheses on other methods
141
+ # array.delete(e)
142
+ #
143
+ # @example AllowedPatterns: ["^assert"]
144
+ #
145
+ # # good
146
+ # assert_equal 'test', x
147
+ # assert_match(/foo/, bar)
148
+ # # still enforces parentheses on other methods
149
+ # array.delete(e)
150
+ #
135
151
  # @example AllowParenthesesInMultilineCall: false (default)
136
152
  #
137
153
  # # bad
@@ -29,6 +29,8 @@ module RuboCop
29
29
  MSG = 'Do not use parallel assignment.'
30
30
 
31
31
  def on_masgn(node) # rubocop:disable Metrics/AbcSize
32
+ return if part_of_ignored_node?(node)
33
+
32
34
  rhs = node.rhs
33
35
  rhs = rhs.body if rhs.rescue_type?
34
36
  rhs_elements = Array(rhs).compact # edge case for one constant
@@ -41,6 +43,7 @@ module RuboCop
41
43
  add_offense(range) do |corrector|
42
44
  autocorrect(corrector, node, rhs)
43
45
  end
46
+ ignore_node(node)
44
47
  end
45
48
 
46
49
  private
@@ -49,7 +49,7 @@ module RuboCop
49
49
  (block
50
50
  $(call _ :fetch _)
51
51
  (args)
52
- ${nil? #basic_literal? #const_type?})
52
+ ${nil? basic_literal? const_type?})
53
53
  PATTERN
54
54
 
55
55
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
@@ -71,14 +71,6 @@ module RuboCop
71
71
 
72
72
  private
73
73
 
74
- def basic_literal?(node)
75
- node&.basic_literal?
76
- end
77
-
78
- def const_type?(node)
79
- node&.const_type?
80
- end
81
-
82
74
  def should_not_check?(send, body)
83
75
  (body&.const_type? && !check_for_constant?) ||
84
76
  (body&.str_type? && !check_for_string?) ||
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for uses of `Object#freeze` on immutable objects.
6
+ # Checks for uses of `Object#freeze` on immutable objects.
7
7
  #
8
8
  # NOTE: `Regexp` and `Range` literals are frozen objects since Ruby 3.0.
9
9
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for redundant line continuation.
6
+ # Checks for redundant line continuation.
7
7
  #
8
8
  # This cop marks a line continuation as redundant if removing the backslash
9
9
  # does not result in a syntax error.
@@ -169,6 +169,7 @@ module RuboCop
169
169
  end
170
170
  return 'an interpolated expression' if interpolation?(begin_node)
171
171
  return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
172
+ return 'a one-line rescue' if !begin_node.parent&.call_type? && node.rescue_type?
172
173
 
173
174
  return if begin_node.chained?
174
175
 
@@ -65,7 +65,7 @@ module RuboCop
65
65
  return false if target_ruby_version < 3.0
66
66
  return false if disallow_endless_method_style?
67
67
  return false unless body_node
68
- return false if body_node.parent.assignment_method? ||
68
+ return false if body_node.basic_conditional? || body_node.parent.assignment_method? ||
69
69
  NOT_SUPPORTED_ENDLESS_METHOD_BODY_TYPES.include?(body_node.type)
70
70
 
71
71
  !body_node.type?(:begin, :kwbegin)
@@ -86,10 +86,10 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def correct_to_endless(corrector, node)
89
- self_receiver = node.self_receiver? ? 'self.' : ''
89
+ receiver = "#{node.receiver.source}." if node.receiver
90
90
  arguments = node.arguments.any? ? node.arguments.source : '()'
91
91
  body_source = method_body_source(node.body)
92
- replacement = "def #{self_receiver}#{node.method_name}#{arguments} = #{body_source}"
92
+ replacement = "def #{receiver}#{node.method_name}#{arguments} = #{body_source}"
93
93
 
94
94
  corrector.replace(node, replacement)
95
95
  end
@@ -130,7 +130,10 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def require_parentheses?(method_body)
133
- method_body.send_type? && !method_body.arguments.empty? && !method_body.comparison_method?
133
+ return false unless method_body.send_type?
134
+ return false if method_body.arithmetic_operation?
135
+
136
+ !method_body.arguments.empty? && !method_body.comparison_method?
134
137
  end
135
138
 
136
139
  def disallow_endless_method_style?
@@ -175,6 +175,8 @@ module RuboCop
175
175
 
176
176
  if parenthesize_method?(condition)
177
177
  parenthesized_method_arguments(condition)
178
+ elsif condition.and_type?
179
+ parenthesized_and(condition)
178
180
  else
179
181
  "(#{condition.source})"
180
182
  end
@@ -186,12 +188,19 @@ module RuboCop
186
188
  end
187
189
 
188
190
  def add_parentheses?(node)
189
- return true if node.assignment? || (node.operator_keyword? && !node.and_type?)
191
+ return true if node.assignment? || node.or_type?
192
+ return true if assignment_in_and?(node)
190
193
  return false unless node.call_type?
191
194
 
192
195
  (node.arguments.any? && !node.parenthesized?) || node.prefix_not?
193
196
  end
194
197
 
198
+ def assignment_in_and?(node)
199
+ return false unless node.and_type?
200
+
201
+ node.each_descendant.any?(&:assignment?)
202
+ end
203
+
195
204
  def parenthesized_method_arguments(node)
196
205
  method_call = node.source_range.begin.join(node.loc.selector.end).source
197
206
  arguments = node.first_argument.source_range.begin.join(node.source_range.end).source
@@ -199,6 +208,26 @@ module RuboCop
199
208
  "#{method_call}(#{arguments})"
200
209
  end
201
210
 
211
+ def parenthesized_and(node)
212
+ # We only need to add parentheses around the last clause if it's an assignment,
213
+ # because other clauses will be unchanged by merging conditionals.
214
+ lhs = node.lhs.source
215
+ rhs = parenthesized_and_clause(node.rhs)
216
+ operator = range_with_surrounding_space(node.loc.operator, whitespace: true).source
217
+
218
+ "#{lhs}#{operator}#{rhs}"
219
+ end
220
+
221
+ def parenthesized_and_clause(node)
222
+ if node.and_type?
223
+ parenthesized_and(node)
224
+ elsif node.assignment?
225
+ "(#{node.source})"
226
+ else
227
+ node.source
228
+ end
229
+ end
230
+
202
231
  def allow_modifier?
203
232
  cop_config['AllowModifier']
204
233
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Check for parentheses around stabby lambda arguments.
6
+ # Checks for parentheses around stabby lambda arguments.
7
7
  # There are two different styles. Defaults to `require_parentheses`.
8
8
  #
9
9
  # @example EnforcedStyle: require_parentheses (default)
@@ -296,7 +296,7 @@ module RuboCop
296
296
  variable_table.accessible_variables.each { |variable| variable.reference!(node) }
297
297
  end
298
298
 
299
- # Mark all assignments which are referenced in the same loop
299
+ # Mark last assignments which are referenced in the same loop
300
300
  # as referenced by ignoring AST order since they would be referenced
301
301
  # in next iteration.
302
302
  def mark_assignments_as_referenced_in_loop(node)
@@ -308,13 +308,12 @@ module RuboCop
308
308
  # would be skipped here.
309
309
  next unless variable
310
310
 
311
- variable.assignments.each do |assignment|
312
- next if assignment_nodes_in_loop.none? do |assignment_node|
313
- assignment_node.equal?(assignment.node)
314
- end
315
-
316
- assignment.reference!(node)
311
+ loop_assignments = variable.assignments.select do |assignment|
312
+ assignment_nodes_in_loop.include?(assignment.node)
317
313
  end
314
+ next unless loop_assignments.any?
315
+
316
+ reference_assignments(loop_assignments, node)
318
317
  end
319
318
  end
320
319
 
@@ -354,6 +353,16 @@ module RuboCop
354
353
  end
355
354
  end
356
355
 
356
+ def reference_assignments(loop_assignments, node)
357
+ # If inside a case statement, mark all as referenced.
358
+ # Otherwise, mark only the last assignment as referenced.
359
+ if loop_assignments.first.node.each_ancestor(:case, :case_match).any?
360
+ loop_assignments.each { |assignment| assignment.reference!(node) }
361
+ else
362
+ loop_assignments.last&.reference!(node)
363
+ end
364
+ end
365
+
357
366
  def scanned_node?(node)
358
367
  scanned_nodes.include?(node)
359
368
  end
@@ -7,6 +7,7 @@ require 'yard'
7
7
  # @api private
8
8
  class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
9
9
  include ::RuboCop::Cop::Documentation
10
+
10
11
  CopData = Struct.new(
11
12
  :cop, :description, :example_objects, :safety_objects, :see_objects, :config, keyword_init: true
12
13
  )
@@ -6,6 +6,7 @@ module RuboCop
6
6
  class MarkdownFormatter < BaseFormatter
7
7
  include TextUtil
8
8
  include PathUtil
9
+
9
10
  attr_reader :files, :summary
10
11
 
11
12
  def initialize(output, options = {})
@@ -9,6 +9,7 @@ module RuboCop
9
9
  # https://github.com/go-labs/rspec_pacman_formatter
10
10
  class PacmanFormatter < ClangStyleFormatter
11
11
  include TextUtil
12
+
12
13
  attr_accessor :progress_line
13
14
 
14
15
  FALLBACK_TERMINAL_WIDTH = 80
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # Reports information about pending cops that are not explicitly configured.
5
+ #
6
+ # This class is responsible for displaying warnings when new cops have been added to RuboCop
7
+ # but have not yet been enabled or disabled in the user's configuration.
8
+ # It provides a centralized way to determine whether such warnings should be shown,
9
+ # based on global flags or configuration settings.
10
+ class PendingCopsReporter
11
+ class << self
12
+ PENDING_BANNER = <<~BANNER
13
+ The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
14
+
15
+ Please also note that you can opt-in to new cops by default by adding this to your config:
16
+ AllCops:
17
+ NewCops: enable
18
+ BANNER
19
+
20
+ attr_accessor :disable_pending_cops, :enable_pending_cops
21
+
22
+ def warn_if_needed(config)
23
+ return if possible_new_cops?(config)
24
+
25
+ pending_cops = pending_cops_only_qualified(config.pending_cops)
26
+ warn_on_pending_cops(pending_cops) unless pending_cops.empty?
27
+ end
28
+
29
+ private
30
+
31
+ def pending_cops_only_qualified(pending_cops)
32
+ pending_cops.select { |cop| Cop::Registry.qualified_cop?(cop.name) }
33
+ end
34
+
35
+ def possible_new_cops?(config)
36
+ disable_pending_cops || enable_pending_cops ||
37
+ config.disabled_new_cops? || config.enabled_new_cops?
38
+ end
39
+
40
+ def warn_on_pending_cops(pending_cops)
41
+ warn Rainbow(PENDING_BANNER).yellow
42
+
43
+ pending_cops.each { |cop| warn_pending_cop cop }
44
+
45
+ warn Rainbow('For more information: https://docs.rubocop.org/rubocop/versioning.html').yellow
46
+ end
47
+
48
+ def warn_pending_cop(cop)
49
+ version = cop.metadata['VersionAdded'] || 'N/A'
50
+
51
+ warn Rainbow("#{cop.name}: # new in #{version}").yellow
52
+ warn Rainbow(' Enabled: true').yellow
53
+ end
54
+ end
55
+ end
56
+ end