rubocop 1.75.3 → 1.76.2

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +55 -7
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  6. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  7. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +50 -6
  8. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  9. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  10. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  11. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
  12. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  13. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  14. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  15. data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
  16. data/lib/rubocop/cop/layout/space_before_brackets.rb +6 -32
  17. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +5 -1
  18. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  19. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  20. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  21. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  22. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
  23. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  24. data/lib/rubocop/cop/lint/duplicate_methods.rb +84 -2
  25. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  26. data/lib/rubocop/cop/lint/float_comparison.rb +27 -0
  27. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  28. data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
  29. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  30. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  31. data/lib/rubocop/cop/lint/useless_access_modifier.rb +21 -4
  32. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  33. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  34. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  35. data/lib/rubocop/cop/lint/void.rb +2 -2
  36. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  37. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  38. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  39. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  40. data/lib/rubocop/cop/mixin/trailing_comma.rb +6 -2
  41. data/lib/rubocop/cop/naming/predicate_method.rb +245 -0
  42. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +2 -2
  43. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  44. data/lib/rubocop/cop/style/arguments_forwarding.rb +4 -1
  45. data/lib/rubocop/cop/style/class_and_module_children.rb +12 -2
  46. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  47. data/lib/rubocop/cop/style/comparable_between.rb +5 -2
  48. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  49. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  50. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  51. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  52. data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
  53. data/lib/rubocop/cop/style/if_unless_modifier.rb +22 -4
  54. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  55. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  56. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  57. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -1
  58. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  59. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  60. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  61. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  62. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  63. data/lib/rubocop/cop/style/redundant_parentheses.rb +31 -5
  64. data/lib/rubocop/cop/style/redundant_self.rb +5 -5
  65. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  66. data/lib/rubocop/cop/style/safe_navigation.rb +25 -12
  67. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  68. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  69. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  70. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
  71. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  72. data/lib/rubocop/cop/team.rb +1 -1
  73. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  74. data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -0
  75. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  76. data/lib/rubocop/rspec/cop_helper.rb +2 -2
  77. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  78. data/lib/rubocop/rspec/shared_contexts.rb +1 -2
  79. data/lib/rubocop/version.rb +1 -1
  80. data/lib/rubocop.rb +6 -1
  81. metadata +12 -7
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for useless OR (`||` and `or`) expressions.
7
+ #
8
+ # Some methods always return a truthy value, even when called
9
+ # on `nil` (e.g. `nil.to_i` evaluates to `0`). Therefore, OR expressions
10
+ # appended after these methods will never evaluate.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # x.to_a || fallback
16
+ # x.to_c || fallback
17
+ # x.to_d || fallback
18
+ # x.to_i || fallback
19
+ # x.to_f || fallback
20
+ # x.to_h || fallback
21
+ # x.to_r || fallback
22
+ # x.to_s || fallback
23
+ # x.to_sym || fallback
24
+ # x.intern || fallback
25
+ # x.inspect || fallback
26
+ # x.hash || fallback
27
+ # x.object_id || fallback
28
+ # x.__id__ || fallback
29
+ #
30
+ # x.to_s or fallback
31
+ #
32
+ # # good - if fallback is same as return value of method called on nil
33
+ # x.to_a # nil.to_a returns []
34
+ # x.to_c # nil.to_c returns (0+0i)
35
+ # x.to_d # nil.to_d returns 0.0
36
+ # x.to_i # nil.to_i returns 0
37
+ # x.to_f # nil.to_f returns 0.0
38
+ # x.to_h # nil.to_h returns {}
39
+ # x.to_r # nil.to_r returns (0/1)
40
+ # x.to_s # nil.to_s returns ''
41
+ # x.to_sym # nil.to_sym raises an error
42
+ # x.intern # nil.intern raises an error
43
+ # x.inspect # nil.inspect returns "nil"
44
+ # x.hash # nil.hash returns an Integer
45
+ # x.object_id # nil.object_id returns an Integer
46
+ # x.__id__ # nil.object_id returns an Integer
47
+ #
48
+ # # good - if the intention is not to call the method on nil
49
+ # x&.to_a || fallback
50
+ # x&.to_c || fallback
51
+ # x&.to_d || fallback
52
+ # x&.to_i || fallback
53
+ # x&.to_f || fallback
54
+ # x&.to_h || fallback
55
+ # x&.to_r || fallback
56
+ # x&.to_s || fallback
57
+ # x&.to_sym || fallback
58
+ # x&.intern || fallback
59
+ # x&.inspect || fallback
60
+ # x&.hash || fallback
61
+ # x&.object_id || fallback
62
+ # x&.__id__ || fallback
63
+ #
64
+ # x&.to_s or fallback
65
+ #
66
+ class UselessOr < Base
67
+ MSG = '`%<rhs>s` will never evaluate because `%<lhs>s` always returns a truthy value.'
68
+
69
+ TRUTHY_RETURN_VALUE_METHODS = Set[:to_a, :to_c, :to_d, :to_i, :to_f, :to_h, :to_r,
70
+ :to_s, :to_sym, :intern, :inspect, :hash, :object_id,
71
+ :__id__].freeze
72
+
73
+ # @!method truthy_return_value_method?(node)
74
+ def_node_matcher :truthy_return_value_method?, <<~PATTERN
75
+ (send _ %TRUTHY_RETURN_VALUE_METHODS)
76
+ PATTERN
77
+
78
+ def on_or(node)
79
+ if truthy_return_value_method?(node.lhs)
80
+ report_offense(node, node.lhs)
81
+ elsif truthy_return_value_method?(node.rhs)
82
+ parent = node.parent
83
+ parent = parent.parent if parent&.begin_type?
84
+
85
+ report_offense(parent, node.rhs) if parent&.or_type?
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def report_offense(or_node, truthy_node)
92
+ add_offense(or_node.loc.operator.join(or_node.rhs.source_range),
93
+ message: format(MSG, lhs: truthy_node.source, rhs: or_node.rhs.source))
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -128,8 +128,8 @@ module RuboCop
128
128
 
129
129
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
130
130
  def check_void_op(node, &block)
131
- node = node.children.first while node.begin_type?
132
- return unless node.call_type? && OPERATORS.include?(node.method_name)
131
+ node = node.children.first while node&.begin_type?
132
+ return unless node&.call_type? && OPERATORS.include?(node.method_name)
133
133
  if !UNARY_OPERATORS.include?(node.method_name) && node.loc.dot && node.arguments.none?
134
134
  return
135
135
  end
@@ -39,7 +39,7 @@ module RuboCop
39
39
  class AbcSize < Base
40
40
  include MethodComplexity
41
41
 
42
- MSG = 'Assignment Branch Condition size for %<method>s is too high. ' \
42
+ MSG = 'Assignment Branch Condition size for `%<method>s` is too high. ' \
43
43
  '[%<abc_vector>s %<complexity>.4g/%<max>.4g]'
44
44
 
45
45
  private
@@ -89,7 +89,7 @@ module RuboCop
89
89
 
90
90
  if first_non_comment_token
91
91
  # `line` is 1-indexed so we need to subtract 1 to get the array index
92
- processed_source.lines[0...first_non_comment_token.line - 1]
92
+ processed_source.lines[0...(first_non_comment_token.line - 1)]
93
93
  else
94
94
  processed_source.lines
95
95
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  true
11
11
  end
12
12
 
13
- def deltas_for_first_pair(first_pair, _node)
13
+ def deltas_for_first_pair(first_pair)
14
14
  {
15
15
  separator: separator_delta(first_pair),
16
16
  value: value_delta(first_pair)
@@ -81,13 +81,7 @@ module RuboCop
81
81
  class TableAlignment
82
82
  include ValueAlignment
83
83
 
84
- def initialize
85
- self.max_key_width = 0
86
- end
87
-
88
- def deltas_for_first_pair(first_pair, node)
89
- self.max_key_width = node.keys.map { |key| key.source.length }.max
90
-
84
+ def deltas_for_first_pair(first_pair)
91
85
  separator_delta = separator_delta(first_pair, first_pair, 0)
92
86
  {
93
87
  separator: separator_delta,
@@ -97,30 +91,37 @@ module RuboCop
97
91
 
98
92
  private
99
93
 
100
- attr_accessor :max_key_width
101
-
102
94
  def key_delta(first_pair, current_pair)
103
95
  first_pair.key_delta(current_pair)
104
96
  end
105
97
 
106
98
  def hash_rocket_delta(first_pair, current_pair)
107
- first_pair.loc.column + max_key_width + 1 - current_pair.loc.operator.column
99
+ first_pair.loc.column + max_key_width(first_pair.parent) + 1 -
100
+ current_pair.loc.operator.column
108
101
  end
109
102
 
110
103
  def value_delta(first_pair, current_pair)
111
104
  correct_value_column = first_pair.key.loc.column +
112
- current_pair.delimiter(true).length +
113
- max_key_width
105
+ max_key_width(first_pair.parent) +
106
+ max_delimiter_width(first_pair.parent)
114
107
 
115
108
  current_pair.value_omission? ? 0 : correct_value_column - current_pair.value.loc.column
116
109
  end
110
+
111
+ def max_key_width(hash_node)
112
+ hash_node.keys.map { |key| key.source.length }.max
113
+ end
114
+
115
+ def max_delimiter_width(hash_node)
116
+ hash_node.pairs.map { |pair| pair.delimiter(true).length }.max
117
+ end
117
118
  end
118
119
 
119
120
  # Handles calculation of deltas when the enforced style is 'separator'.
120
121
  class SeparatorAlignment
121
122
  include ValueAlignment
122
123
 
123
- def deltas_for_first_pair(*_nodes)
124
+ def deltas_for_first_pair(_first_pair)
124
125
  {}
125
126
  end
126
127
 
@@ -24,7 +24,7 @@ module RuboCop
24
24
  gem_canonical_name(string_a) < gem_canonical_name(string_b)
25
25
  end
26
26
 
27
- def consecutive_lines(previous, current)
27
+ def consecutive_lines?(previous, current)
28
28
  first_line = get_source_range(current, treat_comments_as_separators).first_line
29
29
  previous.source_range.last_line == first_line - 1
30
30
  end
@@ -107,7 +107,7 @@ module RuboCop
107
107
  # of the argument is not considered multiline, even if the argument
108
108
  # itself might span multiple lines.
109
109
  def allowed_multiline_argument?(node)
110
- elements(node).one? && !Util.begins_its_line?(node.loc.end)
110
+ elements(node).one? && !Util.begins_its_line?(node_end_location(node))
111
111
  end
112
112
 
113
113
  def elements(node)
@@ -127,10 +127,14 @@ module RuboCop
127
127
 
128
128
  def no_elements_on_same_line?(node)
129
129
  items = elements(node).map(&:source_range)
130
- items << node.loc.end
130
+ items << node_end_location(node)
131
131
  items.each_cons(2).none? { |a, b| on_same_line?(a, b) }
132
132
  end
133
133
 
134
+ def node_end_location(node)
135
+ node.loc.end || node.source_range.end.adjust(begin_pos: -1)
136
+ end
137
+
134
138
  def on_same_line?(range1, range2)
135
139
  range1.last_line == range2.line
136
140
  end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Naming
6
+ # Checks that predicate methods end with `?` and non-predicate methods do not.
7
+ #
8
+ # The names of predicate methods (methods that return a boolean value) should end
9
+ # in a question mark. Methods that don't return a boolean, shouldn't
10
+ # end in a question mark.
11
+ #
12
+ # The cop assesses a predicate method as one that returns boolean values. Likewise,
13
+ # a method that only returns literal values is assessed as non-predicate. The cop does
14
+ # not make an assessment if the return type is unknown (method calls, variables, etc.).
15
+ #
16
+ # NOTE: Operator methods (`def ==`, etc.) are ignored.
17
+ #
18
+ # By default, the cop runs in `conservative` mode, which allows a method to be named
19
+ # with a question mark as long as at least one return value is boolean. In `aggressive`
20
+ # mode, methods with a question mark will register an offense if any known non-boolean
21
+ # return values are detected.
22
+ #
23
+ # The cop also has `AllowedMethods` configuration in order to prevent the cop from
24
+ # registering an offense from a method name that does not confirm to the naming
25
+ # guidelines. By default, `call` is allowed. The cop also has `AllowedPatterns`
26
+ # configuration to allow method names by regular expression.
27
+ #
28
+ # The cop can furthermore be configured to allow all bang methods (method names
29
+ # ending with `!`), with `AllowBangMethods: true` (default false).
30
+ #
31
+ # @example Mode: conservative (default)
32
+ # # bad
33
+ # def foo
34
+ # bar == baz
35
+ # end
36
+ #
37
+ # # good
38
+ # def foo?
39
+ # bar == baz
40
+ # end
41
+ #
42
+ # # bad
43
+ # def foo?
44
+ # 5
45
+ # end
46
+ #
47
+ # # good
48
+ # def foo
49
+ # 5
50
+ # end
51
+ #
52
+ # # good - operator method
53
+ # def ==(other)
54
+ # hash == other.hash
55
+ # end
56
+ #
57
+ # # good - at least one return value is boolean
58
+ # def foo?
59
+ # return unless bar?
60
+ # true
61
+ # end
62
+ #
63
+ # # ok - return type is not known
64
+ # def foo?
65
+ # bar
66
+ # end
67
+ #
68
+ # # ok - return type is not known
69
+ # def foo
70
+ # bar?
71
+ # end
72
+ #
73
+ # @example Mode: aggressive
74
+ # # bad - the method returns nil in some cases
75
+ # def foo?
76
+ # return unless bar?
77
+ # true
78
+ # end
79
+ #
80
+ # @example AllowBangMethods: false (default)
81
+ # # bad
82
+ # def save!
83
+ # true
84
+ # end
85
+ #
86
+ # @example AllowBangMethods: true
87
+ # # good
88
+ # def save!
89
+ # true
90
+ # end
91
+ #
92
+ class PredicateMethod < Base
93
+ include AllowedMethods
94
+ include AllowedPattern
95
+
96
+ MSG_PREDICATE = 'Predicate method names should end with `?`.'
97
+ MSG_NON_PREDICATE = 'Non-predicate method names should not end with `?`.'
98
+
99
+ def on_def(node)
100
+ return if allowed?(node)
101
+
102
+ return_values = return_values(node.body)
103
+ return if acceptable?(return_values)
104
+
105
+ if node.predicate_method? && potential_non_predicate?(return_values)
106
+ add_offense(node.loc.name, message: MSG_NON_PREDICATE)
107
+ elsif !node.predicate_method? && all_return_values_boolean?(return_values)
108
+ add_offense(node.loc.name, message: MSG_PREDICATE)
109
+ end
110
+ end
111
+ alias on_defs on_def
112
+
113
+ private
114
+
115
+ def allowed?(node)
116
+ allowed_method?(node.method_name) ||
117
+ matches_allowed_pattern?(node.method_name) ||
118
+ allowed_bang_method?(node) ||
119
+ node.operator_method? ||
120
+ node.body.nil?
121
+ end
122
+
123
+ def acceptable?(return_values)
124
+ # In `conservative` mode, if the method returns `super`, `zsuper`, or a
125
+ # non-comparison method call, the method name is acceptable.
126
+ return false unless conservative?
127
+
128
+ return_values.any? do |value|
129
+ value.type?(:super, :zsuper) || non_comparison_call?(value)
130
+ end
131
+ end
132
+
133
+ def non_comparison_call?(value)
134
+ value.call_type? && !value.comparison_method?
135
+ end
136
+
137
+ def return_values(node)
138
+ # Collect all the (implicit and explicit) return values of a node
139
+ return_values = Set.new(node.begin_type? ? [] : [extract_return_value(node)])
140
+
141
+ node.each_descendant(:return) do |return_node|
142
+ return_values << extract_return_value(return_node)
143
+ end
144
+
145
+ last_value = last_value(node)
146
+ return_values << last_value if last_value
147
+
148
+ process_return_values(return_values)
149
+ end
150
+
151
+ def all_return_values_boolean?(return_values)
152
+ values = return_values.reject { |value| value.type?(:super, :zsuper) }
153
+ return false if values.empty?
154
+
155
+ values.all? { |value| boolean_return?(value) }
156
+ end
157
+
158
+ def boolean_return?(value)
159
+ value.boolean_type? || (value.call_type? && value.comparison_method?)
160
+ end
161
+
162
+ def potential_non_predicate?(return_values)
163
+ # Assumes a method to be non-predicate if all return values are non-boolean literals.
164
+ #
165
+ # In `Mode: conservative`, if any of the return values is a boolean,
166
+ # the method name is acceptable.
167
+ # In `Mode: aggressive`, all return values must be booleans for a predicate
168
+ # method, or else an offense will be registered.
169
+ return false if conservative? && return_values.any? { |value| boolean_return?(value) }
170
+
171
+ return_values.any? do |value|
172
+ value.literal? && !value.boolean_type?
173
+ end
174
+ end
175
+
176
+ def extract_return_value(node)
177
+ return node unless node.return_type?
178
+
179
+ # `return` without a value is a `nil` return.
180
+ return s(:nil) if node.arguments.empty?
181
+
182
+ # When there's a multiple return, it cannot be a predicate
183
+ # so just return an `array` sexp for simplicity.
184
+ return s(:array) unless node.arguments.one?
185
+
186
+ node.first_argument
187
+ end
188
+
189
+ def last_value(node)
190
+ value = node.begin_type? ? node.children.last : node
191
+ value&.return_type? ? extract_return_value(value) : value
192
+ end
193
+
194
+ def process_return_values(return_values)
195
+ return_values.flat_map do |value|
196
+ if value.conditional?
197
+ process_return_values(extract_conditional_branches(value))
198
+ elsif and_or?(value)
199
+ process_return_values(extract_and_or_clauses(value))
200
+ else
201
+ value
202
+ end
203
+ end
204
+ end
205
+
206
+ def and_or?(node)
207
+ node.type?(:and, :or)
208
+ end
209
+
210
+ def extract_and_or_clauses(node)
211
+ # Recursively traverse an `and` or `or` node to collect all clauses within
212
+ return node unless and_or?(node)
213
+
214
+ [extract_and_or_clauses(node.lhs), extract_and_or_clauses(node.rhs)].flatten
215
+ end
216
+
217
+ def extract_conditional_branches(node)
218
+ return node unless node.conditional?
219
+
220
+ if node.type?(:while, :until)
221
+ # If there is no body, act as implicit `nil`.
222
+ node.body ? [last_value(node.body)] : [s(:nil)]
223
+ else
224
+ # Branches with no value act as an implicit `nil`.
225
+ node.branches.filter_map { |branch| branch ? last_value(branch) : s(:nil) }
226
+ end
227
+ end
228
+
229
+ def conservative?
230
+ cop_config.fetch('Mode', :conservative).to_sym == :conservative
231
+ end
232
+
233
+ def allowed_bang_method?(node)
234
+ return false unless allow_bang_methods?
235
+
236
+ node.bang_method?
237
+ end
238
+
239
+ def allow_bang_methods?
240
+ cop_config.fetch('AllowBangMethods', false)
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
@@ -100,7 +100,7 @@ module RuboCop
100
100
  # # good
101
101
  # def_node_matcher(:even?) { |value| }
102
102
  #
103
- class PredicateName < Base
103
+ class PredicatePrefix < Base
104
104
  include AllowedMethods
105
105
 
106
106
  # @!method dynamic_method_define(node)
@@ -143,7 +143,7 @@ module RuboCop
143
143
  next if predicate_prefixes.include?(forbidden_prefix)
144
144
 
145
145
  raise ValidationError, <<~MSG.chomp
146
- The `Naming/PredicateName` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes.
146
+ The `Naming/PredicatePrefix` cop is misconfigured. Prefix #{forbidden_prefix} must be included in NamePrefix because it is included in ForbiddenPrefixes.
147
147
  MSG
148
148
  end
149
149
  end
@@ -195,15 +195,27 @@ module RuboCop
195
195
  def autocorrect(corrector, node)
196
196
  case style
197
197
  when :group
198
- def_nodes = find_corresponding_def_nodes(node)
199
- return unless def_nodes.any?
200
-
201
- replace_defs(corrector, node, def_nodes)
198
+ autocorrect_group_style(corrector, node)
202
199
  when :inline
200
+ autocorrect_inline_style(corrector, node)
201
+ end
202
+ end
203
+
204
+ def autocorrect_group_style(corrector, node)
205
+ def_nodes = find_corresponding_def_nodes(node)
206
+ return unless def_nodes.any?
207
+
208
+ replace_defs(corrector, node, def_nodes)
209
+ end
210
+
211
+ def autocorrect_inline_style(corrector, node)
212
+ if node.parent&.begin_type?
213
+ remove_modifier_node_within_begin(corrector, node, node.parent)
214
+ else
203
215
  remove_nodes(corrector, node)
204
- select_grouped_def_nodes(node).each do |grouped_def_node|
205
- insert_inline_modifier(corrector, grouped_def_node, node.method_name)
206
- end
216
+ end
217
+ select_grouped_def_nodes(node).each do |grouped_def_node|
218
+ insert_inline_modifier(corrector, grouped_def_node, node.method_name)
207
219
  end
208
220
  end
209
221
 
@@ -224,9 +236,13 @@ module RuboCop
224
236
  end
225
237
 
226
238
  def offense?(node)
227
- (group_style? && access_modifier_is_inlined?(node) &&
228
- !node.parent&.if_type? && !right_siblings_same_inline_method?(node)) ||
229
- (inline_style? && access_modifier_is_not_inlined?(node))
239
+ if group_style?
240
+ return false if node.parent ? node.parent.if_type? : access_modifier_with_symbol?(node)
241
+
242
+ access_modifier_is_inlined?(node) && !right_siblings_same_inline_method?(node)
243
+ else
244
+ access_modifier_is_not_inlined?(node) && select_grouped_def_nodes(node).any?
245
+ end
230
246
  end
231
247
 
232
248
  def correctable_group_offense?(node)
@@ -331,6 +347,12 @@ module RuboCop
331
347
  end
332
348
  end
333
349
 
350
+ def remove_modifier_node_within_begin(corrector, modifier_node, begin_node)
351
+ def_node = begin_node.children[1]
352
+ range = modifier_node.source_range.begin.join(def_node.source_range.begin)
353
+ corrector.remove(range)
354
+ end
355
+
334
356
  def def_source(node, def_nodes)
335
357
  [
336
358
  *processed_source.ast_with_comments[node].map(&:text),
@@ -146,7 +146,7 @@ module RuboCop
146
146
  minimum_target_ruby_version 2.7
147
147
 
148
148
  FORWARDING_LVAR_TYPES = %i[splat kwsplat block_pass].freeze
149
- ADDITIONAL_ARG_TYPES = %i[lvar arg].freeze
149
+ ADDITIONAL_ARG_TYPES = %i[lvar arg optarg].freeze
150
150
 
151
151
  FORWARDING_MSG = 'Use shorthand syntax `...` for arguments forwarding.'
152
152
  ARGS_MSG = 'Use anonymous positional arguments forwarding (`*`).'
@@ -479,6 +479,9 @@ module RuboCop
479
479
  end
480
480
 
481
481
  def ruby_32_only_anonymous_forwarding?
482
+ # A block argument and an anonymous block argument are never passed together.
483
+ return false if @send_node.each_ancestor(:any_block).any?
484
+
482
485
  def_all_anonymous_args?(@def_node) && send_all_anonymous_args?(@send_node)
483
486
  end
484
487
 
@@ -149,9 +149,9 @@ module RuboCop
149
149
  return unless node.body.children.last
150
150
 
151
151
  last_child_leading_spaces = leading_spaces(node.body.children.last)
152
- return if leading_spaces(node).size == last_child_leading_spaces.size
152
+ return if spaces_size(leading_spaces(node)) == spaces_size(last_child_leading_spaces)
153
153
 
154
- column_delta = configured_indentation_width - last_child_leading_spaces.size
154
+ column_delta = configured_indentation_width - spaces_size(last_child_leading_spaces)
155
155
  return if column_delta.zero?
156
156
 
157
157
  AlignmentCorrector.correct(corrector, processed_source, node, column_delta)
@@ -161,6 +161,16 @@ module RuboCop
161
161
  node.source_range.source_line[/\A\s*/]
162
162
  end
163
163
 
164
+ def spaces_size(spaces_string)
165
+ mapping = { "\t" => tab_indentation_width }
166
+ spaces_string.chars.sum { |character| mapping.fetch(character, 1) }
167
+ end
168
+
169
+ def tab_indentation_width
170
+ config.for_cop('Layout/IndentationStyle')['IndentationWidth'] ||
171
+ configured_indentation_width
172
+ end
173
+
164
174
  def check_style(node, body, style)
165
175
  return if node.identifier.namespace&.cbase_type?
166
176
 
@@ -173,7 +173,7 @@ module RuboCop
173
173
  end
174
174
 
175
175
  def preferred_delimiters_config
176
- config.for_cop('Style/PercentLiteralDelimiters') ['PreferredDelimiters']
176
+ config.for_cop('Style/PercentLiteralDelimiters')['PreferredDelimiters']
177
177
  end
178
178
  end
179
179
  end
@@ -9,6 +9,9 @@ module RuboCop
9
9
  # although the difference generally isn't observable. If you require maximum
10
10
  # performance, consider using logical comparison.
11
11
  #
12
+ # @safety
13
+ # This cop is unsafe because the receiver may not respond to `between?`.
14
+ #
12
15
  # @example
13
16
  #
14
17
  # # bad
@@ -61,8 +64,8 @@ module RuboCop
61
64
 
62
65
  def register_offense(node, min_and_value, max_and_value)
63
66
  value = (min_and_value & max_and_value).first
64
- min = min_and_value.find { _1 != value }
65
- max = max_and_value.find { _1 != value }
67
+ min = min_and_value.find { _1 != value } || value
68
+ max = max_and_value.find { _1 != value } || value
66
69
 
67
70
  prefer = "#{value.source}.between?(#{min.source}, #{max.source})"
68
71
  add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
@@ -451,7 +451,9 @@ module RuboCop
451
451
  corrector.remove_preceding(condition.loc.else, condition.loc.else.column - column)
452
452
  end
453
453
 
454
- return unless condition.loc.end && !same_line?(condition.loc.end, condition)
454
+ return unless condition.loc.end && !same_line?(
455
+ condition.branches.last.parent.else_branch, condition.loc.end
456
+ )
455
457
 
456
458
  corrector.remove_preceding(condition.loc.end, condition.loc.end.column - column)
457
459
  end
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Checks for inheritance from `Data.define` to avoid creating the anonymous parent class.
7
+ # Inheriting from `Data.define` adds a superfluous level in inheritance tree.
7
8
  #
8
9
  # @safety
9
10
  # Autocorrection is unsafe because it will change the inheritance
@@ -17,12 +18,18 @@ module RuboCop
17
18
  # end
18
19
  # end
19
20
  #
21
+ # Person.ancestors
22
+ # # => [Person, #<Class:0x000000010b4e14a0>, Data, (...)]
23
+ #
20
24
  # # good
21
25
  # Person = Data.define(:first_name, :last_name) do
22
26
  # def age
23
27
  # 42
24
28
  # end
25
29
  # end
30
+ #
31
+ # Person.ancestors
32
+ # # => [Person, Data, (...)]
26
33
  class DataInheritance < Base
27
34
  include RangeHelp
28
35
  extend AutoCorrector