rubocop 1.57.2 → 1.60.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -17,12 +17,11 @@ module RuboCop
17
17
  include Parentheses
18
18
  extend AutoCorrector
19
19
 
20
+ ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
21
+
20
22
  # @!method square_brackets?(node)
21
23
  def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
22
24
 
23
- # @!method range_end?(node)
24
- def_node_matcher :range_end?, '^^{irange erange}'
25
-
26
25
  # @!method method_node_and_args(node)
27
26
  def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
28
27
 
@@ -54,15 +53,16 @@ module RuboCop
54
53
  def ignore_syntax?(node)
55
54
  return false unless (parent = node.parent)
56
55
 
57
- parent.while_post_type? || parent.until_post_type? ||
58
- like_method_argument_parentheses?(parent)
56
+ parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
57
+ like_method_argument_parentheses?(parent) || multiline_control_flow_statements?(node)
59
58
  end
60
59
 
61
60
  def allowed_expression?(node)
62
61
  allowed_ancestor?(node) ||
63
62
  allowed_method_call?(node) ||
64
63
  allowed_multiple_expression?(node) ||
65
- allowed_ternary?(node)
64
+ allowed_ternary?(node) ||
65
+ node.parent&.range_type?
66
66
  end
67
67
 
68
68
  def allowed_ancestor?(node)
@@ -104,6 +104,13 @@ module RuboCop
104
104
  !node.arithmetic_operation? && node.first_argument.begin_type?
105
105
  end
106
106
 
107
+ def multiline_control_flow_statements?(node)
108
+ return false unless (parent = node.parent)
109
+ return false if parent.single_line?
110
+
111
+ parent.return_type? || parent.next_type? || parent.break_type?
112
+ end
113
+
107
114
  def empty_parentheses?(node)
108
115
  # Don't flag `()`
109
116
  node.children.empty?
@@ -136,27 +143,44 @@ module RuboCop
136
143
  check_send(begin_node, node) if node.call_type?
137
144
  end
138
145
 
139
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
146
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
140
147
  def find_offense_message(begin_node, node)
141
148
  return 'a keyword' if keyword_with_redundant_parentheses?(node)
142
149
  return 'a literal' if disallowed_literal?(begin_node, node)
143
150
  return 'a variable' if node.variable?
144
151
  return 'a constant' if node.const_type?
152
+ if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
153
+ return 'an expression'
154
+ end
145
155
  return 'an interpolated expression' if interpolation?(begin_node)
146
156
 
147
- return if begin_node.chained? || !begin_node.parent.nil?
157
+ return if begin_node.chained?
148
158
 
149
159
  if node.and_type? || node.or_type?
160
+ return if node.semantic_operator? && begin_node.parent
161
+ return if node.multiline? && allow_in_multiline_conditions?
162
+ return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
163
+ return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
164
+
150
165
  'a logical expression'
151
166
  elsif node.respond_to?(:comparison_method?) && node.comparison_method?
167
+ return unless begin_node.parent.nil?
168
+
152
169
  'a comparison expression'
153
170
  end
154
171
  end
155
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
172
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
156
173
 
157
174
  # @!method interpolation?(node)
158
175
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
159
176
 
177
+ def allow_in_multiline_conditions?
178
+ parentheses_around_condition_config = config.for_cop('Style/ParenthesesAroundCondition')
179
+ return false unless parentheses_around_condition_config['Enabled']
180
+
181
+ !!parentheses_around_condition_config['AllowInMultilineConditions']
182
+ end
183
+
160
184
  def check_send(begin_node, node)
161
185
  return check_unary(begin_node, node) if node.unary_operation?
162
186
 
@@ -221,7 +245,6 @@ module RuboCop
221
245
  def method_call_with_redundant_parentheses?(node)
222
246
  return false unless node.call_type?
223
247
  return false if node.prefix_not?
224
- return false if range_end?(node)
225
248
 
226
249
  send_node, args = method_node_and_args(node)
227
250
 
@@ -58,7 +58,7 @@ module RuboCop
58
58
 
59
59
  MSG = 'Redundant `return` detected.'
60
60
  MULTI_RETURN_MSG = 'To return multiple values, use an array.'
61
- RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
61
+ RESTRICT_ON_SEND = %i[define_method define_singleton_method lambda].freeze
62
62
 
63
63
  def on_send(node)
64
64
  return unless (parent = node.parent) && parent.block_type?
@@ -17,7 +17,8 @@ module RuboCop
17
17
  # protected scope, you cannot send private messages this way.
18
18
  #
19
19
  # Note we allow uses of `self` with operators because it would be awkward
20
- # otherwise.
20
+ # otherwise. Also allows the use of `self.it` without arguments in blocks,
21
+ # as in `0.times { self.it }`, following `Lint/ItWithoutArgumentsInBlock` cop.
21
22
  #
22
23
  # @example
23
24
  #
@@ -107,8 +108,8 @@ module RuboCop
107
108
  def on_send(node)
108
109
  return unless node.self_receiver? && regular_method_call?(node)
109
110
  return if node.parent&.mlhs_type?
110
-
111
111
  return if allowed_send_node?(node)
112
+ return if it_method_in_block?(node)
112
113
 
113
114
  add_offense(node.receiver) do |corrector|
114
115
  corrector.remove(node.receiver)
@@ -155,6 +156,20 @@ module RuboCop
155
156
  KERNEL_METHODS.include?(node.method_name)
156
157
  end
157
158
 
159
+ # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
160
+ #
161
+ # $ ruby -e '0.times { begin; it; end }'
162
+ # -e:1: warning: `it` calls without arguments will refer to the first block param in
163
+ # Ruby 3.4; use it() or self.it
164
+ #
165
+ def it_method_in_block?(node)
166
+ return false unless node.method?(:it)
167
+ return false unless (block_node = node.each_ancestor(:block).first)
168
+ return false unless block_node.arguments.empty_and_without_delimiters?
169
+
170
+ node.arguments.empty? && !node.block_literal?
171
+ end
172
+
158
173
  def regular_method_call?(node)
159
174
  !(node.operator_method? ||
160
175
  KEYWORDS.include?(node.method_name) ||
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # This cop is unsafe, because `sort...last` and `max` may not return the
17
17
  # same element in all cases.
18
18
  #
19
- # In an enumerable where there are multiple elements where `a <=> b == 0`,
19
+ # In an enumerable where there are multiple elements where ``a <=> b == 0``,
20
20
  # or where the transformation done by the `sort_by` block has the
21
21
  # same result, `sort.last` and `max` (or `sort_by.last` and `max_by`)
22
22
  # will return different elements. `sort.last` will return the last
@@ -87,15 +87,15 @@ module RuboCop
87
87
  # @!method redundant_sort?(node)
88
88
  def_node_matcher :redundant_sort?, <<~MATCHER
89
89
  {
90
- (send $(send _ $:sort) ${:last :first})
91
- (send $(send _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
90
+ (call $(call _ $:sort) ${:last :first})
91
+ (call $(call _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
92
92
 
93
- (send $(send _ $:sort_by _) ${:last :first})
93
+ (call $(call _ $:sort_by _) ${:last :first})
94
94
  (send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
95
95
 
96
- (send ({block numblock} $(send _ ${:sort_by :sort}) ...) ${:last :first})
97
- (send
98
- ({block numblock} $(send _ ${:sort_by :sort}) ...)
96
+ (call ({block numblock} $(call _ ${:sort_by :sort}) ...) ${:last :first})
97
+ (call
98
+ ({block numblock} $(call _ ${:sort_by :sort}) ...)
99
99
  ${:[] :at :slice} {(int 0) (int -1)}
100
100
  )
101
101
  }
@@ -108,6 +108,7 @@ module RuboCop
108
108
 
109
109
  register_offense(ancestor, sort_node, sorter, accessor)
110
110
  end
111
+ alias on_csend on_send
111
112
 
112
113
  private
113
114
 
@@ -180,7 +181,7 @@ module RuboCop
180
181
  end
181
182
 
182
183
  def arg_node(node)
183
- node.arguments.first
184
+ node.first_argument
184
185
  end
185
186
 
186
187
  def arg_value(node)
@@ -46,12 +46,12 @@ module RuboCop
46
46
 
47
47
  # @!method redundant_sort_by_block(node)
48
48
  def_node_matcher :redundant_sort_by_block, <<~PATTERN
49
- (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
49
+ (block $(call _ :sort_by) (args (arg $_x)) (lvar _x))
50
50
  PATTERN
51
51
 
52
52
  # @!method redundant_sort_by_numblock(node)
53
53
  def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
54
- (numblock $(send _ :sort_by) 1 (lvar :_1))
54
+ (numblock $(call _ :sort_by) 1 (lvar :_1))
55
55
  PATTERN
56
56
 
57
57
  def sort_by_range(send, node)
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def percent_array_literal?(node)
136
- (percent_w_literal?(node) || percent_w_upper_literal?(node))
136
+ percent_w_literal?(node) || percent_w_upper_literal?(node)
137
137
  end
138
138
 
139
139
  def heredoc_with_disabled_interpolation?(node)
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # @!method sample_candidate?(node)
37
37
  def_node_matcher :sample_candidate?, <<~PATTERN
38
- (send $(send _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
38
+ (call $(call _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
39
39
  PATTERN
40
40
 
41
41
  def on_send(node)
@@ -52,6 +52,7 @@ module RuboCop
52
52
  end
53
53
  end
54
54
  end
55
+ alias on_csend on_send
55
56
 
56
57
  private
57
58
 
@@ -55,8 +55,8 @@ module RuboCop
55
55
  # @!method regexp_match?(node)
56
56
  def_node_matcher :regexp_match?, <<~PATTERN
57
57
  {
58
- (block send (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
- (numblock send $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
58
+ (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
+ (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
60
60
  }
61
61
  PATTERN
62
62
 
@@ -64,9 +64,9 @@ module RuboCop
64
64
  # @!method creates_hash?(node)
65
65
  def_node_matcher :creates_hash?, <<~PATTERN
66
66
  {
67
- (send (const _ :Hash) {:new :[]} ...)
68
- (block (send (const _ :Hash) :new ...) ...)
69
- (send _ { :to_h :to_hash } ...)
67
+ (call (const _ :Hash) {:new :[]} ...)
68
+ (block (call (const _ :Hash) :new ...) ...)
69
+ (call _ { :to_h :to_hash } ...)
70
70
  }
71
71
  PATTERN
72
72
 
@@ -100,6 +100,7 @@ module RuboCop
100
100
  register_offense(node, block_node, regexp, replacement)
101
101
  end
102
102
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
103
+ alias on_csend on_send
103
104
 
104
105
  private
105
106
 
@@ -146,7 +147,7 @@ module RuboCop
146
147
  return node.child_nodes.first if node.match_with_lvasgn_type?
147
148
 
148
149
  if node.receiver.lvar_type? &&
149
- (block.numblock_type? || node.receiver.source == block.arguments.first.source)
150
+ (block.numblock_type? || node.receiver.source == block.first_argument.source)
150
151
  node.first_argument
151
152
  elsif node.first_argument.lvar_type?
152
153
  node.receiver
@@ -16,7 +16,7 @@ module RuboCop
16
16
  extend AutoCorrector
17
17
 
18
18
  MSG = 'Use self-assignment shorthand `%<method>s=`.'
19
- OPS = %i[+ - * ** / | &].freeze
19
+ OPS = %i[+ - * ** / % ^ << >> | &].freeze
20
20
 
21
21
  def self.autocorrect_incompatible_with
22
22
  [Layout::SpaceAroundOperators]
@@ -80,6 +80,7 @@ module RuboCop
80
80
  processed_source.tokens.group_by(&:line)
81
81
  end
82
82
 
83
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
84
  def semicolon_position(tokens)
84
85
  if tokens.last.semicolon?
85
86
  -1
@@ -90,10 +91,13 @@ module RuboCop
90
91
  elsif exist_semicolon_after_left_curly_brace?(tokens) ||
91
92
  exist_semicolon_after_left_string_interpolation_brace?(tokens)
92
93
  2
94
+ elsif exist_semicolon_after_left_lambda_curly_brace?(tokens)
95
+ 3
93
96
  elsif exist_semicolon_before_right_string_interpolation_brace?(tokens)
94
97
  -4
95
98
  end
96
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
101
 
98
102
  def exist_semicolon_before_right_curly_brace?(tokens)
99
103
  tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
@@ -103,6 +107,10 @@ module RuboCop
103
107
  tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
108
  end
105
109
 
110
+ def exist_semicolon_after_left_lambda_curly_brace?(tokens)
111
+ tokens[2]&.type == :tLAMBEG && tokens[3]&.semicolon?
112
+ end
113
+
106
114
  def exist_semicolon_before_right_string_interpolation_brace?(tokens)
107
115
  tokens[-3]&.type == :tSTRING_DEND && tokens[-4]&.semicolon?
108
116
  end
@@ -3,8 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Sometimes using dig method ends up with just a single
7
- # argument. In such cases, dig should be replaced with [].
6
+ # Sometimes using `dig` method ends up with just a single
7
+ # argument. In such cases, dig should be replaced with `[]`.
8
+ #
9
+ # Since replacing `hash&.dig(:key)` with `hash[:key]` could potentially lead to error,
10
+ # calls to the `dig` method using safe navigation will be ignored.
8
11
  #
9
12
  # @safety
10
13
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -3,8 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks that arrays are sliced with endless ranges instead of
7
- # `ary[start..-1]` on Ruby 2.6+.
6
+ # Checks that arrays are not sliced with the redundant `ary[0..-1]`, replacing it with `ary`,
7
+ # and ensures arrays are sliced with endless ranges instead of `ary[start..-1]` on Ruby 2.6+,
8
+ # and with beginless ranges instead of `ary[nil..end]` on Ruby 2.7+.
8
9
  #
9
10
  # @safety
10
11
  # This cop is unsafe because `x..-1` and `x..` are only guaranteed to
@@ -21,29 +22,94 @@ module RuboCop
21
22
  #
22
23
  # @example
23
24
  # # bad
24
- # items[1..-1]
25
+ # items[0..-1]
26
+ # items[0..nil]
27
+ # items[0...nil]
25
28
  #
26
29
  # # good
27
- # items[1..]
30
+ # items
31
+ #
32
+ # # bad
33
+ # items[1..-1] # Ruby 2.6+
34
+ # items[1..nil] # Ruby 2.6+
35
+ #
36
+ # # good
37
+ # items[1..] # Ruby 2.6+
38
+ #
39
+ # # bad
40
+ # items[nil..42] # Ruby 2.7+
41
+ #
42
+ # # good
43
+ # items[..42] # Ruby 2.7+
44
+ # items[0..42] # Ruby 2.7+
45
+ #
28
46
  class SlicingWithRange < Base
29
47
  extend AutoCorrector
30
48
  extend TargetRubyVersion
31
49
 
32
50
  minimum_target_ruby_version 2.6
33
51
 
34
- MSG = 'Prefer ary[n..] over ary[n..-1].'
52
+ MSG = 'Prefer `%<prefer>s` over `%<current>s`.'
53
+ MSG_USELESS_RANGE = 'Remove the useless `%<prefer>s`.'
35
54
  RESTRICT_ON_SEND = %i[[]].freeze
36
55
 
56
+ # @!method range_from_zero_till_minus_one?(node)
57
+ def_node_matcher :range_from_zero_till_minus_one?, <<~PATTERN
58
+ {
59
+ (irange (int 0) {(int -1) nil})
60
+ (erange (int 0) nil)
61
+ }
62
+ PATTERN
63
+
37
64
  # @!method range_till_minus_one?(node)
38
- def_node_matcher :range_till_minus_one?, '(irange !nil? (int -1))'
65
+ def_node_matcher :range_till_minus_one?, <<~PATTERN
66
+ {
67
+ (irange !nil? {(int -1) nil})
68
+ (erange !nil? nil)
69
+ }
70
+ PATTERN
71
+
72
+ # @!method range_from_zero?(node)
73
+ def_node_matcher :range_from_zero?, <<~PATTERN
74
+ (irange nil !nil?)
75
+ PATTERN
39
76
 
40
77
  def on_send(node)
41
- return unless node.arguments.count == 1
42
- return unless range_till_minus_one?(node.arguments.first)
78
+ return unless node.arguments.one?
43
79
 
44
- add_offense(node.first_argument) do |corrector|
45
- corrector.remove(node.first_argument.end)
80
+ range_node = node.first_argument
81
+ selector = node.loc.selector
82
+ unless (message, removal_range = offense_message_with_removal_range(range_node, selector))
83
+ return
46
84
  end
85
+
86
+ add_offense(selector, message: message) do |corrector|
87
+ corrector.remove(removal_range)
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def offense_message_with_removal_range(range_node, selector)
94
+ if range_from_zero_till_minus_one?(range_node)
95
+ [format(MSG_USELESS_RANGE, prefer: selector.source), selector]
96
+ elsif range_till_minus_one?(range_node)
97
+ [
98
+ format(MSG, prefer: endless(range_node), current: selector.source), range_node.end
99
+ ]
100
+ elsif range_from_zero?(range_node) && target_ruby_version >= 2.7
101
+ [
102
+ format(MSG, prefer: beginless(range_node), current: selector.source), range_node.begin
103
+ ]
104
+ end
105
+ end
106
+
107
+ def endless(range_node)
108
+ "[#{range_node.begin.source}#{range_node.loc.operator.source}]"
109
+ end
110
+
111
+ def beginless(range_node)
112
+ "[#{range_node.loc.operator.source}#{range_node.end.source}]"
47
113
  end
48
114
  end
49
115
  end
@@ -35,6 +35,7 @@ module RuboCop
35
35
  corrector.replace(range, 'chars')
36
36
  end
37
37
  end
38
+ alias on_csend on_send
38
39
  end
39
40
  end
40
41
  end
@@ -22,20 +22,23 @@ module RuboCop
22
22
 
23
23
  # @!method lstrip_rstrip(node)
24
24
  def_node_matcher :lstrip_rstrip, <<~PATTERN
25
- {(send $(send _ $:rstrip) $:lstrip)
26
- (send $(send _ $:lstrip) $:rstrip)}
25
+ {
26
+ (call $(call _ :rstrip) :lstrip)
27
+ (call $(call _ :lstrip) :rstrip)
28
+ }
27
29
  PATTERN
28
30
 
29
31
  def on_send(node)
30
- lstrip_rstrip(node) do |first_send, method_one, method_two|
32
+ lstrip_rstrip(node) do |first_send|
31
33
  range = range_between(first_send.loc.selector.begin_pos, node.source_range.end_pos)
32
- message = format(MSG, methods: "#{method_one}.#{method_two}")
34
+ message = format(MSG, methods: range.source)
33
35
 
34
36
  add_offense(range, message: message) do |corrector|
35
37
  corrector.replace(range, 'strip')
36
38
  end
37
39
  end
38
40
  end
41
+ alias on_csend on_send
39
42
  end
40
43
  end
41
44
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Enforces the presence of parentheses in `super` containing arguments.
7
+ #
8
+ # `super` is a keyword and is provided as a distinct cop from those designed for method call.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # super name, age
14
+ #
15
+ # # good
16
+ # super(name, age)
17
+ #
18
+ class SuperWithArgsParentheses < Base
19
+ extend AutoCorrector
20
+
21
+ MSG = 'Use parentheses for `super` with arguments.'
22
+
23
+ def on_super(node)
24
+ return if node.parenthesized?
25
+
26
+ add_offense(node) do |corrector|
27
+ range = node.loc.keyword.end.join(node.first_argument.source_range.begin)
28
+ corrector.replace(range, '(')
29
+ corrector.insert_after(node.last_argument, ')')
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -37,6 +37,42 @@ module RuboCop
37
37
  # # ArgumentError: wrong number of arguments (given 1, expected 0)
38
38
  # ----
39
39
  #
40
+ # It is also unsafe because `Symbol#to_proc` does not work with
41
+ # `protected` methods which would otherwise be accessible.
42
+ #
43
+ # For example:
44
+ #
45
+ # [source,ruby]
46
+ # ----
47
+ # class Box
48
+ # def initialize
49
+ # @secret = rand
50
+ # end
51
+ #
52
+ # def normal_matches?(*others)
53
+ # others.map { |other| other.secret }.any?(secret)
54
+ # end
55
+ #
56
+ # def symbol_to_proc_matches?(*others)
57
+ # others.map(&:secret).any?(secret)
58
+ # end
59
+ #
60
+ # protected
61
+ #
62
+ # attr_reader :secret
63
+ # end
64
+ #
65
+ # boxes = [Box.new, Box.new]
66
+ # Box.new.normal_matches?(*boxes)
67
+ # # => false
68
+ # boxes.first.normal_matches?(*boxes)
69
+ # # => true
70
+ # Box.new.symbol_to_proc_matches?(*boxes)
71
+ # # => NoMethodError: protected method `secret' called for #<Box...>
72
+ # boxes.first.symbol_to_proc_matches?(*boxes)
73
+ # # => NoMethodError: protected method `secret' called for #<Box...>
74
+ # ----
75
+ #
40
76
  # @example
41
77
  # # bad
42
78
  # something.map { |s| s.upcase }
@@ -23,38 +23,35 @@ module RuboCop
23
23
 
24
24
  minimum_target_ruby_version 2.4
25
25
 
26
- MSG = 'Use `%<receiver>s.unpack1(%<format>s)` instead of ' \
27
- '`%<receiver>s.unpack(%<format>s)%<method>s`.'
26
+ MSG = 'Use `unpack1(%<format>s)` instead of `%<current>s`.'
28
27
  RESTRICT_ON_SEND = %i[first [] slice at].freeze
29
28
 
30
29
  # @!method unpack_and_first_element?(node)
31
30
  def_node_matcher :unpack_and_first_element?, <<~PATTERN
32
31
  {
33
- (send $(send (...) :unpack $(...)) :first)
34
- (send $(send (...) :unpack $(...)) {:[] :slice :at} (int 0))
32
+ (call $(call (...) :unpack $(...)) :first)
33
+ (call $(call (...) :unpack $(...)) {:[] :slice :at} (int 0))
35
34
  }
36
35
  PATTERN
37
36
 
38
37
  def on_send(node)
39
38
  unpack_and_first_element?(node) do |unpack_call, unpack_arg|
40
- range = first_element_range(node, unpack_call)
41
- message = format(MSG,
42
- receiver: unpack_call.receiver.source,
43
- format: unpack_arg.source,
44
- method: range.source)
45
- add_offense(node, message: message) do |corrector|
46
- corrector.remove(first_element_range(node, unpack_call))
39
+ first_element_range = first_element_range(node, unpack_call)
40
+ offense_range = unpack_call.loc.selector.join(node.source_range.end)
41
+ message = format(MSG, format: unpack_arg.source, current: offense_range.source)
42
+
43
+ add_offense(offense_range, message: message) do |corrector|
44
+ corrector.remove(first_element_range)
47
45
  corrector.replace(unpack_call.loc.selector, 'unpack1')
48
46
  end
49
47
  end
50
48
  end
49
+ alias on_csend on_send
51
50
 
52
51
  private
53
52
 
54
53
  def first_element_range(node, unpack_call)
55
- Parser::Source::Range.new(node.source_range.source_buffer,
56
- unpack_call.source_range.end_pos,
57
- node.source_range.end_pos)
54
+ unpack_call.source_range.end.join(node.source_range.end)
58
55
  end
59
56
  end
60
57
  end
@@ -251,9 +251,18 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
251
251
  "\ninclude::../partials/#{filename}[]\n"
252
252
  end
253
253
 
254
+ # rubocop:disable Metrics/MethodLength
254
255
  def print_cops_of_department(department)
255
256
  selected_cops = cops_of_department(department)
256
- content = +"= #{department}\n"
257
+ content = +<<~HEADER
258
+ ////
259
+ Do NOT edit this file by hand directly, as it is automatically generated.
260
+
261
+ Please make any necessary changes to the cop documentation within the source files themselves.
262
+ ////
263
+
264
+ = #{department}
265
+ HEADER
257
266
  selected_cops.each { |cop| content << print_cop_with_doc(cop) }
258
267
  content << footer_for_department(department)
259
268
  file_name = "#{docs_path}/#{department_to_basename(department)}.adoc"
@@ -262,6 +271,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
262
271
  file.write("#{content.strip}\n")
263
272
  end
264
273
  end
274
+ # rubocop:enable Metrics/MethodLength
265
275
 
266
276
  def print_cop_with_doc(cop) # rubocop:todo Metrics/AbcSize, Metrics/MethodLength
267
277
  cop_config = config.for_cop(cop)