rubocop 1.81.1 → 1.82.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 (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +25 -7
  4. data/config/obsoletion.yml +4 -0
  5. data/lib/rubocop/cli.rb +2 -1
  6. data/lib/rubocop/comment_config.rb +62 -17
  7. data/lib/rubocop/config_loader.rb +2 -1
  8. data/lib/rubocop/config_loader_resolver.rb +7 -6
  9. data/lib/rubocop/cop/autocorrect_logic.rb +4 -0
  10. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -2
  11. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -3
  12. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -2
  13. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +10 -5
  14. data/lib/rubocop/cop/internal_affairs/location_exists.rb +28 -2
  15. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +1 -1
  16. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +3 -0
  17. data/lib/rubocop/cop/layout/end_alignment.rb +4 -0
  18. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -5
  19. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  20. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -4
  21. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  22. data/lib/rubocop/cop/layout/indentation_width.rb +12 -1
  23. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_length.rb +8 -4
  25. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  26. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -1
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +5 -3
  28. data/lib/rubocop/cop/layout/space_after_comma.rb +2 -10
  29. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  30. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  31. data/lib/rubocop/cop/lint/circular_argument_reference.rb +47 -3
  32. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +3 -2
  33. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +14 -8
  34. data/lib/rubocop/cop/lint/debugger.rb +0 -2
  35. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +4 -4
  36. data/lib/rubocop/cop/lint/else_layout.rb +19 -0
  37. data/lib/rubocop/cop/lint/empty_interpolation.rb +11 -0
  38. data/lib/rubocop/cop/lint/literal_as_condition.rb +4 -0
  39. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  40. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +16 -6
  41. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +4 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +23 -9
  43. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -2
  44. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +7 -1
  45. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -4
  46. data/lib/rubocop/cop/lint/self_assignment.rb +10 -2
  47. data/lib/rubocop/cop/lint/unreachable_code.rb +5 -3
  48. data/lib/rubocop/cop/lint/useless_assignment.rb +44 -16
  49. data/lib/rubocop/cop/lint/useless_or.rb +15 -2
  50. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
  51. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +4 -3
  52. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -4
  53. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  54. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -1
  55. data/lib/rubocop/cop/mixin/line_length_help.rb +21 -2
  56. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  57. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  58. data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +1 -1
  59. data/lib/rubocop/cop/mixin/space_after_punctuation.rb +5 -4
  60. data/lib/rubocop/cop/mixin/statement_modifier.rb +0 -6
  61. data/lib/rubocop/cop/mixin/trailing_comma.rb +7 -4
  62. data/lib/rubocop/cop/naming/method_name.rb +4 -2
  63. data/lib/rubocop/cop/naming/predicate_method.rb +4 -4
  64. data/lib/rubocop/cop/security/json_load.rb +33 -11
  65. data/lib/rubocop/cop/style/array_intersect.rb +2 -2
  66. data/lib/rubocop/cop/style/bare_percent_literals.rb +1 -2
  67. data/lib/rubocop/cop/style/case_equality.rb +11 -13
  68. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -0
  69. data/lib/rubocop/cop/style/conditional_assignment.rb +8 -14
  70. data/lib/rubocop/cop/style/constant_visibility.rb +17 -12
  71. data/lib/rubocop/cop/style/empty_method.rb +0 -6
  72. data/lib/rubocop/cop/style/endless_method.rb +15 -2
  73. data/lib/rubocop/cop/style/float_division.rb +15 -1
  74. data/lib/rubocop/cop/style/guard_clause.rb +0 -11
  75. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +12 -1
  77. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +17 -4
  78. data/lib/rubocop/cop/style/module_member_existence_check.rb +74 -0
  79. data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -4
  80. data/lib/rubocop/cop/style/one_line_conditional.rb +17 -9
  81. data/lib/rubocop/cop/style/operator_method_call.rb +11 -2
  82. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  83. data/lib/rubocop/cop/style/redundant_argument.rb +2 -0
  84. data/lib/rubocop/cop/style/redundant_format.rb +10 -4
  85. data/lib/rubocop/cop/style/redundant_interpolation.rb +11 -2
  86. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -2
  87. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +5 -0
  88. data/lib/rubocop/cop/style/redundant_sort.rb +7 -7
  89. data/lib/rubocop/cop/style/semicolon.rb +23 -7
  90. data/lib/rubocop/cop/style/sole_nested_conditional.rb +8 -1
  91. data/lib/rubocop/cop/style/super_arguments.rb +2 -2
  92. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +11 -11
  93. data/lib/rubocop/cop/util.rb +2 -3
  94. data/lib/rubocop/cops_documentation_generator.rb +4 -4
  95. data/lib/rubocop/directive_comment.rb +46 -3
  96. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
  97. data/lib/rubocop/lsp/diagnostic.rb +12 -17
  98. data/lib/rubocop/lsp/routes.rb +9 -36
  99. data/lib/rubocop/lsp/runtime.rb +2 -2
  100. data/lib/rubocop/lsp/server.rb +2 -2
  101. data/lib/rubocop/magic_comment.rb +20 -0
  102. data/lib/rubocop/rake_task.rb +1 -1
  103. data/lib/rubocop/remote_config.rb +7 -8
  104. data/lib/rubocop/result_cache.rb +38 -27
  105. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  106. data/lib/rubocop/rspec/support.rb +1 -1
  107. data/lib/rubocop/runner.rb +4 -0
  108. data/lib/rubocop/target_ruby.rb +1 -1
  109. data/lib/rubocop/version.rb +1 -1
  110. data/lib/rubocop.rb +1 -0
  111. metadata +8 -10
@@ -7,26 +7,26 @@ module RuboCop
7
7
  #
8
8
  # @example
9
9
  # # bad
10
- # a, b, _ = foo()
11
- # a, b, _, = foo()
12
- # a, _, _ = foo()
13
- # a, _, _, = foo()
10
+ # a, b, _ = foo
11
+ # a, b, _, = foo
12
+ # a, _, _ = foo
13
+ # a, _, _, = foo
14
14
  #
15
15
  # # good
16
- # a, b, = foo()
17
- # a, = foo()
18
- # *a, b, _ = foo()
16
+ # a, b, = foo
17
+ # a, = foo
18
+ # *a, b, _ = foo
19
19
  # # => We need to know to not include 2 variables in a
20
- # a, *b, _ = foo()
21
- # # => The correction `a, *b, = foo()` is a syntax error
20
+ # a, *b, _ = foo
21
+ # # => The correction `a, *b, = foo` is a syntax error
22
22
  #
23
23
  # @example AllowNamedUnderscoreVariables: true (default)
24
24
  # # good
25
- # a, b, _something = foo()
25
+ # a, b, _something = foo
26
26
  #
27
27
  # @example AllowNamedUnderscoreVariables: false
28
28
  # # bad
29
- # a, b, _something = foo()
29
+ # a, b, _something = foo
30
30
  #
31
31
  class TrailingUnderscoreVariable < Base
32
32
  include SurroundingSpace
@@ -117,10 +117,9 @@ module RuboCop
117
117
  # with calls chained to the end of it.
118
118
  def first_part_of_call_chain(node)
119
119
  while node
120
- case node.type
121
- when :send
120
+ if node.call_type?
122
121
  node = node.receiver
123
- when :block
122
+ elsif node.any_block_type?
124
123
  node = node.send_node
125
124
  else
126
125
  break
@@ -194,10 +194,10 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
194
194
 
195
195
  def configurations(department, cop, cop_config)
196
196
  header = ['Name', 'Default value', 'Configurable values']
197
- configs = cop_config
198
- .each_key
199
- .reject { |key| key.start_with?('Supported') }
200
- .reject { |key| key.start_with?('AllowMultipleStyles') }
197
+ configs = cop_config.each_key.reject do |key|
198
+ key == 'AllowMultipleStyles' ||
199
+ (key != 'SupportedTypes' && key.start_with?('Supported'))
200
+ end
201
201
  return '' if configs.empty?
202
202
 
203
203
  content = configs.map do |name|
@@ -14,11 +14,17 @@ module RuboCop
14
14
  # @api private
15
15
  COP_NAME_PATTERN = '([A-Za-z]\w+/)*(?:[A-Za-z]\w+)'
16
16
  # @api private
17
+ COP_NAME_PATTERN_NC = '(?:[A-Za-z]\w+/)*[A-Za-z]\w+'
18
+ # @api private
19
+ COP_NAMES_PATTERN_NC = "(?:#{COP_NAME_PATTERN_NC} , )*#{COP_NAME_PATTERN_NC}"
20
+ # @api private
17
21
  COP_NAMES_PATTERN = "(?:#{COP_NAME_PATTERN} , )*#{COP_NAME_PATTERN}"
18
22
  # @api private
19
23
  COPS_PATTERN = "(all|#{COP_NAMES_PATTERN})"
20
24
  # @api private
21
- AVAILABLE_MODES = %w[disable enable todo].freeze
25
+ PUSH_POP_ARGS_PATTERN = "([+\\-]#{COP_NAME_PATTERN_NC}(?:\\s+[+\\-]#{COP_NAME_PATTERN_NC})*)"
26
+ # @api private
27
+ AVAILABLE_MODES = %w[disable enable todo push pop].freeze
22
28
  # @api private
23
29
  DIRECTIVE_MARKER_PATTERN = '# rubocop : '
24
30
  # @api private
@@ -27,7 +33,7 @@ module RuboCop
27
33
  DIRECTIVE_HEADER_PATTERN = "#{DIRECTIVE_MARKER_PATTERN}((?:#{AVAILABLE_MODES.join('|')}))\\b"
28
34
  # @api private
29
35
  DIRECTIVE_COMMENT_REGEXP = Regexp.new(
30
- "#{DIRECTIVE_HEADER_PATTERN} #{COPS_PATTERN}"
36
+ "#{DIRECTIVE_HEADER_PATTERN}(?:\\s+#{COPS_PATTERN}|\\s+#{PUSH_POP_ARGS_PATTERN})?"
31
37
  .gsub(' ', '\s*')
32
38
  )
33
39
  # @api private
@@ -58,6 +64,7 @@ module RuboCop
58
64
  # Checks if the comment is malformed as a `# rubocop:` directive
59
65
  def malformed?
60
66
  return true if !start_with_marker? || @match_data.nil?
67
+ return true if missing_cop_name?
61
68
 
62
69
  tail = @match_data.post_match.lstrip
63
70
  !(tail.empty? || tail.start_with?(TRAILING_COMMENT_MARKER))
@@ -65,6 +72,8 @@ module RuboCop
65
72
 
66
73
  # Checks if the directive comment is missing a cop name
67
74
  def missing_cop_name?
75
+ return false if push? || pop?
76
+
68
77
  MALFORMED_DIRECTIVE_WITHOUT_COP_NAME_REGEXP.match?(comment.text)
69
78
  end
70
79
 
@@ -88,7 +97,13 @@ module RuboCop
88
97
 
89
98
  # Returns match captures to directive comment pattern
90
99
  def match_captures
91
- @match_captures ||= @match_data&.captures
100
+ @match_captures ||= @match_data && begin
101
+ captures = @match_data.captures
102
+ mode = captures[0]
103
+ # COPS_PATTERN is at captures[1], PUSH_POP_ARGS_PATTERN is at captures[4]
104
+ cops = captures[1] || captures[4]
105
+ [mode, cops]
106
+ end
92
107
  end
93
108
 
94
109
  # Checks if this directive disables cops
@@ -101,6 +116,21 @@ module RuboCop
101
116
  mode == 'enable'
102
117
  end
103
118
 
119
+ # Checks if this directive is a push
120
+ def push?
121
+ mode == 'push'
122
+ end
123
+
124
+ # Checks if this directive is a pop
125
+ def pop?
126
+ mode == 'pop'
127
+ end
128
+
129
+ # Returns the push arguments as a hash of cop names with their operations
130
+ def push_args
131
+ @push_args ||= parse_push_args
132
+ end
133
+
104
134
  # Checks if this directive enables all cops
105
135
  def enabled_all?
106
136
  !disabled? && all_cops?
@@ -176,5 +206,18 @@ module RuboCop
176
206
  def exclude_lint_department_cops(cops)
177
207
  cops - [LINT_REDUNDANT_DIRECTIVE_COP, LINT_SYNTAX_COP]
178
208
  end
209
+
210
+ def parse_push_args
211
+ return {} unless push? && cops
212
+
213
+ args = {}
214
+ cops.split.each do |cop_spec|
215
+ op = cop_spec[0]
216
+ cop_name = cop_spec[1..]
217
+ args[op] ||= []
218
+ args[op] << cop_name
219
+ end
220
+ args
221
+ end
179
222
  end
180
223
  end
@@ -27,6 +27,7 @@ module RuboCop
27
27
  References
28
28
  Safe
29
29
  SafeAutoCorrect
30
+ Severity
30
31
  StyleGuide
31
32
  VersionAdded
32
33
  VersionChanged
@@ -16,8 +16,8 @@ module RuboCop
16
16
  # Diagnostic for Language Server Protocol of RuboCop.
17
17
  # @api private
18
18
  class Diagnostic
19
- def initialize(position_encoding, offense, uri, cop_class)
20
- @position_encoding = position_encoding
19
+ def initialize(document_encoding, offense, uri, cop_class)
20
+ @document_encoding = document_encoding
21
21
  @offense = offense
22
22
  @uri = uri
23
23
  @cop_class = cop_class
@@ -149,7 +149,7 @@ module RuboCop
149
149
 
150
150
  eol = LanguageServer::Protocol::Interface::Position.new(
151
151
  line: @offense.line - 1,
152
- character: to_position_character
152
+ character: to_position_character(@offense.source_line.length)
153
153
  )
154
154
 
155
155
  # TODO: fails for multiline strings - may be preferable to use block
@@ -162,6 +162,15 @@ module RuboCop
162
162
  [inline_comment]
163
163
  end
164
164
 
165
+ def to_position_character(utf8_index)
166
+ str = @offense.source_line[0, utf8_index]
167
+ if @document_encoding == Encoding::UTF_16LE || @document_encoding.nil?
168
+ str.length + str.b.count("\xf0-\xff".b)
169
+ else
170
+ str.length
171
+ end
172
+ end
173
+
165
174
  def correctable?
166
175
  !@offense.corrector.nil?
167
176
  end
@@ -171,20 +180,6 @@ module RuboCop
171
180
  uri.scheme = 'file' if uri.scheme.nil?
172
181
  uri
173
182
  end
174
-
175
- def to_position_character(utf8_index = nil)
176
- str = utf8_index ? @offense.source_line[0, utf8_index] : @offense.source_line
177
- case @position_encoding
178
- when 'utf-8', Encoding::UTF_8
179
- str.bytesize
180
- when 'utf-32', Encoding::UTF_32
181
- str.size
182
- else # 'utf-16'
183
- # utf-16 is default position encoding on LSP
184
- # https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
185
- str.size + str.count("\u{10000}-\u{10FFFF}")
186
- end
187
- end
188
183
  end
189
184
  end
190
185
  end
@@ -15,7 +15,7 @@ module RuboCop
15
15
  module LSP
16
16
  # Routes for Language Server Protocol of RuboCop.
17
17
  # @api private
18
- class Routes # rubocop:disable Metrics/ClassLength
18
+ class Routes
19
19
  CONFIGURATION_FILE_PATTERNS = [
20
20
  RuboCop::ConfigFinder::DOTFILE,
21
21
  RuboCop::CLI::Command::AutoGenerateConfig::AUTO_GENERATED_FILE
@@ -42,7 +42,6 @@ module RuboCop
42
42
 
43
43
  handle 'initialize' do |request|
44
44
  initialization_options = extract_initialization_options_from(request)
45
- @position_encoding = initialization_options[:position_encoding]
46
45
 
47
46
  @server.configure(initialization_options)
48
47
 
@@ -54,8 +53,7 @@ module RuboCop
54
53
  text_document_sync: LanguageServer::Protocol::Interface::TextDocumentSyncOptions.new(
55
54
  change: LanguageServer::Protocol::Constant::TextDocumentSyncKind::INCREMENTAL,
56
55
  open_close: true
57
- ),
58
- position_encoding: @position_encoding
56
+ )
59
57
  )
60
58
  )
61
59
  )
@@ -186,26 +184,14 @@ module RuboCop
186
184
 
187
185
  def extract_initialization_options_from(request)
188
186
  safe_autocorrect = request.dig(:params, :initializationOptions, :safeAutocorrect)
189
- position_encodings = request.dig(:params, :capabilities, :general, :positionEncodings)
190
187
 
191
188
  {
192
189
  safe_autocorrect: safe_autocorrect.nil? || safe_autocorrect == true,
193
190
  lint_mode: request.dig(:params, :initializationOptions, :lintMode) == true,
194
- layout_mode: request.dig(:params, :initializationOptions, :layoutMode) == true,
195
- position_encoding: position_encoding(position_encodings)
191
+ layout_mode: request.dig(:params, :initializationOptions, :layoutMode) == true
196
192
  }
197
193
  end
198
194
 
199
- def position_encoding(position_encodings)
200
- if position_encodings&.include?('utf-8')
201
- 'utf-8'
202
- elsif position_encodings&.include?('utf-32')
203
- 'utf-32'
204
- else
205
- 'utf-16'
206
- end
207
- end
208
-
209
195
  def format_file(file_uri, command: nil)
210
196
  unless (text = @text_cache[file_uri])
211
197
  Logger.log("Format request arrived before text synchronized; skipping: `#{file_uri}'")
@@ -233,8 +219,7 @@ module RuboCop
233
219
  method: 'textDocument/publishDiagnostics',
234
220
  params: {
235
221
  uri: file_uri,
236
- diagnostics: @server.offenses(convert_file_uri_to_path(file_uri),
237
- text, @position_encoding)
222
+ diagnostics: @server.offenses(convert_file_uri_to_path(file_uri), text)
238
223
  }
239
224
  }
240
225
  end
@@ -244,8 +229,9 @@ module RuboCop
244
229
 
245
230
  start_pos = text_pos(orig_text, range[:start])
246
231
  end_pos = text_pos(orig_text, range[:end])
247
- orig_text[start_pos...end_pos] = text
248
- orig_text
232
+ text_bin = orig_text.b
233
+ text_bin[start_pos...end_pos] = text.b
234
+ text_bin.force_encoding(orig_text.encoding)
249
235
  end
250
236
 
251
237
  def text_pos(text, range)
@@ -254,27 +240,14 @@ module RuboCop
254
240
  pos = 0
255
241
  text.each_line.with_index do |l, i|
256
242
  if i == line
257
- pos += line_pos(l, char)
243
+ pos += l.encode('utf-16be').b[0, char * 2].encode('utf-8', 'utf-16be').bytesize
258
244
  return pos
259
245
  end
260
- pos += l.size
246
+ pos += l.bytesize
261
247
  end
262
248
  pos
263
249
  end
264
250
 
265
- def line_pos(line, char)
266
- case @position_encoding
267
- when 'utf-8'
268
- line.byteslice(0, char).size
269
- when 'utf-32'
270
- char
271
- else # 'utf-16'
272
- # utf-16 is default position encoding on LSP
273
- # https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/
274
- line.encode('utf-16be').byteslice(0, char * 2).size
275
- end
276
- end
277
-
278
251
  def convert_file_uri_to_path(uri)
279
252
  URI.decode_www_form_component(uri.delete_prefix('file://'))
280
253
  end
@@ -44,14 +44,14 @@ module RuboCop
44
44
  @runner.formatted_source
45
45
  end
46
46
 
47
- def offenses(path, text, position_encoding, prism_result: nil)
47
+ def offenses(path, text, document_encoding = nil, prism_result: nil)
48
48
  diagnostic_options = {}
49
49
  diagnostic_options[:only] = config_only_options if @lint_mode || @layout_mode
50
50
 
51
51
  @runner.run(path, text, diagnostic_options, prism_result: prism_result)
52
52
  @runner.offenses.map do |offense|
53
53
  Diagnostic.new(
54
- position_encoding, offense, path, @cop_registry[offense.cop_name]&.first
54
+ document_encoding, offense, path, @cop_registry[offense.cop_name]&.first
55
55
  ).to_lsp_diagnostic(@runner.config_for_working_directory)
56
56
  end
57
57
  end
@@ -51,8 +51,8 @@ module RuboCop
51
51
  @runtime.format(path, text, command: command)
52
52
  end
53
53
 
54
- def offenses(path, text, position_encoding)
55
- @runtime.offenses(path, text, position_encoding)
54
+ def offenses(path, text)
55
+ @runtime.offenses(path, text)
56
56
  end
57
57
 
58
58
  def configure(options)
@@ -11,6 +11,7 @@ module RuboCop
11
11
  KEYWORDS = {
12
12
  encoding: '(?:en)?coding',
13
13
  frozen_string_literal: 'frozen[_-]string[_-]literal',
14
+ rbs_inline: 'rbs_inline',
14
15
  shareable_constant_value: 'shareable[_-]constant[_-]value',
15
16
  typed: 'typed'
16
17
  }.freeze
@@ -36,6 +37,7 @@ module RuboCop
36
37
  def any?
37
38
  frozen_string_literal_specified? ||
38
39
  encoding_specified? ||
40
+ rbs_inline_specified? ||
39
41
  shareable_constant_value_specified? ||
40
42
  typed_specified?
41
43
  end
@@ -60,6 +62,10 @@ module RuboCop
60
62
  [true, false].include?(frozen_string_literal)
61
63
  end
62
64
 
65
+ def valid_rbs_inline_value?
66
+ %w[enabled disabled].include?(extract_rbs_inline_value)
67
+ end
68
+
63
69
  def valid_shareable_constant_value?
64
70
  %w[none literal experimental_everything experimental_copy].include?(shareable_constant_value)
65
71
  end
@@ -105,6 +111,10 @@ module RuboCop
105
111
  specified?(encoding)
106
112
  end
107
113
 
114
+ def rbs_inline_specified?
115
+ valid_rbs_inline_value?
116
+ end
117
+
108
118
  # Was the Sorbet `typed` sigil specified?
109
119
  #
110
120
  # @return [Boolean]
@@ -203,6 +213,9 @@ module RuboCop
203
213
  match(KEYWORDS[:frozen_string_literal])
204
214
  end
205
215
 
216
+ # Emacs comments cannot specify RBS::inline behavior.
217
+ def extract_rbs_inline_value; end
218
+
206
219
  def extract_shareable_constant_value
207
220
  match(KEYWORDS[:shareable_constant_value])
208
221
  end
@@ -242,6 +255,9 @@ module RuboCop
242
255
  # Vim comments cannot specify frozen string literal behavior.
243
256
  def frozen_string_literal; end
244
257
 
258
+ # Vim comments cannot specify RBS::inline behavior.
259
+ def extract_rbs_inline_value; end
260
+
245
261
  # Vim comments cannot specify shareable constant values behavior.
246
262
  def shareable_constant_value; end
247
263
 
@@ -296,6 +312,10 @@ module RuboCop
296
312
  extract(/\A\s*#\s*#{KEYWORDS[:frozen_string_literal]}:\s*#{TOKEN}\s*\z/io)
297
313
  end
298
314
 
315
+ def extract_rbs_inline_value
316
+ extract(/\A\s*#\s*#{KEYWORDS[:rbs_inline]}:\s*#{TOKEN}\s*\z/io)
317
+ end
318
+
299
319
  def extract_shareable_constant_value
300
320
  extract(/\A\s*#\s*#{KEYWORDS[:shareable_constant_value]}:\s*#{TOKEN}\s*\z/io)
301
321
  end
@@ -75,7 +75,7 @@ module RuboCop
75
75
  def setup_subtasks(name, *args, &task_block) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
76
76
  namespace(name) do
77
77
  # rubocop:todo Naming/InclusiveLanguage
78
- task(:auto_correct, *args) do
78
+ task(:auto_correct, *args) do |_, task_args|
79
79
  require 'rainbow'
80
80
  warn Rainbow(
81
81
  'rubocop:auto_correct task is deprecated; ' \
@@ -11,13 +11,13 @@ module RuboCop
11
11
 
12
12
  CACHE_LIFETIME = 24 * 60 * 60
13
13
 
14
- def initialize(url, base_dir)
14
+ def initialize(url, cache_root)
15
15
  begin
16
16
  @uri = URI.parse(url)
17
17
  rescue URI::InvalidURIError
18
18
  raise ConfigNotFoundError, "Failed to resolve configuration: '#{url}' is not a valid URI"
19
19
  end
20
- @base_dir = base_dir
20
+ @cache_root = cache_root
21
21
  end
22
22
 
23
23
  def file
@@ -27,16 +27,17 @@ module RuboCop
27
27
  next if response.is_a?(Net::HTTPNotModified)
28
28
  next if response.is_a?(SocketError)
29
29
 
30
+ FileUtils.mkdir_p(File.dirname(cache_path))
30
31
  File.write(cache_path, response.body)
31
32
  end
32
33
 
33
34
  cache_path
34
35
  end
35
36
 
36
- def inherit_from_remote(file, path)
37
+ def inherit_from_remote(file)
37
38
  new_uri = @uri.dup
38
39
  new_uri.path.gsub!(%r{/[^/]*$}, "/#{file.delete_prefix('./')}")
39
- RemoteConfig.new(new_uri.to_s, File.dirname(path))
40
+ RemoteConfig.new(new_uri.to_s, @cache_root)
40
41
  end
41
42
 
42
43
  private
@@ -80,7 +81,7 @@ module RuboCop
80
81
  end
81
82
 
82
83
  def cache_path
83
- File.expand_path(".rubocop-#{cache_name_from_uri}", @base_dir)
84
+ @cache_path ||= File.expand_path(".rubocop-remote-#{cache_name_from_uri}", @cache_root)
84
85
  end
85
86
 
86
87
  def cache_path_exists?
@@ -97,9 +98,7 @@ module RuboCop
97
98
  end
98
99
 
99
100
  def cache_name_from_uri
100
- uri = cloned_url
101
- uri.query = nil
102
- uri.to_s.gsub!(/[^0-9A-Za-z]/, '-')
101
+ "#{Digest::MD5.hexdigest(@uri.to_s)}.yml"
103
102
  end
104
103
 
105
104
  def cloned_url
@@ -22,7 +22,7 @@ module RuboCop
22
22
  # Remove old files so that the cache doesn't grow too big. When the
23
23
  # threshold MaxFilesInCache has been exceeded, the oldest 50% of all the
24
24
  # files in the cache are removed. The reason for removing so much is that
25
- # cleaning should be done relatively seldom, since there is a slight risk
25
+ # removing should be done relatively seldom, since there is a slight risk
26
26
  # that some other RuboCop process was just about to read the file, when
27
27
  # there's parallel execution and the cache is shared.
28
28
  def self.cleanup(config_store, verbose, cache_root_override = nil)
@@ -31,10 +31,12 @@ module RuboCop
31
31
  rubocop_cache_dir = cache_root(config_store, cache_root_override)
32
32
  return unless File.exist?(rubocop_cache_dir)
33
33
 
34
- files, dirs = Find.find(rubocop_cache_dir).partition { |path| File.file?(path) }
34
+ # We know the cache entries are 3 level deep, so globing
35
+ # for `*/*/*` only returns files.
36
+ files = Dir[File.join(rubocop_cache_dir, '*/*/*')]
35
37
  return unless requires_file_removal?(files.length, config_store)
36
38
 
37
- remove_oldest_files(files, dirs, rubocop_cache_dir, verbose)
39
+ remove_oldest_files(files, rubocop_cache_dir, verbose)
38
40
  end
39
41
 
40
42
  class << self
@@ -49,26 +51,36 @@ module RuboCop
49
51
  file_count > 1 && file_count > config_store.for_pwd.for_all_cops['MaxFilesInCache']
50
52
  end
51
53
 
52
- def remove_oldest_files(files, dirs, rubocop_cache_dir, verbose)
54
+ def remove_oldest_files(files, rubocop_cache_dir, verbose)
53
55
  # Add 1 to half the number of files, so that we remove the file if
54
56
  # there's only 1 left.
55
57
  remove_count = (files.length / 2) + 1
56
58
  puts "Removing the #{remove_count} oldest files from #{rubocop_cache_dir}" if verbose
57
59
  sorted = files.sort_by { |path| File.mtime(path) }
58
- remove_files(sorted, dirs, remove_count)
60
+ remove_files(sorted, remove_count)
59
61
  rescue Errno::ENOENT
60
62
  # This can happen if parallel RuboCop invocations try to remove the
61
63
  # same files. No problem.
62
64
  puts $ERROR_INFO if verbose
63
65
  end
64
66
 
65
- def remove_files(files, dirs, remove_count)
67
+ def remove_files(files, remove_count)
66
68
  # Batch file deletions, deleting over 130,000+ files will crash
67
69
  # File.delete.
68
70
  files[0, remove_count].each_slice(10_000).each do |files_slice|
69
71
  File.delete(*files_slice)
70
72
  end
71
- dirs.each { |dir| Dir.rmdir(dir) if Dir["#{dir}/*"].empty? }
73
+
74
+ dirs = files.map { |f| File.dirname(f) }.uniq
75
+ until dirs.empty?
76
+ dirs.select! do |dir|
77
+ Dir.rmdir(dir)
78
+ true
79
+ rescue SystemCallError # ENOTEMPTY etc
80
+ false
81
+ end
82
+ dirs = dirs.map { |f| File.dirname(f) }.uniq
83
+ end
72
84
  end
73
85
  end
74
86
 
@@ -90,7 +102,7 @@ module RuboCop
90
102
  @allow_symlinks_in_cache_location =
91
103
  ResultCache.allow_symlinks_in_cache_location?(config_store)
92
104
  @path = File.join(rubocop_cache_dir,
93
- rubocop_checksum,
105
+ self.class.source_checksum,
94
106
  context_checksum(team, options),
95
107
  file_checksum(file, config_store))
96
108
  @cached_data = CachedData.new(file)
@@ -167,13 +179,11 @@ module RuboCop
167
179
  end
168
180
 
169
181
  class << self
170
- attr_accessor :source_checksum, :inhibit_cleanup
171
- end
182
+ attr_accessor :inhibit_cleanup
172
183
 
173
- # The checksum of the RuboCop program running the inspection.
174
- def rubocop_checksum
175
- ResultCache.source_checksum ||=
176
- begin
184
+ # The checksum of the RuboCop program running the inspection.
185
+ def source_checksum
186
+ @source_checksum ||= begin
177
187
  digest = Digest::SHA1.new
178
188
  rubocop_extra_features
179
189
  .select { |path| File.file?(path) }
@@ -184,21 +194,22 @@ module RuboCop
184
194
  digest << RuboCop::Version::STRING << RuboCop::AST::Version::STRING
185
195
  digest.hexdigest
186
196
  end
187
- end
197
+ end
188
198
 
189
- def digest(path)
190
- content = if path.end_with?(*DL_EXTENSIONS)
191
- # Shared libraries often contain timestamps of when
192
- # they were compiled and other non-stable data.
193
- File.basename(path)
194
- else
195
- File.binread(path) # mtime not reliable
196
- end
197
- Zlib.crc32(content).to_s
198
- end
199
+ private
200
+
201
+ def digest(path)
202
+ content = if path.end_with?(*DL_EXTENSIONS)
203
+ # Shared libraries often contain timestamps of when
204
+ # they were compiled and other non-stable data.
205
+ File.basename(path)
206
+ else
207
+ File.binread(path) # mtime not reliable
208
+ end
209
+ Zlib.crc32(content).to_s
210
+ end
199
211
 
200
- def rubocop_extra_features
201
- @rubocop_extra_features ||= begin
212
+ def rubocop_extra_features
202
213
  lib_root = File.join(File.dirname(__FILE__), '..')
203
214
  exe_root = File.join(lib_root, '..', 'exe')
204
215
 
@@ -266,6 +266,6 @@ RSpec.shared_context 'ruby 3.4' do
266
266
  let(:ruby_version) { 3.4 }
267
267
  end
268
268
 
269
- RSpec.shared_context 'ruby 3.5' do
270
- let(:ruby_version) { 3.5 }
269
+ RSpec.shared_context 'ruby 4.0' do
270
+ let(:ruby_version) { 4.0 }
271
271
  end
@@ -30,5 +30,5 @@ RSpec.configure do |config|
30
30
  config.include_context 'ruby 3.2', :ruby32
31
31
  config.include_context 'ruby 3.3', :ruby33
32
32
  config.include_context 'ruby 3.4', :ruby34
33
- config.include_context 'ruby 3.5', :ruby35
33
+ config.include_context 'ruby 4.0', :ruby40
34
34
  end
@@ -65,6 +65,10 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def run(paths)
68
+ # Compute the cache source checksum once to avoid potential
69
+ # inconsistencies between workers.
70
+ ResultCache.source_checksum
71
+
68
72
  target_files = find_target_files(paths)
69
73
  if @options[:list_target_files]
70
74
  list_files(target_files)
@@ -4,7 +4,7 @@ module RuboCop
4
4
  # The kind of Ruby that code inspected by RuboCop is written in.
5
5
  # @api private
6
6
  class TargetRuby
7
- KNOWN_RUBIES = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5].freeze
7
+ KNOWN_RUBIES = [2.0, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7, 3.0, 3.1, 3.2, 3.3, 3.4, 4.0].freeze
8
8
  DEFAULT_VERSION = 2.7
9
9
 
10
10
  OBSOLETE_RUBIES = {
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.81.1'
6
+ STRING = '1.82.1'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \