rubocop 1.69.1 → 1.70.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 (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