rubocop 1.57.2 → 1.60.1

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 (139) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/config/default.yml +46 -3
  5. data/lib/rubocop/config.rb +0 -2
  6. data/lib/rubocop/config_loader.rb +0 -1
  7. data/lib/rubocop/config_obsoletion.rb +11 -8
  8. data/lib/rubocop/config_validator.rb +0 -2
  9. data/lib/rubocop/cop/base.rb +6 -0
  10. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  11. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  13. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  14. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  15. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  16. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  17. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  18. data/lib/rubocop/cop/layout/end_alignment.rb +5 -1
  19. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  20. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  21. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  22. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  23. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  25. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
  26. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  27. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  28. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  29. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  30. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  31. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  32. data/lib/rubocop/cop/lint/debugger.rb +2 -1
  33. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  34. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  35. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  36. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  37. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  38. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  39. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  40. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  41. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  42. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +43 -0
  43. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  44. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  45. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  46. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  47. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  48. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  49. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  50. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  51. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  52. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  53. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  54. data/lib/rubocop/cop/lint/void.rb +14 -1
  55. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  56. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  57. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  58. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  59. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  60. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  61. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  62. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  63. data/lib/rubocop/cop/security/open.rb +2 -2
  64. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  65. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  66. data/lib/rubocop/cop/style/arguments_forwarding.rb +120 -17
  67. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  68. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  69. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  70. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  71. data/lib/rubocop/cop/style/class_check.rb +1 -0
  72. data/lib/rubocop/cop/style/collection_compact.rb +18 -8
  73. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  74. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  75. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  76. data/lib/rubocop/cop/style/date_time.rb +5 -4
  77. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  78. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  79. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  80. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  81. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  82. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  83. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  84. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  85. data/lib/rubocop/cop/style/identical_conditional_branches.rb +4 -1
  86. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  87. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  88. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  89. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  90. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  91. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  92. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  93. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  94. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  95. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
  96. data/lib/rubocop/cop/style/next.rb +1 -1
  97. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  98. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  99. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  100. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  101. data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
  102. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +3 -3
  103. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  104. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  105. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  106. data/lib/rubocop/cop/style/redundant_parentheses.rb +33 -10
  107. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  108. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  109. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  110. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  111. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  112. data/lib/rubocop/cop/style/sample.rb +2 -1
  113. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  114. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  115. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  116. data/lib/rubocop/cop/style/single_argument_dig.rb +5 -2
  117. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  118. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  119. data/lib/rubocop/cop/style/strip.rb +7 -4
  120. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  121. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  122. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  123. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  124. data/lib/rubocop/ext/regexp_node.rb +9 -4
  125. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  126. data/lib/rubocop/formatter/html_formatter.rb +1 -2
  127. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  128. data/lib/rubocop/formatter.rb +1 -1
  129. data/lib/rubocop/lsp/routes.rb +1 -1
  130. data/lib/rubocop/options.rb +0 -8
  131. data/lib/rubocop/result_cache.rb +0 -1
  132. data/lib/rubocop/rspec/shared_contexts.rb +6 -0
  133. data/lib/rubocop/rspec/support.rb +1 -0
  134. data/lib/rubocop/runner.rb +1 -1
  135. data/lib/rubocop/server/cache.rb +1 -2
  136. data/lib/rubocop/version.rb +1 -1
  137. data/lib/rubocop.rb +4 -0
  138. metadata +15 -10
  139. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Identifies usages of `arr[0]` and `arr[-1]` and suggests to change
7
+ # them to use `arr.first` and `arr.last` instead.
8
+ #
9
+ # The cop is disabled by default due to safety concerns.
10
+ #
11
+ # @safety
12
+ # This cop is unsafe because `[0]` or `[-1]` can be called on a Hash,
13
+ # which returns a value for `0` or `-1` key, but changing these to use
14
+ # `.first` or `.last` will return first/last tuple instead. Also, String
15
+ # does not implement `first`/`last` methods.
16
+ #
17
+ # @example
18
+ # # bad
19
+ # arr[0]
20
+ # arr[-1]
21
+ #
22
+ # # good
23
+ # arr.first
24
+ # arr.last
25
+ # arr[0] = 2
26
+ # arr[0][-2]
27
+ #
28
+ class ArrayFirstLast < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Use `%<preferred>s`.'
32
+ RESTRICT_ON_SEND = %i[[]].freeze
33
+
34
+ # rubocop:disable Metrics/AbcSize
35
+ def on_send(node)
36
+ return unless node.arguments.size == 1 && node.first_argument.int_type?
37
+
38
+ value = node.first_argument.value
39
+ return unless [0, -1].include?(value)
40
+
41
+ node = innermost_braces_node(node)
42
+ return if node.parent && brace_method?(node.parent)
43
+
44
+ preferred = (value.zero? ? 'first' : 'last')
45
+ add_offense(node.loc.selector, message: format(MSG, preferred: preferred)) do |corrector|
46
+ corrector.replace(node.loc.selector, ".#{preferred}")
47
+ end
48
+ end
49
+ # rubocop:enable Metrics/AbcSize
50
+
51
+ private
52
+
53
+ def innermost_braces_node(node)
54
+ node = node.receiver while node.receiver.send_type? && node.receiver.method?(:[])
55
+ node
56
+ end
57
+
58
+ def brace_method?(node)
59
+ node.send_type? && (node.method?(:[]) || node.method?(:[]=))
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -16,31 +16,38 @@ module RuboCop
16
16
  # File.open('file') do |f|
17
17
  # # ...
18
18
  # end
19
+ #
20
+ # # bad
21
+ # f = Tempfile.open('temp')
22
+ #
23
+ # # good
24
+ # Tempfile.open('temp') do |f|
25
+ # # ...
26
+ # end
19
27
  class AutoResourceCleanup < Base
20
- MSG = 'Use the block version of `%<class>s.%<method>s`.'
21
-
22
- TARGET_METHODS = { File: :open }.freeze
28
+ MSG = 'Use the block version of `%<current>s`.'
29
+ RESTRICT_ON_SEND = %i[open].freeze
23
30
 
24
- RESTRICT_ON_SEND = TARGET_METHODS.values.freeze
31
+ # @!method file_open_method?(node)
32
+ def_node_matcher :file_open_method?, <<~PATTERN
33
+ (send (const {nil? cbase} {:File :Tempfile}) :open ...)
34
+ PATTERN
25
35
 
26
36
  def on_send(node)
27
- TARGET_METHODS.each do |target_class, target_method|
28
- next if node.method_name != target_method
37
+ return if !file_open_method?(node) || cleanup?(node)
29
38
 
30
- target_receiver = s(:const, nil, target_class)
31
- next if node.receiver != target_receiver
39
+ current = node.receiver.source_range.begin.join(node.selector.end).source
32
40
 
33
- next if cleanup?(node)
34
-
35
- add_offense(node, message: format(MSG, class: target_class, method: target_method))
36
- end
41
+ add_offense(node, message: format(MSG, current: current))
37
42
  end
38
43
 
39
44
  private
40
45
 
41
46
  def cleanup?(node)
42
- parent = node.parent
43
- node.block_argument? || (parent && (parent.block_type? || !parent.lvasgn_type?))
47
+ return true if node.block_argument?
48
+ return false unless (parent = node.parent)
49
+
50
+ parent.block_type? || !parent.lvasgn_type?
44
51
  end
45
52
  end
46
53
  end
@@ -33,7 +33,7 @@ module RuboCop
33
33
  def on_class(class_node)
34
34
  @macros_to_rewrite[class_node] = Set.new
35
35
 
36
- find_macros(class_node.body).each do |_visibility, macros|
36
+ find_macros(class_node.body).each_value do |macros|
37
37
  bisected = find_bisection(macros)
38
38
  next unless bisected.any?
39
39
 
@@ -74,7 +74,7 @@ module RuboCop
74
74
  def find_macros(class_def)
75
75
  # Find all the macros (`attr_reader`, `attr_writer`, etc.) in the class body
76
76
  # and turn them into `Macro` objects so that they can be processed.
77
- return [] if !class_def || class_def.def_type?
77
+ return {} if !class_def || class_def.def_type?
78
78
 
79
79
  send_nodes =
80
80
  if class_def.send_type?
@@ -125,7 +125,7 @@ module RuboCop
125
125
  when :==, :eql?, :equal?
126
126
  find_target_in_equality_node(node)
127
127
  when :===
128
- node.arguments.first
128
+ node.first_argument
129
129
  when :include?, :cover?
130
130
  find_target_in_include_or_cover_node(node)
131
131
  when :match, :match?, :=~
@@ -134,7 +134,7 @@ module RuboCop
134
134
  end
135
135
 
136
136
  def find_target_in_equality_node(node)
137
- argument = node.arguments.first
137
+ argument = node.first_argument
138
138
  receiver = node.receiver
139
139
  return unless argument && receiver
140
140
 
@@ -152,7 +152,7 @@ module RuboCop
152
152
  end
153
153
 
154
154
  def find_target_in_match_node(node)
155
- argument = node.arguments.first
155
+ argument = node.first_argument
156
156
  receiver = node.receiver
157
157
  return unless receiver
158
158
 
@@ -185,7 +185,7 @@ module RuboCop
185
185
  def condition_from_send_node(node, target)
186
186
  case node.method_name
187
187
  when :is_a?
188
- node.arguments.first if node.receiver == target
188
+ node.first_argument if node.receiver == target
189
189
  when :==, :eql?, :equal?
190
190
  condition_from_equality_node(node, target)
191
191
  when :=~, :match, :match?
@@ -40,6 +40,7 @@ module RuboCop
40
40
  corrector.replace(node.loc.selector, replacement)
41
41
  end
42
42
  end
43
+ alias on_csend on_send
43
44
 
44
45
  def message(node)
45
46
  if node.method?(:is_a?)
@@ -23,6 +23,8 @@ module RuboCop
23
23
  # array.reject { |e| e.nil? }
24
24
  # array.delete_if { |e| e.nil? }
25
25
  # array.select { |e| !e.nil? }
26
+ # array.grep_v(nil)
27
+ # array.grep_v(NilClass)
26
28
  #
27
29
  # # good
28
30
  # array.compact
@@ -46,14 +48,14 @@ module RuboCop
46
48
  extend TargetRubyVersion
47
49
 
48
50
  MSG = 'Use `%<good>s` instead of `%<bad>s`.'
49
- RESTRICT_ON_SEND = %i[reject delete_if reject! select select!].freeze
51
+ RESTRICT_ON_SEND = %i[reject delete_if reject! select select! grep_v].freeze
50
52
  TO_ENUM_METHODS = %i[to_enum lazy].freeze
51
53
 
52
54
  minimum_target_ruby_version 2.4
53
55
 
54
56
  # @!method reject_method_with_block_pass?(node)
55
57
  def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
56
- (send !nil? {:reject :delete_if :reject!}
58
+ (call !nil? {:reject :delete_if :reject!}
57
59
  (block_pass
58
60
  (sym :nil?)))
59
61
  PATTERN
@@ -61,24 +63,29 @@ module RuboCop
61
63
  # @!method reject_method?(node)
62
64
  def_node_matcher :reject_method?, <<~PATTERN
63
65
  (block
64
- (send
66
+ (call
65
67
  !nil? {:reject :delete_if :reject!})
66
68
  $(args ...)
67
- (send
69
+ (call
68
70
  $(lvar _) :nil?))
69
71
  PATTERN
70
72
 
71
73
  # @!method select_method?(node)
72
74
  def_node_matcher :select_method?, <<~PATTERN
73
75
  (block
74
- (send
76
+ (call
75
77
  !nil? {:select :select!})
76
78
  $(args ...)
77
- (send
78
- (send
79
+ (call
80
+ (call
79
81
  $(lvar _) :nil?) :!))
80
82
  PATTERN
81
83
 
84
+ # @!method grep_v_with_nil?(node)
85
+ def_node_matcher :grep_v_with_nil?, <<~PATTERN
86
+ (send _ :grep_v {(nil) (const {nil? cbase} :NilClass)})
87
+ PATTERN
88
+
82
89
  def on_send(node)
83
90
  return unless (range = offense_range(node))
84
91
  return if allowed_receiver?(node.receiver)
@@ -91,11 +98,13 @@ module RuboCop
91
98
 
92
99
  add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
93
100
  end
101
+ alias on_csend on_send
94
102
 
95
103
  private
96
104
 
105
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
106
  def offense_range(node)
98
- if reject_method_with_block_pass?(node)
107
+ if reject_method_with_block_pass?(node) || grep_v_with_nil?(node)
99
108
  range(node, node)
100
109
  else
101
110
  block_node = node.parent
@@ -109,6 +118,7 @@ module RuboCop
109
118
  range(node, block_node)
110
119
  end
111
120
  end
121
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
112
122
 
113
123
  def to_enum_method?(node)
114
124
  return false unless node.receiver.send_type?
@@ -59,8 +59,6 @@ module RuboCop
59
59
  class CombinableLoops < Base
60
60
  extend AutoCorrector
61
61
 
62
- include RangeHelp
63
-
64
62
  MSG = 'Combine this loop with the previous loop.'
65
63
 
66
64
  def on_block(node)
@@ -105,11 +103,19 @@ module RuboCop
105
103
  end
106
104
 
107
105
  def combine_with_left_sibling(corrector, node)
108
- corrector.replace(
109
- node.left_sibling.body,
110
- "#{node.left_sibling.body.source}\n#{node.body.source}"
111
- )
112
- corrector.remove(range_with_surrounding_space(range: node.source_range, side: :left))
106
+ corrector.remove(node.left_sibling.body.source_range.end.join(node.left_sibling.loc.end))
107
+ corrector.remove(node.source_range.begin.join(node.body.source_range.begin))
108
+
109
+ correct_end_of_block(corrector, node)
110
+ end
111
+
112
+ def correct_end_of_block(corrector, node)
113
+ return unless node.left_sibling.respond_to?(:braces?)
114
+ return if node.right_sibling&.block_type? || node.right_sibling&.numblock_type?
115
+
116
+ end_of_block = node.left_sibling.braces? ? '}' : ' end'
117
+ corrector.remove(node.loc.end)
118
+ corrector.insert_before(node.source_range.end, end_of_block)
113
119
  end
114
120
  end
115
121
  end
@@ -63,6 +63,7 @@ module RuboCop
63
63
  end
64
64
  end
65
65
  # rubocop:enable Metrics
66
+ alias on_csend on_send
66
67
 
67
68
  private
68
69
 
@@ -233,7 +233,7 @@ module RuboCop
233
233
  PATTERN
234
234
 
235
235
  ASSIGNMENT_TYPES.each do |type|
236
- define_method "on_#{type}" do |node|
236
+ define_method :"on_#{type}" do |node|
237
237
  return if part_of_ignored_node?(node)
238
238
  return if node.parent&.shorthand_asgn?
239
239
 
@@ -534,7 +534,7 @@ module RuboCop
534
534
  end
535
535
 
536
536
  def element_assignment?(node)
537
- node.send_type? && node.method_name != :[]=
537
+ node.send_type? && !node.method?(:[]=)
538
538
  end
539
539
 
540
540
  def extract_branches(node)
@@ -49,12 +49,12 @@ module RuboCop
49
49
  class DateTime < Base
50
50
  extend AutoCorrector
51
51
 
52
- CLASS_MSG = 'Prefer Time over DateTime.'
53
- COERCION_MSG = 'Do not use #to_datetime.'
52
+ CLASS_MSG = 'Prefer `Time` over `DateTime`.'
53
+ COERCION_MSG = 'Do not use `#to_datetime`.'
54
54
 
55
55
  # @!method date_time?(node)
56
56
  def_node_matcher :date_time?, <<~PATTERN
57
- (send (const {nil? (cbase)} :DateTime) ...)
57
+ (call (const {nil? (cbase)} :DateTime) ...)
58
58
  PATTERN
59
59
 
60
60
  # @!method historic_date?(node)
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  # @!method to_datetime?(node)
66
66
  def_node_matcher :to_datetime?, <<~PATTERN
67
- (send _ :to_datetime)
67
+ (call _ :to_datetime)
68
68
  PATTERN
69
69
 
70
70
  def on_send(node)
@@ -75,6 +75,7 @@ module RuboCop
75
75
 
76
76
  add_offense(node, message: message) { |corrector| autocorrect(corrector, node) }
77
77
  end
78
+ alias on_csend on_send
78
79
 
79
80
  private
80
81
 
@@ -32,27 +32,27 @@ module RuboCop
32
32
 
33
33
  send_node = node.send_node
34
34
 
35
- range = send_node.receiver.source_range.join(send_node.loc.selector)
36
-
37
- add_offense(range) do |corrector|
35
+ add_offense(send_node) do |corrector|
38
36
  range_type, min, max = each_range(node)
39
37
 
40
38
  max += 1 if range_type == :irange
41
39
 
42
- corrector.replace(node.send_node, "#{max - min}.times")
40
+ corrector.replace(send_node, "#{max - min}.times")
43
41
  end
44
42
  end
45
43
 
46
44
  private
47
45
 
48
46
  def offending?(node)
47
+ return false unless node.arguments.empty?
48
+
49
49
  each_range_with_zero_origin?(node) || each_range_without_block_argument?(node)
50
50
  end
51
51
 
52
52
  # @!method each_range(node)
53
53
  def_node_matcher :each_range, <<~PATTERN
54
54
  (block
55
- (send
55
+ (call
56
56
  (begin
57
57
  (${irange erange}
58
58
  (int $_) (int $_)))
@@ -64,7 +64,7 @@ module RuboCop
64
64
  # @!method each_range_with_zero_origin?(node)
65
65
  def_node_matcher :each_range_with_zero_origin?, <<~PATTERN
66
66
  (block
67
- (send
67
+ (call
68
68
  (begin
69
69
  ({irange erange}
70
70
  (int 0) (int _)))
@@ -76,7 +76,7 @@ module RuboCop
76
76
  # @!method each_range_without_block_argument?(node)
77
77
  def_node_matcher :each_range_without_block_argument?, <<~PATTERN
78
78
  (block
79
- (send
79
+ (call
80
80
  (begin
81
81
  ({irange erange}
82
82
  (int _) (int _)))
@@ -58,12 +58,12 @@ module RuboCop
58
58
 
59
59
  # @!method each_with_object_block_candidate?(node)
60
60
  def_node_matcher :each_with_object_block_candidate?, <<~PATTERN
61
- (block $(send _ {:inject :reduce} _) $_ $_)
61
+ (block $(call _ {:inject :reduce} _) $_ $_)
62
62
  PATTERN
63
63
 
64
64
  # @!method each_with_object_numblock_candidate?(node)
65
65
  def_node_matcher :each_with_object_numblock_candidate?, <<~PATTERN
66
- (numblock $(send _ {:inject :reduce} _) 2 $_)
66
+ (numblock $(call _ {:inject :reduce} _) 2 $_)
67
67
  PATTERN
68
68
 
69
69
  def autocorrect_block(corrector, node, return_value)
@@ -83,7 +83,7 @@ module RuboCop
83
83
  parent = node.parent
84
84
  return false unless parent && %i[send super zsuper].include?(parent.type)
85
85
 
86
- node.equal?(parent.arguments.first) && !parentheses?(node.parent)
86
+ node.equal?(parent.first_argument) && !parentheses?(node.parent)
87
87
  end
88
88
 
89
89
  def replacement_range(node)
@@ -84,7 +84,7 @@ module RuboCop
84
84
  # are considered.
85
85
  return if node.method?(:eval) && !valid_eval_receiver?(node.receiver)
86
86
 
87
- code = node.arguments.first
87
+ code = node.first_argument
88
88
  return unless code && (code.str_type? || code.dstr_type?)
89
89
 
90
90
  check_location(node, code)
@@ -128,17 +128,6 @@ module RuboCop
128
128
  node.method?(:eval) ? node.arguments.size >= 2 : true
129
129
  end
130
130
 
131
- # FIXME: It's a Style/ConditionalAssignment's false positive.
132
- # rubocop:disable Style/ConditionalAssignment
133
- def with_lineno?(node)
134
- if node.method?(:eval)
135
- node.arguments.size == 4
136
- else
137
- node.arguments.size == 3
138
- end
139
- end
140
- # rubocop:enable Style/ConditionalAssignment
141
-
142
131
  def add_offense_for_incorrect_line(method_name, line_node, sign, line_diff)
143
132
  expected = expected_line(sign, line_diff)
144
133
  message = format(MSG_INCORRECT_LINE,
@@ -165,7 +154,7 @@ module RuboCop
165
154
  end
166
155
 
167
156
  def check_line(node, code)
168
- line_node = node.arguments.last
157
+ line_node = node.last_argument
169
158
  line_diff = line_difference(line_node, code)
170
159
  if line_diff.zero?
171
160
  add_offense_for_same_line(node, line_node)
@@ -227,7 +216,7 @@ module RuboCop
227
216
  end
228
217
 
229
218
  def missing_line(node, code)
230
- line_diff = line_difference(node.arguments.last, code)
219
+ line_diff = line_difference(node.last_argument, code)
231
220
  sign = line_diff.positive? ? :+ : :-
232
221
  expected_line(sign, line_diff)
233
222
  end
@@ -30,7 +30,7 @@ module RuboCop
30
30
 
31
31
  # @!method exact_regexp_match(node)
32
32
  def_node_matcher :exact_regexp_match, <<~PATTERN
33
- (send
33
+ (call
34
34
  _ {:=~ :=== :!~ :match :match?}
35
35
  (regexp
36
36
  (str $_)
@@ -49,6 +49,7 @@ module RuboCop
49
49
  corrector.replace(node, prefer)
50
50
  end
51
51
  end
52
+ alias on_csend on_send
52
53
 
53
54
  private
54
55
 
@@ -86,7 +86,7 @@ module RuboCop
86
86
 
87
87
  def extract_block_name(def_node)
88
88
  if def_node.block_argument?
89
- def_node.arguments.last.name
89
+ def_node.last_argument.name
90
90
  else
91
91
  'block'
92
92
  end
@@ -127,7 +127,7 @@ module RuboCop
127
127
  end
128
128
 
129
129
  def insert_argument(node, corrector, block_name)
130
- last_arg = node.arguments.last
130
+ last_arg = node.last_argument
131
131
  arg_range = range_with_surrounding_comma(last_arg.source_range, :right)
132
132
  replacement = " &#{block_name}"
133
133
  replacement = ",#{replacement}" unless arg_range.source.end_with?(',')
@@ -17,10 +17,16 @@ module RuboCop
17
17
  # @example
18
18
  # # bad
19
19
  # hash.keys.each { |k| p k }
20
- # hash.values.each { |v| p v }
20
+ # hash.each { |k, unused_value| p k }
21
21
  #
22
22
  # # good
23
23
  # hash.each_key { |k| p k }
24
+ #
25
+ # # bad
26
+ # hash.values.each { |v| p v }
27
+ # hash.each { |unused_key, v| p v }
28
+ #
29
+ # # good
24
30
  # hash.each_value { |v| p v }
25
31
  #
26
32
  # @example AllowedReceivers: ['execute']
@@ -33,22 +39,46 @@ module RuboCop
33
39
  extend AutoCorrector
34
40
 
35
41
  MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
42
+ UNUSED_BLOCK_ARG_MSG = "#{MSG.chop} and remove the unused `%<unused_code>s` block argument."
36
43
 
37
44
  # @!method kv_each(node)
38
45
  def_node_matcher :kv_each, <<~PATTERN
39
- ({block numblock} $(send (send _ ${:keys :values}) :each) ...)
46
+ ({block numblock} $(call (call _ ${:keys :values}) :each) ...)
47
+ PATTERN
48
+
49
+ # @!method each_arguments(node)
50
+ def_node_matcher :each_arguments, <<~PATTERN
51
+ (block (call _ :each)(args $_key $_value) ...)
40
52
  PATTERN
41
53
 
42
54
  # @!method kv_each_with_block_pass(node)
43
55
  def_node_matcher :kv_each_with_block_pass, <<~PATTERN
44
- (send $(send _ ${:keys :values}) :each (block_pass (sym _)))
56
+ (call $(call _ ${:keys :values}) :each (block_pass (sym _)))
45
57
  PATTERN
46
58
 
59
+ # rubocop:disable Metrics/AbcSize
47
60
  def on_block(node)
61
+ return unless handleable?(node)
62
+
48
63
  kv_each(node) do |target, method|
49
- register_kv_offense(target, method)
64
+ register_kv_offense(target, method) and return
65
+ end
66
+
67
+ return unless (key, value = each_arguments(node))
68
+
69
+ if unused_block_arg_exist?(node, value)
70
+ message = message('each_key', node.method_name, value.source)
71
+ unused_range = key.source_range.end.join(value.source_range.end)
72
+
73
+ register_each_args_offense(node, message, 'each_key', unused_range)
74
+ elsif unused_block_arg_exist?(node, key)
75
+ message = message('each_value', node.method_name, key.source)
76
+ unused_range = key.source_range.begin.join(value.source_range.begin)
77
+
78
+ register_each_args_offense(node, message, 'each_value', unused_range)
50
79
  end
51
80
  end
81
+ # rubocop:enable Metrics/AbcSize
52
82
 
53
83
  alias on_numblock on_block
54
84
 
@@ -60,27 +90,70 @@ module RuboCop
60
90
 
61
91
  private
62
92
 
93
+ def handleable?(node)
94
+ return false unless (root_receiver = root_receiver(node))
95
+
96
+ !root_receiver.literal? || root_receiver.hash_type?
97
+ end
98
+
63
99
  def register_kv_offense(target, method)
64
100
  return unless (parent_receiver = target.receiver.receiver)
65
101
  return if allowed_receiver?(parent_receiver)
66
102
 
67
- add_offense(kv_range(target), message: format_message(method)) do |corrector|
103
+ current = target.receiver.loc.selector.join(target.source_range.end).source
104
+
105
+ add_offense(kv_range(target), message: format_message(method, current)) do |corrector|
68
106
  correct_key_value_each(target, corrector)
69
107
  end
70
108
  end
71
109
 
110
+ def unused_block_arg_exist?(node, block_arg)
111
+ lvar_sources = node.body.each_descendant(:lvar).map(&:source)
112
+
113
+ if block_arg.mlhs_type?
114
+ block_arg.each_descendant(:arg, :restarg).all? do |block_arg|
115
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
116
+ end
117
+ else
118
+ lvar_sources.none?(block_arg.source.delete_prefix('*'))
119
+ end
120
+ end
121
+
122
+ def message(prefer, method_name, unused_code)
123
+ format(
124
+ UNUSED_BLOCK_ARG_MSG, prefer: prefer, current: method_name, unused_code: unused_code
125
+ )
126
+ end
127
+
128
+ def register_each_args_offense(node, message, prefer, unused_range)
129
+ add_offense(node, message: message) do |corrector|
130
+ corrector.replace(node.send_node.loc.selector, prefer)
131
+ corrector.remove(unused_range)
132
+ end
133
+ end
134
+
72
135
  def register_kv_with_block_pass_offense(node, target, method)
73
136
  return unless (parent_receiver = node.parent.receiver.receiver)
74
137
  return if allowed_receiver?(parent_receiver)
75
138
 
76
- range = target.loc.selector.with(end_pos: node.parent.loc.selector.end_pos)
77
- add_offense(range, message: format_message(method)) do |corrector|
139
+ range = target.loc.selector.join(node.parent.loc.selector.end)
140
+
141
+ add_offense(range, message: format_message(method, range.source)) do |corrector|
78
142
  corrector.replace(range, "each_#{method[0..-2]}")
79
143
  end
80
144
  end
81
145
 
82
- def format_message(method_name)
83
- format(MSG, prefer: "each_#{method_name[0..-2]}", current: "#{method_name}.each")
146
+ def root_receiver(node)
147
+ receiver = node.receiver
148
+ if receiver&.receiver
149
+ root_receiver(receiver)
150
+ else
151
+ receiver
152
+ end
153
+ end
154
+
155
+ def format_message(method_name, current)
156
+ format(MSG, prefer: "each_#{method_name[0..-2]}", current: current)
84
157
  end
85
158
 
86
159
  def check_argument(variable)
@@ -103,7 +176,7 @@ module RuboCop
103
176
  name = "each_#{node.receiver.method_name.to_s.chop}"
104
177
  return correct_implicit(node, corrector, name) unless receiver
105
178
 
106
- new_source = receiver.source + ".#{name}"
179
+ new_source = receiver.source + "#{node.loc.dot.source}#{name}"
107
180
  corrector.replace(node, new_source)
108
181
  end
109
182