rubocop 1.66.1 → 1.67.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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +15 -6
  4. data/config/internal_affairs.yml +11 -0
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  6. data/lib/rubocop/cli/command/lsp.rb +2 -2
  7. data/lib/rubocop/config_loader_resolver.rb +3 -3
  8. data/lib/rubocop/config_validator.rb +2 -1
  9. data/lib/rubocop/cop/base.rb +6 -2
  10. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  11. data/lib/rubocop/cop/cop.rb +8 -0
  12. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  13. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  14. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  15. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  16. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  17. data/lib/rubocop/cop/internal_affairs.rb +16 -0
  18. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  19. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  21. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  22. data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
  23. data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
  24. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  25. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  26. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  27. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  28. data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
  29. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  30. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  31. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
  32. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  33. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
  34. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  35. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
  36. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
  37. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  38. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  39. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  40. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  41. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  42. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  43. data/lib/rubocop/cop/offense.rb +2 -2
  44. data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
  45. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  46. data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
  47. data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
  48. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  49. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  50. data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
  51. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  52. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  53. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  54. data/lib/rubocop/cop/style/guard_clause.rb +1 -1
  55. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  56. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  57. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  58. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -3
  59. data/lib/rubocop/cop/style/lambda.rb +1 -1
  60. data/lib/rubocop/cop/style/map_into_array.rb +53 -7
  61. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
  62. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  63. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  64. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  65. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
  66. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  67. data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
  68. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  69. data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
  70. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  71. data/lib/rubocop/cop/style/require_order.rb +1 -1
  72. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  73. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  74. data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
  75. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  76. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  77. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  78. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  79. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  80. data/lib/rubocop/cop/team.rb +8 -1
  81. data/lib/rubocop/cop/util.rb +1 -1
  82. data/lib/rubocop/cops_documentation_generator.rb +73 -34
  83. data/lib/rubocop/file_finder.rb +9 -4
  84. data/lib/rubocop/lsp/runtime.rb +2 -0
  85. data/lib/rubocop/lsp/server.rb +0 -1
  86. data/lib/rubocop/rspec/expect_offense.rb +1 -0
  87. data/lib/rubocop/runner.rb +3 -0
  88. data/lib/rubocop/server/cache.rb +6 -1
  89. data/lib/rubocop/server/core.rb +1 -0
  90. data/lib/rubocop/target_ruby.rb +12 -12
  91. data/lib/rubocop/version.rb +3 -1
  92. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  93. data/lib/rubocop.rb +2 -0
  94. metadata +10 -8
@@ -6,14 +6,38 @@ require 'fileutils'
6
6
  # @api private
7
7
  class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
8
8
  include ::RuboCop::Cop::Documentation
9
+ CopData = Struct.new(
10
+ :cop, :description, :example_objects, :safety_objects, :see_objects, :config, keyword_init: true
11
+ )
12
+
13
+ STRUCTURE = {
14
+ name: ->(data) { cop_header(data.cop) },
15
+ required_ruby_version: ->(data) { required_ruby_version(data.cop) },
16
+ properties: ->(data) { properties(data.cop) },
17
+ description: ->(data) { "#{data.description}\n" },
18
+ safety: ->(data) { safety_object(data.safety_objects, data.cop) },
19
+ examples: ->(data) { examples(data.example_objects, data.cop) },
20
+ configuration: ->(data) { configurations(data.cop.department, data.config, data.cop) },
21
+ references: ->(data) { references(data.cop, data.see_objects) }
22
+ }.freeze
23
+
9
24
  # This class will only generate documentation for cops that belong to one of
10
25
  # the departments given in the `departments` array. E.g. if we only wanted
11
26
  # documentation for Lint cops:
12
27
  #
13
28
  # CopsDocumentationGenerator.new(departments: ['Lint']).call
14
29
  #
15
- def initialize(departments: [])
30
+ # You can append additional information:
31
+ #
32
+ # callback = ->(data) { required_rails_version(data.cop) }
33
+ # CopsDocumentationGenerator.new(extra_info: { ruby_version: callback }).call
34
+ #
35
+ # This will insert the string returned from the lambda _after_ the section from RuboCop itself.
36
+ # See `CopsDocumentationGenerator::STRUCTURE` for available sections.
37
+ #
38
+ def initialize(departments: [], extra_info: {})
16
39
  @departments = departments.map(&:to_sym).sort!
40
+ @extra_info = extra_info
17
41
  @cops = RuboCop::Cop::Registry.global
18
42
  @config = RuboCop::ConfigLoader.default_configuration
19
43
  @docs_path = "#{Dir.pwd}/docs/modules/ROOT/pages/"
@@ -37,24 +61,21 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
37
61
  cops.with_department(department).sort!
38
62
  end
39
63
 
40
- def cops_body(cop, description, examples_objects, safety_objects, see_objects, pars) # rubocop:disable Metrics/AbcSize, Metrics/ParameterLists
41
- check_examples_to_have_the_default_enforced_style!(examples_objects, cop)
42
-
43
- content = h2(cop.cop_name)
44
- content << required_ruby_version(cop)
45
- content << properties(cop)
46
- content << "#{description}\n"
47
- content << safety_object(safety_objects) if safety_objects.any? { |s| !s.text.blank? }
48
- content << examples(examples_objects) if examples_objects.any?
49
- content << configurations(cop.department, pars)
50
- content << references(cop, see_objects)
64
+ def cops_body(data)
65
+ check_examples_to_have_the_default_enforced_style!(data.example_objects, data.cop)
66
+
67
+ content = +''
68
+ STRUCTURE.each do |section, block|
69
+ content << instance_exec(data, &block)
70
+ content << @extra_info[section].call(data) if @extra_info[section]
71
+ end
51
72
  content
52
73
  end
53
74
 
54
- def check_examples_to_have_the_default_enforced_style!(examples_object, cop)
55
- return if examples_object.none?
75
+ def check_examples_to_have_the_default_enforced_style!(example_objects, cop)
76
+ return if example_objects.none?
56
77
 
57
- examples_describing_enforced_style = examples_object.map(&:name).grep(/EnforcedStyle:/)
78
+ examples_describing_enforced_style = example_objects.map(&:name).grep(/EnforcedStyle:/)
58
79
  return if examples_describing_enforced_style.none?
59
80
 
60
81
  if examples_describing_enforced_style.index { |name| name.match?('default') }.nonzero?
@@ -66,16 +87,20 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
66
87
  raise "Specify the default EnforcedStyle for #{cop.cop_name}"
67
88
  end
68
89
 
69
- def examples(examples_object)
70
- examples_object.each_with_object(h3('Examples').dup) do |example, content|
90
+ def examples(example_objects, cop)
91
+ return '' if example_objects.none?
92
+
93
+ example_objects.each_with_object(cop_subsection('Examples', cop).dup) do |example, content|
71
94
  content << "\n" unless content.end_with?("\n\n")
72
- content << h4(example.name) unless example.name == ''
95
+ content << example_header(example.name, cop) unless example.name == ''
73
96
  content << code_example(example)
74
97
  end
75
98
  end
76
99
 
77
- def safety_object(safety_object_objects)
78
- safety_object_objects.each_with_object(h3('Safety').dup) do |safety_object, content|
100
+ def safety_object(safety_objects, cop)
101
+ return '' if safety_objects.all? { |s| s.text.blank? }
102
+
103
+ safety_objects.each_with_object(cop_subsection('Safety', cop).dup) do |safety_object, content|
79
104
  next if safety_object.text.blank?
80
105
 
81
106
  content << "\n" unless content.end_with?("\n\n")
@@ -115,22 +140,25 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
115
140
  end
116
141
  # rubocop:enable Metrics/MethodLength
117
142
 
118
- def h2(title)
143
+ def cop_header(cop)
119
144
  content = +"\n"
120
- content << "== #{title}\n"
145
+ content << "[##{to_anchor(cop.cop_name)}]\n"
146
+ content << "== #{cop.cop_name}\n"
121
147
  content << "\n"
122
148
  content
123
149
  end
124
150
 
125
- def h3(title)
151
+ def cop_subsection(title, cop)
126
152
  content = +"\n"
153
+ content << "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
127
154
  content << "=== #{title}\n"
128
155
  content << "\n"
129
156
  content
130
157
  end
131
158
 
132
- def h4(title)
133
- content = +"==== #{title}\n"
159
+ def example_header(title, cop)
160
+ content = "[##{to_anchor(title)}-#{to_anchor(cop.cop_name)}]\n"
161
+ content << +"==== #{title}\n"
134
162
  content << "\n"
135
163
  content
136
164
  end
@@ -142,7 +170,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
142
170
  content
143
171
  end
144
172
 
145
- def configurations(department, pars)
173
+ def configurations(department, pars, cop)
146
174
  return '' if pars.empty?
147
175
 
148
176
  header = ['Name', 'Default value', 'Configurable values']
@@ -157,7 +185,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
157
185
  [configuration_name(department, name), default, configurable]
158
186
  end
159
187
 
160
- h3('Configurable attributes') + to_table(header, content)
188
+ cop_subsection('Configurable attributes', cop) + to_table(header, content)
161
189
  end
162
190
 
163
191
  def configuration_name(department, name)
@@ -235,7 +263,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
235
263
  urls = RuboCop::Cop::MessageAnnotator.new(config, cop.name, cop_config, {}).urls
236
264
  return '' if urls.empty? && see_objects.empty?
237
265
 
238
- content = h3('References')
266
+ content = cop_subsection('References', cop)
239
267
  content << urls.map { |url| "* #{url}" }.join("\n")
240
268
  content << "\n" unless urls.empty?
241
269
  content << see_objects.map { |see| "* #{see.name}" }.join("\n")
@@ -283,14 +311,16 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
283
311
  ]
284
312
  pars = cop_config.reject { |k| non_display_keys.include? k }
285
313
  description = 'No documentation'
286
- examples_object = safety_object = see_object = []
314
+ example_objects = safety_objects = see_objects = []
287
315
  cop_code(cop) do |code_object|
288
316
  description = code_object.docstring unless code_object.docstring.blank?
289
- examples_object = code_object.tags('example')
290
- safety_object = code_object.tags('safety')
291
- see_object = code_object.tags('see')
317
+ example_objects = code_object.tags('example')
318
+ safety_objects = code_object.tags('safety')
319
+ see_objects = code_object.tags('see')
292
320
  end
293
- cops_body(cop, description, examples_object, safety_object, see_object, pars)
321
+ data = CopData.new(cop: cop, description: description, example_objects: example_objects,
322
+ safety_objects: safety_objects, see_objects: see_objects, config: pars)
323
+ cops_body(data)
294
324
  end
295
325
 
296
326
  def cop_code(cop)
@@ -306,7 +336,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
306
336
  filename = "#{department_to_basename(department)}.adoc"
307
337
  content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
308
338
  cops_of_department(department).each do |cop|
309
- anchor = cop.cop_name.delete('/').downcase
339
+ anchor = to_anchor(cop.cop_name)
310
340
  content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
311
341
  end
312
342
 
@@ -338,4 +368,13 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
338
368
 
339
369
  status == 'pending' ? 'Pending' : 'Enabled'
340
370
  end
371
+
372
+ # HTML anchor are somewhat limited in what characters they can contain, just
373
+ # accept a known-good subset. As long as it's consistent it doesn't matter.
374
+ #
375
+ # Style/AccessModifierDeclarations => styleaccessmodifierdeclarations
376
+ # OnlyFor: [] (default) => onlyfor_-__-_default_
377
+ def to_anchor(title)
378
+ title.delete('/').tr(' ', '-').gsub(/[^a-zA-Z0-9-]/, '_').downcase
379
+ end
341
380
  end
@@ -23,15 +23,20 @@ module RuboCop
23
23
  last_file
24
24
  end
25
25
 
26
+ def traverse_directories_upwards(start_dir, stop_dir = nil)
27
+ Pathname.new(start_dir).expand_path.ascend do |dir|
28
+ yield(dir)
29
+ dir = dir.to_s
30
+ break if dir == stop_dir || dir == FileFinder.root_level
31
+ end
32
+ end
33
+
26
34
  private
27
35
 
28
36
  def traverse_files_upwards(filename, start_dir, stop_dir)
29
- Pathname.new(start_dir).expand_path.ascend do |dir|
37
+ traverse_directories_upwards(start_dir, stop_dir) do |dir|
30
38
  file = dir + filename
31
39
  yield(file.to_s) if file.exist?
32
-
33
- dir = dir.to_s
34
- break if dir == stop_dir || dir == FileFinder.root_level
35
40
  end
36
41
  end
37
42
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'stringio'
4
+
3
5
  #
4
6
  # This code is based on https://github.com/standardrb/standard.
5
7
  #
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'language_server-protocol'
4
- require_relative '../lsp'
5
4
  require_relative 'logger'
6
5
  require_relative 'routes'
7
6
  require_relative 'runtime'
@@ -169,6 +169,7 @@ module RuboCop
169
169
  raise 'Expected correction but no corrections were made' if new_source == source
170
170
 
171
171
  expect(new_source).to eq(correction)
172
+ expect(@processed_source).to be_valid_syntax, 'Expected correction to be valid syntax'
172
173
  end
173
174
  # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
174
175
 
@@ -362,6 +362,9 @@ module RuboCop
362
362
  self.class.ruby_extractors.find do |ruby_extractor|
363
363
  result = ruby_extractor.call(processed_source)
364
364
  break result if result
365
+ rescue StandardError
366
+ raise Error, "Ruby extractor #{ruby_extractor.source_location[0]} failed to process " \
367
+ "#{processed_source.path}."
365
368
  end
366
369
  end
367
370
 
@@ -43,13 +43,18 @@ module RuboCop
43
43
  @project_dir_cache_key ||= project_dir[1..].tr('/', '+')
44
44
  end
45
45
 
46
+ # rubocop:disable Metrics/AbcSize
46
47
  def restart_key
47
48
  lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
48
49
  Pathname(project_dir).join(lockfile_name)
49
50
  end.find(&:exist?)
51
+ version_data = lockfile_path&.read || RuboCop::Version::STRING
52
+ config_data = Pathname(ConfigFinder.find_config_path(Dir.pwd)).read
53
+ todo_data = (rubocop_todo = Pathname('.rubocop_todo.yml')).exist? ? rubocop_todo.read : ''
50
54
 
51
- Digest::SHA1.hexdigest(lockfile_path&.read || RuboCop::Version::STRING)
55
+ Digest::SHA1.hexdigest(version_data + config_data + todo_data)
52
56
  end
57
+ # rubocop:enable Metrics/AbcSize
53
58
 
54
59
  def dir
55
60
  Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'securerandom'
4
4
  require 'socket'
5
+ require 'stringio'
5
6
 
6
7
  #
7
8
  # This code is based on https://github.com/fohte/rubocop-daemon.
@@ -53,8 +53,6 @@ module RuboCop
53
53
  class GemspecFile < Source
54
54
  extend NodePattern::Macros
55
55
 
56
- GEMSPEC_EXTENSION = '.gemspec'
57
-
58
56
  # @!method required_ruby_version(node)
59
57
  def_node_search :required_ruby_version, <<~PATTERN
60
58
  (send _ :required_ruby_version= $_)
@@ -68,7 +66,7 @@ module RuboCop
68
66
  PATTERN
69
67
 
70
68
  def name
71
- "`required_ruby_version` parameter (in #{gemspec_filename})"
69
+ "`required_ruby_version` parameter (in #{gemspec_filepath})"
72
70
  end
73
71
 
74
72
  private
@@ -83,16 +81,18 @@ module RuboCop
83
81
  find_minimal_known_ruby(right_hand_side)
84
82
  end
85
83
 
86
- def gemspec_filename
87
- @gemspec_filename ||= begin
88
- basename = Pathname.new(@config.base_dir_for_path_parameters).basename.to_s
89
- "#{basename}#{GEMSPEC_EXTENSION}"
90
- end
91
- end
92
-
93
84
  def gemspec_filepath
94
- @gemspec_filepath ||=
95
- @config.find_file_upwards(gemspec_filename, @config.base_dir_for_path_parameters)
85
+ return @gemspec_filepath if defined?(@gemspec_filepath)
86
+
87
+ @gemspec_filepath =
88
+ @config.traverse_directories_upwards(@config.base_dir_for_path_parameters) do |dir|
89
+ # NOTE: Can't use `dir.glob` because of JRuby 9.4.8.0 incompatibility:
90
+ # https://github.com/jruby/jruby/issues/8358
91
+ candidates = Pathname.glob("#{dir}/*.gemspec")
92
+ # Bundler will use a gemspec whatever the filename is, as long as its the only one in
93
+ # the folder.
94
+ break candidates.first if candidates.one?
95
+ end
96
96
  end
97
97
 
98
98
  def version_from_gemspec_file(file)
@@ -3,10 +3,11 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.66.1'
6
+ STRING = '1.67.0'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
10
+ 'analyzing as Ruby %<target_ruby_version>s, ' \
10
11
  'running on %<ruby_engine>s %<ruby_version>s)%<server_mode>s [%<ruby_platform>s]'
11
12
 
12
13
  CANONICAL_FEATURE_NAMES = {
@@ -22,6 +23,7 @@ module RuboCop
22
23
  if debug
23
24
  verbose_version = format(MSG, version: STRING, parser_version: parser_version,
24
25
  rubocop_ast_version: RuboCop::AST::Version::STRING,
26
+ target_ruby_version: TargetRuby.new(Config.new).version,
25
27
  ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION,
26
28
  server_mode: server_mode,
27
29
  ruby_platform: RUBY_PLATFORM)
@@ -5,37 +5,30 @@ module RuboCop
5
5
  # @api private
6
6
  module YAMLDuplicationChecker
7
7
  def self.check(yaml_string, filename, &on_duplicated)
8
- # Ruby 2.6+
9
- tree = if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0')
10
- # Specify filename to display helpful message when it raises
11
- # an error.
12
- YAML.parse(yaml_string, filename: filename)
13
- else
14
- YAML.parse(yaml_string, filename)
15
- end
16
- return unless tree
17
-
18
- traverse(tree, &on_duplicated)
19
- tree
8
+ handler = DuplicationCheckHandler.new(&on_duplicated)
9
+ parser = Psych::Parser.new(handler)
10
+ parser.parse(yaml_string, filename)
11
+ parser.handler.root.children[0]
20
12
  end
21
13
 
22
- def self.traverse(tree, &on_duplicated)
23
- case tree
24
- when Psych::Nodes::Mapping
25
- tree.children.each_slice(2).with_object([]) do |(key, value), keys|
26
- exist = keys.find { |key2| key2.value == key.value }
27
- yield(exist, key) if exist
28
- keys << key
29
- traverse(value, &on_duplicated)
30
- end
31
- else
32
- children = tree.children
33
- return unless children
14
+ class DuplicationCheckHandler < Psych::TreeBuilder # :nodoc:
15
+ def initialize(&block)
16
+ super()
17
+ @block = block
18
+ end
34
19
 
35
- children.each { |c| traverse(c, &on_duplicated) }
20
+ def end_mapping
21
+ mapping_node = super
22
+ # OPTIMIZE: Use a hash for faster lookup since there can
23
+ # be quite a few keys at the top-level.
24
+ keys = {}
25
+ mapping_node.children.each_slice(2) do |key, _value|
26
+ duplicate = keys[key.value]
27
+ @block.call(duplicate, key) if duplicate
28
+ keys[key.value] = key
29
+ end
30
+ mapping_node
36
31
  end
37
32
  end
38
-
39
- private_class_method :traverse
40
33
  end
41
34
  end
data/lib/rubocop.rb CHANGED
@@ -312,6 +312,7 @@ require_relative 'rubocop/cop/lint/duplicate_methods'
312
312
  require_relative 'rubocop/cop/lint/duplicate_regexp_character_class_element'
313
313
  require_relative 'rubocop/cop/lint/duplicate_require'
314
314
  require_relative 'rubocop/cop/lint/duplicate_rescue_exception'
315
+ require_relative 'rubocop/cop/lint/duplicate_set_element'
315
316
  require_relative 'rubocop/cop/lint/each_with_object_argument'
316
317
  require_relative 'rubocop/cop/lint/else_layout'
317
318
  require_relative 'rubocop/cop/lint/empty_block'
@@ -750,6 +751,7 @@ require_relative 'rubocop/config_store'
750
751
  require_relative 'rubocop/config_validator'
751
752
  require_relative 'rubocop/feature_loader'
752
753
  require_relative 'rubocop/lockfile'
754
+ require_relative 'rubocop/lsp'
753
755
  require_relative 'rubocop/target_finder'
754
756
  require_relative 'rubocop/directive_comment'
755
757
  require_relative 'rubocop/comment_config'
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.66.1
4
+ version: 1.67.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
8
8
  - Jonas Arvidsson
9
9
  - Yuji Nakayama
10
- autorequire:
10
+ autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2024-09-04 00:00:00.000000000 Z
13
+ date: 2024-10-15 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -179,6 +179,7 @@ files:
179
179
  - assets/output.css.erb
180
180
  - assets/output.html.erb
181
181
  - config/default.yml
182
+ - config/internal_affairs.yml
182
183
  - config/obsoletion.yml
183
184
  - exe/rubocop
184
185
  - lib/rubocop.rb
@@ -425,6 +426,7 @@ files:
425
426
  - lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb
426
427
  - lib/rubocop/cop/lint/duplicate_require.rb
427
428
  - lib/rubocop/cop/lint/duplicate_rescue_exception.rb
429
+ - lib/rubocop/cop/lint/duplicate_set_element.rb
428
430
  - lib/rubocop/cop/lint/each_with_object_argument.rb
429
431
  - lib/rubocop/cop/lint/else_layout.rb
430
432
  - lib/rubocop/cop/lint/empty_block.rb
@@ -1017,12 +1019,12 @@ licenses:
1017
1019
  - MIT
1018
1020
  metadata:
1019
1021
  homepage_uri: https://rubocop.org/
1020
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.66.1
1022
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.67.0
1021
1023
  source_code_uri: https://github.com/rubocop/rubocop/
1022
- documentation_uri: https://docs.rubocop.org/rubocop/1.66/
1024
+ documentation_uri: https://docs.rubocop.org/rubocop/1.67/
1023
1025
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1024
1026
  rubygems_mfa_required: 'true'
1025
- post_install_message:
1027
+ post_install_message:
1026
1028
  rdoc_options: []
1027
1029
  require_paths:
1028
1030
  - lib
@@ -1037,8 +1039,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1037
1039
  - !ruby/object:Gem::Version
1038
1040
  version: '0'
1039
1041
  requirements: []
1040
- rubygems_version: 3.4.22
1041
- signing_key:
1042
+ rubygems_version: 3.3.7
1043
+ signing_key:
1042
1044
  specification_version: 4
1043
1045
  summary: Automatic Ruby code style checking tool.
1044
1046
  test_files: []