rubocop 1.71.2 → 1.73.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +51 -11
  4. data/config/internal_affairs.yml +16 -0
  5. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  6. data/lib/rubocop/comment_config.rb +1 -1
  7. data/lib/rubocop/config.rb +4 -0
  8. data/lib/rubocop/config_loader.rb +44 -8
  9. data/lib/rubocop/config_loader_resolver.rb +23 -9
  10. data/lib/rubocop/config_validator.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
  12. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  13. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  15. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  16. data/lib/rubocop/cop/internal_affairs.rb +1 -16
  17. data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
  18. data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
  19. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  20. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
  21. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  22. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  23. data/lib/rubocop/cop/layout/line_length.rb +3 -3
  24. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  25. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  26. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  27. data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
  28. data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
  29. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  30. data/lib/rubocop/cop/lint/literal_as_condition.rb +104 -7
  31. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  32. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  33. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +252 -0
  34. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  35. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
  36. data/lib/rubocop/cop/lint/void.rb +6 -0
  37. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  38. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  39. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  40. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  41. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
  42. data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
  43. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  44. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  45. data/lib/rubocop/cop/mixin/range_help.rb +3 -3
  46. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  47. data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
  48. data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
  49. data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
  50. data/lib/rubocop/cop/naming/variable_name.rb +64 -6
  51. data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
  52. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
  53. data/lib/rubocop/cop/style/endless_method.rb +163 -18
  54. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  55. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
  56. data/lib/rubocop/cop/style/redundant_condition.rb +34 -0
  57. data/lib/rubocop/cop/style/redundant_format.rb +250 -0
  58. data/lib/rubocop/cop/style/redundant_parentheses.rb +18 -4
  59. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  60. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  61. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  62. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  63. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  64. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  65. data/lib/rubocop/cop/util.rb +1 -1
  66. data/lib/rubocop/cop/utils/format_string.rb +7 -5
  67. data/lib/rubocop/cops_documentation_generator.rb +12 -1
  68. data/lib/rubocop/directive_comment.rb +35 -2
  69. data/lib/rubocop/lsp/runtime.rb +2 -0
  70. data/lib/rubocop/lsp/server.rb +0 -2
  71. data/lib/rubocop/options.rb +26 -11
  72. data/lib/rubocop/path_util.rb +4 -0
  73. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  74. data/lib/rubocop/plugin/load_error.rb +26 -0
  75. data/lib/rubocop/plugin/loader.rb +100 -0
  76. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  77. data/lib/rubocop/plugin.rb +46 -0
  78. data/lib/rubocop/rake_task.rb +4 -1
  79. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  80. data/lib/rubocop/rspec/shared_contexts.rb +15 -0
  81. data/lib/rubocop/rspec/support.rb +1 -0
  82. data/lib/rubocop/server/cache.rb +35 -2
  83. data/lib/rubocop/server/cli.rb +2 -2
  84. data/lib/rubocop/version.rb +17 -2
  85. data/lib/rubocop.rb +5 -1
  86. data/lib/ruby_lsp/rubocop/addon.rb +7 -10
  87. data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +5 -8
  88. metadata +35 -10
  89. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -0,0 +1,252 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for redundant uses of `to_s`, `to_sym`, `to_i`, `to_f`, `to_r`, `to_c`,
7
+ # `to_a`, `to_h`, and `to_set`.
8
+ #
9
+ # When one of these methods is called on an object of the same type, that object
10
+ # is returned, making the call unnecessary. The cop detects conversion methods called
11
+ # on object literals, class constructors, class `[]` methods, and the `Kernel` methods
12
+ # `String()`, `Integer()`, `Float()`, `Rational()`, `Complex()` and `Array()`.
13
+ #
14
+ # Specifically, these cases are detected for each conversion method:
15
+ #
16
+ # * `to_s` when called on a string literal, interpolated string, heredoc,
17
+ # or with `String.new` or `String()`.
18
+ # * `to_sym` when called on a symbol literal or interpolated symbol.
19
+ # * `to_i` when called on an integer literal or with `Integer()`.
20
+ # * `to_f` when called on a float literal of with `Float()`.
21
+ # * `to_r` when called on a rational literal or with `Rational()`.
22
+ # * `to_c` when called on a complex literal of with `Complex()`.
23
+ # * `to_a` when called on an array literal, or with `Array.new`, `Array()` or `Array[]`.
24
+ # * `to_h` when called on a hash literal, or with `Hash.new`, `Hash()` or `Hash[]`.
25
+ # * `to_set` when called on `Set.new` or `Set[]`.
26
+ #
27
+ # In all cases, chaining one same `to_*` conversion methods listed above is redundant.
28
+ #
29
+ # The cop can also register an offense for chaining conversion methods on methods that are
30
+ # expected to return a specific type regardless of receiver (eg. `foo.inspect.to_s`).
31
+ #
32
+ # @example
33
+ # # bad
34
+ # "text".to_s
35
+ # :sym.to_sym
36
+ # 42.to_i
37
+ # 8.5.to_f
38
+ # 12r.to_r
39
+ # 1i.to_c
40
+ # [].to_a
41
+ # {}.to_h
42
+ # Set.new.to_set
43
+ #
44
+ # # good
45
+ # "text"
46
+ # :sym
47
+ # 42
48
+ # 8.5
49
+ # 12r
50
+ # 1i
51
+ # []
52
+ # {}
53
+ # Set.new
54
+ #
55
+ # # bad
56
+ # Integer(var).to_i
57
+ #
58
+ # # good
59
+ # Integer(var)
60
+ #
61
+ # # good - chaining to a type constructor with exceptions suppressed
62
+ # # in this case, `Integer()` could return `nil`
63
+ # Integer(var, exception: false).to_i
64
+ #
65
+ # # bad - chaining the same conversion
66
+ # foo.to_s.to_s
67
+ #
68
+ # # good
69
+ # foo.to_s
70
+ #
71
+ # # bad - chaining a conversion to a method that is expected to return the same type
72
+ # inspect.to_s
73
+ #
74
+ # # good
75
+ # inspect
76
+ #
77
+ class RedundantTypeConversion < Base
78
+ extend AutoCorrector
79
+
80
+ MSG = 'Redundant `%<method>s` detected.'
81
+
82
+ # Maps conversion methods to the node types for the literals of that type
83
+ LITERAL_NODE_TYPES = {
84
+ to_s: %i[str dstr],
85
+ to_sym: %i[sym dsym],
86
+ to_i: %i[int],
87
+ to_f: %i[float],
88
+ to_r: %i[rational],
89
+ to_c: %i[complex],
90
+ to_a: %i[array],
91
+ to_h: %i[hash],
92
+ to_set: [] # sets don't have a literal or node type
93
+ }.freeze
94
+
95
+ # Maps each conversion method to the pattern matcher for that type's constructors
96
+ # Not every type has a constructor, for instance Symbol.
97
+ CONSTRUCTOR_MAPPING = {
98
+ to_s: 'string_constructor?',
99
+ to_i: 'integer_constructor?',
100
+ to_f: 'float_constructor?',
101
+ to_r: 'rational_constructor?',
102
+ to_c: 'complex_constructor?',
103
+ to_a: 'array_constructor?',
104
+ to_h: 'hash_constructor?',
105
+ to_set: 'set_constructor?'
106
+ }.freeze
107
+
108
+ # Methods that already are expected to return a given type, which makes a further
109
+ # conversion redundant.
110
+ TYPED_METHODS = { to_s: %i[inspect] }.freeze
111
+
112
+ CONVERSION_METHODS = Set[*LITERAL_NODE_TYPES.keys].freeze
113
+ RESTRICT_ON_SEND = CONVERSION_METHODS
114
+
115
+ private_constant :LITERAL_NODE_TYPES, :CONSTRUCTOR_MAPPING
116
+
117
+ # @!method type_constructor?(node, type_symbol)
118
+ def_node_matcher :type_constructor?, <<~PATTERN
119
+ (send {nil? (const {cbase nil?} :Kernel)} %1 ...)
120
+ PATTERN
121
+
122
+ # @!method string_constructor?(node)
123
+ def_node_matcher :string_constructor?, <<~PATTERN
124
+ {
125
+ (send (const {cbase nil?} :String) :new ...)
126
+ #type_constructor?(:String)
127
+ }
128
+ PATTERN
129
+
130
+ # @!method integer_constructor?(node)
131
+ def_node_matcher :integer_constructor?, <<~PATTERN
132
+ #type_constructor?(:Integer)
133
+ PATTERN
134
+
135
+ # @!method float_constructor?(node)
136
+ def_node_matcher :float_constructor?, <<~PATTERN
137
+ #type_constructor?(:Float)
138
+ PATTERN
139
+
140
+ # @!method rational_constructor?(node)
141
+ def_node_matcher :rational_constructor?, <<~PATTERN
142
+ #type_constructor?(:Rational)
143
+ PATTERN
144
+
145
+ # @!method complex_constructor?(node)
146
+ def_node_matcher :complex_constructor?, <<~PATTERN
147
+ #type_constructor?(:Complex)
148
+ PATTERN
149
+
150
+ # @!method array_constructor?(node)
151
+ def_node_matcher :array_constructor?, <<~PATTERN
152
+ {
153
+ (send (const {cbase nil?} :Array) {:new :[]} ...)
154
+ #type_constructor?(:Array)
155
+ }
156
+ PATTERN
157
+
158
+ # @!method hash_constructor?(node)
159
+ def_node_matcher :hash_constructor?, <<~PATTERN
160
+ {
161
+ (block (send (const {cbase nil?} :Hash) :new) ...)
162
+ (send (const {cbase nil?} :Hash) {:new :[]} ...)
163
+ (send {nil? (const {cbase nil?} :Kernel)} :Hash ...)
164
+ }
165
+ PATTERN
166
+
167
+ # @!method set_constructor?(node)
168
+ def_node_matcher :set_constructor?, <<~PATTERN
169
+ {
170
+ (send (const {cbase nil?} :Set) {:new :[]} ...)
171
+ }
172
+ PATTERN
173
+
174
+ # @!method exception_false_keyword_argument?(node)
175
+ def_node_matcher :exception_false_keyword_argument?, <<~PATTERN
176
+ (hash (pair (sym :exception) false))
177
+ PATTERN
178
+
179
+ # rubocop:disable Metrics/AbcSize
180
+ def on_send(node)
181
+ return if hash_or_set_with_block?(node)
182
+
183
+ receiver = find_receiver(node)
184
+ return unless literal_receiver?(node, receiver) ||
185
+ constructor?(node, receiver) ||
186
+ chained_conversion?(node, receiver) ||
187
+ chained_to_typed_method?(node, receiver)
188
+
189
+ message = format(MSG, method: node.method_name)
190
+
191
+ add_offense(node.loc.selector, message: message) do |corrector|
192
+ corrector.remove(node.loc.dot.join(node.loc.selector))
193
+ end
194
+ end
195
+ # rubocop:enable Metrics/AbcSize
196
+ alias on_csend on_send
197
+
198
+ private
199
+
200
+ def hash_or_set_with_block?(node)
201
+ return false if !node.method?(:to_h) && !node.method?(:to_set)
202
+
203
+ node.parent&.any_block_type? || node.last_argument&.block_pass_type?
204
+ end
205
+
206
+ def find_receiver(node)
207
+ receiver = node.receiver
208
+ return unless receiver
209
+
210
+ while receiver.begin_type?
211
+ break unless receiver.children.one?
212
+
213
+ receiver = receiver.children.first
214
+ end
215
+
216
+ receiver
217
+ end
218
+
219
+ def literal_receiver?(node, receiver)
220
+ return false unless receiver
221
+
222
+ receiver.type?(*LITERAL_NODE_TYPES[node.method_name])
223
+ end
224
+
225
+ def constructor?(node, receiver)
226
+ matcher = CONSTRUCTOR_MAPPING[node.method_name]
227
+ return false unless matcher
228
+
229
+ public_send(matcher, receiver) && !constructor_suppresses_exceptions?(receiver)
230
+ end
231
+
232
+ def constructor_suppresses_exceptions?(receiver)
233
+ # If the constructor suppresses exceptions with `exception: false`, it is possible
234
+ # it could return `nil`, and therefore a chained conversion is not redundant.
235
+ receiver.arguments.any? { |arg| exception_false_keyword_argument?(arg) }
236
+ end
237
+
238
+ def chained_conversion?(node, receiver)
239
+ return false unless receiver&.call_type?
240
+
241
+ receiver.method?(node.method_name)
242
+ end
243
+
244
+ def chained_to_typed_method?(node, receiver)
245
+ return false unless receiver&.call_type?
246
+
247
+ TYPED_METHODS.fetch(node.method_name, []).include?(receiver.method_name)
248
+ end
249
+ end
250
+ end
251
+ end
252
+ end
@@ -0,0 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for cases where exceptions unrelated to the numeric constructors `Integer()`,
7
+ # `Float()`, `BigDecimal()`, `Complex()`, and `Rational()` may be unintentionally swallowed.
8
+ #
9
+ # @safety
10
+ # The cop is unsafe for autocorrection because unexpected errors occurring in the argument
11
+ # passed to numeric constructor (e.g., `Integer()`) can lead to incompatible behavior.
12
+ # For example, changing it to `Integer(potential_exception_method_call, exception: false)`
13
+ # ensures that exceptions raised by `potential_exception_method_call` are not ignored.
14
+ #
15
+ # [source,ruby]
16
+ # ----
17
+ # Integer(potential_exception_method_call) rescue nil
18
+ # ----
19
+ #
20
+ # @example
21
+ #
22
+ # # bad
23
+ # Integer(arg) rescue nil
24
+ #
25
+ # # bad
26
+ # begin
27
+ # Integer(arg)
28
+ # rescue
29
+ # nil
30
+ # end
31
+ #
32
+ # # bad
33
+ # begin
34
+ # Integer(arg)
35
+ # rescue
36
+ # end
37
+ #
38
+ # # good
39
+ # Integer(arg, exception: false)
40
+ #
41
+ class SuppressedExceptionInNumberConversion < Base
42
+ extend AutoCorrector
43
+ extend TargetRubyVersion
44
+
45
+ MSG = 'Use `%<prefer>s` instead.'
46
+ EXPECTED_EXCEPTION_CLASSES = %w[ArgumentError TypeError ::ArgumentError ::TypeError].freeze
47
+
48
+ # @!method numeric_constructor_rescue_nil(node)
49
+ def_node_matcher :numeric_constructor_rescue_nil, <<~PATTERN
50
+ (rescue
51
+ $#numeric_method?
52
+ (resbody nil? nil? (nil)) nil?)
53
+ PATTERN
54
+
55
+ # @!method begin_numeric_constructor_rescue_nil(node)
56
+ def_node_matcher :begin_numeric_constructor_rescue_nil, <<~PATTERN
57
+ (kwbegin
58
+ (rescue
59
+ $#numeric_method?
60
+ (resbody $_? nil?
61
+ {(nil) nil?}) nil?))
62
+ PATTERN
63
+
64
+ # @!method numeric_method?(node)
65
+ def_node_matcher :numeric_method?, <<~PATTERN
66
+ {
67
+ (call #constructor_receiver? {:Integer :BigDecimal :Complex :Rational}
68
+ _ _?)
69
+ (call #constructor_receiver? :Float
70
+ _)
71
+ }
72
+ PATTERN
73
+
74
+ # @!method constructor_receiver?(node)
75
+ def_node_matcher :constructor_receiver?, <<~PATTERN
76
+ {nil? (const {nil? cbase} :Kernel)}
77
+ PATTERN
78
+
79
+ minimum_target_ruby_version 2.6
80
+
81
+ # rubocop:disable Metrics/AbcSize
82
+ def on_rescue(node)
83
+ if (method, exception_classes = begin_numeric_constructor_rescue_nil(node.parent))
84
+ return unless expected_exception_classes_only?(exception_classes)
85
+
86
+ node = node.parent
87
+ else
88
+ return unless (method = numeric_constructor_rescue_nil(node))
89
+ end
90
+
91
+ arguments = method.arguments.map(&:source) << 'exception: false'
92
+ prefer = "#{method.method_name}(#{arguments.join(', ')})"
93
+ prefer = "#{method.receiver.source}#{method.loc.dot.source}#{prefer}" if method.receiver
94
+
95
+ add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
96
+ corrector.replace(node, prefer)
97
+ end
98
+ end
99
+ # rubocop:enable Metrics/AbcSize
100
+
101
+ private
102
+
103
+ def expected_exception_classes_only?(exception_classes)
104
+ return true unless (arguments = exception_classes.first)
105
+
106
+ (arguments.values.map(&:source) - EXPECTED_EXCEPTION_CLASSES).none?
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for useless constant scoping. Private constants must be defined using
7
+ # `private_constant` or `class << self`. Even if `private` access modifier is used,
8
+ # it is public scope despite its appearance.
9
+ #
10
+ # It does not support autocorrection due to behavior change and multiple ways to fix it.
11
+ # Or a public constant may be intended.
12
+ #
13
+ # @example
14
+ #
15
+ # # bad
16
+ # class Foo
17
+ # private
18
+ # PRIVATE_CONST = 42
19
+ # end
20
+ #
21
+ # # good
22
+ # class Foo
23
+ # PRIVATE_CONST = 42
24
+ # private_constant :PRIVATE_CONST
25
+ # end
26
+ #
27
+ # # good
28
+ # class Foo
29
+ # class << self
30
+ # private
31
+ # PRIVATE_CONST = 42
32
+ # end
33
+ # end
34
+ #
35
+ # # good
36
+ # class Foo
37
+ # PUBLIC_CONST = 42 # If private scope is not intended.
38
+ # end
39
+ #
40
+ class UselessConstantScoping < Base
41
+ MSG = 'Useless `private` access modifier for constant scope.'
42
+
43
+ # @!method private_constants(node)
44
+ def_node_matcher :private_constants, <<~PATTERN
45
+ (send nil? :private_constant $...)
46
+ PATTERN
47
+
48
+ def on_casgn(node)
49
+ return if node.each_ancestor(:sclass).any?
50
+ return unless after_private_modifier?(node.left_siblings)
51
+ return if private_constantize?(node.right_siblings, node.name)
52
+
53
+ add_offense(node)
54
+ end
55
+
56
+ private
57
+
58
+ def after_private_modifier?(left_siblings)
59
+ access_modifier_candidates = left_siblings.compact.select do |left_sibling|
60
+ left_sibling.respond_to?(:send_type?) && left_sibling.send_type?
61
+ end
62
+
63
+ access_modifier_candidates.any? do |candidate|
64
+ candidate.command?(:private) && candidate.arguments.none?
65
+ end
66
+ end
67
+
68
+ def private_constantize?(right_siblings, const_value)
69
+ private_constant_arguments = right_siblings.map { |node| private_constants(node) }
70
+
71
+ private_constant_values = private_constant_arguments.flatten.filter_map do |constant|
72
+ constant.value.to_sym if constant.respond_to?(:value)
73
+ end
74
+
75
+ private_constant_values.include?(const_value)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -125,9 +125,14 @@ module RuboCop
125
125
  check_nonmutating(expr)
126
126
  end
127
127
 
128
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
128
129
  def check_void_op(node, &block)
129
130
  node = node.children.first while node.begin_type?
130
131
  return unless node.call_type? && OPERATORS.include?(node.method_name)
132
+ if !UNARY_OPERATORS.include?(node.method_name) && node.loc.dot && node.arguments.none?
133
+ return
134
+ end
135
+
131
136
  return if block && yield(node)
132
137
 
133
138
  add_offense(node.loc.selector,
@@ -135,6 +140,7 @@ module RuboCop
135
140
  autocorrect_void_op(corrector, node)
136
141
  end
137
142
  end
143
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
138
144
 
139
145
  def check_var(node)
140
146
  return unless node.variable? || node.const_type?
@@ -26,6 +26,13 @@ module RuboCop
26
26
  extend NodePattern::Macros
27
27
  include RuboCop::AST::Sexp
28
28
 
29
+ VAR_SETTER_TO_GETTER = {
30
+ lvasgn: :lvar,
31
+ ivasgn: :ivar,
32
+ cvasgn: :cvar,
33
+ gvasgn: :gvar
34
+ }.freeze
35
+
29
36
  # Plug into the calculator
30
37
  def initialize(node, discount_repeated_attributes: false)
31
38
  super(node)
@@ -114,13 +121,6 @@ module RuboCop
114
121
  calls.fetch(value) { yield [calls, value] }
115
122
  end
116
123
 
117
- VAR_SETTER_TO_GETTER = {
118
- lvasgn: :lvar,
119
- ivasgn: :ivar,
120
- cvasgn: :cvar,
121
- gvasgn: :gvar
122
- }.freeze
123
-
124
124
  # @returns `[receiver, method | nil]` for the given setter `node`
125
125
  # or `nil` if it is not a setter.
126
126
  def setter_to_getter(node)
@@ -5,10 +5,10 @@ module RuboCop
5
5
  # This module checks for nodes that should be aligned to the left or right.
6
6
  # This amount is determined by the instance variable @column_delta.
7
7
  module Alignment
8
- private
9
-
10
8
  SPACE = ' '
11
9
 
10
+ private
11
+
12
12
  attr_reader :column_delta
13
13
 
14
14
  def configured_indentation_width
@@ -18,12 +18,12 @@ module RuboCop
18
18
  end
19
19
 
20
20
  # @deprecated Use allowed_line? instead
21
- def ignored_line?
21
+ def ignored_line?(line)
22
22
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
23
23
  `ignored_line?` is deprecated. Use `allowed_line?` instead.
24
24
  WARNING
25
25
 
26
- allowed_line?
26
+ allowed_line?(line)
27
27
  end
28
28
 
29
29
  def matches_allowed_pattern?(line)
@@ -31,12 +31,12 @@ module RuboCop
31
31
  end
32
32
 
33
33
  # @deprecated Use matches_allowed_pattern? instead
34
- def matches_ignored_pattern?
34
+ def matches_ignored_pattern?(line)
35
35
  warn Rainbow(<<~WARNING).yellow, uplevel: 1
36
36
  `matches_ignored_pattern?` is deprecated. Use `matches_allowed_pattern?` instead.
37
37
  WARNING
38
38
 
39
- matches_allowed_pattern?
39
+ matches_allowed_pattern?(line)
40
40
  end
41
41
 
42
42
  def allowed_patterns
@@ -82,7 +82,7 @@ module RuboCop
82
82
  next_sibling.source_range
83
83
  next_sibling.loc.line
84
84
  elsif (parent = node.parent)
85
- if parent.loc.respond_to?(:end) && parent.loc.end
85
+ if parent.loc?(:end)
86
86
  parent.loc.end.line
87
87
  else
88
88
  parent.loc.line
@@ -11,6 +11,24 @@ module RuboCop
11
11
  DO_NOT_MIX_OMIT_VALUE_MSG = "#{DO_NOT_MIX_MSG_PREFIX} #{OMIT_HASH_VALUE_MSG}"
12
12
  DO_NOT_MIX_EXPLICIT_VALUE_MSG = "#{DO_NOT_MIX_MSG_PREFIX} #{EXPLICIT_HASH_VALUE_MSG}"
13
13
 
14
+ DefNode = Struct.new(:node) do
15
+ def selector
16
+ if node.loc.respond_to?(:selector)
17
+ node.loc.selector
18
+ else
19
+ node.loc.keyword
20
+ end
21
+ end
22
+
23
+ def first_argument
24
+ node.first_argument
25
+ end
26
+
27
+ def last_argument
28
+ node.last_argument
29
+ end
30
+ end
31
+
14
32
  def on_hash_for_mixed_shorthand(hash_node)
15
33
  return if ignore_mixed_hash_shorthand_syntax?(hash_node)
16
34
 
@@ -212,24 +230,6 @@ module RuboCop
212
230
  register_offense(pair_node, OMIT_HASH_VALUE_MSG, replacement)
213
231
  end
214
232
  end
215
-
216
- DefNode = Struct.new(:node) do
217
- def selector
218
- if node.loc.respond_to?(:selector)
219
- node.loc.selector
220
- else
221
- node.loc.keyword
222
- end
223
- end
224
-
225
- def first_argument
226
- node.first_argument
227
- end
228
-
229
- def last_argument
230
- node.last_argument
231
- end
232
- end
233
233
  end
234
234
  end
235
235
  # rubocop:enable Metrics/ModuleLength
@@ -23,7 +23,7 @@ module RuboCop
23
23
  (call _ _)
24
24
  (args
25
25
  $(arg _key)
26
- (arg _))
26
+ $(arg _))
27
27
  {
28
28
  $(send
29
29
  {(lvar _key) $_ _ | _ $_ (lvar _key)})
@@ -67,7 +67,7 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def extracts_hash_subset?(block)
70
- block_with_first_arg_check?(block) do |key_arg, send_node, method|
70
+ block_with_first_arg_check?(block) do |key_arg, value_arg, send_node, method|
71
71
  # Only consider methods that have one argument
72
72
  return false unless send_node.arguments.one?
73
73
 
@@ -76,15 +76,22 @@ module RuboCop
76
76
 
77
77
  case method
78
78
  when :include?, :exclude?
79
- send_node.first_argument.source == key_arg.source
79
+ slices_key?(send_node, :first_argument, key_arg, value_arg)
80
80
  when :in?
81
- send_node.receiver.source == key_arg.source
81
+ slices_key?(send_node, :receiver, key_arg, value_arg)
82
82
  else
83
83
  true
84
84
  end
85
85
  end
86
86
  end
87
87
 
88
+ def slices_key?(send_node, method, key_arg, value_arg)
89
+ return false if using_value_variable?(send_node, value_arg)
90
+
91
+ node = method == :receiver ? send_node.receiver : send_node.first_argument
92
+ node.source == key_arg.source
93
+ end
94
+
88
95
  def range_include?(send_node)
89
96
  # When checking `include?`, `exclude?` and `in?` for offenses, if the receiver
90
97
  # or first argument is a range, an offense should not be registered.
@@ -97,6 +104,14 @@ module RuboCop
97
104
  receiver.range_type?
98
105
  end
99
106
 
107
+ def using_value_variable?(send_node, value_arg)
108
+ # If the receiver of `include?` or `exclude?`, or the first argument of `in?` is the
109
+ # hash value block argument, an offense should not be registered.
110
+ # ie. `v.include?(k)` or `k.in?(v)`
111
+ (send_node.receiver.lvar_type? && send_node.receiver.name == value_arg.name) ||
112
+ (send_node.first_argument.lvar_type? && send_node.first_argument.name == value_arg.name)
113
+ end
114
+
100
115
  def supported_subset_method?(method)
101
116
  if active_support_extensions_enabled?
102
117
  ACTIVE_SUPPORT_SUBSET_METHODS.include?(method)