rubocop 0.64.0 → 0.65.0

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