rubocop 0.64.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6c744e17f8f6d38daae30955f4198b9d34856917a7f3ec60b143b2cec3047aff
4
- data.tar.gz: 9eb2f51c58eda0558444895d38fa246d21b6a055aadc4d901a320dca2f4ce4eb
3
+ metadata.gz: ed6850978760ea5017481293cb1b14ac57cf36f3cdbc2592c54bbe10fd9be632
4
+ data.tar.gz: 3389c4b2855e381dbfddbf1448e5b3216118d2c47fb6903ce91590ce00345b9b
5
5
  SHA512:
6
- metadata.gz: c2d2beac27d36a4ef480f55c4125ddab230e1b208b45c90f59b3435f83eaa87f969ef0228bd959a7c25c3a91763bb941a9a778da5f6c9c925f0c9c698a631ef6
7
- data.tar.gz: 597c8b8c1454fd8e3734ed545a5d148353d1b8860232ac3eed9b633c15f17fb2399f12874c7ec13191bdcd57709ff12653877080982f900426ff2452c6421a41
6
+ metadata.gz: cde514edb63d0f5bf73c690ec7eeea0fa3af4240bcdde7e570582854fe09760fc81907ca42a92042502cc15cf40b3166591cf859ba29837283fc46854545f1d0
7
+ data.tar.gz: eceae6226ef3cae7ff49335ce9536138eaae8abb1151b505fb8b559c2c3fd82cbd271b0c5393236c4ed0cd5d37e19620b5404d0c85b39053d61f387dde00190a
data/README.md CHANGED
@@ -53,7 +53,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
53
53
  might want to use a conservative version locking in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 0.64.0', require: false
56
+ gem 'rubocop', '~> 0.65.0', require: false
57
57
  ```
58
58
 
59
59
  ## Quickstart
@@ -3490,11 +3490,13 @@ Style/ModuleFunction:
3490
3490
  StyleGuide: '#module-function'
3491
3491
  Enabled: true
3492
3492
  VersionAdded: '0.11'
3493
- VersionChanged: '0.53'
3493
+ VersionChanged: '0.65'
3494
3494
  EnforcedStyle: module_function
3495
3495
  SupportedStyles:
3496
3496
  - module_function
3497
3497
  - extend_self
3498
+ Autocorrect: false
3499
+ SafeAutoCorrect: false
3498
3500
 
3499
3501
  Style/MultilineBlockChain:
3500
3502
  Description: 'Avoid multi-line chains of blocks.'
@@ -3549,6 +3551,17 @@ Style/MutableConstant:
3549
3551
  Description: 'Do not assign mutable objects to constants.'
3550
3552
  Enabled: true
3551
3553
  VersionAdded: '0.34'
3554
+ VersionChanged: '0.65'
3555
+ EnforcedStyle: literals
3556
+ SupportedStyles:
3557
+ # literals: freeze literals assigned to constants
3558
+ # strict: freeze all constants
3559
+ # Strict mode is considered an experimental feature. It has not been updated
3560
+ # with an exhaustive list of all methods that will produce frozen objects so
3561
+ # there is a decent chance of getting some false positives. Luckily, there is
3562
+ # no harm in freezing an already frozen object.
3563
+ - literals
3564
+ - strict
3552
3565
 
3553
3566
  Style/NegatedIf:
3554
3567
  Description: >-
@@ -3928,7 +3941,7 @@ Style/SafeNavigation:
3928
3941
  safe navigation (`&.`).
3929
3942
  Enabled: true
3930
3943
  VersionAdded: '0.43'
3931
- VersionChanged: '0.44'
3944
+ VersionChanged: '0.56'
3932
3945
  # Safe navigation may cause a statement to start returning `nil` in addition
3933
3946
  # to whatever it used to return.
3934
3947
  ConvertCodeThatCanStartToReturnNil: false
@@ -3938,7 +3951,6 @@ Style/SafeNavigation:
3938
3951
  - presence
3939
3952
  - try
3940
3953
  - try!
3941
- VersionChanged: '0.56'
3942
3954
 
3943
3955
  Style/SelfAssignment:
3944
3956
  Description: >-
@@ -38,6 +38,7 @@ require_relative 'rubocop/ast/node/and_node'
38
38
  require_relative 'rubocop/ast/node/args_node'
39
39
  require_relative 'rubocop/ast/node/array_node'
40
40
  require_relative 'rubocop/ast/node/block_node'
41
+ require_relative 'rubocop/ast/node/break_node'
41
42
  require_relative 'rubocop/ast/node/case_node'
42
43
  require_relative 'rubocop/ast/node/def_node'
43
44
  require_relative 'rubocop/ast/node/defined_node'
@@ -51,6 +52,7 @@ require_relative 'rubocop/ast/node/pair_node'
51
52
  require_relative 'rubocop/ast/node/range_node'
52
53
  require_relative 'rubocop/ast/node/regexp_node'
53
54
  require_relative 'rubocop/ast/node/resbody_node'
55
+ require_relative 'rubocop/ast/node/retry_node'
54
56
  require_relative 'rubocop/ast/node/send_node'
55
57
  require_relative 'rubocop/ast/node/str_node'
56
58
  require_relative 'rubocop/ast/node/super_node'
@@ -650,3 +652,4 @@ require_relative 'rubocop/runner'
650
652
  require_relative 'rubocop/cli'
651
653
  require_relative 'rubocop/options'
652
654
  require_relative 'rubocop/remote_config'
655
+ require_relative 'rubocop/yaml_duplication_checker'
@@ -19,6 +19,7 @@ module RuboCop
19
19
  args: ArgsNode,
20
20
  array: ArrayNode,
21
21
  block: BlockNode,
22
+ break: BreakNode,
22
23
  case: CaseNode,
23
24
  def: DefNode,
24
25
  defined?: DefinedNode,
@@ -34,6 +35,7 @@ module RuboCop
34
35
  pair: PairNode,
35
36
  regexp: RegexpNode,
36
37
  resbody: ResbodyNode,
38
+ retry: RetryNode,
37
39
  csend: SendNode,
38
40
  send: SendNode,
39
41
  str: StrNode,
@@ -471,6 +471,10 @@ module RuboCop
471
471
  int_type? || float_type?
472
472
  end
473
473
 
474
+ def range_type?
475
+ irange_type? || erange_type?
476
+ end
477
+
474
478
  def_node_matcher :guard_clause?, <<-PATTERN
475
479
  [{(send nil? {:raise :fail} ...) return break next} single_line?]
476
480
  PATTERN
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A node extension for `break` nodes. This will be used in place of a
6
+ # plain node when the builder constructs the AST, making its methods
7
+ # available to all `break` nodes within RuboCop.
8
+ class BreakNode < Node
9
+ include MethodDispatchNode
10
+ include ParameterizedNode
11
+
12
+ def arguments
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
@@ -8,6 +8,10 @@ module RuboCop
8
8
  class DefinedNode < Node
9
9
  include ParameterizedNode
10
10
  include MethodDispatchNode
11
+
12
+ def node_parts
13
+ [nil, :defined?, *to_a]
14
+ end
11
15
  end
12
16
  end
13
17
  end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module AST
5
+ # A node extension for `retry` nodes. This will be used in place of a
6
+ # plain node when the builder constructs the AST, making its methods
7
+ # available to all `retry` nodes within RuboCop.
8
+ class RetryNode < Node
9
+ include MethodDispatchNode
10
+ include ParameterizedNode
11
+
12
+ def arguments
13
+ []
14
+ end
15
+ end
16
+ end
17
+ end
@@ -158,6 +158,7 @@ module RuboCop
158
158
 
159
159
  def load_yaml_configuration(absolute_path)
160
160
  yaml_code = read_file(absolute_path)
161
+ check_duplication(yaml_code, absolute_path)
161
162
  hash = yaml_safe_load(yaml_code, absolute_path) || {}
162
163
 
163
164
  puts "configuration from #{absolute_path}" if debug?
@@ -169,6 +170,18 @@ module RuboCop
169
170
  hash
170
171
  end
171
172
 
173
+ def check_duplication(yaml_code, absolute_path)
174
+ smart_path = PathUtil.smart_path(absolute_path)
175
+ YAMLDuplicationChecker.check(yaml_code, absolute_path) do |key1, key2|
176
+ value = key1.value
177
+ line1 = key1.start_line + 1
178
+ line2 = key2.start_line + 1
179
+ message = "#{smart_path}:#{line1}: " \
180
+ "`#{value}` is concealed by line #{line2}"
181
+ warn Rainbow(message).yellow
182
+ end
183
+ end
184
+
172
185
  # Read the specified file, or exit with a friendly, concise message on
173
186
  # stderr. Care is taken to use the standard OS exit code for a "file not
174
187
  # found" error.
@@ -183,8 +196,7 @@ module RuboCop
183
196
  if defined?(SafeYAML) && SafeYAML.respond_to?(:load)
184
197
  SafeYAML.load(yaml_code, filename,
185
198
  whitelisted_tags: %w[!ruby/regexp])
186
- # Ruby 2.6+
187
- elsif Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0.pre1')
199
+ else
188
200
  YAML.safe_load(
189
201
  yaml_code,
190
202
  permitted_classes: [Regexp, Symbol],
@@ -192,8 +204,6 @@ module RuboCop
192
204
  aliases: false,
193
205
  filename: filename
194
206
  )
195
- else
196
- YAML.safe_load(yaml_code, [Regexp, Symbol], [], false, filename)
197
207
  end
198
208
  end
199
209
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def requires_parentheses?
39
- collection_node.irange_type? || collection_node.erange_type?
39
+ collection_node.range_type?
40
40
  end
41
41
 
42
42
  def end_position
@@ -45,7 +45,7 @@ module RuboCop
45
45
  # @example EnforcedStyle: consistent
46
46
  # # The `consistent` style enforces that the first key in a hash
47
47
  # # literal where the opening brace and the first key are on
48
- # # seprate lines is indented the same as a hash literal which is not
48
+ # # separate lines is indented the same as a hash literal which is not
49
49
  # # defined inside a method call.
50
50
  #
51
51
  # # bad
@@ -91,7 +91,7 @@ module RuboCop
91
91
  end
92
92
 
93
93
  def method_name_and_arguments_on_same_line?(node)
94
- node.send_type? &&
94
+ %i[send csend].include?(node.type) &&
95
95
  node.loc.selector.line == node.arguments.last.last_line &&
96
96
  node.last_line == node.arguments.last.last_line
97
97
  end
@@ -104,7 +104,7 @@ module RuboCop
104
104
  end
105
105
 
106
106
  def elements(node)
107
- return node.children unless node.send_type?
107
+ return node.children unless %i[csend send].include?(node.type)
108
108
 
109
109
  node.arguments.flat_map do |argument|
110
110
  # For each argument, if it is a multi-line hash without braces,
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # # good
15
15
  # has_many :accounts, class_name: 'Account'
16
16
  class ReflectionClassName < Cop
17
- MSG = 'Use a string has value for a class_name.'.freeze
17
+ MSG = 'Use a string value for `class_name`.'.freeze
18
18
 
19
19
  def_node_matcher :association_with_options?, <<-PATTERN
20
20
  (send nil? {:has_many :has_one :belongs_to} _ (hash $...))
@@ -278,7 +278,7 @@ module RuboCop
278
278
  end
279
279
 
280
280
  def array_or_range?(node)
281
- node.array_type? || node.irange_type? || node.erange_type?
281
+ node.array_type? || node.range_type?
282
282
  end
283
283
  end
284
284
  end
@@ -263,6 +263,7 @@ module RuboCop
263
263
  node.parent &&
264
264
  (node.parent.pair_type? ||
265
265
  node.parent.array_type? ||
266
+ node.parent.range_type? ||
266
267
  splat?(node.parent) ||
267
268
  ternary_if?(node.parent))
268
269
  end
@@ -275,7 +276,8 @@ module RuboCop
275
276
  end
276
277
 
277
278
  def call_in_optional_arguments?(node)
278
- node.parent && node.parent.optarg_type?
279
+ node.parent &&
280
+ (node.parent.optarg_type? || node.parent.kwoptarg_type?)
279
281
  end
280
282
 
281
283
  def call_with_ambiguous_arguments?(node)
@@ -46,7 +46,7 @@ module RuboCop
46
46
  # # ...
47
47
  # end
48
48
  #
49
- # These offenses are not auto-corrected since there are different
49
+ # These offenses are not safe to auto-correct since there are different
50
50
  # implications to each approach.
51
51
  class ModuleFunction < Cop
52
52
  include ConfigurableEnforcedStyle
@@ -6,7 +6,15 @@ module RuboCop
6
6
  # This cop checks whether some constant value isn't a
7
7
  # mutable literal (e.g. array or hash).
8
8
  #
9
- # @example
9
+ # Strict mode can be used to freeze all constants, rather than
10
+ # just literals.
11
+ # Strict mode is considered an experimental feature. It has not been
12
+ # updated with an exhaustive list of all methods that will produce
13
+ # frozen objects so there is a decent chance of getting some false
14
+ # positives. Luckily, there is no harm in freezing an already
15
+ # frozen object.
16
+ #
17
+ # @example EnforcedStyle: literals (default)
10
18
  # # bad
11
19
  # CONST = [1, 2, 3]
12
20
  #
@@ -15,10 +23,36 @@ module RuboCop
15
23
  #
16
24
  # # good
17
25
  # CONST = <<~TESTING.freeze
18
- # This is a heredoc
26
+ # This is a heredoc
19
27
  # TESTING
28
+ #
29
+ # # good
30
+ # CONST = Something.new
31
+ #
32
+ #
33
+ # @example EnforcedStyle: strict
34
+ # # bad
35
+ # CONST = Something.new
36
+ #
37
+ # # bad
38
+ # CONST = Struct.new do
39
+ # def foo
40
+ # puts 1
41
+ # end
42
+ # end
43
+ #
44
+ # # good
45
+ # CONST = Something.new.freeze
46
+ #
47
+ # # good
48
+ # CONST = Struct.new do
49
+ # def foo
50
+ # puts 1
51
+ # end
52
+ # end.freeze
20
53
  class MutableConstant < Cop
21
54
  include FrozenStringLiteral
55
+ include ConfigurableEnforcedStyle
22
56
 
23
57
  MSG = 'Freeze mutable objects assigned to constants.'.freeze
24
58
 
@@ -39,24 +73,41 @@ module RuboCop
39
73
  expr = node.source_range
40
74
 
41
75
  lambda do |corrector|
42
- if node.array_type? && !node.bracketed?
76
+ splat_value = splat_value(node)
77
+ if splat_value
78
+ correct_splat_expansion(corrector, expr, splat_value)
79
+ elsif node.array_type? && !node.bracketed?
43
80
  corrector.insert_before(expr, '[')
44
- corrector.insert_after(expr, '].freeze')
45
- elsif node.irange_type? || node.erange_type?
81
+ corrector.insert_after(expr, ']')
82
+ elsif requires_parentheses?(node)
46
83
  corrector.insert_before(expr, '(')
47
- corrector.insert_after(expr, ').freeze')
48
- else
49
- corrector.insert_after(expr, '.freeze')
84
+ corrector.insert_after(expr, ')')
50
85
  end
86
+
87
+ corrector.insert_after(expr, '.freeze')
51
88
  end
52
89
  end
53
90
 
54
91
  private
55
92
 
56
93
  def on_assignment(value)
57
- range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
94
+ if style == :strict
95
+ strict_check(value)
96
+ else
97
+ check(value)
98
+ end
99
+ end
58
100
 
59
- value = splat_value(value) if splat_value(value)
101
+ def strict_check(value)
102
+ return if immutable_literal?(value)
103
+ return if operation_produces_immutable_object?(value)
104
+ return if frozen_string_literal?(value)
105
+
106
+ add_offense(value)
107
+ end
108
+
109
+ def check(value)
110
+ range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
60
111
 
61
112
  return unless mutable_literal?(value) ||
62
113
  range_enclosed_in_parentheses
@@ -70,10 +121,50 @@ module RuboCop
70
121
  value && value.mutable_literal?
71
122
  end
72
123
 
124
+ def immutable_literal?(node)
125
+ node.nil? || node.immutable_literal?
126
+ end
127
+
128
+ def frozen_string_literal?(node)
129
+ FROZEN_STRING_LITERAL_TYPES.include?(node.type) &&
130
+ frozen_string_literals_enabled?
131
+ end
132
+
133
+ def requires_parentheses?(node)
134
+ node.range_type? ||
135
+ (node.send_type? && node.loc.dot.nil?)
136
+ end
137
+
138
+ def correct_splat_expansion(corrector, expr, splat_value)
139
+ if range_enclosed_in_parentheses?(splat_value)
140
+ corrector.replace(expr, "#{splat_value.source}.to_a")
141
+ else
142
+ corrector.replace(expr, "(#{splat_value.source}).to_a")
143
+ end
144
+ end
145
+
73
146
  def_node_matcher :splat_value, <<-PATTERN
74
147
  (array (splat $_))
75
148
  PATTERN
76
149
 
150
+ # Some of these patterns may not actually return an immutable object,
151
+ # but we want to consider them immutable for this cop.
152
+ def_node_matcher :operation_produces_immutable_object?, <<-PATTERN
153
+ {
154
+ (const _ _)
155
+ (send (const nil? :Struct) :new ...)
156
+ (block (send (const nil? :Struct) :new ...) ...)
157
+ (send _ :freeze)
158
+ (send {float int} {:+ :- :* :** :/ :% :<<} _)
159
+ (send _ {:+ :- :* :** :/ :%} {float int})
160
+ (send _ {:== :=== :!= :<= :>= :< :>} _)
161
+ (send (const nil? :ENV) :[] _)
162
+ (or (send (const nil? :ENV) :[] _) _)
163
+ (send _ {:count :length :size} ...)
164
+ (block (send _ {:count :length :size} ...) ...)
165
+ }
166
+ PATTERN
167
+
77
168
  def_node_matcher :range_enclosed_in_parentheses?, <<-PATTERN
78
169
  (begin ({irange erange} _ _))
79
170
  PATTERN
@@ -91,7 +91,7 @@ module RuboCop
91
91
  return false unless node.keyword?
92
92
  return true if node.prefix_not?
93
93
 
94
- !node.parenthesized_call?
94
+ node.arguments? && !node.parenthesized_call?
95
95
  end
96
96
  end
97
97
  end
@@ -66,12 +66,14 @@ module RuboCop
66
66
  def correction_compact_to_exploded(node)
67
67
  exception_node, _new, message_node = *node.first_argument
68
68
 
69
- message = message_node && message_node.source
69
+ arguments =
70
+ [exception_node, message_node].compact.map(&:source).join(', ')
70
71
 
71
- correction = exception_node.source
72
- correction = "#{correction}, #{message}" if message
73
-
74
- "#{node.method_name} #{correction}"
72
+ if node.parent && requires_parens?(node.parent)
73
+ "#{node.method_name}(#{arguments})"
74
+ else
75
+ "#{node.method_name} #{arguments}"
76
+ end
75
77
  end
76
78
 
77
79
  def correction_exploded_to_compact(node)
@@ -79,7 +81,12 @@ module RuboCop
79
81
  return node.source if message_nodes.size > 1
80
82
 
81
83
  argument = message_nodes.first.source
82
- "#{node.method_name} #{exception_node.const_name}.new(#{argument})"
84
+
85
+ if node.parent && requires_parens?(node.parent)
86
+ "#{node.method_name}(#{exception_node.const_name}.new(#{argument}))"
87
+ else
88
+ "#{node.method_name} #{exception_node.const_name}.new(#{argument})"
89
+ end
83
90
  end
84
91
 
85
92
  def check_compact(node)
@@ -120,6 +127,11 @@ module RuboCop
120
127
  arg.hash_type? || arg.splat_type?
121
128
  end
122
129
 
130
+ def requires_parens?(parent)
131
+ parent.and_type? || parent.or_type? ||
132
+ parent.if_type? && parent.ternary?
133
+ end
134
+
123
135
  def message(node)
124
136
  if style == :compact
125
137
  format(COMPACT_MSG, method: node.method_name)
@@ -62,6 +62,13 @@ module RuboCop
62
62
  @pwd = nil
63
63
  end
64
64
 
65
+ def self.chdir(dir, &block)
66
+ reset_pwd
67
+ Dir.chdir(dir, &block)
68
+ ensure
69
+ reset_pwd
70
+ end
71
+
65
72
  def hidden_file_in_not_hidden_dir?(pattern, path)
66
73
  File.fnmatch?(
67
74
  pattern, path,
@@ -57,6 +57,20 @@ module RuboCop
57
57
  # simpler assertion since it just inspects the source and checks
58
58
  # that there were no offenses. The `expect_offense` method has
59
59
  # to do more work by parsing out lines that contain carets.
60
+ #
61
+ # If the code produces an offense that could not be auto-corrected, you can
62
+ # use `expect_no_corrections` after `expect_offense`.
63
+ #
64
+ # @example `expect_offense` and `expect_no_corrections`
65
+ #
66
+ # expect_offense(<<-RUBY.strip_indent)
67
+ # a do
68
+ # b
69
+ # end.c
70
+ # ^^^^^ Avoid chaining a method call on a do...end block.
71
+ # RUBY
72
+ #
73
+ # expect_no_corrections
60
74
  module ExpectOffense
61
75
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
62
76
  def expect_offense(source, file = nil)
@@ -98,6 +112,23 @@ module RuboCop
98
112
  expect(new_source).to eq(correction)
99
113
  end
100
114
 
115
+ def expect_no_corrections
116
+ unless @processed_source
117
+ raise '`expect_no_corrections` must follow `expect_offense`'
118
+ end
119
+
120
+ return if cop.corrections.empty?
121
+
122
+ # In order to print a nice diff, e.g. what source got corrected to,
123
+ # we need to run the actual corrections
124
+
125
+ corrector =
126
+ RuboCop::Cop::Corrector.new(@processed_source.buffer, cop.corrections)
127
+ new_source = corrector.rewrite
128
+
129
+ expect(new_source).to eq(@processed_source.buffer.source)
130
+ end
131
+
101
132
  def expect_no_offenses(source, file = nil)
102
133
  inspect_source(source, file)
103
134
 
@@ -10,7 +10,7 @@ module HostEnvironmentSimulatorHelper
10
10
  pid = ::Process.fork do
11
11
  # Need to write coverage result under different name
12
12
  if defined?(SimpleCov)
13
- SimpleCov.coverage_dir "coverage/ignored_results_#{Process.pid}"
13
+ SimpleCov.command_name "rspec-fork-#{Process.pid}"
14
14
  SimpleCov.pid = Process.pid
15
15
  end
16
16
 
@@ -23,7 +23,7 @@ RSpec.shared_context 'isolated environment', :isolated_environment do
23
23
  working_dir = File.join(tmpdir, 'work')
24
24
  Dir.mkdir(working_dir)
25
25
 
26
- Dir.chdir(working_dir) do
26
+ RuboCop::PathUtil.chdir(working_dir) do
27
27
  example.run
28
28
  end
29
29
  ensure
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.64.0'.freeze
6
+ STRING = '0.65.0'.freeze
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, running on ' \
9
9
  '%<ruby_engine>s %<ruby_version>s %<ruby_platform>s)'.freeze
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # Find duplicated keys from YAML.
5
+ module YAMLDuplicationChecker
6
+ def self.check(yaml_string, filename, &on_duplicated)
7
+ # Specify filename to display helpful message when it raises an error.
8
+ tree = YAML.parse(yaml_string, filename: filename)
9
+ return unless tree
10
+
11
+ traverse(tree, &on_duplicated)
12
+ end
13
+
14
+ def self.traverse(tree, &on_duplicated)
15
+ case tree
16
+ when Psych::Nodes::Mapping
17
+ tree.children.each_slice(2).with_object([]) do |(key, value), keys|
18
+ exist = keys.find { |key2| key2.value == key.value }
19
+ on_duplicated.call(exist, key) if exist
20
+ keys << key
21
+ traverse(value, &on_duplicated)
22
+ end
23
+ else
24
+ children = tree.children
25
+ return unless children
26
+
27
+ children.each { |c| traverse(c, &on_duplicated) }
28
+ end
29
+ end
30
+
31
+ private_class_method :traverse
32
+ end
33
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2019-02-10 00:00:00.000000000 Z
13
+ date: 2019-02-19 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: jaro_winkler
@@ -74,6 +74,20 @@ dependencies:
74
74
  - - "~>"
75
75
  - !ruby/object:Gem::Version
76
76
  version: '0.1'
77
+ - !ruby/object:Gem::Dependency
78
+ name: psych
79
+ requirement: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 3.1.0
84
+ type: :runtime
85
+ prerelease: false
86
+ version_requirements: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 3.1.0
77
91
  - !ruby/object:Gem::Dependency
78
92
  name: rainbow
79
93
  requirement: !ruby/object:Gem::Requirement
@@ -182,6 +196,7 @@ files:
182
196
  - lib/rubocop/ast/node/args_node.rb
183
197
  - lib/rubocop/ast/node/array_node.rb
184
198
  - lib/rubocop/ast/node/block_node.rb
199
+ - lib/rubocop/ast/node/break_node.rb
185
200
  - lib/rubocop/ast/node/case_node.rb
186
201
  - lib/rubocop/ast/node/def_node.rb
187
202
  - lib/rubocop/ast/node/defined_node.rb
@@ -205,6 +220,7 @@ files:
205
220
  - lib/rubocop/ast/node/range_node.rb
206
221
  - lib/rubocop/ast/node/regexp_node.rb
207
222
  - lib/rubocop/ast/node/resbody_node.rb
223
+ - lib/rubocop/ast/node/retry_node.rb
208
224
  - lib/rubocop/ast/node/send_node.rb
209
225
  - lib/rubocop/ast/node/str_node.rb
210
226
  - lib/rubocop/ast/node/super_node.rb
@@ -807,6 +823,7 @@ files:
807
823
  - lib/rubocop/token.rb
808
824
  - lib/rubocop/version.rb
809
825
  - lib/rubocop/warning.rb
826
+ - lib/rubocop/yaml_duplication_checker.rb
810
827
  homepage: https://github.com/rubocop-hq/rubocop
811
828
  licenses:
812
829
  - MIT
@@ -824,7 +841,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
824
841
  requirements:
825
842
  - - ">="
826
843
  - !ruby/object:Gem::Version
827
- version: 2.2.0
844
+ version: 2.2.2
828
845
  required_rubygems_version: !ruby/object:Gem::Requirement
829
846
  requirements:
830
847
  - - ">="