rubocop 1.92.1 → 1.92.2

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 (47) hide show
  1. checksums.yaml +4 -4
  2. data/lib/rubocop/config.rb +0 -4
  3. data/lib/rubocop/config_obsoletion.rb +9 -0
  4. data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
  5. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
  6. data/lib/rubocop/cop/layout/end_alignment.rb +2 -8
  7. data/lib/rubocop/cop/layout/extra_spacing.rb +17 -6
  8. data/lib/rubocop/cop/layout/line_length.rb +23 -5
  9. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  10. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
  11. data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
  12. data/lib/rubocop/cop/layout/space_before_block_braces.rb +12 -0
  13. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -1
  14. data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
  15. data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
  16. data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
  17. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
  18. data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
  19. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +27 -8
  20. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
  21. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  22. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +17 -1
  23. data/lib/rubocop/cop/mixin/statement_modifier.rb +14 -1
  24. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  25. data/lib/rubocop/cop/style/and_or.rb +33 -10
  26. data/lib/rubocop/cop/style/case_equality.rb +14 -3
  27. data/lib/rubocop/cop/style/class_and_module_children.rb +4 -3
  28. data/lib/rubocop/cop/style/endless_method.rb +34 -6
  29. data/lib/rubocop/cop/style/float_division.rb +16 -1
  30. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -1
  31. data/lib/rubocop/cop/style/hash_lookup_method.rb +10 -0
  32. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -0
  33. data/lib/rubocop/cop/style/method_def_parentheses.rb +9 -1
  34. data/lib/rubocop/cop/style/numeric_predicate.rb +7 -0
  35. data/lib/rubocop/cop/style/parentheses_around_condition.rb +18 -0
  36. data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
  37. data/lib/rubocop/cop/style/raise_args.rb +66 -7
  38. data/lib/rubocop/cop/style/reduce_to_hash.rb +32 -34
  39. data/lib/rubocop/cop/style/redundant_begin.rb +24 -2
  40. data/lib/rubocop/cop/style/redundant_exception.rb +13 -9
  41. data/lib/rubocop/cop/style/redundant_self.rb +9 -1
  42. data/lib/rubocop/cop/style/single_line_methods.rb +21 -0
  43. data/lib/rubocop/cop/style/string_concatenation.rb +12 -1
  44. data/lib/rubocop/plugin/configuration_integrator.rb +24 -3
  45. data/lib/rubocop/runner.rb +1 -1
  46. data/lib/rubocop/version.rb +1 -1
  47. metadata +2 -2
@@ -146,8 +146,8 @@ module RuboCop
146
146
  comment = directive_comment(line_range)
147
147
  next if skip_directive?(comment)
148
148
 
149
- next_range = line_ranges[line_range_index + 1]
150
- redundant = find_redundant_directive(cop, comment, line_range, next_range)
149
+ following_ranges = line_ranges[(line_range_index + 1)..]
150
+ redundant = find_redundant_directive(cop, comment, line_range, following_ranges)
151
151
  yield comment, redundant if redundant
152
152
  end
153
153
  end
@@ -191,14 +191,33 @@ module RuboCop
191
191
  directive.push? || directive.pop? || directive.next?
192
192
  end
193
193
 
194
- def find_redundant_directive(cop, comment, line_range, next_range)
195
- if all_disabled?(comment)
196
- find_redundant_all(line_range, next_range)
197
- elsif department_disabled?(cop, comment)
198
- find_redundant_department(cop, line_range)
194
+ def find_redundant_directive(cop, comment, line_range, following_ranges)
195
+ return find_redundant_all(line_range, following_ranges.first) if all_disabled?(comment)
196
+
197
+ range = line_range_through_repeated_disables(cop, line_range, following_ranges)
198
+ if department_disabled?(cop, comment)
199
+ find_redundant_department(cop, range)
199
200
  else
200
- find_redundant_cop(cop, line_range)
201
+ find_redundant_cop(cop, range)
202
+ end
203
+ end
204
+
205
+ def line_range_through_repeated_disables(cop, line_range, following_ranges)
206
+ last_range = line_range
207
+ following_ranges.each do |range|
208
+ break unless followed_ranges?(last_range, range) && repeated_disable?(cop, range)
209
+
210
+ last_range = range
201
211
  end
212
+
213
+ line_range.begin..last_range.end
214
+ end
215
+
216
+ def repeated_disable?(cop, range)
217
+ comment = directive_comment(range)
218
+ return false if comment.nil? || skip_directive?(comment)
219
+
220
+ !all_disabled?(comment) && !department_disabled?(cop, comment)
202
221
  end
203
222
 
204
223
  # rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
@@ -118,10 +118,7 @@ module RuboCop
118
118
  def redundant_splat_expansion(node)
119
119
  literal_expansion(node) do |expanded_item|
120
120
  if expanded_item.send_type?
121
- return if array_new_inside_array_literal?(expanded_item)
122
-
123
- grandparent = node.parent.parent
124
- return if grandparent && !ASSIGNMENT_TYPES.include?(grandparent.type)
121
+ return unless redundant_array_new_splat?(node, expanded_item)
125
122
  # An empty array/percent literal (`*[]`, `*%w()`, ...) expands to nothing, so
126
123
  # removing the splat would produce invalid or semantically different code.
127
124
  elsif expanded_item.array_type? && expanded_item.children.empty?
@@ -132,6 +129,16 @@ module RuboCop
132
129
  end
133
130
  end
134
131
 
132
+ # A splatted `Array.new` passed as a method argument provides the arguments,
133
+ # so the splat is only redundant directly inside an array.
134
+ def redundant_array_new_splat?(node, array_new_node)
135
+ return false unless node.parent.array_type?
136
+ return false if array_new_inside_array_literal?(array_new_node)
137
+
138
+ grandparent = node.parent.parent
139
+ !grandparent || ASSIGNMENT_TYPES.include?(grandparent.type)
140
+ end
141
+
135
142
  def array_new_inside_array_literal?(array_new_node)
136
143
  return false unless array_new?(array_new_node)
137
144
 
@@ -66,7 +66,7 @@ module RuboCop
66
66
  # the line at which the parent node ends.
67
67
  # rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
68
68
  def find_end_line(node)
69
- if node.if_type?
69
+ if node.if_type? && !node.modifier_form?
70
70
  if node.else?
71
71
  node.loc.else.line
72
72
  elsif node.ternary?
@@ -55,10 +55,17 @@ module RuboCop
55
55
  # Common functionality for checking alignment of hash values.
56
56
  module ValueAlignment
57
57
  def checkable_layout?(node)
58
- !node.pairs_on_same_line? && !node.mixed_delimiters? && alignable_values?(node)
58
+ !node.pairs_on_same_line? && !node.mixed_delimiters? && alignable_values?(node) &&
59
+ !pair_after_kwsplat_on_same_line?(node)
59
60
  end
60
61
 
62
+ # With several enforced styles, deltas are computed even for a hash this style
63
+ # can't check. A pair that doesn't begin its line can't be moved without
64
+ # deleting whatever precedes it, so it's left alone.
61
65
  def deltas(first_pair, current_pair)
66
+ return {} unless current_pair.equal?(first_pair) ||
67
+ Util.begins_its_line?(current_pair.source_range)
68
+
62
69
  key_delta = key_delta(first_pair, current_pair)
63
70
  shift = separator_shift(current_pair, key_delta)
64
71
  separator_delta = separator_delta(first_pair, current_pair, shift)
@@ -79,6 +86,15 @@ module RuboCop
79
86
  pair.value && pair.key.last_line != pair.value.first_line
80
87
  end
81
88
 
89
+ def pair_after_kwsplat_on_same_line?(node)
90
+ first_pair = node.pairs.first
91
+
92
+ node.children.each_cons(2).any? do |previous, current|
93
+ current.pair_type? && !current.equal?(first_pair) &&
94
+ previous.last_line == current.first_line
95
+ end
96
+ end
97
+
82
98
  def separator_shift(current_pair, key_delta)
83
99
  current_pair.key.multiline? ? 0 : key_delta
84
100
  end
@@ -7,6 +7,8 @@ module RuboCop
7
7
  include LineLengthHelp
8
8
  include AllowedPattern
9
9
 
10
+ SPACE_INSIDE_PARENS_STYLES = %w[space compact].freeze
11
+
10
12
  private
11
13
 
12
14
  def single_line_as_modifier?(node)
@@ -42,7 +44,10 @@ module RuboCop
42
44
  def modifier_fits_on_single_line?(node)
43
45
  return true unless max_line_length
44
46
 
45
- line_length(line_in_modifier_form(node)) <= max_line_length
47
+ length = line_length(line_in_modifier_form(node))
48
+ length += 2 if parenthesize?(node) && space_inside_parens?
49
+
50
+ length <= max_line_length
46
51
  end
47
52
 
48
53
  def line_in_modifier_form(node)
@@ -52,6 +57,14 @@ module RuboCop
52
57
  "#{code_before}#{to_modifier_form(node)}#{code_after(node)}"
53
58
  end
54
59
 
60
+ def space_inside_parens?
61
+ return false unless config.cop_enabled?('Layout/SpaceInsideParens')
62
+
63
+ style = config.for_cop('Layout/SpaceInsideParens')['EnforcedStyle']
64
+
65
+ SPACE_INSIDE_PARENS_STYLES.include?(style)
66
+ end
67
+
55
68
  # Whether an existing line is acceptable to `Layout/LineLength`,
56
69
  # exemptions (the cop disabled at that line, an allowed pattern, an
57
70
  # allowed cop directive, an allowed URI) included. Only for judging
@@ -163,7 +163,7 @@ module RuboCop
163
163
 
164
164
  def put_comma(items, kind)
165
165
  last_item = items.last
166
- return if last_item.block_pass_type?
166
+ return if last_item.type?(:block_pass, :forwarded_args)
167
167
 
168
168
  range = autocorrect_range(last_item)
169
169
  msg = format(MSG, command: 'Put a', unit: format(kind, article: 'a multiline'))
@@ -72,13 +72,7 @@ module RuboCop
72
72
 
73
73
  message = message(node)
74
74
  add_offense(node.loc.operator, message: message) do |corrector|
75
- node.each_child_node do |expr|
76
- if expr.call_type?
77
- correct_send(expr, corrector)
78
- elsif expr.type?(:return, :next, :break, :yield) || expr.assignment?
79
- correct_other(expr, corrector)
80
- end
81
- end
75
+ node.each_child_node { |expr| correct_operand(expr, corrector) }
82
76
 
83
77
  corrector.replace(node.loc.operator, node.alternate_operator)
84
78
 
@@ -86,6 +80,22 @@ module RuboCop
86
80
  end
87
81
  end
88
82
 
83
+ def correct_operand(expr, corrector)
84
+ expr = expr.send_node if expr.any_block_type?
85
+
86
+ if expr.type?(:call, :super)
87
+ correct_send(expr, corrector)
88
+ elsif wrap_in_parentheses?(expr)
89
+ correct_other(expr, corrector)
90
+ end
91
+ end
92
+
93
+ # These bind looser than `&&` and `||`, or can't be their operands at all.
94
+ def wrap_in_parentheses?(node)
95
+ node.type?(:return, :next, :break, :yield, :any_match_pattern, :range, :defined?) ||
96
+ node.assignment? || (node.if_type? && node.ternary?)
97
+ end
98
+
89
99
  def on_conditionals(node)
90
100
  node.condition.each_node(*AST::Node::OPERATOR_KEYWORDS) do |operator|
91
101
  process_logical_operator(operator)
@@ -101,6 +111,7 @@ module RuboCop
101
111
  return correct_setter(node, corrector) if node.setter_method?
102
112
  return correct_other(node, corrector) if node.comparison_method?
103
113
 
114
+ correct_block_receiver(node, corrector)
104
115
  return unless correctable_send?(node)
105
116
 
106
117
  whitespace_before_arg_range = whitespace_before_arg(node)
@@ -120,7 +131,8 @@ module RuboCop
120
131
  # however, 'not x' also parses as (send x :!)
121
132
  def correct_not(node, receiver, corrector)
122
133
  if node.prefix_bang?
123
- return unless receiver.call_type?
134
+ receiver = receiver.send_node if receiver.any_block_type?
135
+ return unless receiver.type?(:call, :super)
124
136
 
125
137
  correct_send(receiver, corrector)
126
138
  elsif node.prefix_not?
@@ -145,12 +157,23 @@ module RuboCop
145
157
  end
146
158
  end
147
159
 
160
+ # A method chained onto a `do` block, as in `bar baz do ... end.qux`,
161
+ # still needs parentheses around the arguments of the call that takes the block.
162
+ def correct_block_receiver(node, corrector)
163
+ receiver = node.receiver
164
+ receiver = receiver.receiver while receiver&.call_type?
165
+ correct_operand(receiver, corrector) if receiver&.any_block_type?
166
+ end
167
+
148
168
  def correctable_send?(node)
149
- !node.parenthesized? && node.arguments? && !node.method?(:[])
169
+ # Operator syntax such as `a + b` or `a[b]` already binds tighter than `&&` and `||`.
170
+ return false if node.operator_method? && !node.loc.dot
171
+
172
+ !node.parenthesized? && node.arguments?
150
173
  end
151
174
 
152
175
  def whitespace_before_arg(node)
153
- begin_paren = node.loc.selector.end_pos
176
+ begin_paren = node.selector.end_pos
154
177
  end_paren = begin_paren
155
178
  # Increment position of parenthesis, unless message is a predicate
156
179
  # method followed by a non-whitespace char (e.g. is_a?String).
@@ -7,7 +7,7 @@ module RuboCop
7
7
  # The `===` operator has different behavior depending on the
8
8
  # receiver and its use outside of `case`/`when` is confusing.
9
9
  # Prefer more explicit alternatives like `is_a?`, `include?`,
10
- # or `match?`.
10
+ # `cover?`, or `match?`.
11
11
  #
12
12
  # If `AllowOnConstant` option is enabled, the cop will ignore violations when the receiver of
13
13
  # the case equality operator is a constant.
@@ -22,9 +22,11 @@ module RuboCop
22
22
  # @example
23
23
  # # bad
24
24
  # (1..100) === 7
25
+ # ('a'..'z') === char
25
26
  #
26
27
  # # good
27
28
  # (1..100).include?(7)
29
+ # ('a'..'z').cover?(char)
28
30
  #
29
31
  # @example AllowOnConstant: false (default)
30
32
  # # bad
@@ -90,9 +92,18 @@ module RuboCop
90
92
  end
91
93
 
92
94
  def begin_replacement(lhs, rhs)
93
- return unless lhs.children.first&.range_type?
95
+ range = lhs.children.first
96
+ return unless range&.range_type?
94
97
 
95
- "#{lhs.source}.include?(#{rhs.source})"
98
+ method = cover_semantics?(range) ? 'cover?' : 'include?'
99
+
100
+ "#{lhs.source}.#{method}(#{rhs.source})"
101
+ end
102
+
103
+ # `Range#===` behaves like `Range#cover?` since Ruby 2.7 (since Ruby 2.6 for ranges
104
+ # that aren't string ranges), and `Range#include?` only matches that for numeric ranges.
105
+ def cover_semantics?(range)
106
+ target_ruby_version >= 2.7 && !range.children.compact.all?(&:numeric_type?)
96
107
  end
97
108
 
98
109
  def const_replacement(lhs, rhs)
@@ -65,10 +65,11 @@ module RuboCop
65
65
  NESTED_MSG = 'Use nested module/class definitions instead of compact style.'
66
66
  COMPACT_MSG = 'Use compact module/class definition instead of nested style.'
67
67
 
68
- # `Style/StructInheritance` turns a class definition into a constant assignment in
69
- # the same pass, which the compacted `class` keyword would then precede.
68
+ # `Style/StructInheritance` and `Style/DataInheritance` turn a class definition into
69
+ # a constant assignment in the same pass, which the compacted `class` keyword would
70
+ # then precede.
70
71
  def self.autocorrect_incompatible_with
71
- [Style::StructInheritance]
72
+ [Style::DataInheritance, Style::StructInheritance]
72
73
  end
73
74
 
74
75
  def on_class(node)
@@ -151,8 +151,17 @@ module RuboCop
151
151
  # which an endless definition cannot hold.
152
152
  # `Style/RaiseArgs` would drop the parentheses of a `raise` that becomes
153
153
  # the body in the same pass, which a method passed as an argument needs.
154
+ # `Style/IfUnlessModifier` and `Style/WhileUntilModifier` would turn the body into
155
+ # a modifier form, which then applies to the whole endless definition.
154
156
  def self.autocorrect_incompatible_with
155
- [Style::GuardClause, Style::MethodCallWithArgsParentheses, Style::MethodDefParentheses, Style::RaiseArgs]
157
+ [
158
+ Style::GuardClause,
159
+ Style::IfUnlessModifier,
160
+ Style::MethodCallWithArgsParentheses,
161
+ Style::MethodDefParentheses,
162
+ Style::RaiseArgs,
163
+ Style::WhileUntilModifier
164
+ ]
156
165
  end
157
166
 
158
167
  def on_def(node)
@@ -189,9 +198,7 @@ module RuboCop
189
198
  add_offense(node, message: MSG_MULTI_LINE) do |corrector|
190
199
  correct_to_multiline(corrector, node)
191
200
  end
192
- elsif !node.endless? && can_be_made_endless?(node) && single_line_when_made_endless?(node)
193
- return if too_long_when_made_endless?(node)
194
-
201
+ elsif convertible_to_endless?(node) && single_line_when_made_endless?(node)
195
202
  add_offense(node, message: MSG_REQUIRE_SINGLE) do |corrector|
196
203
  correct_to_endless(corrector, node)
197
204
  end
@@ -199,14 +206,18 @@ module RuboCop
199
206
  end
200
207
 
201
208
  def handle_require_always_style(node)
202
- return if node.endless? || !can_be_made_endless?(node)
203
- return if too_long_when_made_endless?(node)
209
+ return unless convertible_to_endless?(node)
204
210
 
205
211
  add_offense(node, message: MSG_REQUIRE_ALWAYS) do |corrector|
206
212
  correct_to_endless(corrector, node)
207
213
  end
208
214
  end
209
215
 
216
+ def convertible_to_endless?(node)
217
+ !node.endless? && can_be_made_endless?(node) &&
218
+ !too_long_when_made_endless?(node) && !ambiguous_when_made_endless?(node)
219
+ end
220
+
210
221
  def handle_disallow_style(node)
211
222
  return unless node.endless?
212
223
 
@@ -297,6 +308,23 @@ module RuboCop
297
308
  !endless_replacement(node).include?("\n")
298
309
  end
299
310
 
311
+ def ambiguous_when_made_endless?(node)
312
+ (operation = ambiguous_operation(node)) && ambiguous_enclosing_operation?(operation)
313
+ end
314
+
315
+ # @!method ambiguous_operation(node)
316
+ def_node_matcher :ambiguous_operation, <<~PATTERN
317
+ ^${
318
+ (if _ <any_def _>)
319
+ ({and or} any_def _)
320
+ ({while until} _ any_def)
321
+ }
322
+ PATTERN
323
+
324
+ def ambiguous_enclosing_operation?(operation)
325
+ operation.operator_keyword? || operation.modifier_form?
326
+ end
327
+
300
328
  def too_long_when_made_endless?(node)
301
329
  return false unless config.cop_enabled?('Layout/LineLength')
302
330
 
@@ -146,11 +146,26 @@ module RuboCop
146
146
  end
147
147
 
148
148
  def comment_before_to_f?(node)
149
+ return true if comment_around_fdiv_operator?(node)
150
+
149
151
  operands_losing_to_f(node).any? do |operand|
150
152
  next false unless to_f_method?(operand)
151
153
 
152
154
  range = operand.receiver.source_range.end.join(operand.loc.selector.begin)
153
- processed_source.contains_comment?(range)
155
+ comment_in_range?(range)
156
+ end
157
+ end
158
+
159
+ def comment_around_fdiv_operator?(node)
160
+ return false unless style == :fdiv
161
+
162
+ range = node.receiver.source_range.end.join(node.first_argument.source_range.begin)
163
+ comment_in_range?(range)
164
+ end
165
+
166
+ def comment_in_range?(range)
167
+ processed_source.each_comment_in_lines(range.line..range.last_line).any? do |comment|
168
+ range.contains?(comment.source_range)
154
169
  end
155
170
  end
156
171
 
@@ -45,7 +45,7 @@ module RuboCop
45
45
  extend AutoCorrector
46
46
 
47
47
  def self.autocorrect_incompatible_with
48
- [Style::TrailingCommaInArrayLiteral]
48
+ [Layout::MultilineArrayBraceLayout, Style::TrailingCommaInArrayLiteral]
49
49
  end
50
50
 
51
51
  def on_hash(node)
@@ -90,9 +90,19 @@ module RuboCop
90
90
  end
91
91
 
92
92
  def exempt_from_brackets?(node)
93
+ return true if forwarded_or_splatted?(node.first_argument)
94
+
93
95
  comment_before_dot?(node) || env_fetch_preferred?(node.receiver)
94
96
  end
95
97
 
98
+ def forwarded_or_splatted?(argument)
99
+ if argument.hash_type? && !argument.braces?
100
+ argument.children.any? { |child| child.type?(:kwsplat, :forwarded_kwrestarg) }
101
+ else
102
+ argument.type?(:forwarded_args, :forwarded_restarg, :splat, :block_pass)
103
+ end
104
+ end
105
+
96
106
  def comment_before_dot?(node)
97
107
  processed_source.contains_comment?(node.receiver.source_range.end.join(node.loc.dot))
98
108
  end
@@ -243,6 +243,7 @@ module RuboCop
243
243
 
244
244
  def self.autocorrect_incompatible_with
245
245
  [
246
+ Layout::HeredocArgumentClosingParenthesis,
246
247
  Style::NestedParenthesizedCalls,
247
248
  Style::RedundantParentheses,
248
249
  Style::RescueModifier,
@@ -169,12 +169,20 @@ module RuboCop
169
169
  location = node.arguments.source_range
170
170
 
171
171
  add_offense(location, message: MSG_MISSING) do |corrector|
172
- add_parentheses(node.arguments, corrector)
172
+ corrector.replace(range_before_arguments(node), '(')
173
+ corrector.insert_after(location, ')')
173
174
 
174
175
  unexpected_style_detected 'require_no_parentheses'
175
176
  end
176
177
  end
177
178
 
179
+ def range_before_arguments(node)
180
+ range = node.loc.name.end.join(node.arguments.source_range.begin)
181
+ last_line_break = range.source.rindex("\n")
182
+
183
+ last_line_break ? range.resize(last_line_break) : range
184
+ end
185
+
178
186
  def unwanted_parentheses(args)
179
187
  add_offense(args, message: MSG_PRESENT) do |corrector|
180
188
  # offense is registered on args node when parentheses are unwanted
@@ -99,7 +99,10 @@ module RuboCop
99
99
 
100
100
  message = format(MSG, prefer: replacement, current: node.source)
101
101
  add_offense(node, message: message) do |corrector|
102
+ next if inside_corrected_predicate?(node)
103
+
102
104
  corrector.replace(node, replacement)
105
+ ignore_node(node)
103
106
  end
104
107
  end
105
108
 
@@ -115,6 +118,10 @@ module RuboCop
115
118
  BitwisePredicate.support_target_ruby_version?(target_ruby_version)
116
119
  end
117
120
 
121
+ def inside_corrected_predicate?(node)
122
+ node.each_ancestor(:send).any? { |ancestor| ignored_node?(ancestor) }
123
+ end
124
+
118
125
  def check(node)
119
126
  numeric, operator =
120
127
  if style == :predicate
@@ -84,6 +84,7 @@ module RuboCop
84
84
  return if require_parentheses?(node, first_child)
85
85
  return if semicolon_separated_expressions?(first_child, rest_children)
86
86
  return if modifier_op?(first_child) || first_child.masgn_type?
87
+ return if command_call_assignment?(first_child)
87
88
  return if parens_allowed?(cond)
88
89
 
89
90
  message = message(cond)
@@ -115,6 +116,23 @@ module RuboCop
115
116
  node.basic_conditional? && node.modifier_form?
116
117
  end
117
118
 
119
+ def command_call_assignment?(node)
120
+ return false if !node.assignment? && !setter_method?(node)
121
+
122
+ value = node.children.last
123
+ value = value.body if value.rescue_type?
124
+
125
+ command_call_assignment?(value) || command_call?(value)
126
+ end
127
+
128
+ def command_call?(node)
129
+ node = node.send_node if node.any_block_type?
130
+ return false if !node.type?(:call, :super, :yield) || !node.arguments?
131
+ return false if node.parenthesized? || node.operator_method? || node.setter_method?
132
+
133
+ !node.method?(:[])
134
+ end
135
+
118
136
  def message(node)
119
137
  kw = node.parent.keyword
120
138
  article = kw == 'while' ? 'a' : 'an'
@@ -86,14 +86,12 @@ module RuboCop
86
86
  end
87
87
 
88
88
  def correct_quotes(str)
89
- correction = if style == :single_quotes
90
- to_string_literal(str)
91
- else
92
- str.gsub("\\'", "'").inspect
93
- end
94
-
95
89
  # The conversion process doubles escaped slashes, so they have to be reverted
96
- correction.gsub('\\\\', '\\').gsub('\"', '"')
90
+ if style == :single_quotes
91
+ to_string_literal(str).gsub('\\\\', '\\').gsub('\"', '"')
92
+ else
93
+ str.gsub("\\'", "'").inspect.gsub('\\\\', '\\')
94
+ end
97
95
  end
98
96
 
99
97
  def style