rubocop 1.78.0 → 1.79.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -3
  3. data/config/default.yml +32 -19
  4. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  5. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  6. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +99 -0
  7. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  8. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  9. data/lib/rubocop/cop/lint/literal_as_condition.rb +12 -0
  10. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  11. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  12. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  13. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  14. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  15. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  16. data/lib/rubocop/cop/naming/method_name.rb +40 -1
  17. data/lib/rubocop/cop/naming/predicate_method.rb +4 -1
  18. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  19. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  20. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  21. data/lib/rubocop/cop/style/array_intersect.rb +53 -23
  22. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  23. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  24. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  25. data/lib/rubocop/cop/style/exponential_notation.rb +1 -0
  26. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  27. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  28. data/lib/rubocop/cop/style/it_block_parameter.rb +2 -0
  29. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
  30. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  31. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  32. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  33. data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -0
  34. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  35. data/lib/rubocop/cop/style/sole_nested_conditional.rb +30 -1
  36. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  37. data/lib/rubocop/cop/variable_force.rb +18 -7
  38. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  39. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  40. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  41. data/lib/rubocop/lsp/routes.rb +4 -4
  42. data/lib/rubocop/version.rb +1 -1
  43. data/lib/rubocop.rb +2 -0
  44. metadata +12 -7
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'tsort'
4
-
5
3
  module RuboCop
6
4
  module Cop
7
5
  module Style
@@ -29,6 +27,8 @@ module RuboCop
29
27
  MSG = 'Do not use parallel assignment.'
30
28
 
31
29
  def on_masgn(node) # rubocop:disable Metrics/AbcSize
30
+ return if part_of_ignored_node?(node)
31
+
32
32
  rhs = node.rhs
33
33
  rhs = rhs.body if rhs.rescue_type?
34
34
  rhs_elements = Array(rhs).compact # edge case for one constant
@@ -41,6 +41,7 @@ module RuboCop
41
41
  add_offense(range) do |corrector|
42
42
  autocorrect(corrector, node, rhs)
43
43
  end
44
+ ignore_node(node)
44
45
  end
45
46
 
46
47
  private
@@ -91,15 +92,9 @@ module RuboCop
91
92
  def find_valid_order(left_elements, right_elements)
92
93
  # arrange left_elements in an order such that no corresponding right
93
94
  # element refers to a left element earlier in the sequence
94
- # this can be done using an algorithm called a "topological sort"
95
- # fortunately for us, Ruby's stdlib contains an implementation
96
95
  assignments = left_elements.zip(right_elements)
97
96
 
98
- begin
99
- AssignmentSorter.new(assignments).tsort
100
- rescue TSort::Cyclic
101
- nil
102
- end
97
+ AssignmentSorter.new(assignments).tsort
103
98
  end
104
99
 
105
100
  # Converts (send nil :something) nodes to (send (:self) :something).
@@ -114,10 +109,9 @@ module RuboCop
114
109
  # @!method implicit_self_getter?(node)
115
110
  def_node_matcher :implicit_self_getter?, '(send nil? $_)'
116
111
 
117
- # Helper class necessitated by silly design of TSort prior to Ruby 2.1
118
- # Newer versions have a better API, but that doesn't help us
112
+ # Topologically sorts the assignments with Kahn's algorithm.
113
+ # https://en.wikipedia.org/wiki/Topological_sorting#Kahn's_algorithm
119
114
  class AssignmentSorter
120
- include TSort
121
115
  extend RuboCop::NodePattern::Macros
122
116
 
123
117
  # @!method var_name(node)
@@ -133,21 +127,39 @@ module RuboCop
133
127
  @assignments = assignments
134
128
  end
135
129
 
136
- def tsort_each_node(...)
137
- @assignments.each(...)
130
+ def tsort
131
+ dependencies = @assignments.to_h do |assignment|
132
+ [assignment, dependencies_for_assignment(assignment)]
133
+ end
134
+ result = []
135
+
136
+ while (matched_node, = dependencies.find { |_node, edges| edges.empty? })
137
+ dependencies.delete(matched_node)
138
+ result.push(matched_node)
139
+
140
+ dependencies.each do |node, edges|
141
+ dependencies[node].delete(matched_node) if edges.include?(matched_node)
142
+ end
143
+ end
144
+ # Cyclic dependency
145
+ return nil if dependencies.any?
146
+
147
+ result
138
148
  end
139
149
 
140
- def tsort_each_child(assignment)
141
- # yield all the assignments which must come after `assignment`
142
- # (due to dependencies on the previous value of the assigned var)
150
+ # Returns all the assignments which must come after `assignment`
151
+ # (due to dependencies on the previous value of the assigned var)
152
+ def dependencies_for_assignment(assignment)
143
153
  my_lhs, _my_rhs = *assignment
144
154
 
145
- @assignments.each do |other|
146
- _other_lhs, other_rhs = *other
155
+ @assignments.filter_map do |other|
156
+ # Exclude self, there are no dependencies in cases such as `a, b = a, b`.
157
+ next if other == assignment
147
158
 
159
+ _other_lhs, other_rhs = *other
148
160
  next unless dependency?(my_lhs, other_rhs)
149
161
 
150
- yield other
162
+ other
151
163
  end
152
164
  end
153
165
 
@@ -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 oneline_rescue_parentheses_required?(begin_node, node)
172
173
 
173
174
  return if begin_node.chained?
174
175
 
@@ -200,6 +201,14 @@ module RuboCop
200
201
  parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
201
202
  end
202
203
 
204
+ def oneline_rescue_parentheses_required?(begin_node, node)
205
+ return false unless node.rescue_type?
206
+ return false unless (parent = begin_node.parent)
207
+ return false if parent.if_type? && parent.ternary?
208
+
209
+ !parent.type?(:call, :array, :pair)
210
+ end
211
+
203
212
  def method_call_parentheses_required?(node)
204
213
  return false unless node.call_type?
205
214
 
@@ -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
@@ -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,18 @@ module RuboCop
354
353
  end
355
354
  end
356
355
 
356
+ def reference_assignments(loop_assignments, node)
357
+ # If inside a branching statement, mark all as referenced.
358
+ # Otherwise, mark only the last assignment as referenced.
359
+ # Note that `rescue` must be considered as branching because of
360
+ # the `retry` keyword.
361
+ if loop_assignments.first.node.each_ancestor(:if, :rescue, :case, :case_match).any?
362
+ loop_assignments.each { |assignment| assignment.reference!(node) }
363
+ else
364
+ loop_assignments.last&.reference!(node)
365
+ end
366
+ end
367
+
357
368
  def scanned_node?(node)
358
369
  scanned_nodes.include?(node)
359
370
  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
@@ -194,7 +194,7 @@ module RuboCop
194
194
  return []
195
195
  end
196
196
 
197
- new_text = @server.format(remove_file_protocol_from(file_uri), text, command: command)
197
+ new_text = @server.format(convert_file_uri_to_path(file_uri), text, command: command)
198
198
 
199
199
  return [] if new_text == text
200
200
 
@@ -214,13 +214,13 @@ module RuboCop
214
214
  method: 'textDocument/publishDiagnostics',
215
215
  params: {
216
216
  uri: file_uri,
217
- diagnostics: @server.offenses(remove_file_protocol_from(file_uri), text)
217
+ diagnostics: @server.offenses(convert_file_uri_to_path(file_uri), text)
218
218
  }
219
219
  }
220
220
  end
221
221
 
222
- def remove_file_protocol_from(uri)
223
- uri.delete_prefix('file://')
222
+ def convert_file_uri_to_path(uri)
223
+ URI.decode_www_form_component(uri.delete_prefix('file://'))
224
224
  end
225
225
  end
226
226
  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.78.0'
6
+ STRING = '1.79.1'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
data/lib/rubocop.rb CHANGED
@@ -211,6 +211,7 @@ require_relative 'rubocop/cop/layout/empty_line_after_guard_clause'
211
211
  require_relative 'rubocop/cop/layout/empty_line_after_magic_comment'
212
212
  require_relative 'rubocop/cop/layout/empty_line_after_multiline_condition'
213
213
  require_relative 'rubocop/cop/layout/empty_line_between_defs'
214
+ require_relative 'rubocop/cop/layout/empty_lines_after_module_inclusion'
214
215
  require_relative 'rubocop/cop/layout/empty_lines_around_access_modifier'
215
216
  require_relative 'rubocop/cop/layout/empty_lines_around_arguments'
216
217
  require_relative 'rubocop/cop/layout/empty_lines_around_attribute_accessor'
@@ -290,6 +291,7 @@ require_relative 'rubocop/cop/layout/space_inside_string_interpolation'
290
291
  require_relative 'rubocop/cop/layout/trailing_empty_lines'
291
292
  require_relative 'rubocop/cop/layout/trailing_whitespace'
292
293
 
294
+ require_relative 'rubocop/cop/lint/utils/nil_receiver_checker'
293
295
  require_relative 'rubocop/cop/lint/ambiguous_assignment'
294
296
  require_relative 'rubocop/cop/lint/ambiguous_block_association'
295
297
  require_relative 'rubocop/cop/lint/ambiguous_operator'
metadata CHANGED
@@ -1,15 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.78.0
4
+ version: 1.79.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  - Jonas Arvidsson
9
9
  - Yuji Nakayama
10
+ autorequire:
10
11
  bindir: exe
11
12
  cert_chain: []
12
- date: 2025-07-08 00:00:00.000000000 Z
13
+ date: 2025-07-31 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: json
@@ -127,7 +128,7 @@ dependencies:
127
128
  requirements:
128
129
  - - ">="
129
130
  - !ruby/object:Gem::Version
130
- version: 1.45.1
131
+ version: 1.46.0
131
132
  - - "<"
132
133
  - !ruby/object:Gem::Version
133
134
  version: '2.0'
@@ -137,7 +138,7 @@ dependencies:
137
138
  requirements:
138
139
  - - ">="
139
140
  - !ruby/object:Gem::Version
140
- version: 1.45.1
141
+ version: 1.46.0
141
142
  - - "<"
142
143
  - !ruby/object:Gem::Version
143
144
  version: '2.0'
@@ -342,6 +343,7 @@ files:
342
343
  - lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb
343
344
  - lib/rubocop/cop/layout/empty_line_between_defs.rb
344
345
  - lib/rubocop/cop/layout/empty_lines.rb
346
+ - lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb
345
347
  - lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb
346
348
  - lib/rubocop/cop/layout/empty_lines_around_arguments.rb
347
349
  - lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb
@@ -572,6 +574,7 @@ files:
572
574
  - lib/rubocop/cop/lint/useless_ruby2_keywords.rb
573
575
  - lib/rubocop/cop/lint/useless_setter_call.rb
574
576
  - lib/rubocop/cop/lint/useless_times.rb
577
+ - lib/rubocop/cop/lint/utils/nil_receiver_checker.rb
575
578
  - lib/rubocop/cop/lint/void.rb
576
579
  - lib/rubocop/cop/message_annotator.rb
577
580
  - lib/rubocop/cop/metrics/abc_size.rb
@@ -1088,11 +1091,12 @@ licenses:
1088
1091
  - MIT
1089
1092
  metadata:
1090
1093
  homepage_uri: https://rubocop.org/
1091
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.78.0
1094
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.79.1
1092
1095
  source_code_uri: https://github.com/rubocop/rubocop/
1093
- documentation_uri: https://docs.rubocop.org/rubocop/1.78/
1096
+ documentation_uri: https://docs.rubocop.org/rubocop/1.79/
1094
1097
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1095
1098
  rubygems_mfa_required: 'true'
1099
+ post_install_message:
1096
1100
  rdoc_options: []
1097
1101
  require_paths:
1098
1102
  - lib
@@ -1107,7 +1111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1107
1111
  - !ruby/object:Gem::Version
1108
1112
  version: '0'
1109
1113
  requirements: []
1110
- rubygems_version: 3.6.2
1114
+ rubygems_version: 3.3.7
1115
+ signing_key:
1111
1116
  specification_version: 4
1112
1117
  summary: Automatic Ruby code style checking tool.
1113
1118
  test_files: []