rubocop 1.84.2 → 1.85.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 (116) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +83 -4
  3. data/config/obsoletion.yml +5 -0
  4. data/lib/rubocop/cli/command/mcp.rb +19 -0
  5. data/lib/rubocop/cli.rb +6 -3
  6. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  7. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  8. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  9. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  10. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  11. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  12. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  13. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  14. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  15. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  16. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  17. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  18. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  19. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  20. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  21. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  22. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  23. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  24. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  25. data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
  26. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  27. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  28. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  29. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  30. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -1
  31. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +0 -9
  32. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +7 -6
  33. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  34. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  35. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  36. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  37. data/lib/rubocop/cop/lint/void.rb +32 -12
  38. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  39. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  40. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  41. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  42. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  43. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  44. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  45. data/lib/rubocop/cop/security/eval.rb +15 -2
  46. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  47. data/lib/rubocop/cop/style/alias.rb +4 -1
  48. data/lib/rubocop/cop/style/array_join.rb +4 -2
  49. data/lib/rubocop/cop/style/ascii_comments.rb +5 -2
  50. data/lib/rubocop/cop/style/attr.rb +5 -2
  51. data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
  52. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  53. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  54. data/lib/rubocop/cop/style/case_equality.rb +4 -0
  55. data/lib/rubocop/cop/style/class_and_module_children.rb +10 -2
  56. data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
  57. data/lib/rubocop/cop/style/copyright.rb +1 -1
  58. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  59. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  60. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  61. data/lib/rubocop/cop/style/empty_class_definition.rb +21 -20
  62. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  63. data/lib/rubocop/cop/style/encoding.rb +7 -1
  64. data/lib/rubocop/cop/style/end_block.rb +3 -1
  65. data/lib/rubocop/cop/style/endless_method.rb +8 -3
  66. data/lib/rubocop/cop/style/file_open.rb +63 -0
  67. data/lib/rubocop/cop/style/for.rb +3 -0
  68. data/lib/rubocop/cop/style/format_string_token.rb +29 -2
  69. data/lib/rubocop/cop/style/global_vars.rb +4 -1
  70. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
  71. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  72. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  73. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  74. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  75. data/lib/rubocop/cop/style/map_join.rb +123 -0
  76. data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
  77. data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
  78. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  79. data/lib/rubocop/cop/style/not.rb +2 -0
  80. data/lib/rubocop/cop/style/numeric_literals.rb +2 -1
  81. data/lib/rubocop/cop/style/one_class_per_file.rb +95 -0
  82. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  83. data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
  84. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  85. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  86. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  87. data/lib/rubocop/cop/style/proc.rb +3 -2
  88. data/lib/rubocop/cop/style/reduce_to_hash.rb +169 -0
  89. data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
  90. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  91. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  92. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  93. data/lib/rubocop/cop/style/redundant_parentheses.rb +6 -3
  94. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  95. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +104 -0
  96. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  97. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  98. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  99. data/lib/rubocop/cop/style/semicolon.rb +2 -0
  100. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  101. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  102. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  103. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  104. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  105. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  106. data/lib/rubocop/cop/variable_force/branch.rb +2 -2
  107. data/lib/rubocop/directive_comment.rb +2 -1
  108. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  109. data/lib/rubocop/lsp/diagnostic.rb +1 -0
  110. data/lib/rubocop/mcp/server.rb +174 -0
  111. data/lib/rubocop/options.rb +10 -1
  112. data/lib/rubocop/server/cache.rb +5 -7
  113. data/lib/rubocop/target_ruby.rb +18 -12
  114. data/lib/rubocop/version.rb +1 -1
  115. data/lib/rubocop.rb +14 -0
  116. metadata +34 -3
@@ -11,9 +11,11 @@ module RuboCop
11
11
  # (`transform_keys` was added in Ruby 2.5.)
12
12
  #
13
13
  # @safety
14
- # This cop is unsafe, as it can produce false positives if we are
15
- # transforming an enumerable of key-value-like pairs that isn't actually
16
- # a hash, e.g.: `[[k1, v1], [k2, v2], ...]`
14
+ # This cop identifies the receiver as a hash by checking for literal hash
15
+ # syntax and common methods that are known to return hashes (e.g. `to_h`,
16
+ # `merge`, `invert`, `group_by`, etc.). However, it is unsafe because it
17
+ # is possible for a custom class to define one of these methods and return
18
+ # something other than a hash.
17
19
  #
18
20
  # @example
19
21
  # # bad
@@ -21,10 +23,18 @@ module RuboCop
21
23
  # Hash[{a: 1, b: 2}.collect { |k, v| [foo(k), v] }]
22
24
  # {a: 1, b: 2}.map { |k, v| [k.to_s, v] }.to_h
23
25
  # {a: 1, b: 2}.to_h { |k, v| [k.to_s, v] }
26
+ # foo.to_h.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
27
+ # foo.merge(bar).map { |k, v| [k.to_s, v] }.to_h
24
28
  #
25
29
  # # good
26
30
  # {a: 1, b: 2}.transform_keys { |k| foo(k) }
27
31
  # {a: 1, b: 2}.transform_keys { |k| k.to_s }
32
+ # foo.to_h.transform_keys { |k| k.to_sym }
33
+ # foo.merge(bar).transform_keys { |k| k.to_s }
34
+ #
35
+ # # Won't register an offense - receiver is not known to be a hash
36
+ # foo.bar.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
37
+ # baz.map { |k, v| [k.to_s, v] }.to_h
28
38
  class HashTransformKeys < Base
29
39
  include HashTransformMethod
30
40
  extend AutoCorrector
@@ -35,7 +45,7 @@ module RuboCop
35
45
  # @!method on_bad_each_with_object(node)
36
46
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
37
47
  (block
38
- (call !#array_receiver? :each_with_object (hash))
48
+ (call #hash_receiver? :each_with_object (hash))
39
49
  (args
40
50
  (mlhs
41
51
  (arg $_)
@@ -50,7 +60,7 @@ module RuboCop
50
60
  (const _ :Hash)
51
61
  :[]
52
62
  (block
53
- (call !#array_receiver? {:map :collect})
63
+ (call #hash_receiver? {:map :collect})
54
64
  (args
55
65
  (arg $_)
56
66
  (arg _val))
@@ -61,7 +71,7 @@ module RuboCop
61
71
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
62
72
  (call
63
73
  (block
64
- (call !#array_receiver? {:map :collect})
74
+ (call #hash_receiver? {:map :collect})
65
75
  (args
66
76
  (arg $_)
67
77
  (arg _val))
@@ -72,7 +82,7 @@ module RuboCop
72
82
  # @!method on_bad_to_h(node)
73
83
  def_node_matcher :on_bad_to_h, <<~PATTERN
74
84
  (block
75
- (call !#array_receiver? :to_h)
85
+ (call #hash_receiver? :to_h)
76
86
  (args
77
87
  (arg $_)
78
88
  (arg _val))
@@ -9,9 +9,11 @@ module RuboCop
9
9
  # call to `transform_values` instead.
10
10
  #
11
11
  # @safety
12
- # This cop is unsafe, as it can produce false positives if we are
13
- # transforming an enumerable of key-value-like pairs that isn't actually
14
- # a hash, e.g.: `[[k1, v1], [k2, v2], ...]`
12
+ # This cop identifies the receiver as a hash by checking for literal hash
13
+ # syntax and common methods that are known to return hashes (e.g. `to_h`,
14
+ # `merge`, `invert`, `group_by`, etc.). However, it is unsafe because it
15
+ # is possible for a custom class to define one of these methods and return
16
+ # something other than a hash.
15
17
  #
16
18
  # @example
17
19
  # # bad
@@ -19,10 +21,18 @@ module RuboCop
19
21
  # Hash[{a: 1, b: 2}.collect { |k, v| [k, foo(v)] }]
20
22
  # {a: 1, b: 2}.map { |k, v| [k, v * v] }.to_h
21
23
  # {a: 1, b: 2}.to_h { |k, v| [k, v * v] }
24
+ # foo.to_h.each_with_object({}) { |(k, v), h| h[k] = foo(v) }
25
+ # foo.merge(bar).map { |k, v| [k, v.to_s] }.to_h
22
26
  #
23
27
  # # good
24
28
  # {a: 1, b: 2}.transform_values { |v| foo(v) }
25
29
  # {a: 1, b: 2}.transform_values { |v| v * v }
30
+ # foo.to_h.transform_values { |v| foo(v) }
31
+ # foo.merge(bar).transform_values { |v| v.to_s }
32
+ #
33
+ # # Won't register an offense - receiver is not known to be a hash
34
+ # foo.bar.each_with_object({}) { |(k, v), h| h[k] = v.to_s }
35
+ # baz.map { |k, v| [k, v.to_s] }.to_h
26
36
  class HashTransformValues < Base
27
37
  include HashTransformMethod
28
38
  extend AutoCorrector
@@ -33,7 +43,7 @@ module RuboCop
33
43
  # @!method on_bad_each_with_object(node)
34
44
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
35
45
  (block
36
- (call !#array_receiver? :each_with_object (hash))
46
+ (call #hash_receiver? :each_with_object (hash))
37
47
  (args
38
48
  (mlhs
39
49
  (arg _key)
@@ -48,7 +58,7 @@ module RuboCop
48
58
  (const _ :Hash)
49
59
  :[]
50
60
  (block
51
- (call !#array_receiver? {:map :collect})
61
+ (call #hash_receiver? {:map :collect})
52
62
  (args
53
63
  (arg _key)
54
64
  (arg $_))
@@ -59,7 +69,7 @@ module RuboCop
59
69
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
60
70
  (call
61
71
  (block
62
- (call !#array_receiver? {:map :collect})
72
+ (call #hash_receiver? {:map :collect})
63
73
  (args
64
74
  (arg _key)
65
75
  (arg $_))
@@ -70,7 +80,7 @@ module RuboCop
70
80
  # @!method on_bad_to_h(node)
71
81
  def_node_matcher :on_bad_to_h, <<~PATTERN
72
82
  (block
73
- (call !#array_receiver? :to_h)
83
+ (call #hash_receiver? :to_h)
74
84
  (args
75
85
  (arg _key)
76
86
  (arg $_))
@@ -89,9 +89,9 @@ module RuboCop
89
89
  [Style::SoleNestedConditional]
90
90
  end
91
91
 
92
- # rubocop:disable Metrics/AbcSize
92
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
93
93
  def on_if(node)
94
- return if endless_method?(node.body)
94
+ return if endless_method?(node.body) || node.ancestors.any?(&:dstr_type?)
95
95
 
96
96
  condition = node.condition
97
97
  return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
@@ -106,7 +106,7 @@ module RuboCop
106
106
  ignore_node(node)
107
107
  end
108
108
  end
109
- # rubocop:enable Metrics/AbcSize
109
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
110
110
 
111
111
  private
112
112
 
@@ -3,7 +3,10 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for trailing inline comments.
6
+ # Checks for trailing inline comments. Inline comments can
7
+ # make lines harder to read, especially when they are long.
8
+ # Placing comments on their own line above the code they
9
+ # describe is often clearer.
7
10
  #
8
11
  # @example
9
12
  #
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for `map { |x| x.to_s }.join` and similar calls where the
7
+ # `map` is redundant because `Array#join` implicitly calls `#to_s` on
8
+ # each element.
9
+ #
10
+ # @safety
11
+ # This cop is unsafe because it cannot guarantee that the receiver
12
+ # is an `Array` by static analysis. If the receiver does not have
13
+ # an `Array#join`-compatible implementation (i.e. one that calls
14
+ # `#to_s` on elements), the correction may change behavior.
15
+ #
16
+ # @example
17
+ # # bad
18
+ # array.map(&:to_s).join(', ')
19
+ #
20
+ # # bad
21
+ # array.map { |x| x.to_s }.join(', ')
22
+ #
23
+ # # bad
24
+ # array.collect(&:to_s).join
25
+ #
26
+ # # good
27
+ # array.join(', ')
28
+ #
29
+ # # good
30
+ # array.join
31
+ #
32
+ class MapJoin < Base
33
+ extend AutoCorrector
34
+ include RangeHelp
35
+
36
+ MSG = 'Remove redundant `%<method>s(&:to_s)` before `join`.'
37
+ RESTRICT_ON_SEND = %i[join].freeze
38
+
39
+ # map(&:to_s).join(...)
40
+ # @!method map_to_s_join?(node)
41
+ def_node_matcher :map_to_s_join?, <<~PATTERN
42
+ (call
43
+ $(call _ ${:map :collect} (block_pass (sym :to_s)))
44
+ :join ...)
45
+ PATTERN
46
+
47
+ # map { |x| x.to_s }.join(...)
48
+ # @!method map_to_s_block_join?(node)
49
+ def_node_matcher :map_to_s_block_join?, <<~PATTERN
50
+ (call
51
+ $(block
52
+ (call _ ${:map :collect})
53
+ (args (arg _x))
54
+ (send (lvar _x) :to_s))
55
+ :join ...)
56
+ PATTERN
57
+
58
+ # map { _1.to_s }.join(...)
59
+ # @!method map_to_s_numblock_join?(node)
60
+ def_node_matcher :map_to_s_numblock_join?, <<~PATTERN
61
+ (call
62
+ $(numblock
63
+ (call _ ${:map :collect})
64
+ 1
65
+ (send (lvar :_1) :to_s))
66
+ :join ...)
67
+ PATTERN
68
+
69
+ # map { it.to_s }.join(...)
70
+ # @!method map_to_s_itblock_join?(node)
71
+ def_node_matcher :map_to_s_itblock_join?, <<~PATTERN
72
+ (call
73
+ $(itblock
74
+ (call _ ${:map :collect})
75
+ :it
76
+ (send (lvar :it) :to_s))
77
+ :join ...)
78
+ PATTERN
79
+
80
+ def on_send(node)
81
+ map_to_s_join?(node) { |m, n| register_offense(node, m, n) } ||
82
+ map_to_s_block_join?(node) { |m, n| register_offense(node, m, n) } ||
83
+ map_to_s_numblock_join?(node) { |m, n| register_offense(node, m, n) } ||
84
+ map_to_s_itblock_join?(node) { |m, n| register_offense(node, m, n) }
85
+ end
86
+ alias on_csend on_send
87
+
88
+ private
89
+
90
+ def register_offense(join_node, map_node, method_name)
91
+ map_send = map_node.any_block_type? ? map_node.send_node : map_node
92
+ message = format(MSG, method: method_name)
93
+
94
+ add_offense(map_send.loc.selector, message: message) do |corrector|
95
+ remove_map_call(corrector, join_node, map_node, map_send)
96
+ end
97
+ end
98
+
99
+ def remove_map_call(corrector, join_node, map_node, map_send)
100
+ receiver = map_send.receiver
101
+ if receiver
102
+ corrector.replace(removal_range(receiver, map_node, map_send), '')
103
+ else
104
+ corrector.replace(no_receiver_range(map_node, join_node), '')
105
+ end
106
+ end
107
+
108
+ def removal_range(receiver, map_node, map_send)
109
+ start_pos = if receiver.last_line < map_send.loc.dot.line
110
+ receiver.source_range.end_pos
111
+ else
112
+ map_send.loc.dot.begin_pos
113
+ end
114
+ range_between(start_pos, map_node.source_range.end_pos)
115
+ end
116
+
117
+ def no_receiver_range(map_node, join_node)
118
+ range_between(map_node.source_range.begin_pos, join_node.loc.dot.end_pos)
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of the `then` keyword in multi-line if statements.
6
+ # Checks for uses of the `then` keyword in multi-line `if` statements.
7
+ # In multi-line `if` statements, `then` is redundant because the newline
8
+ # already separates the condition from the body.
7
9
  #
8
10
  # @example
9
11
  # # bad
@@ -4,9 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for comparison of something with nil using `==` and
7
- # `nil?`.
8
- #
9
- # Supported styles are: predicate, comparison.
7
+ # `nil?`. Enforcing a consistent style (either the `nil?`
8
+ # predicate or `==` comparison) improves readability.
10
9
  #
11
10
  # @example EnforcedStyle: predicate (default)
12
11
  #
@@ -43,7 +43,7 @@ module RuboCop
43
43
  { ({return next break} nil) (nil) }
44
44
  PATTERN
45
45
 
46
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
46
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
47
47
  return unless node.lambda_or_proc?
48
48
  return unless nil_return?(node.body)
49
49
 
@@ -4,6 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for uses of the keyword `not` instead of `!`.
7
+ # The `not` keyword has lower precedence than `!`, which can
8
+ # lead to surprising behavior and often requires parentheses.
7
9
  #
8
10
  # @example
9
11
  #
@@ -4,7 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for big numeric literals without `_` between groups
7
- # of digits in them.
7
+ # of digits in them. Underscores make large numbers easier to
8
+ # read by visually separating groups of digits.
8
9
  #
9
10
  # Additional allowed patterns can be added by adding regexps to
10
11
  # the `AllowedPatterns` configuration. All regexps are treated
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks that each source file defines at most one top-level class or module.
7
+ #
8
+ # Keeping one class or module per file makes it easier to find and navigate
9
+ # code, and follows the convention used by most Ruby projects.
10
+ #
11
+ # Classes and modules listed in `AllowedClasses` are not counted toward the
12
+ # limit. This is useful for small ancillary classes like custom exception
13
+ # classes that logically belong with the main class.
14
+ #
15
+ # @example
16
+ # # bad
17
+ # class Foo
18
+ # end
19
+ #
20
+ # class Bar
21
+ # end
22
+ #
23
+ # # bad
24
+ # class Foo
25
+ # end
26
+ #
27
+ # module Bar
28
+ # end
29
+ #
30
+ # # good
31
+ # class Foo
32
+ # end
33
+ #
34
+ # # good
35
+ # class Foo
36
+ # class Bar
37
+ # end
38
+ # end
39
+ #
40
+ # @example AllowedClasses: ['AllowedClass']
41
+ # # good
42
+ # class Foo
43
+ # end
44
+ #
45
+ # class AllowedClass
46
+ # end
47
+ #
48
+ class OneClassPerFile < Base
49
+ include RangeHelp
50
+
51
+ MSG = 'Do not define multiple classes/modules at the top level in a single file.'
52
+
53
+ def on_new_investigation
54
+ @top_level_definitions = []
55
+ end
56
+
57
+ def on_class(node)
58
+ check_top_level(node)
59
+ end
60
+
61
+ def on_module(node)
62
+ check_top_level(node)
63
+ end
64
+
65
+ private
66
+
67
+ def check_top_level(node)
68
+ return unless top_level_definition?(node)
69
+ return if allowed_class?(node)
70
+
71
+ @top_level_definitions << node
72
+ return unless @top_level_definitions.length > 1
73
+
74
+ add_offense(range_between(node.source_range.begin_pos, node.loc.name.end_pos))
75
+ end
76
+
77
+ def top_level_definition?(node)
78
+ if node.parent&.begin_type?
79
+ node.parent.root?
80
+ else
81
+ node.root?
82
+ end
83
+ end
84
+
85
+ def allowed_class?(node)
86
+ allowed_classes.include?(node.identifier.short_name)
87
+ end
88
+
89
+ def allowed_classes
90
+ @allowed_classes ||= cop_config.fetch('AllowedClasses', []).map(&:intern)
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
@@ -3,9 +3,10 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses of if/then/else/end constructs on a single line.
7
- # `AlwaysCorrectToMultiline` config option can be set to true to autocorrect all offenses to
8
- # multi-line constructs. When `AlwaysCorrectToMultiline` is false (default case) the
6
+ # Checks for uses of `if/then/else/end` constructs on a single line.
7
+ # A ternary operator (`?:`) or multi-line `if` is more readable.
8
+ # `AlwaysCorrectToMultiline` config option can be set to `true` to autocorrect all offenses to
9
+ # multi-line constructs. When `AlwaysCorrectToMultiline` is `false` (default case) the
9
10
  # autocorrect will first try converting them to ternary operators.
10
11
  #
11
12
  # @example
@@ -4,6 +4,10 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for simple usages of parallel assignment.
7
+ # Parallel assignment is less readable than individual
8
+ # assignments and makes it harder to follow what each
9
+ # variable is being set to.
10
+ #
7
11
  # This will only complain when the number of variables
8
12
  # being assigned matched the number of assigning variables.
9
13
  #