rubocop 1.66.1 → 1.67.0

Sign up to get free protection for your applications and to get access to all the features.
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: []