rubocop 0.91.0 → 1.0.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 (131) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +100 -54
  4. data/lib/rubocop.rb +10 -6
  5. data/lib/rubocop/cached_data.rb +2 -1
  6. data/lib/rubocop/cli/command/version.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +9 -5
  8. data/lib/rubocop/config.rb +4 -0
  9. data/lib/rubocop/config_loader.rb +19 -2
  10. data/lib/rubocop/config_loader_resolver.rb +7 -5
  11. data/lib/rubocop/config_regeneration.rb +33 -0
  12. data/lib/rubocop/config_validator.rb +7 -6
  13. data/lib/rubocop/cop/badge.rb +9 -24
  14. data/lib/rubocop/cop/base.rb +16 -1
  15. data/lib/rubocop/cop/commissioner.rb +34 -20
  16. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -2
  17. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -10
  19. data/lib/rubocop/cop/layout/array_alignment.rb +1 -0
  20. data/lib/rubocop/cop/layout/case_indentation.rb +4 -7
  21. data/lib/rubocop/cop/layout/class_structure.rb +8 -1
  22. data/lib/rubocop/cop/layout/dot_position.rb +6 -9
  23. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +2 -2
  24. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +4 -13
  25. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +13 -8
  26. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +2 -2
  27. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -2
  28. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +10 -1
  29. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +4 -13
  30. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  31. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -7
  32. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +0 -4
  33. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +4 -18
  34. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -2
  35. data/lib/rubocop/cop/layout/space_inside_string_interpolation.rb +2 -2
  36. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  37. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -0
  38. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +18 -1
  39. data/lib/rubocop/cop/lint/boolean_symbol.rb +3 -0
  40. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +23 -3
  41. data/lib/rubocop/cop/lint/duplicate_rescue_exception.rb +2 -4
  42. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +37 -0
  43. data/lib/rubocop/cop/lint/identity_comparison.rb +5 -3
  44. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +2 -5
  45. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +22 -12
  48. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +14 -4
  49. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +78 -0
  50. data/lib/rubocop/cop/lint/rescue_type.rb +0 -1
  51. data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -6
  52. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  53. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -5
  54. data/lib/rubocop/cop/lint/useless_access_modifier.rb +3 -9
  55. data/lib/rubocop/cop/lint/useless_times.rb +11 -2
  56. data/lib/rubocop/cop/metrics/block_length.rb +3 -1
  57. data/lib/rubocop/cop/metrics/class_length.rb +14 -6
  58. data/lib/rubocop/cop/metrics/parameter_lists.rb +4 -1
  59. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +25 -16
  60. data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
  61. data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
  62. data/lib/rubocop/cop/mixin/rescue_node.rb +1 -0
  63. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -3
  64. data/lib/rubocop/cop/mixin/visibility_help.rb +4 -16
  65. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  66. data/lib/rubocop/cop/offense.rb +15 -2
  67. data/lib/rubocop/cop/security/open.rb +12 -10
  68. data/lib/rubocop/cop/style/access_modifier_declarations.rb +6 -2
  69. data/lib/rubocop/cop/style/accessor_grouping.rb +3 -0
  70. data/lib/rubocop/cop/style/array_coercion.rb +4 -0
  71. data/lib/rubocop/cop/style/case_like_if.rb +20 -4
  72. data/lib/rubocop/cop/style/class_equality_comparison.rb +64 -0
  73. data/lib/rubocop/cop/style/combinable_loops.rb +13 -11
  74. data/lib/rubocop/cop/style/comment_annotation.rb +6 -0
  75. data/lib/rubocop/cop/style/commented_keyword.rb +7 -8
  76. data/lib/rubocop/cop/style/date_time.rb +12 -1
  77. data/lib/rubocop/cop/style/explicit_block_argument.rb +6 -2
  78. data/lib/rubocop/cop/style/for.rb +0 -4
  79. data/lib/rubocop/cop/style/format_string_token.rb +48 -3
  80. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +15 -6
  81. data/lib/rubocop/cop/style/if_unless_modifier.rb +0 -4
  82. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -6
  83. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  84. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -11
  85. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  86. data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
  87. data/lib/rubocop/cop/style/mixin_usage.rb +7 -27
  88. data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -2
  89. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -0
  90. data/lib/rubocop/cop/style/nested_ternary_operator.rb +2 -0
  91. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -1
  92. data/lib/rubocop/cop/style/optional_boolean_parameter.rb +12 -1
  93. data/lib/rubocop/cop/style/raise_args.rb +0 -3
  94. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  95. data/lib/rubocop/cop/style/redundant_assignment.rb +1 -9
  96. data/lib/rubocop/cop/style/redundant_begin.rb +36 -8
  97. data/lib/rubocop/cop/style/redundant_condition.rb +5 -1
  98. data/lib/rubocop/cop/style/redundant_conditional.rb +4 -5
  99. data/lib/rubocop/cop/style/redundant_interpolation.rb +6 -1
  100. data/lib/rubocop/cop/style/redundant_parentheses.rb +6 -3
  101. data/lib/rubocop/cop/style/redundant_percent_q.rb +9 -11
  102. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +39 -24
  103. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -15
  104. data/lib/rubocop/cop/style/redundant_return.rb +17 -17
  105. data/lib/rubocop/cop/style/redundant_self.rb +10 -9
  106. data/lib/rubocop/cop/style/redundant_sort.rb +13 -24
  107. data/lib/rubocop/cop/style/redundant_sort_by.rb +5 -9
  108. data/lib/rubocop/cop/style/rescue_standard_error.rb +20 -16
  109. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  110. data/lib/rubocop/cop/style/string_concatenation.rb +14 -2
  111. data/lib/rubocop/cop/style/ternary_parentheses.rb +2 -3
  112. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +4 -3
  113. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  114. data/lib/rubocop/cop/util.rb +0 -1
  115. data/lib/rubocop/cop/variable_force/branch.rb +0 -4
  116. data/lib/rubocop/directive_comment.rb +32 -0
  117. data/lib/rubocop/ext/regexp_node.rb +20 -4
  118. data/lib/rubocop/formatter/disabled_config_formatter.rb +12 -5
  119. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  120. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  121. data/lib/rubocop/options.rb +22 -17
  122. data/lib/rubocop/result_cache.rb +8 -2
  123. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  124. data/lib/rubocop/rspec/expect_offense.rb +5 -5
  125. data/lib/rubocop/runner.rb +9 -5
  126. data/lib/rubocop/target_finder.rb +27 -26
  127. data/lib/rubocop/target_ruby.rb +1 -1
  128. data/lib/rubocop/version.rb +61 -6
  129. metadata +14 -17
  130. data/lib/rubocop/cop/mixin/regexp_literal_help.rb +0 -43
  131. data/lib/rubocop/cop/tokens_util.rb +0 -84
@@ -83,8 +83,8 @@ module RuboCop
83
83
 
84
84
  def on_send(node)
85
85
  return unless node.access_modifier?
86
- return if node.parent.pair_type?
87
- return if cop_config['AllowModifiersOnSymbols'] && access_modifier_with_symbol?(node)
86
+ return if node.parent&.pair_type?
87
+ return if allow_modifiers_on_symbols?(node)
88
88
 
89
89
  if offense?(node)
90
90
  add_offense(node.loc.selector) if opposite_style_detected
@@ -95,6 +95,10 @@ module RuboCop
95
95
 
96
96
  private
97
97
 
98
+ def allow_modifiers_on_symbols?(node)
99
+ cop_config['AllowModifiersOnSymbols'] && access_modifier_with_symbol?(node)
100
+ end
101
+
98
102
  def offense?(node)
99
103
  (group_style? && access_modifier_is_inlined?(node)) ||
100
104
  (inline_style? && access_modifier_is_not_inlined?(node))
@@ -7,6 +7,9 @@ module RuboCop
7
7
  # By default it enforces accessors to be placed in grouped declarations,
8
8
  # but it can be configured to enforce separating them in multiple declarations.
9
9
  #
10
+ # NOTE: `Sorbet` is not compatible with "grouped" style. Consider "separated" style
11
+ # or disabling this cop.
12
+ #
10
13
  # @example EnforcedStyle: grouped (default)
11
14
  # # bad
12
15
  # class Foo
@@ -5,6 +5,10 @@ module RuboCop
5
5
  module Style
6
6
  # This cop enforces the use of `Array()` instead of explicit `Array` check or `[*var]`.
7
7
  #
8
+ # This cop is disabled by default because false positive will occur if
9
+ # the argument of `Array()` is not an array (e.g. Hash, Set),
10
+ # an array will be returned as an incompatibility result.
11
+ #
8
12
  # @example
9
13
  # # bad
10
14
  # paths = [paths] unless paths.is_a?(Array)
@@ -42,6 +42,8 @@ module RuboCop
42
42
  convertible = true
43
43
 
44
44
  branch_conditions(node).each do |branch_condition|
45
+ return false if regexp_with_working_captures?(branch_condition)
46
+
45
47
  conditions << []
46
48
  convertible = collect_conditions(branch_condition, target, conditions.last)
47
49
  break unless convertible
@@ -49,9 +51,7 @@ module RuboCop
49
51
 
50
52
  return unless convertible
51
53
 
52
- add_offense(node) do |corrector|
53
- autocorrect(corrector, node)
54
- end
54
+ add_offense(node) { |corrector| autocorrect(corrector, node) }
55
55
  end
56
56
 
57
57
  private
@@ -107,7 +107,7 @@ module RuboCop
107
107
  when :include?, :cover?
108
108
  receiver = deparenthesize(node.receiver)
109
109
  node.arguments.first if receiver.range_type?
110
- when :match, :match?
110
+ when :match, :match?, :=~
111
111
  find_target_in_match_node(node)
112
112
  end
113
113
  end
@@ -230,6 +230,22 @@ module RuboCop
230
230
  def indent(node)
231
231
  ' ' * node.loc.column
232
232
  end
233
+
234
+ # Named captures work with `=~` (if regexp is on lhs) and with `match` (both sides)
235
+ def regexp_with_working_captures?(node)
236
+ case node.type
237
+ when :match_with_lvasgn
238
+ lhs, _rhs = *node
239
+ node.loc.selector.source == '=~' && regexp_with_named_captures?(lhs)
240
+ when :send
241
+ lhs, method, rhs = *node
242
+ method == :match && [lhs, rhs].any? { |n| regexp_with_named_captures?(n) }
243
+ end
244
+ end
245
+
246
+ def regexp_with_named_captures?(node)
247
+ node.regexp_type? && node.each_capture(named: true).count.positive?
248
+ end
233
249
  end
234
250
  end
235
251
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop enforces the use of `Object#instance_of?` instead of class comparison
7
+ # for equality.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # var.class == Date
12
+ # var.class.equal?(Date)
13
+ # var.class.eql?(Date)
14
+ # var.class.name == 'Date'
15
+ #
16
+ # # good
17
+ # var.instance_of?(Date)
18
+ #
19
+ class ClassEqualityComparison < Base
20
+ include RangeHelp
21
+ include IgnoredMethods
22
+ extend AutoCorrector
23
+
24
+ MSG = 'Use `instance_of?(%<class_name>s)` instead of comparing classes.'
25
+
26
+ RESTRICT_ON_SEND = %i[== equal? eql?].freeze
27
+
28
+ def_node_matcher :class_comparison_candidate?, <<~PATTERN
29
+ (send
30
+ {$(send _ :class) (send $(send _ :class) :name)}
31
+ {:== :equal? :eql?} $_)
32
+ PATTERN
33
+
34
+ def on_send(node)
35
+ def_node = node.each_ancestor(:def, :defs).first
36
+ return if def_node && ignored_method?(def_node.method_name)
37
+
38
+ class_comparison_candidate?(node) do |receiver_node, class_node|
39
+ range = offense_range(receiver_node, node)
40
+ class_name = class_name(class_node, node)
41
+
42
+ add_offense(range, message: format(MSG, class_name: class_name)) do |corrector|
43
+ corrector.replace(range, "instance_of?(#{class_name})")
44
+ end
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ def class_name(class_node, node)
51
+ if node.children.first.method?(:name)
52
+ class_node.source.delete('"').delete("'")
53
+ else
54
+ class_node.source
55
+ end
56
+ end
57
+
58
+ def offense_range(receiver_node, node)
59
+ range_between(receiver_node.loc.selector.begin_pos, node.source_range.end_pos)
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -49,18 +49,26 @@ module RuboCop
49
49
  # end
50
50
  # end
51
51
  #
52
+ # # good
53
+ # def method
54
+ # each_slice(2) { |slice| do_something(slice) }
55
+ # each_slice(3) { |slice| do_something(slice) }
56
+ # end
57
+ #
52
58
  class CombinableLoops < Base
53
59
  MSG = 'Combine this loop with the previous loop.'
54
60
 
55
61
  def on_block(node)
62
+ return unless node.parent&.begin_type?
56
63
  return unless collection_looping_method?(node)
57
64
 
58
- sibling = left_sibling_of(node)
59
- add_offense(node) if same_collection_looping?(node, sibling)
65
+ add_offense(node) if same_collection_looping?(node, node.left_sibling)
60
66
  end
61
67
 
62
68
  def on_for(node)
63
- sibling = left_sibling_of(node)
69
+ return unless node.parent&.begin_type?
70
+
71
+ sibling = node.left_sibling
64
72
  add_offense(node) if sibling&.for_type? && node.collection == sibling.collection
65
73
  end
66
74
 
@@ -71,17 +79,11 @@ module RuboCop
71
79
  method_name.match?(/^each/) || method_name.match?(/_each$/)
72
80
  end
73
81
 
74
- def left_sibling_of(node)
75
- return unless node.parent&.begin_type?
76
-
77
- index = node.sibling_index - 1
78
- node.parent.children[index] if index >= 0
79
- end
80
-
81
82
  def same_collection_looping?(node, sibling)
82
83
  sibling&.block_type? &&
83
84
  sibling.send_node.method?(node.method_name) &&
84
- sibling.send_node.receiver == node.send_node.receiver
85
+ sibling.send_node.receiver == node.send_node.receiver &&
86
+ sibling.send_node.arguments == node.send_node.arguments
85
87
  end
86
88
  end
87
89
  end
@@ -6,6 +6,12 @@ module RuboCop
6
6
  # This cop checks that comment annotation keywords are written according
7
7
  # to guidelines.
8
8
  #
9
+ # NOTE: With a multiline comment block (where each line is only a
10
+ # comment), only the first line will be able to register an offense, even
11
+ # if an annotation keyword starts another line. This is done to prevent
12
+ # incorrect registering of keywords (eg. `review`) inside a paragraph as an
13
+ # annotation.
14
+ #
9
15
  # @example
10
16
  # # bad
11
17
  # # TODO make better
@@ -33,13 +33,17 @@ module RuboCop
33
33
  # class X # :nodoc:
34
34
  # y
35
35
  # end
36
- class CommentedKeyword < Cop
36
+ class CommentedKeyword < Base
37
37
  MSG = 'Do not place comments on the same line as the ' \
38
38
  '`%<keyword>s` keyword.'
39
39
 
40
- def investigate(processed_source)
40
+ def on_new_investigation
41
41
  processed_source.comments.each do |comment|
42
- add_offense(comment) if offensive?(comment)
42
+ next unless (match = line(comment).match(/(?<keyword>\S+).*#/))
43
+
44
+ if offensive?(comment)
45
+ add_offense(comment, message: format(MSG, keyword: match[:keyword]))
46
+ end
43
47
  end
44
48
  end
45
49
 
@@ -62,11 +66,6 @@ module RuboCop
62
66
  ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
63
67
  end
64
68
 
65
- def message(comment)
66
- keyword = line(comment).match(/(\S+).*#/)[1]
67
- format(MSG, keyword: keyword)
68
- end
69
-
70
69
  def line(comment)
71
70
  comment.location.expression.source_line
72
71
  end
@@ -42,6 +42,8 @@ module RuboCop
42
42
  # # good
43
43
  # something.to_time
44
44
  class DateTime < Base
45
+ extend AutoCorrector
46
+
45
47
  CLASS_MSG = 'Prefer Time over DateTime.'
46
48
  COERCION_MSG = 'Do not use #to_datetime.'
47
49
 
@@ -63,7 +65,10 @@ module RuboCop
63
65
  return if historic_date?(node)
64
66
 
65
67
  message = to_datetime?(node) ? COERCION_MSG : CLASS_MSG
66
- add_offense(node, message: message)
68
+
69
+ add_offense(node, message: message) do |corrector|
70
+ autocorrect(corrector, node)
71
+ end
67
72
  end
68
73
 
69
74
  private
@@ -71,6 +76,12 @@ module RuboCop
71
76
  def disallow_coercion?
72
77
  !cop_config['AllowCoercion']
73
78
  end
79
+
80
+ def autocorrect(corrector, node)
81
+ return if to_datetime?(node)
82
+
83
+ corrector.replace(node.receiver.loc.name, 'Time')
84
+ end
74
85
  end
75
86
  end
76
87
  end
@@ -57,12 +57,16 @@ module RuboCop
57
57
  yielding_block?(block_node) do |send_node, block_args, yield_args|
58
58
  return unless yielding_arguments?(block_args, yield_args)
59
59
 
60
+ def_node = block_node.each_ancestor(:def, :defs).first
61
+ # if `yield` is being called outside of a method context, ignore
62
+ # this is not a valid ruby pattern, but can happen in haml or erb,
63
+ # so this can cause crashes in haml_lint
64
+ return unless def_node
65
+
60
66
  add_offense(block_node) do |corrector|
61
67
  corrector.remove(block_body_range(block_node, send_node))
62
68
 
63
69
  add_block_argument(send_node, corrector)
64
-
65
- def_node = block_node.each_ancestor(:def, :defs).first
66
70
  add_block_argument(def_node, corrector) if @def_nodes.add?(def_node)
67
71
  end
68
72
  end
@@ -49,8 +49,6 @@ module RuboCop
49
49
 
50
50
  def on_for(node)
51
51
  if style == :each
52
- return unless opposite_style_detected
53
-
54
52
  add_offense(node, message: PREFER_EACH) do |corrector|
55
53
  ForToEachCorrector.new(node).call(corrector)
56
54
  end
@@ -63,8 +61,6 @@ module RuboCop
63
61
  return unless suspect_enumerable?(node)
64
62
 
65
63
  if style == :for
66
- return unless opposite_style_detected
67
-
68
64
  add_offense(node, message: PREFER_FOR) do |corrector|
69
65
  EachToForCorrector.new(node).call(corrector)
70
66
  end
@@ -33,10 +33,31 @@ module RuboCop
33
33
  #
34
34
  # # bad
35
35
  # format('%<greeting>s', greeting: 'Hello')
36
- # format('%{greeting}', 'Hello')
36
+ # format('%{greeting}', greeting: 'Hello')
37
37
  #
38
38
  # # good
39
39
  # format('%s', 'Hello')
40
+ #
41
+ # It is allowed to contain unannotated token
42
+ # if the number of them is less than or equals to
43
+ # `MaxUnannotatedPlaceholdersAllowed`.
44
+ #
45
+ # @example MaxUnannotatedPlaceholdersAllowed: 0
46
+ #
47
+ # # bad
48
+ # format('%06d', 10)
49
+ # format('%s %s.', 'Hello', 'world')
50
+ #
51
+ # # good
52
+ # format('%<number>06d', number: 10)
53
+ #
54
+ # @example MaxUnannotatedPlaceholdersAllowed: 1 (default)
55
+ #
56
+ # # bad
57
+ # format('%s %s.', 'Hello', 'world')
58
+ #
59
+ # # good
60
+ # format('%06d', 10)
40
61
  class FormatStringToken < Base
41
62
  include ConfigurableEnforcedStyle
42
63
 
@@ -44,8 +65,12 @@ module RuboCop
44
65
  return unless node.value.include?('%')
45
66
  return if node.each_ancestor(:xstr, :regexp).any?
46
67
 
47
- tokens(node) do |detected_style, token_range|
48
- if detected_style == style || unannotated_format?(node, detected_style)
68
+ detections = collect_detections(node)
69
+ return if detections.empty?
70
+ return if allowed_unannotated?(detections)
71
+
72
+ detections.each do |detected_style, token_range|
73
+ if detected_style == style
49
74
  correct_style_detected
50
75
  else
51
76
  style_detected(detected_style)
@@ -112,6 +137,26 @@ module RuboCop
112
137
  yield(detected_style, token)
113
138
  end
114
139
  end
140
+
141
+ def collect_detections(node)
142
+ detections = []
143
+ tokens(node) do |detected_style, token_range|
144
+ unless unannotated_format?(node, detected_style)
145
+ detections << [detected_style, token_range]
146
+ end
147
+ end
148
+ detections
149
+ end
150
+
151
+ def allowed_unannotated?(detections)
152
+ return false if detections.size > max_unannotated_placeholders_allowed
153
+
154
+ detections.all? { |detected_style,| detected_style == :unannotated }
155
+ end
156
+
157
+ def max_unannotated_placeholders_allowed
158
+ cop_config['MaxUnannotatedPlaceholdersAllowed']
159
+ end
115
160
  end
116
161
  end
117
162
  end
@@ -33,7 +33,8 @@ module RuboCop
33
33
  extend AutoCorrector
34
34
 
35
35
  def on_hash(node)
36
- return unless last_array_item?(node)
36
+ return unless (array = containing_array(node))
37
+ return unless last_array_item?(array, node) && explicit_array?(array)
37
38
 
38
39
  if braces_style?
39
40
  check_braces(node)
@@ -44,12 +45,20 @@ module RuboCop
44
45
 
45
46
  private
46
47
 
47
- def last_array_item?(node)
48
- parent = node.parent
49
- return false unless parent&.array_type?
50
- return false if parent.child_nodes.all?(&:hash_type?)
48
+ def containing_array(hash_node)
49
+ parent = hash_node.parent
50
+ parent if parent&.array_type?
51
+ end
52
+
53
+ def last_array_item?(array, node)
54
+ return false if array.child_nodes.all?(&:hash_type?)
55
+
56
+ array.children.last.equal?(node)
57
+ end
51
58
 
52
- parent.children.last.equal?(node)
59
+ def explicit_array?(array)
60
+ # an implicit array cannot have an "unbraced" hash
61
+ array.square_brackets?
53
62
  end
54
63
 
55
64
  def check_braces(node)
@@ -157,10 +157,6 @@ module RuboCop
157
157
  #{indentation}end
158
158
  RUBY
159
159
  end
160
-
161
- def first_line_comment(node)
162
- processed_source.comment_at_line(node.loc.line)&.text
163
- end
164
160
  end
165
161
  end
166
162
  end