rubocop 1.80.2 → 1.81.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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +10 -0
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
  5. data/lib/rubocop/cli.rb +1 -2
  6. data/lib/rubocop/config_loader.rb +3 -1
  7. data/lib/rubocop/config_store.rb +5 -0
  8. data/lib/rubocop/cop/autocorrect_logic.rb +2 -2
  9. data/lib/rubocop/cop/correctors/alignment_corrector.rb +1 -1
  10. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +3 -1
  11. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  12. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  13. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  14. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  15. data/lib/rubocop/cop/layout/line_length.rb +9 -1
  16. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +6 -4
  17. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
  18. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  19. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  20. data/lib/rubocop/cop/lint/void.rb +7 -0
  21. data/lib/rubocop/cop/message_annotator.rb +1 -1
  22. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  23. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  24. data/lib/rubocop/cop/naming/predicate_method.rb +12 -0
  25. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  26. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  27. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  28. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  29. data/lib/rubocop/cop/style/it_block_parameter.rb +1 -1
  30. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  31. data/lib/rubocop/cop/style/numbered_parameters.rb +1 -1
  32. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  33. data/lib/rubocop/cop/style/redundant_format.rb +18 -3
  34. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -0
  35. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  36. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  37. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  38. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  39. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  40. data/lib/rubocop/lsp/diagnostic.rb +21 -20
  41. data/lib/rubocop/lsp/routes.rb +36 -9
  42. data/lib/rubocop/lsp/runtime.rb +2 -2
  43. data/lib/rubocop/lsp/server.rb +2 -2
  44. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  45. data/lib/rubocop/target_ruby.rb +10 -1
  46. data/lib/rubocop/version.rb +1 -1
  47. data/lib/rubocop.rb +1 -0
  48. data/lib/ruby_lsp/rubocop/addon.rb +23 -8
  49. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  50. metadata +11 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 427aedeb12fa3d9d84e7ccd8ea4c125b418a3170c0f91d02e66ef8717006fc0a
4
- data.tar.gz: f5a5b09520f27ed77d9cc5e2077ce9a55fa77db97bdd1d593e399551abd45bb8
3
+ metadata.gz: 740da46aac4303ae1e22af254e9e3032b28d1348feab710e3b7d7670b0803178
4
+ data.tar.gz: bdb0f8b8612d6f2576f3497bc07f879b6abeb755d6654428a7df0fc8a8c93a31
5
5
  SHA512:
6
- metadata.gz: ddcf6fc4b8e900f900f5734c647b3d681d731d6d4bf37729c4960a751ace231d8835ca9800d4e499d1908f7e0474445bbf2490f5c8b9408d1301d6bff1196794
7
- data.tar.gz: 94f856e78e6af3c0c78fd926268ae8349af663c2e7a1bc40a11842bb04db7a6baa1f239cb2c537ef8b442a4a76dfc09a8a7bfe10244abb2a78ed3b3b6600d38b
6
+ metadata.gz: f71823ab8429f83de38d37f56b9fceabec9fba2b119b8525e6dd0bc49082be3c3e94ea6b1a1f9eb5c63a9bf1d27e70bceb26eedade472a9723631c93ce82b7b8
7
+ data.tar.gz: 209b99bda6d9d1c140cb1959e33ef84eca2a9791dd97e91c8cba3de172a4f5fa8d3bd8feec7e397c35ea120f084e2f8bd95881bfa69cc8e6832812253d56f045
data/README.md CHANGED
@@ -51,7 +51,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
51
51
  in your `Gemfile`:
52
52
 
53
53
  ```rb
54
- gem 'rubocop', '~> 1.80', require: false
54
+ gem 'rubocop', '~> 1.81', require: false
55
55
  ```
56
56
 
57
57
  See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
data/config/default.yml CHANGED
@@ -3341,6 +3341,12 @@ Style/ArrayIntersect:
3341
3341
  Safe: false
3342
3342
  VersionAdded: '1.40'
3343
3343
 
3344
+ Style/ArrayIntersectWithSingleElement:
3345
+ Description: 'Use `include?(element)` instead of `intersect?([element])`.'
3346
+ Enabled: 'pending'
3347
+ Safe: false
3348
+ VersionAdded: '1.81'
3349
+
3344
3350
  Style/ArrayJoin:
3345
3351
  Description: 'Use Array#join instead of Array#*.'
3346
3352
  StyleGuide: '#array-join'
@@ -5844,10 +5850,14 @@ Style/TrailingCommaInArguments:
5844
5850
  # parenthesized method calls where each argument is on its own line.
5845
5851
  # If `consistent_comma`, the cop requires a comma after the last argument,
5846
5852
  # for all parenthesized method calls with arguments.
5853
+ # If `diff_comma`, the cop requires a comma after the last argument, but only
5854
+ # when that argument is followed by an immediate newline, even if
5855
+ # there is an inline comment.
5847
5856
  EnforcedStyleForMultiline: no_comma
5848
5857
  SupportedStylesForMultiline:
5849
5858
  - comma
5850
5859
  - consistent_comma
5860
+ - diff_comma
5851
5861
  - no_comma
5852
5862
 
5853
5863
  Style/TrailingCommaInArrayLiteral:
@@ -83,7 +83,7 @@ module RuboCop
83
83
  execute_runner
84
84
  @options.delete(:only)
85
85
  @config_store = ConfigStore.new
86
- @config_store.options_config = @options[:config] if @options[:config]
86
+ @config_store.apply_options!(@options)
87
87
  # Save the todo configuration of the LineLength cop.
88
88
  File.read(AUTO_GENERATED_FILE).lines.drop_while { |line| line.start_with?('#') }.join
89
89
  end
@@ -99,7 +99,7 @@ module RuboCop
99
99
 
100
100
  def reset_config_and_auto_gen_file
101
101
  @config_store = ConfigStore.new
102
- @config_store.options_config = @options[:config] if @options[:config]
102
+ @config_store.apply_options!(@options)
103
103
  File.open(AUTO_GENERATED_FILE, 'w') {} # create or truncate if exists
104
104
  add_inheritance_from_auto_generated_file(@options[:config])
105
105
  end
data/lib/rubocop/cli.rb CHANGED
@@ -164,8 +164,7 @@ module RuboCop
164
164
  set_options_to_pending_cops_reporter
165
165
  handle_editor_mode
166
166
 
167
- @config_store.options_config = @options[:config] if @options[:config]
168
- @config_store.force_default_config! if @options[:force_default_config]
167
+ @config_store.apply_options!(@options)
169
168
 
170
169
  handle_exiting_options
171
170
 
@@ -75,7 +75,9 @@ module RuboCop
75
75
 
76
76
  puts "configuration from #{absolute_path}" if debug?
77
77
 
78
- raise(TypeError, "Malformed configuration in #{absolute_path}") unless hash.is_a?(Hash)
78
+ unless hash.is_a?(Hash)
79
+ raise(ValidationError, "Malformed configuration in #{absolute_path}")
80
+ end
79
81
 
80
82
  hash
81
83
  end
@@ -25,6 +25,11 @@ module RuboCop
25
25
  @validated = true
26
26
  end
27
27
 
28
+ def apply_options!(options)
29
+ self.options_config = options[:config] if options[:config]
30
+ force_default_config! if options[:force_default_config]
31
+ end
32
+
28
33
  def options_config=(options_config)
29
34
  loaded_config = ConfigLoader.load_file(options_config)
30
35
  @options_config = ConfigLoader.merge_with_default(loaded_config, options_config)
@@ -94,7 +94,7 @@ module RuboCop
94
94
  end
95
95
 
96
96
  def surrounding_heredoc?(node)
97
- node.type?(:str, :dstr, :xstr) && node.heredoc?
97
+ node.any_str_type? && node.heredoc?
98
98
  end
99
99
 
100
100
  def heredoc_range(node)
@@ -106,7 +106,7 @@ module RuboCop
106
106
  end
107
107
 
108
108
  def string_continuation?(node)
109
- node.type?(:str, :dstr, :xstr) && node.source.match?(/\\\s*$/)
109
+ node.any_str_type? && node.source.match?(/\\\s*$/)
110
110
  end
111
111
 
112
112
  def multiline_string?(node)
@@ -57,7 +57,7 @@ module RuboCop
57
57
  def inside_string_ranges(node)
58
58
  return [] unless node.is_a?(Parser::AST::Node)
59
59
 
60
- node.each_node(:str, :dstr, :xstr).filter_map { |n| inside_string_range(n) }
60
+ node.each_node(:any_str).filter_map { |n| inside_string_range(n) }
61
61
  end
62
62
 
63
63
  def inside_string_range(node)
@@ -30,6 +30,8 @@ module RuboCop
30
30
  any_block: %i[block numblock itblock],
31
31
  any_def: %i[def defs],
32
32
  any_match_pattern: %i[match_pattern match_pattern_p],
33
+ any_str: %i[str dstr xstr],
34
+ any_sym: %i[sym dsym],
33
35
  argument: %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg forward_arg shadowarg],
34
36
  boolean: %i[true false],
35
37
  call: %i[send csend],
@@ -210,7 +212,7 @@ module RuboCop
210
212
  # A heredoc can be a `dstr` without interpolation, but if there is interpolation
211
213
  # there'll be a `begin` node, in which case, we cannot evaluate the pattern.
212
214
  def acceptable_heredoc?(node)
213
- node.type?(:str, :dstr) && node.heredoc? && node.each_child_node(:begin).none?
215
+ node.any_str_type? && node.heredoc? && node.each_child_node(:begin).none?
214
216
  end
215
217
 
216
218
  def process_pattern(pattern_node)
@@ -76,7 +76,7 @@ module RuboCop
76
76
  end
77
77
 
78
78
  def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
79
- new_identifier = node.first_argument
79
+ return unless (new_identifier = node.first_argument)
80
80
  return unless new_identifier.basic_literal?
81
81
 
82
82
  new_identifier = new_identifier.value
@@ -357,7 +357,7 @@ module RuboCop
357
357
  end
358
358
 
359
359
  def find_heredoc(node)
360
- node.each_node(:str, :dstr, :xstr).find(&:heredoc?)
360
+ node.each_node(:any_str).find(&:heredoc?)
361
361
  end
362
362
 
363
363
  def buffer
@@ -120,7 +120,7 @@ module RuboCop
120
120
  end
121
121
 
122
122
  def heredoc?(node)
123
- node.type?(:str, :dstr) && node.heredoc?
123
+ node.any_str_type? && node.heredoc?
124
124
  end
125
125
 
126
126
  def end_range(node)
@@ -76,28 +76,40 @@ module RuboCop
76
76
  # # good
77
77
  # class ErrorA < BaseError; end
78
78
  # class ErrorB < BaseError; end
79
- # class ErrorC < BaseError; end
80
79
  #
81
80
  # # good
82
81
  # class ErrorA < BaseError; end
83
82
  #
84
83
  # class ErrorB < BaseError; end
85
84
  #
86
- # class ErrorC < BaseError; end
85
+ # # good - DefLikeMacros: [memoize]
86
+ # memoize :attribute_a
87
+ # memoize :attribute_b
88
+ #
89
+ # # good
90
+ # memoize :attribute_a
91
+ #
92
+ # memoize :attribute_b
87
93
  #
88
94
  # @example AllowAdjacentOneLineDefs: false
89
95
  #
90
96
  # # bad
91
97
  # class ErrorA < BaseError; end
92
98
  # class ErrorB < BaseError; end
93
- # class ErrorC < BaseError; end
94
99
  #
95
100
  # # good
96
101
  # class ErrorA < BaseError; end
97
102
  #
98
103
  # class ErrorB < BaseError; end
99
104
  #
100
- # class ErrorC < BaseError; end
105
+ # # bad - DefLikeMacros: [memoize]
106
+ # memoize :attribute_a
107
+ # memoize :attribute_b
108
+ #
109
+ # # good
110
+ # memoize :attribute_a
111
+ #
112
+ # memoize :attribute_b
101
113
  #
102
114
  class EmptyLineBetweenDefs < Base
103
115
  include RangeHelp
@@ -158,6 +170,8 @@ module RuboCop
158
170
  def def_location(correction_node)
159
171
  if correction_node.any_block_type?
160
172
  correction_node.source_range.join(correction_node.children.first.source_range)
173
+ elsif correction_node.send_type?
174
+ correction_node.source_range
161
175
  else
162
176
  correction_node.loc.keyword.join(correction_node.loc.name)
163
177
  end
@@ -175,8 +189,14 @@ module RuboCop
175
189
  end
176
190
 
177
191
  def macro_candidate?(node)
178
- node.any_block_type? && node.children.first.macro? &&
179
- empty_line_between_macros.include?(node.children.first.method_name)
192
+ macro_candidate = if node.any_block_type?
193
+ node.send_node
194
+ elsif node.send_type?
195
+ node
196
+ end
197
+ return false unless macro_candidate
198
+
199
+ macro_candidate.macro? && empty_line_between_macros.include?(macro_candidate.method_name)
180
200
  end
181
201
 
182
202
  def method_candidate?(node)
@@ -240,7 +260,9 @@ module RuboCop
240
260
  end
241
261
 
242
262
  def def_start(node)
243
- if node.any_block_type? && node.children.first.send_type?
263
+ node = node.send_node if node.any_block_type?
264
+
265
+ if node.send_type?
244
266
  node.source_range.line
245
267
  else
246
268
  node.loc.keyword.line
@@ -252,11 +274,7 @@ module RuboCop
252
274
  end
253
275
 
254
276
  def end_loc(node)
255
- if node.any_def_type? && node.endless?
256
- node.source_range.end
257
- else
258
- node.loc.end
259
- end
277
+ node.source_range.end
260
278
  end
261
279
 
262
280
  def autocorrect_remove_lines(corrector, newline_pos, count)
@@ -134,6 +134,7 @@ module RuboCop
134
134
 
135
135
  def check_for_breakable_block(block_node)
136
136
  return unless block_node.single_line?
137
+ return if receiver_contains_heredoc?(block_node)
137
138
 
138
139
  line_index = block_node.loc.line - 1
139
140
  range = breakable_block_range(block_node)
@@ -321,7 +322,7 @@ module RuboCop
321
322
  def extract_heredocs(ast)
322
323
  return [] unless ast
323
324
 
324
- ast.each_node(:str, :dstr, :xstr).select(&:heredoc?).map do |node|
325
+ ast.each_node(:any_str).select(&:heredoc?).map do |node|
325
326
  body = node.location.heredoc_body
326
327
  delimiter = node.location.heredoc_end.source.strip
327
328
  [body.first_line...body.last_line, delimiter]
@@ -341,6 +342,13 @@ module RuboCop
341
342
  heredocs.any? { |range, _delimiter| range.cover?(line_number) }
342
343
  end
343
344
 
345
+ def receiver_contains_heredoc?(node)
346
+ return false unless (receiver = node.receiver)
347
+ return true if receiver.any_str_type? && receiver.heredoc?
348
+
349
+ receiver.each_descendant(:any_str).any?(&:heredoc?)
350
+ end
351
+
344
352
  def check_directive_line(line, line_index)
345
353
  length_without_directive = line_length_without_directive(line)
346
354
  return if length_without_directive <= max
@@ -102,10 +102,12 @@ module RuboCop
102
102
  return true if begins_its_line?(assignment_rhs.source_range)
103
103
  end
104
104
 
105
- given_style == :aligned &&
106
- (kw_node_with_special_indentation(node) ||
107
- assignment_node ||
108
- argument_in_method_call(node, :with_or_without_parentheses))
105
+ return false unless given_style == :aligned
106
+ return true if kw_node_with_special_indentation(node) || assignment_node
107
+
108
+ node = argument_in_method_call(node, :with_or_without_parentheses)
109
+
110
+ node.respond_to?(:def_modifier?) && !node.def_modifier?
109
111
  end
110
112
 
111
113
  def message(node, lhs, rhs)
@@ -194,6 +194,14 @@ module RuboCop
194
194
  def alignment_location(alignment_node)
195
195
  if begin_end_alignment_style == 'start_of_line'
196
196
  start_line_range(alignment_node)
197
+ elsif alignment_node.any_block_type?
198
+ # If the alignment node is a block, the `rescue`/`ensure` keyword should
199
+ # be aligned to the start of the block. It is possible that the block's
200
+ # `send_node` spans multiple lines, in which case it should align to the
201
+ # start of the last line.
202
+ send_node = alignment_node.send_node
203
+ range = processed_source.buffer.line_range(send_node.last_line)
204
+ range.adjust(begin_pos: range.source =~ /\S/)
197
205
  else
198
206
  alignment_node.source_range
199
207
  end
@@ -113,7 +113,7 @@ module RuboCop
113
113
  return [] unless ast
114
114
 
115
115
  heredocs = []
116
- ast.each_node(:str, :dstr, :xstr) do |node|
116
+ ast.each_node(:any_str) do |node|
117
117
  next unless node.heredoc?
118
118
 
119
119
  body = node.location.heredoc_body
@@ -125,13 +125,13 @@ module RuboCop
125
125
  next false if assignment_node.shorthand_asgn?
126
126
  next false unless assignment_node.parent
127
127
 
128
- node_within_block_or_conditional =
129
- node_within_block_or_conditional?(assignment_node.parent, argument.scope.node)
128
+ conditional_assignment =
129
+ conditional_assignment?(assignment_node.parent, argument.scope.node)
130
130
 
131
131
  unless uses_var?(assignment_node, argument.name)
132
132
  # It's impossible to decide whether a branch or block is executed,
133
133
  # so the precise reassignment location is undecidable.
134
- next false if node_within_block_or_conditional
134
+ next false if conditional_assignment
135
135
 
136
136
  yield(assignment.node, location_known)
137
137
  break
@@ -147,13 +147,13 @@ module RuboCop
147
147
  node.source_range.begin_pos
148
148
  end
149
149
 
150
- # Check whether the given node is nested into block or conditional.
150
+ # Check whether the given node is always executed or not
151
151
  #
152
- def node_within_block_or_conditional?(node, stop_search_node)
152
+ def conditional_assignment?(node, stop_search_node)
153
153
  return false if node == stop_search_node
154
154
 
155
- node.conditional? || node.block_type? ||
156
- node_within_block_or_conditional?(node.parent, stop_search_node)
155
+ node.conditional? || node.type?(:block, :rescue) ||
156
+ conditional_assignment?(node.parent, stop_search_node)
157
157
  end
158
158
 
159
159
  # Get argument references without assignments' references
@@ -16,6 +16,12 @@ module RuboCop
16
16
  # enumerator.each { |item| item >= 2 } #=> [2, 3]
17
17
  # ----
18
18
  #
19
+ # NOTE: Return values in assignment method definitions such as `def foo=(arg)` are
20
+ # detected because they are in a void context. However, autocorrection does not remove
21
+ # the return value, as that would change behavior. In such cases, whether to remove
22
+ # the return value or rename the method to something more appropriate should be left to
23
+ # the user.
24
+ #
19
25
  # @example CheckForMethodsWithNoSideEffects: false (default)
20
26
  # # bad
21
27
  # def some_method
@@ -233,6 +239,7 @@ module RuboCop
233
239
 
234
240
  def autocorrect_void_expression(corrector, node)
235
241
  return if node.parent.if_type?
242
+ return if (def_node = node.each_ancestor(:any_def).first) && def_node.assignment_method?
236
243
 
237
244
  corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
238
245
  end
@@ -102,7 +102,7 @@ module RuboCop
102
102
  def reference_urls
103
103
  urls = cop_config
104
104
  .values_at('References', 'Reference') # Support legacy Reference key
105
- .flat_map { Array(_1) }
105
+ .flat_map { |url| Array(url) }
106
106
  .reject(&:empty?)
107
107
 
108
108
  urls unless urls.empty?
@@ -227,7 +227,7 @@ module RuboCop
227
227
 
228
228
  def chained_to_heredoc?(node)
229
229
  while (node = node.receiver)
230
- return true if node.type?(:str, :dstr, :xstr) && node.heredoc?
230
+ return true if node.any_str_type? && node.heredoc?
231
231
  end
232
232
 
233
233
  false
@@ -140,7 +140,7 @@ module RuboCop
140
140
  end
141
141
 
142
142
  def last_item_precedes_newline?(node)
143
- after_last_item = node.children.last.source_range.end.join(node.loc.end.begin)
143
+ after_last_item = node.children.last.source_range.end.join(node.source_range.end)
144
144
 
145
145
  after_last_item.source.start_with?(/,?\s*(#.*)?\n/)
146
146
  end
@@ -113,6 +113,18 @@ module RuboCop
113
113
  # true
114
114
  # end
115
115
  #
116
+ # @example AllowedMethods: [call] (default)
117
+ # # good
118
+ # def call
119
+ # foo == bar
120
+ # end
121
+ #
122
+ # @example AllowedPatterns: [\Afoo]
123
+ # # good
124
+ # def foo?
125
+ # 'foo'
126
+ # end
127
+ #
116
128
  # @example AllowBangMethods: false (default)
117
129
  # # bad
118
130
  # def save!
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Use `include?(element)` instead of `intersect?([element])`.
7
+ #
8
+ # @safety
9
+ # The receiver might not be an array.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # array.intersect?([element])
14
+ #
15
+ # # good
16
+ # array.include?(element)
17
+ class ArrayIntersectWithSingleElement < Base
18
+ extend AutoCorrector
19
+
20
+ MSG = 'Use `include?(element)` instead of `intersect?([element])`.'
21
+
22
+ RESTRICT_ON_SEND = %i[intersect?].freeze
23
+
24
+ # @!method single_element(node)
25
+ def_node_matcher :single_element, <<~PATTERN
26
+ (send _ _ $(array $_))
27
+ PATTERN
28
+
29
+ def on_send(node)
30
+ array, element = single_element(node)
31
+ return unless array
32
+
33
+ add_offense(
34
+ node.source_range.with(begin_pos: node.loc.selector.begin_pos)
35
+ ) do |corrector|
36
+ corrector.replace(node.loc.selector, 'include?')
37
+ corrector.replace(
38
+ array,
39
+ array.percent_literal? ? element.value.inspect : element.source
40
+ )
41
+ end
42
+ end
43
+ alias on_csend on_send
44
+ end
45
+ end
46
+ end
47
+ end
@@ -96,7 +96,7 @@ module RuboCop
96
96
  elsif last_child.type?(:pair, :hash) || last_child.parent.array_type?
97
97
  false
98
98
  else
99
- last_child.last_line <= node.last_line
99
+ last_child.first_line <= node.first_line
100
100
  end
101
101
  end
102
102
 
@@ -56,7 +56,7 @@ module RuboCop
56
56
 
57
57
  def initialize(config = nil, options = nil)
58
58
  super
59
- @def_nodes = Set.new
59
+ @def_nodes = Set.new.compare_by_identity
60
60
  end
61
61
 
62
62
  def on_yield(node)
@@ -212,7 +212,7 @@ module RuboCop
212
212
  end
213
213
 
214
214
  def word_symbol_pair?(pair)
215
- return false unless pair.key.type?(:sym, :dsym)
215
+ return false unless pair.key.any_sym_type?
216
216
 
217
217
  acceptable_19_syntax_symbol?(pair.key.source)
218
218
  end
@@ -94,7 +94,7 @@ module RuboCop
94
94
  def on_itblock(node)
95
95
  case style
96
96
  when :allow_single_line
97
- return if node.single_line?
97
+ return if same_line?(node.source_range.begin, node.source_range.end)
98
98
 
99
99
  add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
100
100
  when :disallow
@@ -43,24 +43,26 @@ module RuboCop
43
43
  # @!method nil_check?(node)
44
44
  def_node_matcher :nil_check?, '(send _ :nil?)'
45
45
 
46
+ # rubocop:disable Metrics/AbcSize
46
47
  def on_send(node)
47
48
  return unless node.receiver
48
49
 
49
50
  style_check?(node) do
50
51
  add_offense(node.loc.selector) do |corrector|
51
- new_code = if prefer_comparison?
52
- node.source.sub('.nil?', ' == nil')
53
- else
54
- node.source.sub(/\s*={2,3}\s*nil/, '.nil?')
55
- end
56
-
57
- corrector.replace(node, new_code)
52
+ if prefer_comparison?
53
+ range = node.loc.dot.join(node.loc.selector.end)
54
+ corrector.replace(range, ' == nil')
55
+ else
56
+ range = node.receiver.source_range.end.join(node.source_range.end)
57
+ corrector.replace(range, '.nil?')
58
+ end
58
59
 
59
60
  parent = node.parent
60
61
  corrector.wrap(node, '(', ')') if parent.respond_to?(:method?) && parent.method?(:!)
61
62
  end
62
63
  end
63
64
  end
65
+ # rubocop:enable Metrics/AbcSize
64
66
 
65
67
  private
66
68
 
@@ -36,7 +36,7 @@ module RuboCop
36
36
  def on_numblock(node)
37
37
  if style == :disallow
38
38
  add_offense(node, message: MSG_DISALLOW)
39
- elsif node.multiline?
39
+ elsif !same_line?(node.source_range.begin, node.source_range.end)
40
40
  add_offense(node, message: MSG_MULTI_LINE)
41
41
  end
42
42
  end
@@ -51,7 +51,7 @@ module RuboCop
51
51
  end
52
52
 
53
53
  def string_message?(message)
54
- message.type?(:str, :dstr, :xstr)
54
+ message.any_str_type?
55
55
  end
56
56
 
57
57
  def fix_compact(node)
@@ -134,6 +134,7 @@ module RuboCop
134
134
  end
135
135
  end
136
136
 
137
+ # rubocop:disable Metrics/CyclomaticComplexity
137
138
  def all_fields_literal?(string, arguments)
138
139
  count = 0
139
140
  sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
@@ -141,6 +142,7 @@ module RuboCop
141
142
 
142
143
  sequences.each do |sequence|
143
144
  next if sequence.percent?
145
+ next if unknown_variable_width?(sequence, arguments)
144
146
 
145
147
  hash = arguments.detect(&:hash_type?)
146
148
  next unless (argument = find_argument(sequence, arguments, hash))
@@ -151,19 +153,32 @@ module RuboCop
151
153
 
152
154
  sequences.size == count
153
155
  end
156
+ # rubocop:enable Metrics/CyclomaticComplexity
154
157
 
158
+ # If the sequence has a variable (`*`) width, it cannot be autocorrected
159
+ # if the width is not given as a numeric literal argument
160
+ def unknown_variable_width?(sequence, arguments)
161
+ return false unless sequence.variable_width?
162
+
163
+ argument = arguments[sequence.variable_width_argument_number - 1]
164
+ !numeric?(argument)
165
+ end
166
+
167
+ # rubocop:disable Metrics/AbcSize
155
168
  def find_argument(sequence, arguments, hash)
156
169
  if hash && (sequence.annotated? || sequence.template?)
157
170
  find_hash_value_node(hash, sequence.name.to_sym).first
171
+ elsif sequence.variable_width?
172
+ # If the specifier contains `*`, the argument for the width can be ignored.
173
+ arguments.delete_at(sequence.variable_width_argument_number - 1)
174
+ arguments.shift
158
175
  elsif sequence.arg_number
159
176
  arguments[sequence.arg_number.to_i - 1]
160
177
  else
161
- # If the specifier contains `*`, the following arguments will be used
162
- # to specify the width and can be ignored.
163
- (sequence.arity - 1).times { arguments.shift }
164
178
  arguments.shift
165
179
  end
166
180
  end
181
+ # rubocop:enable Metrics/AbcSize
167
182
 
168
183
  def matching_argument?(sequence, argument)
169
184
  # Template specifiers don't give a type, any acceptable literal type is ok.