rubocop 1.77.0 → 1.78.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 871a632c96e6a5c92e57b651037eaf7efd7412e64ac77440d5275027ef791d4e
4
- data.tar.gz: f375ec6479b1f90193cd39e863411707d60cd071e170888005339d89a19ea65b
3
+ metadata.gz: 5b840814034fbb98e67e3bb60014f0f00f9387148ab85b8b66f3a7ca711efdd2
4
+ data.tar.gz: b223e62eeeef5effd425654bf869ea22935beb8955df6e6fb22f0ba2a623d0f3
5
5
  SHA512:
6
- metadata.gz: f8dac3eb5cb12741c110c8726ec64595cefb89700527144cc590d7febb676164e3902dff171594278f7895d1c3df336dea3e14f869d285a0c7080b1467bd6362
7
- data.tar.gz: 4aadc7a712774810e1815f4f31bea43fc6d9e121a547621f57894c7df848a6459dfc962af0fb725f4fef6e2017b6a499c203cc05a4d2879d0e50aceaee156b95
6
+ metadata.gz: 83f46c7462c956b9a37e61feb329b3dfdb96e631cab64ffa3d1485bdc2d36a3585dacc17fc0b546ca98a0ad1014e1818eabf328a367654ad0151eaea3eea7369
7
+ data.tar.gz: d7cd91419c2ff881ef14b5f4406c9b87e555d2948c2c20b98bf02e6007e44b3d20b0aa73f3b91a595c5613164e551ba5f728e0852d084d04fe37312a65fa2b96
data/README.md CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
53
53
  in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 1.77', require: false
56
+ gem 'rubocop', '~> 1.78', require: false
57
57
  ```
58
58
 
59
59
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -3072,7 +3072,7 @@ Naming/PredicateMethod:
3072
3072
  Description: 'Checks that predicate methods end with `?` and non-predicate methods do not.'
3073
3073
  Enabled: pending
3074
3074
  VersionAdded: '1.76'
3075
- VersionChanged: '1.76'
3075
+ VersionChanged: '1.78'
3076
3076
  # In `aggressive` mode, the cop will register an offense for predicate methods that
3077
3077
  # may return a non-boolean value.
3078
3078
  # In `conservative` mode, the cop will *not* register an offense for predicate methods
@@ -3082,6 +3082,9 @@ Naming/PredicateMethod:
3082
3082
  - call
3083
3083
  AllowedPatterns: []
3084
3084
  AllowBangMethods: false
3085
+ # Methods that are known to not return a boolean value, despite ending in `?`.
3086
+ WaywardPredicates:
3087
+ - nonzero?
3085
3088
 
3086
3089
  Naming/PredicatePrefix:
3087
3090
  Description: 'Predicate method names should not be prefixed and end with a `?`.'
data/lib/rubocop/cli.rb CHANGED
@@ -12,7 +12,7 @@ module RuboCop
12
12
  STATUS_INTERRUPTED = Signal.list['INT'] + 128
13
13
  DEFAULT_PARALLEL_OPTIONS = %i[
14
14
  color config debug display_style_guide display_time display_only_fail_level_offenses
15
- display_only_failed editor_mode except extra_details fail_level fix_layout format
15
+ display_only_failed editor_mode except extra_details fail_level fix_layout format formatters
16
16
  ignore_disable_comments lint only only_guide_cops require safe
17
17
  autocorrect safe_autocorrect autocorrect_all
18
18
  ].freeze
@@ -48,6 +48,7 @@ module RuboCop
48
48
  validate_options_vs_config
49
49
  parallel_by_default!
50
50
  apply_default_formatter
51
+ report_pending_cops
51
52
  execute_runners
52
53
  end
53
54
  end
@@ -155,6 +156,7 @@ module RuboCop
155
156
 
156
157
  def act_on_options
157
158
  set_options_to_config_loader
159
+ set_options_to_pending_cops_reporter
158
160
  handle_editor_mode
159
161
 
160
162
  @config_store.options_config = @options[:config] if @options[:config]
@@ -179,6 +181,11 @@ module RuboCop
179
181
  ConfigLoader.ignore_unrecognized_cops = @options[:ignore_unrecognized_cops]
180
182
  end
181
183
 
184
+ def set_options_to_pending_cops_reporter
185
+ PendingCopsReporter.disable_pending_cops = @options[:disable_pending_cops]
186
+ PendingCopsReporter.enable_pending_cops = @options[:enable_pending_cops]
187
+ end
188
+
182
189
  def handle_editor_mode
183
190
  RuboCop::LSP.enable if @options[:editor_mode]
184
191
  end
@@ -208,5 +215,9 @@ module RuboCop
208
215
  [[formatter, @options[:output_path]]]
209
216
  end
210
217
  end
218
+
219
+ def report_pending_cops
220
+ PendingCopsReporter.warn_if_needed(@config_store.for_pwd)
221
+ end
211
222
  end
212
223
  end
@@ -22,14 +22,6 @@ module RuboCop
22
22
  class << self
23
23
  include FileFinder
24
24
 
25
- PENDING_BANNER = <<~BANNER
26
- The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
27
-
28
- Please also note that you can opt-in to new cops by default by adding this to your config:
29
- AllCops:
30
- NewCops: enable
31
- BANNER
32
-
33
25
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
34
26
  :ignore_unrecognized_cops
35
27
  attr_writer :default_configuration
@@ -132,21 +124,7 @@ module RuboCop
132
124
  add_excludes_from_files(config, config_file)
133
125
  end
134
126
 
135
- merge_with_default(config, config_file).tap do |merged_config|
136
- unless possible_new_cops?(merged_config)
137
- pending_cops = pending_cops_only_qualified(merged_config.pending_cops)
138
- warn_on_pending_cops(pending_cops) unless pending_cops.empty?
139
- end
140
- end
141
- end
142
-
143
- def pending_cops_only_qualified(pending_cops)
144
- pending_cops.select { |cop| Cop::Registry.qualified_cop?(cop.name) }
145
- end
146
-
147
- def possible_new_cops?(config)
148
- disable_pending_cops || enable_pending_cops ||
149
- config.disabled_new_cops? || config.enabled_new_cops?
127
+ merge_with_default(config, config_file)
150
128
  end
151
129
 
152
130
  def add_excludes_from_files(config, config_file)
@@ -208,21 +186,6 @@ module RuboCop
208
186
  ConfigFinder.project_root
209
187
  end
210
188
 
211
- def warn_on_pending_cops(pending_cops)
212
- warn Rainbow(PENDING_BANNER).yellow
213
-
214
- pending_cops.each { |cop| warn_pending_cop cop }
215
-
216
- warn Rainbow('For more information: https://docs.rubocop.org/rubocop/versioning.html').yellow
217
- end
218
-
219
- def warn_pending_cop(cop)
220
- version = cop.metadata['VersionAdded'] || 'N/A'
221
-
222
- warn Rainbow("#{cop.name}: # new in #{version}").yellow
223
- warn Rainbow(' Enabled: true').yellow
224
- end
225
-
226
189
  # Merges the given configuration with the default one.
227
190
  def merge_with_default(config, config_file, unset_nil: true)
228
191
  resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
@@ -46,7 +46,7 @@ module RuboCop
46
46
  /\A(does not|doesn't) (register|find|flag|report)/ => 'registers',
47
47
  /\A(does not|doesn't) add (a|an|any )?offense/ => 'registers an offense',
48
48
  /\Aregisters no offense/ => 'registers an offense',
49
- /\A(accepts|register)\b/ => 'registers'
49
+ /\A(accepts|allows|register)\b/ => 'registers'
50
50
  }.freeze
51
51
 
52
52
  EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING = {
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # Checks for duplicated instance (or singleton) method
7
7
  # definitions.
8
8
  #
9
+ # NOTE: Aliasing a method to itself is allowed, as it indicates that
10
+ # the developer intends to suppress Ruby's method redefinition warnings.
11
+ # See https://bugs.ruby-lang.org/issues/13574.
12
+ #
9
13
  # @example
10
14
  #
11
15
  # # bad
@@ -40,6 +44,18 @@ module RuboCop
40
44
  #
41
45
  # alias bar foo
42
46
  #
47
+ # # good
48
+ # alias foo foo
49
+ # def foo
50
+ # 1
51
+ # end
52
+ #
53
+ # # good
54
+ # alias_method :foo, :foo
55
+ # def foo
56
+ # 1
57
+ # end
58
+ #
43
59
  # @example AllCops:ActiveSupportExtensionsEnabled: false (default)
44
60
  #
45
61
  # # good
@@ -113,11 +129,13 @@ module RuboCop
113
129
 
114
130
  # @!method method_alias?(node)
115
131
  def_node_matcher :method_alias?, <<~PATTERN
116
- (alias (sym $_name) sym)
132
+ (alias (sym $_name) (sym $_original_name))
117
133
  PATTERN
118
134
 
119
135
  def on_alias(node)
120
- return unless (name = method_alias?(node))
136
+ name, original_name = method_alias?(node)
137
+ return unless name && original_name
138
+ return if name == original_name
121
139
  return if node.ancestors.any?(&:if_type?)
122
140
 
123
141
  found_instance_method(node, name)
@@ -125,7 +143,7 @@ module RuboCop
125
143
 
126
144
  # @!method alias_method?(node)
127
145
  def_node_matcher :alias_method?, <<~PATTERN
128
- (send nil? :alias_method (sym $_name) _)
146
+ (send nil? :alias_method (sym $_name) (sym $_original_name))
129
147
  PATTERN
130
148
 
131
149
  # @!method delegate_method?(node)
@@ -140,7 +158,10 @@ module RuboCop
140
158
  def_node_matcher :sym_name, '(sym $_name)'
141
159
 
142
160
  def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
143
- if (name = alias_method?(node))
161
+ name, original_name = alias_method?(node)
162
+
163
+ if name && original_name
164
+ return if name == original_name
144
165
  return if node.ancestors.any?(&:if_type?)
145
166
 
146
167
  found_instance_method(node, name)
@@ -123,7 +123,9 @@ module RuboCop
123
123
  # rubocop:enable Metrics/AbcSize
124
124
 
125
125
  def on_case(case_node)
126
- if case_node.condition
126
+ if (cond = case_node.condition)
127
+ return if !cond.falsey_literal? && !cond.truthy_literal?
128
+
127
129
  check_case(case_node)
128
130
  else
129
131
  case_node.when_branches.each do |when_node|
@@ -185,9 +185,9 @@ module RuboCop
185
185
  (hash (pair (sym :exception) false))
186
186
  PATTERN
187
187
 
188
- # rubocop:disable Metrics/AbcSize
188
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
189
189
  def on_send(node)
190
- return if hash_or_set_with_block?(node)
190
+ return if node.arguments.any? || hash_or_set_with_block?(node)
191
191
 
192
192
  receiver = find_receiver(node)
193
193
  return unless literal_receiver?(node, receiver) ||
@@ -198,10 +198,10 @@ module RuboCop
198
198
  message = format(MSG, method: node.method_name)
199
199
 
200
200
  add_offense(node.loc.selector, message: message) do |corrector|
201
- corrector.remove(node.loc.dot.join(node.loc.selector))
201
+ corrector.remove(node.loc.dot.join(node.loc.end || node.loc.selector))
202
202
  end
203
203
  end
204
- # rubocop:enable Metrics/AbcSize
204
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
205
205
  alias on_csend on_send
206
206
 
207
207
  private
@@ -39,6 +39,20 @@ module RuboCop
39
39
  # # good
40
40
  # def foo_bar; end
41
41
  #
42
+ # # bad
43
+ # define_method :fooBar do
44
+ # end
45
+ #
46
+ # # good
47
+ # define_method :foo_bar do
48
+ # end
49
+ #
50
+ # # bad
51
+ # Struct.new(:fooBar)
52
+ #
53
+ # # good
54
+ # Struct.new(:foo_bar)
55
+ #
42
56
  # @example EnforcedStyle: camelCase
43
57
  # # bad
44
58
  # def foo_bar; end
@@ -46,6 +60,20 @@ module RuboCop
46
60
  # # good
47
61
  # def fooBar; end
48
62
  #
63
+ # # bad
64
+ # define_method :foo_bar do
65
+ # end
66
+ #
67
+ # # good
68
+ # define_method :fooBar do
69
+ # end
70
+ #
71
+ # # bad
72
+ # Struct.new(:foo_bar)
73
+ #
74
+ # # good
75
+ # Struct.new(:fooBar)
76
+ #
49
77
  # @example ForbiddenIdentifiers: ['def', 'super']
50
78
  # # bad
51
79
  # def def; end
@@ -72,7 +100,46 @@ module RuboCop
72
100
  # @!method str_name(node)
73
101
  def_node_matcher :str_name, '(str $_name)'
74
102
 
103
+ # @!method new_struct?(node)
104
+ def_node_matcher :new_struct?, '(send (const {nil? cbase} :Struct) :new ...)'
105
+
75
106
  def on_send(node)
107
+ if node.method?(:define_method) || node.method?(:define_singleton_method)
108
+ handle_define_method(node)
109
+ elsif new_struct?(node)
110
+ handle_new_struct(node)
111
+ else
112
+ handle_attr_accessor(node)
113
+ end
114
+ end
115
+
116
+ def on_def(node)
117
+ return if node.operator_method? || matches_allowed_pattern?(node.method_name)
118
+
119
+ if forbidden_name?(node.method_name.to_s)
120
+ register_forbidden_name(node)
121
+ else
122
+ check_name(node, node.method_name, node.loc.name)
123
+ end
124
+ end
125
+ alias on_defs on_def
126
+
127
+ private
128
+
129
+ def handle_define_method(node)
130
+ return unless node.first_argument&.type?(:str, :sym)
131
+
132
+ handle_method_name(node, node.first_argument.value)
133
+ end
134
+
135
+ def handle_new_struct(node)
136
+ arguments = node.first_argument&.str_type? ? node.arguments[1..] : node.arguments
137
+ arguments.select { |argument| argument.type?(:sym, :str) }.each do |name|
138
+ handle_method_name(name, name.value)
139
+ end
140
+ end
141
+
142
+ def handle_attr_accessor(node)
76
143
  return unless (attrs = node.attribute_accessor?)
77
144
 
78
145
  attrs.last.each do |name_item|
@@ -87,45 +154,53 @@ module RuboCop
87
154
  end
88
155
  end
89
156
 
90
- def on_def(node)
91
- return if node.operator_method? || matches_allowed_pattern?(node.method_name)
157
+ def handle_method_name(node, name)
158
+ return if !name || matches_allowed_pattern?(name)
92
159
 
93
- if forbidden_name?(node.method_name.to_s)
160
+ if forbidden_name?(name.to_s)
94
161
  register_forbidden_name(node)
95
162
  else
96
- check_name(node, node.method_name, node.loc.name)
163
+ check_name(node, name, range_position(node))
97
164
  end
98
165
  end
99
- alias on_defs on_def
100
-
101
- private
102
166
 
103
167
  def forbidden_name?(name)
104
168
  forbidden_identifier?(name) || forbidden_pattern?(name)
105
169
  end
106
170
 
171
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
107
172
  def register_forbidden_name(node)
108
173
  if node.any_def_type?
109
174
  name_node = node.loc.name
110
175
  method_name = node.method_name
111
- else
112
- attrs = node.attribute_accessor?
176
+ elsif node.literal?
177
+ name_node = node
178
+ method_name = node.value
179
+ elsif (attrs = node.attribute_accessor?)
113
180
  name_node = attrs.last.last
114
181
  method_name = attr_name(name_node)
182
+ else
183
+ name_node = node.first_argument
184
+ method_name = node.first_argument.value
115
185
  end
116
186
  message = format(MSG_FORBIDDEN, identifier: method_name)
117
187
  add_offense(name_node, message: message)
118
188
  end
189
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
119
190
 
120
191
  def attr_name(name_item)
121
192
  sym_name(name_item) || str_name(name_item)
122
193
  end
123
194
 
124
195
  def range_position(node)
125
- selector_end_pos = node.loc.selector.end_pos + 1
126
- expr_end_pos = node.source_range.end_pos
196
+ if node.loc.respond_to?(:selector)
197
+ selector_end_pos = node.loc.selector.end_pos + 1
198
+ expr_end_pos = node.source_range.end_pos
127
199
 
128
- range_between(selector_end_pos, expr_end_pos)
200
+ range_between(selector_end_pos, expr_end_pos)
201
+ else
202
+ node.source_range
203
+ end
129
204
  end
130
205
 
131
206
  def message(style)
@@ -26,6 +26,11 @@ module RuboCop
26
26
  # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
27
27
  # configuration to allow method names by regular expression.
28
28
  #
29
+ # Although returning a call to another predicate method is treated as a boolean value,
30
+ # certain method names can be known to not return a boolean, despite ending in a `?`
31
+ # (for example, `Numeric#nonzero?` returns `self` or `nil`). These methods can be
32
+ # configured using `NonBooleanPredicates`.
33
+ #
29
34
  # The cop can furthermore be configured to allow all bang methods (method names
30
35
  # ending with `!`), with `AllowBangMethods: true` (default false).
31
36
  #
@@ -164,7 +169,7 @@ module RuboCop
164
169
  def unknown_method_call?(value)
165
170
  return false unless value.call_type?
166
171
 
167
- !value.comparison_method? && !value.predicate_method? && !value.negation_method?
172
+ !method_returning_boolean?(value)
168
173
  end
169
174
 
170
175
  def return_values(node)
@@ -190,7 +195,13 @@ module RuboCop
190
195
 
191
196
  def boolean_return?(value)
192
197
  return true if value.boolean_type?
198
+
199
+ method_returning_boolean?(value)
200
+ end
201
+
202
+ def method_returning_boolean?(value)
193
203
  return false unless value.call_type?
204
+ return false if wayward_predicate?(value.method_name)
194
205
 
195
206
  value.comparison_method? || value.predicate_method? || value.negation_method?
196
207
  end
@@ -275,6 +286,17 @@ module RuboCop
275
286
  def allow_bang_methods?
276
287
  cop_config.fetch('AllowBangMethods', false)
277
288
  end
289
+
290
+ # If a method ending in `?` is known to not return a boolean value,
291
+ # (for example, `Numeric#nonzero?`) it should be treated as a non-boolean
292
+ # value, despite the method naming.
293
+ def wayward_predicate?(name)
294
+ wayward_predicates.include?(name.to_s)
295
+ end
296
+
297
+ def wayward_predicates
298
+ Array(cop_config.fetch('WaywardPredicates', []))
299
+ end
278
300
  end
279
301
  end
280
302
  end
@@ -11,13 +11,14 @@ module RuboCop
11
11
  #
12
12
  # eval(something)
13
13
  # binding.eval(something)
14
+ # Kernel.eval(something)
14
15
  class Eval < Base
15
16
  MSG = 'The use of `eval` is a serious security risk.'
16
17
  RESTRICT_ON_SEND = %i[eval].freeze
17
18
 
18
19
  # @!method eval?(node)
19
20
  def_node_matcher :eval?, <<~PATTERN
20
- (send {nil? (send nil? :binding)} :eval $!str ...)
21
+ (send {nil? (send nil? :binding) (const {cbase nil?} :Kernel)} :eval $!str ...)
21
22
  PATTERN
22
23
 
23
24
  def on_send(node)
@@ -34,6 +34,7 @@ module RuboCop
34
34
  # # good (literal strings)
35
35
  # open("foo.text")
36
36
  # URI.open("http://example.com")
37
+ # URI.parse(url).open
37
38
  class Open < Base
38
39
  MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
39
40
  RESTRICT_ON_SEND = %i[open].freeze
@@ -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
@@ -109,7 +109,7 @@ 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
+ node.body.each_descendant(:lvar).select do |descendant|
113
113
  descendant.source == block_argument_name
114
114
  end
115
115
  end
@@ -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
@@ -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?) ||
@@ -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?
@@ -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
@@ -46,12 +46,14 @@ module RuboCop
46
46
  end
47
47
 
48
48
  # rubocop:disable Metrics/AbcSize
49
- def restart_key
49
+ def restart_key(args_config_file_path: nil)
50
50
  lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
51
51
  Pathname(project_dir).join(lockfile_name)
52
52
  end.find(&:exist?)
53
53
  version_data = lockfile_path&.read || RuboCop::Version::STRING
54
- config_data = Pathname(ConfigFinder.find_config_path(Dir.pwd)).read
54
+ config_data = Pathname(
55
+ args_config_file_path || ConfigFinder.find_config_path(Dir.pwd)
56
+ ).read
55
57
  yaml = load_erb_templated_yaml(config_data)
56
58
 
57
59
  inherit_from_data = inherit_from_data(yaml)
@@ -38,6 +38,16 @@ module RuboCop
38
38
  warn 'RuboCop server is not running.' unless running
39
39
  end
40
40
  end
41
+
42
+ class << self
43
+ def args_config_file_path
44
+ first_args_config_key_index = ARGV.index { |value| ['-c', '--config'].include?(value) }
45
+
46
+ return if first_args_config_key_index.nil?
47
+
48
+ ARGV[first_args_config_key_index + 1]
49
+ end
50
+ end
41
51
  end
42
52
  end
43
53
  end
@@ -41,7 +41,8 @@ module RuboCop
41
41
  end
42
42
 
43
43
  def incompatible_version?
44
- Cache.version_path.read != Cache.restart_key
44
+ Cache.version_path.read !=
45
+ Cache.restart_key(args_config_file_path: self.class.args_config_file_path)
45
46
  end
46
47
 
47
48
  def stderr
@@ -34,7 +34,7 @@ module RuboCop
34
34
  exit 0
35
35
  end
36
36
 
37
- Cache.write_version_file(Cache.restart_key)
37
+ write_version_file
38
38
 
39
39
  host = ENV.fetch('RUBOCOP_SERVER_HOST', '127.0.0.1')
40
40
  port = ENV.fetch('RUBOCOP_SERVER_PORT', 0)
@@ -42,6 +42,16 @@ module RuboCop
42
42
  Server::Core.new.start(host, port, detach: @detach)
43
43
  end
44
44
  end
45
+
46
+ private
47
+
48
+ def write_version_file
49
+ Cache.write_version_file(
50
+ Cache.restart_key(
51
+ args_config_file_path: self.class.args_config_file_path
52
+ )
53
+ )
54
+ end
45
55
  end
46
56
  end
47
57
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.77.0'
6
+ STRING = '1.78.0'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -812,6 +812,7 @@ require_relative 'rubocop/options'
812
812
  require_relative 'rubocop/remote_config'
813
813
  require_relative 'rubocop/target_ruby'
814
814
  require_relative 'rubocop/yaml_duplication_checker'
815
+ require_relative 'rubocop/pending_cops_reporter'
815
816
 
816
817
  # rubocop:enable Style/RequireOrder
817
818
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.77.0
4
+ version: 1.78.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -9,7 +9,7 @@ authors:
9
9
  - Yuji Nakayama
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2025-06-20 00:00:00.000000000 Z
12
+ date: 2025-07-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: json
@@ -1040,6 +1040,7 @@ files:
1040
1040
  - lib/rubocop/name_similarity.rb
1041
1041
  - lib/rubocop/options.rb
1042
1042
  - lib/rubocop/path_util.rb
1043
+ - lib/rubocop/pending_cops_reporter.rb
1043
1044
  - lib/rubocop/platform.rb
1044
1045
  - lib/rubocop/plugin.rb
1045
1046
  - lib/rubocop/plugin/configuration_integrator.rb
@@ -1087,9 +1088,9 @@ licenses:
1087
1088
  - MIT
1088
1089
  metadata:
1089
1090
  homepage_uri: https://rubocop.org/
1090
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.77.0
1091
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.78.0
1091
1092
  source_code_uri: https://github.com/rubocop/rubocop/
1092
- documentation_uri: https://docs.rubocop.org/rubocop/1.77/
1093
+ documentation_uri: https://docs.rubocop.org/rubocop/1.78/
1093
1094
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1094
1095
  rubygems_mfa_required: 'true'
1095
1096
  rdoc_options: []