rubocop 1.75.8 → 1.77.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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +72 -7
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  6. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  7. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  8. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  9. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  10. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  11. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  12. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -0
  13. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  14. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  15. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  16. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  17. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  18. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  19. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  20. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  21. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  22. data/lib/rubocop/cop/lint/literal_as_condition.rb +19 -27
  23. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  24. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  25. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  26. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  27. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  28. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  29. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  30. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  31. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  32. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  33. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  34. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  35. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  36. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  37. data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
  38. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  39. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  40. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  41. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -1
  42. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  43. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  44. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  45. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  46. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  47. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  48. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  49. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  50. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  51. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  52. data/lib/rubocop/cop/style/redundant_parentheses.rb +26 -5
  53. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  54. data/lib/rubocop/cop/style/safe_navigation.rb +24 -11
  55. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -1
  56. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  57. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  58. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  59. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  60. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  61. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  62. data/lib/rubocop/version.rb +1 -1
  63. data/lib/rubocop.rb +8 -1
  64. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  65. metadata +14 -7
@@ -87,7 +87,7 @@ module RuboCop
87
87
  true
88
88
  end
89
89
 
90
- def integral(node)
90
+ def integral?(node)
91
91
  mantissa, = node.source.split('e')
92
92
  /^-?[1-9](\d*[1-9])?$/.match?(mantissa)
93
93
  end
@@ -101,7 +101,7 @@ module RuboCop
101
101
  when :engineering
102
102
  !engineering?(node)
103
103
  when :integral
104
- !integral(node)
104
+ !integral?(node)
105
105
  else
106
106
  false
107
107
  end
@@ -9,7 +9,20 @@ module RuboCop
9
9
  # On the other hand, `ENV.fetch` raises `KeyError` or returns the explicitly
10
10
  # specified default value.
11
11
  #
12
- # @example
12
+ # @example DefaultToNil: true (default)
13
+ # # bad
14
+ # ENV['X']
15
+ # x = ENV['X']
16
+ #
17
+ # # good
18
+ # ENV.fetch('X', nil)
19
+ # x = ENV.fetch('X', nil)
20
+ #
21
+ # # also good
22
+ # !ENV['X']
23
+ # ENV['X'].some_method # (e.g. `.nil?`)
24
+ #
25
+ # @example DefaultToNil: false
13
26
  # # bad
14
27
  # ENV['X']
15
28
  # x = ENV['X']
@@ -25,7 +38,8 @@ module RuboCop
25
38
  class FetchEnvVar < Base
26
39
  extend AutoCorrector
27
40
 
28
- MSG = 'Use `ENV.fetch(%<key>s)` or `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
41
+ MSG_WITH_NIL = 'Use `ENV.fetch(%<key>s, nil)` instead of `ENV[%<key>s]`.'
42
+ MSG_WITHOUT_NIL = 'Use `ENV.fetch(%<key>s)` instead of `ENV[%<key>s]`.'
29
43
  RESTRICT_ON_SEND = [:[]].freeze
30
44
 
31
45
  # @!method env_with_bracket?(node)
@@ -37,7 +51,7 @@ module RuboCop
37
51
  env_with_bracket?(node) do |name_node|
38
52
  break unless offensive?(node)
39
53
 
40
- message = format(MSG, key: name_node.source)
54
+ message = format(offense_message, key: name_node.source)
41
55
  add_offense(node, message: message) do |corrector|
42
56
  corrector.replace(node, new_code(name_node))
43
57
  end
@@ -46,6 +60,14 @@ module RuboCop
46
60
 
47
61
  private
48
62
 
63
+ def default_to_nil?
64
+ cop_config.fetch('DefaultToNil', true)
65
+ end
66
+
67
+ def offense_message
68
+ default_to_nil? ? MSG_WITH_NIL : MSG_WITHOUT_NIL
69
+ end
70
+
49
71
  def allowed_var?(node)
50
72
  env_key_node = node.children.last
51
73
  env_key_node.str_type? && cop_config['AllowedVars'].include?(env_key_node.value)
@@ -53,12 +75,12 @@ module RuboCop
53
75
 
54
76
  def used_as_flag?(node)
55
77
  return false if node.root?
56
- return true if used_if_condition_in_body(node)
78
+ return true if used_if_condition_in_body?(node)
57
79
 
58
80
  node.parent.send_type? && (node.parent.prefix_bang? || node.parent.comparison_method?)
59
81
  end
60
82
 
61
- def used_if_condition_in_body(node)
83
+ def used_if_condition_in_body?(node)
62
84
  if_node = node.ancestors.find(&:if_type?)
63
85
 
64
86
  return false unless (condition = if_node&.condition)
@@ -125,7 +147,11 @@ module RuboCop
125
147
  end
126
148
 
127
149
  def new_code(name_node)
128
- "ENV.fetch(#{name_node.source}, nil)"
150
+ if default_to_nil?
151
+ "ENV.fetch(#{name_node.source}, nil)"
152
+ else
153
+ "ENV.fetch(#{name_node.source})"
154
+ end
129
155
  end
130
156
  end
131
157
  end
@@ -54,7 +54,7 @@ module RuboCop
54
54
  def_node_matcher :hash_from_array?, '(send (const {nil? cbase} :Hash) :[] ...)'
55
55
 
56
56
  def on_send(node)
57
- return unless hash_from_array?(node)
57
+ return if part_of_ignored_node?(node) || !hash_from_array?(node)
58
58
 
59
59
  # There are several cases:
60
60
  # If there is one argument:
@@ -63,11 +63,12 @@ module RuboCop
63
63
  # If there is 0 or 2+ arguments:
64
64
  # Hash[a1, a2, a3, a4] => {a1 => a2, a3 => a4}
65
65
  # ...but don't suggest correction if there is odd number of them (it is a bug)
66
- node.arguments.count == 1 ? single_argument(node) : multi_argument(node)
66
+ node.arguments.one? ? single_argument(node) : multi_argument(node)
67
67
  end
68
68
 
69
69
  private
70
70
 
71
+ # rubocop:disable Metrics/MethodLength
71
72
  def single_argument(node)
72
73
  first_argument = node.first_argument
73
74
  if first_argument.hash_type?
@@ -82,8 +83,11 @@ module RuboCop
82
83
  replacement = "(#{replacement})" if requires_parens?(first_argument)
83
84
  corrector.replace(node, "#{replacement}.to_h")
84
85
  end
86
+
87
+ ignore_node(node)
85
88
  end
86
89
  end
90
+ # rubocop:enable Metrics/MethodLength
87
91
 
88
92
  def use_zip_method_without_argument?(first_argument)
89
93
  return false unless first_argument&.send_type?
@@ -111,7 +115,12 @@ module RuboCop
111
115
  end
112
116
 
113
117
  def requires_parens?(node)
114
- (node.call_type? && node.arguments.any? && !node.parenthesized?) || node.operator_keyword?
118
+ if node.call_type?
119
+ return false if node.method?(:[])
120
+ return true if node.arguments.any? && !node.parenthesized?
121
+ end
122
+
123
+ node.operator_keyword?
115
124
  end
116
125
 
117
126
  def multi_argument(node)
@@ -132,12 +132,10 @@ module RuboCop
132
132
  end
133
133
 
134
134
  def pattern_matching_nodes(condition)
135
- if condition.type?(:match_pattern, :match_pattern_p)
135
+ if condition.any_match_pattern_type?
136
136
  [condition]
137
137
  else
138
- condition.each_descendant.select do |node|
139
- node.type?(:match_pattern, :match_pattern_p)
140
- end
138
+ condition.each_descendant.select(&:any_match_pattern_type?)
141
139
  end
142
140
  end
143
141
 
@@ -225,8 +223,17 @@ module RuboCop
225
223
 
226
224
  def too_long_line_based_on_allow_uri?(line)
227
225
  if allow_uri?
228
- uri_range = find_excessive_uri_range(line)
229
- return false if uri_range && allowed_uri_position?(line, uri_range)
226
+ uri_range = find_excessive_range(line, :uri)
227
+ return false if uri_range && allowed_position?(line, uri_range)
228
+ end
229
+
230
+ true
231
+ end
232
+
233
+ def too_long_line_based_on_allow_qualified_name?(line)
234
+ if allow_qualified_name?
235
+ namespace_range = find_excessive_range(line, :namespace)
236
+ return false if namespace_range && allowed_position?(line, namespace_range)
230
237
  end
231
238
 
232
239
  true
@@ -5,15 +5,28 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for blocks with one argument where `it` block parameter can be used.
7
7
  #
8
- # It provides three `EnforcedStyle` options:
8
+ # It provides four `EnforcedStyle` options:
9
9
  #
10
- # 1. `only_numbered_parameters` (default) ... Detects only numbered block parameters.
11
- # 2. `always` ... Always uses the `it` block parameter.
12
- # 3. `disallow` ... Disallows the `it` block parameter.
10
+ # 1. `allow_single_line` (default) ... Always uses the `it` block parameter in a single line.
11
+ # 2. `only_numbered_parameters` ... Detects only numbered block parameters.
12
+ # 3. `always` ... Always uses the `it` block parameter.
13
+ # 4. `disallow` ... Disallows the `it` block parameter.
13
14
  #
14
- # A single numbered parameter is detected when `only_numbered_parameters` or `always`.
15
+ # A single numbered parameter is detected when `allow_single_line`,
16
+ # `only_numbered_parameters`, or `always`.
15
17
  #
16
- # @example EnforcedStyle: only_numbered_parameters (default)
18
+ # @example EnforcedStyle: allow_single_line (default)
19
+ # # bad
20
+ # block do
21
+ # do_something(it)
22
+ # end
23
+ # block { do_something(_1) }
24
+ #
25
+ # # good
26
+ # block { do_something(it) }
27
+ # block { |named_param| do_something(named_param) }
28
+ #
29
+ # @example EnforcedStyle: only_numbered_parameters
17
30
  # # bad
18
31
  # block { do_something(_1) }
19
32
  #
@@ -42,8 +55,9 @@ module RuboCop
42
55
  extend TargetRubyVersion
43
56
  extend AutoCorrector
44
57
 
45
- MSG_USE_IT_BLOCK_PARAMETER = 'Use `it` block parameter.'
46
- MSG_AVOID_IT_BLOCK_PARAMETER = 'Avoid using `it` block parameter.'
58
+ MSG_USE_IT_PARAMETER = 'Use `it` block parameter.'
59
+ MSG_AVOID_IT_PARAMETER = 'Avoid using `it` block parameter.'
60
+ MSG_AVOID_IT_PARAMETER_MULTILINE = 'Avoid using `it` block parameter for multi-line blocks.'
47
61
 
48
62
  minimum_target_ruby_version 3.4
49
63
 
@@ -57,7 +71,7 @@ module RuboCop
57
71
  variables = find_block_variables(node, node.first_argument.source)
58
72
 
59
73
  variables.each do |variable|
60
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
74
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
61
75
  corrector.remove(node.arguments)
62
76
  corrector.replace(variable, 'it')
63
77
  end
@@ -71,19 +85,24 @@ module RuboCop
71
85
  variables = find_block_variables(node, '_1')
72
86
 
73
87
  variables.each do |variable|
74
- add_offense(variable, message: MSG_USE_IT_BLOCK_PARAMETER) do |corrector|
88
+ add_offense(variable, message: MSG_USE_IT_PARAMETER) do |corrector|
75
89
  corrector.replace(variable, 'it')
76
90
  end
77
91
  end
78
92
  end
79
93
 
80
94
  def on_itblock(node)
81
- return unless style == :disallow
95
+ case style
96
+ when :allow_single_line
97
+ return if node.single_line?
82
98
 
83
- variables = find_block_variables(node, 'it')
99
+ add_offense(node, message: MSG_AVOID_IT_PARAMETER_MULTILINE)
100
+ when :disallow
101
+ variables = find_block_variables(node, 'it')
84
102
 
85
- variables.each do |variable|
86
- add_offense(variable, message: MSG_AVOID_IT_BLOCK_PARAMETER)
103
+ variables.each do |variable|
104
+ add_offense(variable, message: MSG_AVOID_IT_PARAMETER)
105
+ end
87
106
  end
88
107
  end
89
108
 
@@ -167,7 +167,7 @@ module RuboCop
167
167
  def call_in_match_pattern?(node)
168
168
  return false unless (parent = node.parent)
169
169
 
170
- parent.type?(:match_pattern, :match_pattern_p)
170
+ parent.any_match_pattern_type?
171
171
  end
172
172
 
173
173
  def hash_literal_in_arguments?(node)
@@ -251,7 +251,7 @@ module RuboCop
251
251
  return false unless (last_argument = node.last_argument)
252
252
  return true if last_argument.forwarded_restarg_type?
253
253
 
254
- last_argument.hash_type? && last_argument.children.first&.forwarded_kwrestarg_type?
254
+ last_argument.hash_type? && last_argument.children.any?(&:forwarded_kwrestarg_type?)
255
255
  end
256
256
  end
257
257
  # rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
@@ -39,13 +39,21 @@ module RuboCop
39
39
  include RangeHelp
40
40
 
41
41
  MSG = 'Use `%<prefer>s` instead.'
42
- GRATER_OPERATORS = %i[> >=].freeze
42
+ GREATER_OPERATORS = %i[> >=].freeze
43
43
  LESS_OPERATORS = %i[< <=].freeze
44
- COMPARISON_OPERATORS = GRATER_OPERATORS + LESS_OPERATORS
44
+ COMPARISON_OPERATORS = (GREATER_OPERATORS + LESS_OPERATORS).to_set.freeze
45
+
46
+ # @!method comparison_condition(node, name)
47
+ def_node_matcher :comparison_condition, <<~PATTERN
48
+ {
49
+ (send $_lhs $COMPARISON_OPERATORS $_rhs)
50
+ (begin (send $_lhs $COMPARISON_OPERATORS $_rhs))
51
+ }
52
+ PATTERN
45
53
 
46
54
  def on_if(node)
47
- lhs, operator, rhs = *node.condition
48
- return unless COMPARISON_OPERATORS.include?(operator)
55
+ lhs, operator, rhs = comparison_condition(node.condition)
56
+ return unless operator
49
57
 
50
58
  if_branch = node.if_branch
51
59
  else_branch = node.else_branch
@@ -63,7 +71,7 @@ module RuboCop
63
71
 
64
72
  def preferred_method(operator, lhs, rhs, if_branch, else_branch)
65
73
  if lhs == if_branch && rhs == else_branch
66
- GRATER_OPERATORS.include?(operator) ? 'max' : 'min'
74
+ GREATER_OPERATORS.include?(operator) ? 'max' : 'min'
67
75
  elsif lhs == else_branch && rhs == if_branch
68
76
  LESS_OPERATORS.include?(operator) ? 'max' : 'min'
69
77
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for redundant calls of `Array#flatten`.
7
+ #
8
+ # `Array#join` joins nested arrays recursively, so flattening an array
9
+ # beforehand is redundant.
10
+ #
11
+ # @safety
12
+ # Cop is unsafe because the receiver of `flatten` method might not
13
+ # be an `Array`, so it's possible it won't respond to `join` method,
14
+ # or the end result would be different.
15
+ # Also, if the global variable `$,` is set to a value other than the default `nil`,
16
+ # false positives may occur.
17
+ #
18
+ # @example
19
+ # # bad
20
+ # x.flatten.join
21
+ # x.flatten(1).join
22
+ #
23
+ # # good
24
+ # x.join
25
+ #
26
+ class RedundantArrayFlatten < Base
27
+ extend AutoCorrector
28
+
29
+ MSG = 'Remove the redundant `flatten`.'
30
+
31
+ RESTRICT_ON_SEND = %i[flatten].freeze
32
+
33
+ # @!method flatten_join?(node)
34
+ def_node_matcher :flatten_join?, <<~PATTERN
35
+ (call (call !nil? :flatten _?) :join (nil)?)
36
+ PATTERN
37
+
38
+ def on_send(node)
39
+ return unless flatten_join?(node.parent)
40
+
41
+ range = node.loc.dot.begin.join(node.source_range.end)
42
+ add_offense(range) do |corrector|
43
+ corrector.remove(range)
44
+ end
45
+ end
46
+ alias on_csend on_send
47
+ end
48
+ end
49
+ end
50
+ end
@@ -130,7 +130,7 @@ module RuboCop
130
130
  end
131
131
 
132
132
  def require_parentheses?(node)
133
- node.send_type? && !node.arguments.count.zero? && !node.parenthesized_call?
133
+ node.send_type? && node.arguments.any? && !node.parenthesized_call?
134
134
  end
135
135
  end
136
136
  end
@@ -49,6 +49,7 @@ module RuboCop
49
49
  empty_parentheses?(node) ||
50
50
  first_arg_begins_with_hash_literal?(node) ||
51
51
  rescue?(node) ||
52
+ in_pattern_matching_in_method_argument?(node) ||
52
53
  allowed_pin_operator?(node) ||
53
54
  allowed_expression?(node)
54
55
  end
@@ -122,6 +123,13 @@ module RuboCop
122
123
  hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
123
124
  end
124
125
 
126
+ def in_pattern_matching_in_method_argument?(begin_node)
127
+ return false unless begin_node.parent&.call_type?
128
+ return false unless (node = begin_node.children.first)
129
+
130
+ target_ruby_version <= 2.7 ? node.match_pattern_type? : node.match_pattern_p_type?
131
+ end
132
+
125
133
  def method_chain_begins_with_hash_literal(node)
126
134
  return if node.nil?
127
135
  return node if node.hash_type?
@@ -134,7 +142,7 @@ module RuboCop
134
142
  node = begin_node.children.first
135
143
 
136
144
  if (message = find_offense_message(begin_node, node))
137
- if node.range_type? && !argument_of_parenthesized_method_call?(begin_node)
145
+ if node.range_type? && !argument_of_parenthesized_method_call?(begin_node, node)
138
146
  begin_node = begin_node.parent
139
147
  end
140
148
 
@@ -156,8 +164,11 @@ module RuboCop
156
164
  if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
157
165
  return 'an expression'
158
166
  end
167
+ if disallowed_one_line_pattern_matching?(begin_node, node)
168
+ return 'a one-line pattern matching'
169
+ end
159
170
  return 'an interpolated expression' if interpolation?(begin_node)
160
- return 'a method argument' if argument_of_parenthesized_method_call?(begin_node)
171
+ return 'a method argument' if argument_of_parenthesized_method_call?(begin_node, node)
161
172
 
162
173
  return if begin_node.chained?
163
174
 
@@ -180,9 +191,10 @@ module RuboCop
180
191
  # @!method interpolation?(node)
181
192
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
182
193
 
183
- def argument_of_parenthesized_method_call?(begin_node)
184
- node = begin_node.children.first
185
- return false if node.basic_conditional? || method_call_parentheses_required?(node)
194
+ def argument_of_parenthesized_method_call?(begin_node, node)
195
+ if node.basic_conditional? || node.rescue_type? || method_call_parentheses_required?(node)
196
+ return false
197
+ end
186
198
  return false unless (parent = begin_node.parent)
187
199
 
188
200
  parent.call_type? && parent.parenthesized? && parent.receiver != begin_node
@@ -242,6 +254,15 @@ module RuboCop
242
254
  end
243
255
  end
244
256
 
257
+ def disallowed_one_line_pattern_matching?(begin_node, node)
258
+ if (parent = begin_node.parent)
259
+ return false if parent.any_def_type? && parent.endless?
260
+ return false if parent.assignment?
261
+ end
262
+
263
+ node.any_match_pattern_type? && node.each_ancestor.none?(&:operator_keyword?)
264
+ end
265
+
245
266
  def raised_to_power_negative_numeric?(begin_node, node)
246
267
  return false unless node.numeric_type?
247
268
 
@@ -67,6 +67,9 @@ module RuboCop
67
67
 
68
68
  def on_or_asgn(node)
69
69
  allow_self(node.lhs)
70
+
71
+ lhs_name = node.lhs.lvasgn_type? ? node.lhs.name : node.lhs
72
+ add_lhs_to_local_variables_scopes(node.rhs, lhs_name)
70
73
  end
71
74
  alias on_and_asgn on_or_asgn
72
75
 
@@ -123,11 +126,11 @@ module RuboCop
123
126
  def on_if(node)
124
127
  # Allow conditional nodes to use `self` in the condition if that variable
125
128
  # name is used in an `lvasgn` or `masgn` within the `if`.
126
- node.child_nodes.each do |child_node|
127
- if child_node.lvasgn_type?
128
- add_lhs_to_local_variables_scopes(node.condition, child_node.lhs)
129
- elsif child_node.masgn_type?
130
- add_masgn_lhs_variables(node.condition, child_node.lhs)
129
+ node.each_descendant(:lvasgn, :masgn) do |descendant_node|
130
+ if descendant_node.lvasgn_type?
131
+ add_lhs_to_local_variables_scopes(node.condition, descendant_node.lhs)
132
+ else
133
+ add_masgn_lhs_variables(node.condition, descendant_node.lhs)
131
134
  end
132
135
  end
133
136
  end
@@ -86,6 +86,10 @@ module RuboCop
86
86
  # foo.baz = bar if foo
87
87
  # foo.baz + bar if foo
88
88
  # foo.bar > 2 if foo
89
+ #
90
+ # foo ? foo[index] : nil # Ignored `foo&.[](index)` due to unclear readability benefit.
91
+ # foo ? foo[idx] = v : nil # Ignored `foo&.[]=(idx, v)` due to unclear readability benefit.
92
+ # foo ? foo * 42 : nil # Ignored `foo&.*(42)` due to unclear readability benefit.
89
93
  class SafeNavigation < Base # rubocop:disable Metrics/ClassLength
90
94
  include NilMethods
91
95
  include RangeHelp
@@ -146,6 +150,7 @@ module RuboCop
146
150
 
147
151
  body = extract_if_body(node)
148
152
  method_call = receiver.parent
153
+ return if dotless_operator_call?(method_call) || method_call.double_colon?
149
154
 
150
155
  removal_ranges = [begin_range(node, body), end_range(node, body)]
151
156
 
@@ -181,6 +186,8 @@ module RuboCop
181
186
  end
182
187
  end
183
188
 
189
+ private
190
+
184
191
  def report_offense(node, rhs, rhs_receiver, *removal_ranges, offense_range: node)
185
192
  add_offense(offense_range) do |corrector|
186
193
  next if ignored_node?(node)
@@ -198,8 +205,6 @@ module RuboCop
198
205
  end
199
206
  end
200
207
 
201
- private
202
-
203
208
  def find_method_chain(node)
204
209
  return node unless node&.parent&.call_type?
205
210
 
@@ -235,7 +240,7 @@ module RuboCop
235
240
  return false if !matching_nodes?(lhs_receiver, rhs_receiver) || rhs_receiver.nil?
236
241
  return false if use_var_only_in_unless_modifier?(node, lhs_receiver)
237
242
  return false if chain_length(rhs, rhs_receiver) > max_chain_length
238
- return false if unsafe_method_used?(rhs, rhs_receiver.parent)
243
+ return false if unsafe_method_used?(node, rhs, rhs_receiver.parent)
239
244
  return false if rhs.send_type? && rhs.method?(:empty?)
240
245
 
241
246
  true
@@ -253,6 +258,12 @@ module RuboCop
253
258
  end
254
259
  end
255
260
 
261
+ def dotless_operator_call?(method_call)
262
+ return false if method_call.loc.dot
263
+
264
+ method_call.method?(:[]) || method_call.method?(:[]=) || method_call.operator_method?
265
+ end
266
+
256
267
  def handle_comments(corrector, node, method_call)
257
268
  comments = comments(node)
258
269
  return if comments.empty?
@@ -334,21 +345,24 @@ module RuboCop
334
345
  end
335
346
  end
336
347
 
337
- def unsafe_method_used?(method_chain, method)
338
- return true if unsafe_method?(method)
348
+ def unsafe_method_used?(node, method_chain, method)
349
+ return true if unsafe_method?(node, method)
339
350
 
340
351
  method.each_ancestor(:send).any? do |ancestor|
341
352
  break true unless config.cop_enabled?('Lint/SafeNavigationChain')
342
353
 
343
- break true if unsafe_method?(ancestor)
354
+ break true if unsafe_method?(node, ancestor)
344
355
  break true if nil_methods.include?(ancestor.method_name)
345
356
  break false if ancestor == method_chain
346
357
  end
347
358
  end
348
359
 
349
- def unsafe_method?(send_node)
350
- negated?(send_node) ||
351
- send_node.assignment? ||
360
+ def unsafe_method?(node, send_node)
361
+ return true if negated?(send_node)
362
+
363
+ return false if node.respond_to?(:ternary?) && node.ternary?
364
+
365
+ send_node.assignment? ||
352
366
  (!send_node.dot? && !send_node.safe_navigation?)
353
367
  end
354
368
 
@@ -377,8 +391,7 @@ module RuboCop
377
391
  method_chain)
378
392
  start_method.each_ancestor do |ancestor|
379
393
  break unless %i[send block].include?(ancestor.type)
380
- next unless ancestor.send_type?
381
- next if ancestor.safe_navigation?
394
+ next if !ancestor.send_type? || ancestor.operator_method?
382
395
 
383
396
  corrector.insert_before(ancestor.loc.dot, '&')
384
397
 
@@ -115,8 +115,9 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def correct_node(corrector, node)
118
- corrector.replace(node.loc.keyword, 'if') if node.unless?
118
+ corrector.replace(node.loc.keyword, 'if') if node.unless? && !part_of_ignored_node?(node)
119
119
  corrector.replace(node.condition, chainable_condition(node))
120
+ ignore_node(node)
120
121
  end
121
122
 
122
123
  def correct_for_guard_condition_style(corrector, node, if_branch)
@@ -270,7 +270,7 @@ module RuboCop
270
270
  end
271
271
 
272
272
  def allow_if_method_has_argument?(send_node)
273
- !!cop_config.fetch('AllowMethodsWithArguments', false) && !send_node.arguments.count.zero?
273
+ !!cop_config.fetch('AllowMethodsWithArguments', false) && send_node.arguments.any?
274
274
  end
275
275
 
276
276
  def allow_comments?
@@ -97,7 +97,7 @@ module RuboCop
97
97
  pipes = tokens.select { |token| token.type == :tPIPE }
98
98
  begin_pos, end_pos = pipes.map { |pipe| tokens.index(pipe) }
99
99
 
100
- tokens[begin_pos + 1..end_pos - 1]
100
+ tokens[(begin_pos + 1)..(end_pos - 1)]
101
101
  end
102
102
  end
103
103
  end
@@ -22,7 +22,7 @@ module RuboCop
22
22
 
23
23
  @severest_offense = nil
24
24
 
25
- file_phrase = target_files.count == 1 ? 'file' : 'files'
25
+ file_phrase = target_files.one? ? 'file' : 'files'
26
26
 
27
27
  # 185/407 files |====== 45 ======> | ETA: 00:00:04
28
28
  # %c / %C | %w > %i | %e
@@ -24,7 +24,7 @@ module RuboCop
24
24
 
25
25
  return unless output.tty?
26
26
 
27
- file_phrase = target_files.count == 1 ? 'file' : 'files'
27
+ file_phrase = target_files.one? ? 'file' : 'files'
28
28
 
29
29
  # 185/407 files |====== 45 ======> | ETA: 00:00:04
30
30
  # %c / %C | %w > %i | %e
@@ -79,7 +79,7 @@ module RuboCop
79
79
  LanguageServer::Protocol::Interface::CodeDescription.new(href: doc_url)
80
80
  end
81
81
 
82
- # rubocop:disable Layout/LineLength, Metrics/MethodLength
82
+ # rubocop:disable Metrics/MethodLength
83
83
  def autocorrect_action
84
84
  LanguageServer::Protocol::Interface::CodeAction.new(
85
85
  title: "Autocorrect #{@offense.cop_name}",
@@ -98,7 +98,7 @@ module RuboCop
98
98
  is_preferred: true
99
99
  )
100
100
  end
101
- # rubocop:enable Layout/LineLength, Metrics/MethodLength
101
+ # rubocop:enable Metrics/MethodLength
102
102
 
103
103
  # rubocop:disable Metrics/MethodLength
104
104
  def offense_replacements
@@ -120,7 +120,7 @@ module RuboCop
120
120
  end
121
121
  # rubocop:enable Metrics/MethodLength
122
122
 
123
- # rubocop:disable Layout/LineLength, Metrics/MethodLength
123
+ # rubocop:disable Metrics/MethodLength
124
124
  def disable_line_action
125
125
  LanguageServer::Protocol::Interface::CodeAction.new(
126
126
  title: "Disable #{@offense.cop_name} for this line",
@@ -138,7 +138,7 @@ module RuboCop
138
138
  )
139
139
  )
140
140
  end
141
- # rubocop:enable Layout/LineLength, Metrics/MethodLength
141
+ # rubocop:enable Metrics/MethodLength
142
142
 
143
143
  def line_disable_comment
144
144
  new_text = if @offense.source_line.include?(' # rubocop:disable ')