rubocop 1.69.1 → 1.70.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +19 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +3 -3
  6. data/lib/rubocop/config.rb +13 -4
  7. data/lib/rubocop/config_loader.rb +4 -0
  8. data/lib/rubocop/config_loader_resolver.rb +14 -3
  9. data/lib/rubocop/config_validator.rb +18 -8
  10. data/lib/rubocop/cop/autocorrect_logic.rb +31 -34
  11. data/lib/rubocop/cop/base.rb +6 -0
  12. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  13. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  15. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  16. data/lib/rubocop/cop/internal_affairs/operator_keyword.rb +4 -2
  17. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  18. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -7
  19. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  20. data/lib/rubocop/cop/layout/empty_lines_around_begin_body.rb +5 -6
  21. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  22. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -7
  23. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  24. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +1 -6
  25. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  26. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -0
  27. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +11 -2
  28. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  29. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +1 -1
  30. data/lib/rubocop/cop/layout/line_length.rb +1 -0
  31. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +24 -0
  32. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  33. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  34. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -3
  35. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  36. data/lib/rubocop/cop/lint/constant_reassignment.rb +152 -0
  37. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  38. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +11 -3
  39. data/lib/rubocop/cop/lint/nested_method_definition.rb +5 -1
  40. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -2
  41. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +6 -14
  42. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  43. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  44. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -4
  45. data/lib/rubocop/cop/lint/void.rb +3 -2
  46. data/lib/rubocop/cop/metrics/class_length.rb +9 -9
  47. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +7 -7
  49. data/lib/rubocop/cop/mixin/comments_help.rb +6 -1
  50. data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
  51. data/lib/rubocop/cop/mixin/line_length_help.rb +5 -4
  52. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +46 -22
  53. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  54. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  55. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  56. data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
  57. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  58. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -1
  59. data/lib/rubocop/cop/style/and_or.rb +1 -1
  60. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -4
  61. data/lib/rubocop/cop/style/block_delimiters.rb +8 -1
  62. data/lib/rubocop/cop/style/class_and_module_children.rb +5 -2
  63. data/lib/rubocop/cop/style/each_for_simple_loop.rb +3 -6
  64. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  65. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  66. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  67. data/lib/rubocop/cop/style/exact_regexp_match.rb +1 -2
  68. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  69. data/lib/rubocop/cop/style/file_null.rb +20 -4
  70. data/lib/rubocop/cop/style/float_division.rb +8 -4
  71. data/lib/rubocop/cop/style/hash_except.rb +54 -67
  72. data/lib/rubocop/cop/style/hash_syntax.rb +5 -2
  73. data/lib/rubocop/cop/style/if_with_semicolon.rb +6 -4
  74. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  75. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +11 -1
  76. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  77. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -1
  78. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  79. data/lib/rubocop/cop/style/multiple_comparison.rb +34 -22
  80. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  81. data/lib/rubocop/cop/style/object_then.rb +13 -15
  82. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  83. data/lib/rubocop/cop/style/raise_args.rb +5 -3
  84. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  85. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  86. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  87. data/lib/rubocop/cop/style/redundant_line_continuation.rb +12 -9
  88. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -4
  89. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
  90. data/lib/rubocop/cop/style/redundant_self_assignment.rb +6 -5
  91. data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
  92. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  93. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  94. data/lib/rubocop/cop/style/single_line_methods.rb +2 -3
  95. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  96. data/lib/rubocop/cop/style/super_arguments.rb +63 -15
  97. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  98. data/lib/rubocop/cop/style/yoda_expression.rb +1 -0
  99. data/lib/rubocop/cop/util.rb +9 -2
  100. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  101. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  102. data/lib/rubocop/lsp/logger.rb +2 -2
  103. data/lib/rubocop/lsp/routes.rb +7 -23
  104. data/lib/rubocop/lsp/runtime.rb +15 -49
  105. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  106. data/lib/rubocop/magic_comment.rb +3 -3
  107. data/lib/rubocop/path_util.rb +11 -8
  108. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  109. data/lib/rubocop/runner.rb +5 -6
  110. data/lib/rubocop/target_ruby.rb +15 -0
  111. data/lib/rubocop/version.rb +1 -1
  112. data/lib/rubocop.rb +3 -0
  113. data/lib/ruby_lsp/rubocop/addon.rb +78 -0
  114. data/lib/ruby_lsp/rubocop/wraps_built_in_lsp_runtime.rb +50 -0
  115. metadata +16 -8
@@ -65,19 +65,23 @@ module RuboCop
65
65
 
66
66
  # @!method right_coerce?(node)
67
67
  def_node_matcher :right_coerce?, <<~PATTERN
68
- (send _ :/ (send _ :to_f))
68
+ (send _ :/ #to_f_method?)
69
69
  PATTERN
70
70
  # @!method left_coerce?(node)
71
71
  def_node_matcher :left_coerce?, <<~PATTERN
72
- (send (send _ :to_f) :/ _)
72
+ (send #to_f_method? :/ _)
73
73
  PATTERN
74
74
  # @!method both_coerce?(node)
75
75
  def_node_matcher :both_coerce?, <<~PATTERN
76
- (send (send _ :to_f) :/ (send _ :to_f))
76
+ (send #to_f_method? :/ #to_f_method?)
77
77
  PATTERN
78
78
  # @!method any_coerce?(node)
79
79
  def_node_matcher :any_coerce?, <<~PATTERN
80
- {(send _ :/ (send _ :to_f)) (send (send _ :to_f) :/ _)}
80
+ {(send _ :/ #to_f_method?) (send #to_f_method? :/ _)}
81
+ PATTERN
82
+ # @!method to_f_method?(node)
83
+ def_node_matcher :to_f_method?, <<~PATTERN
84
+ (send !nil? :to_f)
81
85
  PATTERN
82
86
 
83
87
  def on_send(node)
@@ -10,8 +10,10 @@ module RuboCop
10
10
  # (`Hash#except` was added in Ruby 3.0.)
11
11
  #
12
12
  # For safe detection, it is limited to commonly used string and symbol comparisons
13
- # when used `==`.
14
- # And do not check `Hash#delete_if` and `Hash#keep_if` to change receiver object.
13
+ # when using `==` or `!=`.
14
+ #
15
+ # This cop doesn't check for `Hash#delete_if` and `Hash#keep_if` because they
16
+ # modify the receiver.
15
17
  #
16
18
  # @safety
17
19
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -51,44 +53,31 @@ module RuboCop
51
53
  MSG = 'Use `%<prefer>s` instead.'
52
54
  RESTRICT_ON_SEND = %i[reject select filter].freeze
53
55
 
54
- # @!method bad_method_with_poro?(node)
55
- def_node_matcher :bad_method_with_poro?, <<~PATTERN
56
- (block
57
- (call _ _)
58
- (args
59
- $(arg _)
60
- (arg _))
61
- {
62
- $(send
63
- _ {:== :!= :eql? :include?} _)
64
- (send
65
- $(send
66
- _ {:== :!= :eql? :include?} _) :!)
67
- })
68
- PATTERN
56
+ SUBSET_METHODS = %i[== != eql? include?].freeze
57
+ ACTIVE_SUPPORT_SUBSET_METHODS = (SUBSET_METHODS + %i[in? exclude?]).freeze
69
58
 
70
- # @!method bad_method_with_active_support?(node)
71
- def_node_matcher :bad_method_with_active_support?, <<~PATTERN
59
+ # @!method block_with_first_arg_check?(node)
60
+ def_node_matcher :block_with_first_arg_check?, <<~PATTERN
72
61
  (block
73
- (send _ _)
62
+ (call _ _)
74
63
  (args
75
- $(arg _)
64
+ $(arg _key)
76
65
  (arg _))
77
66
  {
78
67
  $(send
79
- _ {:== :!= :eql? :in? :include? :exclude?} _)
68
+ {(lvar _key) $_ _ | _ $_ (lvar _key)})
80
69
  (send
81
70
  $(send
82
- _ {:== :!= :eql? :in? :include? :exclude?} _) :!)
71
+ {(lvar _key) $_ _ | _ $_ (lvar _key)}) :!)
83
72
  })
84
73
  PATTERN
85
74
 
86
75
  def on_send(node)
87
76
  block = node.parent
88
- return unless bad_method?(block) && semantically_except_method?(node, block)
77
+ return unless extracts_hash_subset?(block) && semantically_except_method?(node, block)
89
78
 
90
79
  except_key = except_key(block)
91
- return if except_key.nil? || !safe_to_register_offense?(block, except_key)
80
+ return unless safe_to_register_offense?(block, except_key)
92
81
 
93
82
  range = offense_range(node)
94
83
  preferred_method = "except(#{except_key_source(except_key)})"
@@ -101,42 +90,40 @@ module RuboCop
101
90
 
102
91
  private
103
92
 
104
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
105
- def bad_method?(block)
106
- if active_support_extensions_enabled?
107
- bad_method_with_active_support?(block) do |key_arg, send_node|
108
- if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
109
- return false
110
- end
111
- return true if !send_node.method?(:include?) && !send_node.method?(:exclude?)
112
-
113
- send_node.first_argument&.source == key_arg.source
114
- end
115
- else
116
- bad_method_with_poro?(block) do |key_arg, send_node|
117
- !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
93
+ def extracts_hash_subset?(block)
94
+ block_with_first_arg_check?(block) do |key_arg, send_node, method|
95
+ return false unless supported_subset_method?(method)
96
+
97
+ case method
98
+ when :include?, :exclude?
99
+ send_node.first_argument.source == key_arg.source
100
+ when :in?
101
+ send_node.receiver.source == key_arg.source
102
+ else
103
+ true
118
104
  end
119
105
  end
120
106
  end
121
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
122
107
 
123
- def semantically_except_method?(send, block)
124
- body = block.body
108
+ def supported_subset_method?(method)
109
+ if active_support_extensions_enabled?
110
+ ACTIVE_SUPPORT_SUBSET_METHODS.include?(method)
111
+ else
112
+ SUBSET_METHODS.include?(method)
113
+ end
114
+ end
125
115
 
126
- negated = body.method?('!')
127
- body = body.receiver if negated
116
+ def semantically_except_method?(node, block)
117
+ body, negated = extract_body_if_negated(block.body)
128
118
 
129
- case send.method_name
130
- when :reject
131
- body.method?('==') || body.method?('eql?') || included?(negated, body)
132
- when :select, :filter
133
- body.method?('!=') || not_included?(negated, body)
119
+ if node.method?('reject')
120
+ body.method?('==') || body.method?('eql?') || included?(body, negated)
134
121
  else
135
- false
122
+ body.method?('!=') || not_included?(body, negated)
136
123
  end
137
124
  end
138
125
 
139
- def included?(negated, body)
126
+ def included?(body, negated)
140
127
  if negated
141
128
  body.method?('exclude?')
142
129
  else
@@ -144,25 +131,26 @@ module RuboCop
144
131
  end
145
132
  end
146
133
 
147
- def not_included?(negated, body)
148
- included?(!negated, body)
134
+ def not_included?(body, negated)
135
+ included?(body, !negated)
149
136
  end
150
137
 
151
138
  def safe_to_register_offense?(block, except_key)
152
- extracted = extract_body_if_negated(block.body)
153
- if extracted.method?('in?') || extracted.method?('include?') ||
154
- extracted.method?('exclude?')
155
- return true
156
- end
157
- return true if block.body.method?('eql?')
139
+ body = block.body
158
140
 
159
- except_key.sym_type? || except_key.str_type?
141
+ if body.method?('==') || body.method?('!=')
142
+ except_key.sym_type? || except_key.str_type?
143
+ else
144
+ true
145
+ end
160
146
  end
161
147
 
162
148
  def extract_body_if_negated(body)
163
- return body unless body.method?('!')
164
-
165
- body.receiver
149
+ if body.method?('!')
150
+ [body.receiver, true]
151
+ else
152
+ [body, false]
153
+ end
166
154
  end
167
155
 
168
156
  def except_key_source(key)
@@ -187,12 +175,11 @@ module RuboCop
187
175
  end
188
176
 
189
177
  def except_key(node)
190
- key_argument = node.argument_list.first.source
191
- body = extract_body_if_negated(node.body)
178
+ key_arg = node.argument_list.first.source
179
+ body, = extract_body_if_negated(node.body)
192
180
  lhs, _method_name, rhs = *body
193
- return if [lhs, rhs].map(&:source).none?(key_argument)
194
181
 
195
- [lhs, rhs].find { |operand| operand.source != key_argument }
182
+ lhs.source == key_arg ? rhs : lhs
196
183
  end
197
184
 
198
185
  def offense_range(node)
@@ -137,6 +137,7 @@ module RuboCop
137
137
  MSG_19 = 'Use the new Ruby 1.9 hash syntax.'
138
138
  MSG_NO_MIXED_KEYS = "Don't mix styles in the same hash."
139
139
  MSG_HASH_ROCKETS = 'Use hash rockets syntax.'
140
+ NO_MIXED_KEYS_STYLES = %i[ruby19_no_mixed_keys no_mixed_keys].freeze
140
141
 
141
142
  def on_hash(node)
142
143
  pairs = node.pairs
@@ -196,7 +197,7 @@ module RuboCop
196
197
  def autocorrect(corrector, node)
197
198
  if style == :hash_rockets || force_hash_rockets?(node.parent.pairs)
198
199
  autocorrect_hash_rockets(corrector, node)
199
- elsif style == :ruby19_no_mixed_keys || style == :no_mixed_keys
200
+ elsif NO_MIXED_KEYS_STYLES.include?(style)
200
201
  autocorrect_no_mixed_keys(corrector, node)
201
202
  else
202
203
  autocorrect_ruby19(corrector, node)
@@ -272,7 +273,9 @@ module RuboCop
272
273
  end
273
274
 
274
275
  def argument_without_space?(node)
275
- node.argument? && node.source_range.begin_pos == node.parent.loc.selector.end_pos
276
+ return false if !node.argument? || !node.parent.loc.selector
277
+
278
+ node.source_range.begin_pos == node.parent.loc.selector.end_pos
276
279
  end
277
280
 
278
281
  def autocorrect_hash_rockets(corrector, pair_node)
@@ -43,7 +43,7 @@ module RuboCop
43
43
  template = if require_newline?(node)
44
44
  MSG_NEWLINE
45
45
  elsif node.else_branch&.if_type? || node.else_branch&.begin_type? ||
46
- use_block_in_branches?(node)
46
+ use_masgn_or_block_in_branches?(node)
47
47
  MSG_IF_ELSE
48
48
  else
49
49
  MSG_TERNARY
@@ -53,7 +53,7 @@ module RuboCop
53
53
  end
54
54
 
55
55
  def autocorrect(corrector, node)
56
- if require_newline?(node) || use_block_in_branches?(node)
56
+ if require_newline?(node) || use_masgn_or_block_in_branches?(node)
57
57
  corrector.replace(node.loc.begin, "\n")
58
58
  else
59
59
  corrector.replace(node, replacement(node))
@@ -64,8 +64,10 @@ module RuboCop
64
64
  node.branches.compact.any?(&:begin_type?) || use_return_with_argument?(node)
65
65
  end
66
66
 
67
- def use_block_in_branches?(node)
68
- node.branches.compact.any? { |branch| branch.block_type? || branch.numblock_type? }
67
+ def use_masgn_or_block_in_branches?(node)
68
+ node.branches.compact.any? do |branch|
69
+ branch.masgn_type? || branch.block_type? || branch.numblock_type?
70
+ end
69
71
  end
70
72
 
71
73
  def use_return_with_argument?(node)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for assignments to a local `it` variable inside a block
7
+ # where `it` can refer to the first anonymous parameter as of Ruby 3.4.
8
+ #
9
+ # Although Ruby allows reassigning `it` in these cases, it could
10
+ # cause confusion if `it` is used as a block parameter elsewhere.
11
+ # For consistency, this also applies to numblocks and blocks with
12
+ # parameters, even though `it` cannot be used in those cases.
13
+ #
14
+ # @example
15
+ # # bad
16
+ # foo { it = 5 }
17
+ # foo { |bar| it = bar }
18
+ # foo { it = _2 }
19
+ #
20
+ # # good - use a different variable name
21
+ # foo { var = 5 }
22
+ # foo { |bar| var = bar }
23
+ # foo { bar = _2 }
24
+ class ItAssignment < Base
25
+ MSG = '`it` is the default block parameter; consider another name.'
26
+
27
+ def on_lvasgn(node)
28
+ return unless node.name == :it
29
+ return unless node.each_ancestor(:block, :numblock).any?
30
+
31
+ add_offense(node.loc.name)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -135,17 +135,20 @@ module RuboCop
135
135
  node.parent&.class_type? && node.parent.single_line?
136
136
  end
137
137
 
138
- def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
138
+ # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
139
+ def call_with_ambiguous_arguments?(node)
139
140
  call_with_braced_block?(node) ||
140
141
  call_in_argument_with_block?(node) ||
141
142
  call_as_argument_or_chain?(node) ||
142
143
  call_in_match_pattern?(node) ||
143
144
  hash_literal_in_arguments?(node) ||
145
+ ambiguous_range_argument?(node) ||
144
146
  node.descendants.any? do |n|
145
147
  n.forwarded_args_type? || n.block_type? || n.numblock_type? ||
146
148
  ambiguous_literal?(n) || logical_operator?(n)
147
149
  end
148
150
  end
151
+ # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity
149
152
 
150
153
  def call_with_braced_block?(node)
151
154
  (node.call_type? || node.super_type?) && node.block_node&.braces?
@@ -177,6 +180,13 @@ module RuboCop
177
180
  end
178
181
  end
179
182
 
183
+ def ambiguous_range_argument?(node)
184
+ return true if (first_arg = node.first_argument)&.range_type? && first_arg.begin.nil?
185
+ return true if (last_arg = node.last_argument)&.range_type? && last_arg.end.nil?
186
+
187
+ false
188
+ end
189
+
180
190
  def allowed_multiline_call_with_parentheses?(node)
181
191
  cop_config['AllowParenthesesInMultilineCall'] && node.multiline?
182
192
  end
@@ -61,6 +61,8 @@ module RuboCop
61
61
  # https://bugs.ruby-lang.org/issues/18396.
62
62
  # - Parentheses are required in anonymous arguments, keyword arguments
63
63
  # and block passing in Ruby 3.2.
64
+ # - Parentheses are required when the first argument is a beginless range or
65
+ # the last argument is an endless range.
64
66
  #
65
67
  # @example EnforcedStyle: require_parentheses (default)
66
68
  #
@@ -107,7 +107,7 @@ module RuboCop
107
107
  end
108
108
 
109
109
  def variable_in_mass_assignment?(variable_name, node)
110
- node.assignments.any? { |n| n.name == variable_name }
110
+ node.assignments.reject(&:send_type?).any? { |n| n.name == variable_name }
111
111
  end
112
112
 
113
113
  def offense_range(node)
@@ -144,6 +144,8 @@ module RuboCop
144
144
  end
145
145
 
146
146
  def autocorrect(corrector, node)
147
+ node = node.ancestors.find { |ancestor| ancestor.loc.end } unless node.loc.end
148
+
147
149
  case empty_else_style
148
150
  when :empty
149
151
  corrector.insert_before(node.loc.end, 'else; nil; ')
@@ -55,6 +55,22 @@ module RuboCop
55
55
  MSG = 'Avoid comparing a variable with multiple items ' \
56
56
  'in a conditional, use `Array#include?` instead.'
57
57
 
58
+ # @!method simple_double_comparison?(node)
59
+ def_node_matcher :simple_double_comparison?, <<~PATTERN
60
+ (send lvar :== lvar)
61
+ PATTERN
62
+
63
+ # @!method simple_comparison_lhs(node)
64
+ def_node_matcher :simple_comparison_lhs, <<~PATTERN
65
+ (send ${lvar call} :== $_)
66
+ PATTERN
67
+
68
+ # @!method simple_comparison_rhs(node)
69
+ def_node_matcher :simple_comparison_rhs, <<~PATTERN
70
+ (send $_ :== ${lvar call})
71
+ PATTERN
72
+
73
+ # rubocop:disable Metrics/AbcSize
58
74
  def on_or(node)
59
75
  root_of_or_node = root_of_or_node(node)
60
76
  return unless node == root_of_or_node
@@ -63,29 +79,20 @@ module RuboCop
63
79
  return unless (variable, values = find_offending_var(node))
64
80
  return if values.size < comparisons_threshold
65
81
 
66
- add_offense(node) do |corrector|
82
+ range = offense_range(values)
83
+
84
+ add_offense(range) do |corrector|
67
85
  elements = values.map(&:source).join(', ')
68
- prefer_method = "[#{elements}].include?(#{variable_name(variable)})"
86
+ argument = variable.lvar_type? ? variable_name(variable) : variable.source
87
+ prefer_method = "[#{elements}].include?(#{argument})"
69
88
 
70
- corrector.replace(node, prefer_method)
89
+ corrector.replace(range, prefer_method)
71
90
  end
72
91
  end
92
+ # rubocop:enable Metrics/AbcSize
73
93
 
74
94
  private
75
95
 
76
- # @!method simple_double_comparison?(node)
77
- def_node_matcher :simple_double_comparison?, '(send $lvar :== $lvar)'
78
-
79
- # @!method simple_comparison_lhs?(node)
80
- def_node_matcher :simple_comparison_lhs?, <<~PATTERN
81
- (send $lvar :== $_)
82
- PATTERN
83
-
84
- # @!method simple_comparison_rhs?(node)
85
- def_node_matcher :simple_comparison_rhs?, <<~PATTERN
86
- (send $_ :== $lvar)
87
- PATTERN
88
-
89
96
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
90
97
  def find_offending_var(node, variables = Set.new, values = [])
91
98
  if node.or_type?
@@ -93,8 +100,8 @@ module RuboCop
93
100
  find_offending_var(node.rhs, variables, values)
94
101
  elsif simple_double_comparison?(node)
95
102
  return
96
- elsif (var, obj = simple_comparison?(node))
97
- return if allow_method_comparison? && obj.send_type?
103
+ elsif (var, obj = simple_comparison(node))
104
+ return if allow_method_comparison? && obj.call_type?
98
105
 
99
106
  variables << var
100
107
  return if variables.size > 1
@@ -106,6 +113,10 @@ module RuboCop
106
113
  end
107
114
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
108
115
 
116
+ def offense_range(values)
117
+ values.first.parent.source_range.begin.join(values.last.parent.source_range.end)
118
+ end
119
+
109
120
  def variable_name(node)
110
121
  node.children[0]
111
122
  end
@@ -119,12 +130,13 @@ module RuboCop
119
130
  end
120
131
 
121
132
  def comparison?(node)
122
- simple_comparison?(node) || nested_comparison?(node)
133
+ !!simple_comparison(node) || nested_comparison?(node)
123
134
  end
124
135
 
125
- def simple_comparison?(node)
126
- if (var, obj = simple_comparison_lhs?(node)) ||
127
- (obj, var = simple_comparison_rhs?(node))
136
+ def simple_comparison(node)
137
+ if (var, obj = simple_comparison_lhs(node)) || (obj, var = simple_comparison_rhs(node))
138
+ return if var.call_type? && !allow_method_comparison?
139
+
128
140
  [var, obj]
129
141
  end
130
142
  end
@@ -238,7 +238,7 @@ module RuboCop
238
238
 
239
239
  # @!method range_enclosed_in_parentheses?(node)
240
240
  def_node_matcher :range_enclosed_in_parentheses?, <<~PATTERN
241
- (begin ({irange erange} _ _))
241
+ (begin (range _ _))
242
242
  PATTERN
243
243
  end
244
244
  end
@@ -33,9 +33,10 @@ module RuboCop
33
33
  RESTRICT_ON_SEND = %i[then yield_self].freeze
34
34
 
35
35
  def on_block(node)
36
+ return unless RESTRICT_ON_SEND.include?(node.method_name)
37
+
36
38
  check_method_node(node.send_node)
37
39
  end
38
-
39
40
  alias on_numblock on_block
40
41
 
41
42
  def on_send(node)
@@ -43,29 +44,26 @@ module RuboCop
43
44
 
44
45
  check_method_node(node)
45
46
  end
47
+ alias on_csend on_send
46
48
 
47
49
  private
48
50
 
49
51
  def check_method_node(node)
50
- return unless preferred_method?(node)
51
-
52
- message = message(node)
53
- add_offense(node.loc.selector, message: message) do |corrector|
54
- prefer = style == :then && node.receiver.nil? ? 'self.then' : style
52
+ if preferred_method?(node)
53
+ correct_style_detected
54
+ else
55
+ opposite_style_detected
56
+ message = message(node)
57
+ add_offense(node.loc.selector, message: message) do |corrector|
58
+ prefer = style == :then && node.receiver.nil? ? 'self.then' : style
55
59
 
56
- corrector.replace(node.loc.selector, prefer)
60
+ corrector.replace(node.loc.selector, prefer)
61
+ end
57
62
  end
58
63
  end
59
64
 
60
65
  def preferred_method?(node)
61
- case style
62
- when :then
63
- node.method?(:yield_self)
64
- when :yield_self
65
- node.method?(:then)
66
- else
67
- false
68
- end
66
+ node.method?(style)
69
67
  end
70
68
 
71
69
  def message(node)
@@ -98,7 +98,7 @@ module RuboCop
98
98
 
99
99
  def style
100
100
  return super unless super == :same_as_string_literals
101
- return :single_quotes unless string_literals_config['Enabled']
101
+ return :single_quotes unless config.cop_enabled?('Style/StringLiterals')
102
102
 
103
103
  string_literals_config['EnforcedStyle'].to_sym
104
104
  end
@@ -50,6 +50,9 @@ module RuboCop
50
50
 
51
51
  EXPLODED_MSG = 'Provide an exception class and message as arguments to `%<method>s`.'
52
52
  COMPACT_MSG = 'Provide an exception object as an argument to `%<method>s`.'
53
+ ACCEPTABLE_ARG_TYPES = %i[
54
+ hash forwarded_restarg splat forwarded_restarg forwarded_args
55
+ ].freeze
53
56
 
54
57
  RESTRICT_ON_SEND = %i[raise fail].freeze
55
58
 
@@ -138,9 +141,8 @@ module RuboCop
138
141
 
139
142
  arg = args.first
140
143
 
141
- # Allow code like `raise Ex.new(kw: arg)`.
142
- # Allow code like `raise Ex.new(*args)`.
143
- arg.hash_type? || arg.splat_type?
144
+ # Allow nodes that may forward more than one argument
145
+ ACCEPTABLE_ARG_TYPES.include?(arg.type)
144
146
  end
145
147
 
146
148
  def allowed_non_exploded_type?(arg)
@@ -36,7 +36,7 @@ module RuboCop
36
36
  (send
37
37
  {nil? (const {nil? cbase} :Random) (const {nil? cbase} :Kernel)}
38
38
  :rand
39
- {int (irange int int) (erange int int)}))
39
+ {int (range int int)}))
40
40
  PATTERN
41
41
 
42
42
  # @!method rand_op_integer?(node)
@@ -45,7 +45,7 @@ module RuboCop
45
45
  (send
46
46
  {nil? (const {nil? cbase} :Random) (const {nil? cbase} :Kernel)}
47
47
  :rand
48
- {int (irange int int) (erange int int)})
48
+ {int (range int int)})
49
49
  {:+ :-}
50
50
  int)
51
51
  PATTERN
@@ -56,7 +56,7 @@ module RuboCop
56
56
  (send
57
57
  {nil? (const {nil? cbase} :Random) (const {nil? cbase} :Kernel)}
58
58
  :rand
59
- {int (irange int int) (erange int int)})
59
+ {int (range int int)})
60
60
  {:succ :pred :next})
61
61
  PATTERN
62
62
 
@@ -3,7 +3,8 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for uses a redundant current directory in path.
6
+ # Checks for paths given to `require_relative` that start with
7
+ # the current directory (`./`), which can be omitted.
7
8
  #
8
9
  # @example
9
10
  #
@@ -11,6 +11,9 @@ module RuboCop
11
11
  # will not register an offense, because it allows the initializer to take a different
12
12
  # number of arguments as its superclass potentially does.
13
13
  #
14
+ # NOTE: If an initializer takes any arguments and has an empty body, RuboCop
15
+ # assumes it to *not* be redundant. This is to prevent potential `ArgumentError`.
16
+ #
14
17
  # NOTE: If an initializer argument has a default value, RuboCop assumes it
15
18
  # to *not* be redundant.
16
19
  #
@@ -19,8 +22,10 @@ module RuboCop
19
22
  # initializer.
20
23
  #
21
24
  # @safety
22
- # This cop is unsafe because if subclass overrides `initialize` method with
23
- # a different arity than superclass.
25
+ # This cop is unsafe because removing an empty initializer may alter
26
+ # the behavior of the code, particularly if the superclass initializer
27
+ # raises an exception. In such cases, the empty initializer may act as
28
+ # a safeguard to prevent unintended errors from propagating.
24
29
  #
25
30
  # @example
26
31
  # # bad
@@ -69,6 +74,10 @@ module RuboCop
69
74
  # end
70
75
  #
71
76
  # # good (changes the parameter requirements)
77
+ # def initialize(_)
78
+ # end
79
+ #
80
+ # # good (changes the parameter requirements)
72
81
  # def initialize(*)
73
82
  # end
74
83
  #
@@ -111,7 +120,7 @@ module RuboCop
111
120
  return if acceptable?(node)
112
121
 
113
122
  if node.body.nil?
114
- register_offense(node, MSG_EMPTY)
123
+ register_offense(node, MSG_EMPTY) if node.arguments.empty?
115
124
  else
116
125
  return if node.body.begin_type?
117
126