rubocop 1.18.4 → 1.21.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +65 -13
  4. data/lib/rubocop/cli.rb +18 -0
  5. data/lib/rubocop/config_loader.rb +2 -2
  6. data/lib/rubocop/config_loader_resolver.rb +21 -6
  7. data/lib/rubocop/cop/base.rb +2 -2
  8. data/lib/rubocop/cop/bundler/gem_filename.rb +103 -0
  9. data/lib/rubocop/cop/bundler/insecure_protocol_source.rb +12 -11
  10. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  11. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +2 -2
  12. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  13. data/lib/rubocop/cop/correctors/require_library_corrector.rb +23 -0
  14. data/lib/rubocop/cop/documentation.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  16. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +34 -0
  17. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +71 -0
  18. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  19. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/class_structure.rb +2 -1
  21. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +9 -0
  22. data/lib/rubocop/cop/layout/end_alignment.rb +3 -2
  23. data/lib/rubocop/cop/layout/hash_alignment.rb +7 -3
  24. data/lib/rubocop/cop/layout/heredoc_indentation.rb +0 -7
  25. data/lib/rubocop/cop/layout/leading_comment_space.rb +2 -2
  26. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +33 -14
  27. data/lib/rubocop/cop/layout/line_length.rb +1 -1
  28. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  29. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +3 -0
  30. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +22 -9
  31. data/lib/rubocop/cop/layout/single_line_block_chain.rb +15 -4
  32. data/lib/rubocop/cop/layout/space_after_not.rb +1 -0
  33. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +2 -1
  34. data/lib/rubocop/cop/layout/space_around_keyword.rb +2 -2
  35. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -1
  36. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -0
  37. data/lib/rubocop/cop/layout/space_before_comment.rb +1 -1
  38. data/lib/rubocop/cop/layout/space_inside_parens.rb +5 -5
  39. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -1
  40. data/lib/rubocop/cop/layout/trailing_whitespace.rb +24 -1
  41. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +107 -0
  42. data/lib/rubocop/cop/lint/ambiguous_range.rb +105 -0
  43. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -2
  44. data/lib/rubocop/cop/lint/debugger.rb +2 -4
  45. data/lib/rubocop/cop/lint/duplicate_methods.rb +8 -5
  46. data/lib/rubocop/cop/lint/empty_in_pattern.rb +1 -1
  47. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  48. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  49. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +67 -0
  50. data/lib/rubocop/cop/lint/number_conversion.rb +7 -1
  51. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -1
  52. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -3
  53. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  54. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  55. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +1 -1
  56. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  57. data/lib/rubocop/cop/mixin/annotation_comment.rb +57 -34
  58. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  59. data/lib/rubocop/cop/mixin/documentation_comment.rb +5 -2
  60. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +19 -1
  61. data/lib/rubocop/cop/mixin/heredoc.rb +7 -0
  62. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -2
  63. data/lib/rubocop/cop/mixin/percent_array.rb +13 -7
  64. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +9 -1
  65. data/lib/rubocop/cop/mixin/require_library.rb +59 -0
  66. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  67. data/lib/rubocop/cop/naming/ascii_identifiers.rb +0 -3
  68. data/lib/rubocop/cop/naming/constant_name.rb +1 -1
  69. data/lib/rubocop/cop/naming/inclusive_language.rb +27 -10
  70. data/lib/rubocop/cop/style/accessor_grouping.rb +2 -2
  71. data/lib/rubocop/cop/style/and_or.rb +4 -0
  72. data/lib/rubocop/cop/style/ascii_comments.rb +0 -3
  73. data/lib/rubocop/cop/style/block_delimiters.rb +39 -6
  74. data/lib/rubocop/cop/style/case_equality.rb +6 -9
  75. data/lib/rubocop/cop/style/collection_methods.rb +2 -1
  76. data/lib/rubocop/cop/style/comment_annotation.rb +25 -39
  77. data/lib/rubocop/cop/style/commented_keyword.rb +2 -1
  78. data/lib/rubocop/cop/style/conditional_assignment.rb +19 -5
  79. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +1 -1
  80. data/lib/rubocop/cop/style/documentation.rb +23 -8
  81. data/lib/rubocop/cop/style/double_negation.rb +12 -1
  82. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  83. data/lib/rubocop/cop/style/encoding.rb +26 -15
  84. data/lib/rubocop/cop/style/explicit_block_argument.rb +46 -11
  85. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  86. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +11 -0
  87. data/lib/rubocop/cop/style/hash_except.rb +4 -3
  88. data/lib/rubocop/cop/style/hash_transform_keys.rb +0 -3
  89. data/lib/rubocop/cop/style/identical_conditional_branches.rb +30 -5
  90. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +3 -2
  91. data/lib/rubocop/cop/style/lambda_call.rb +1 -1
  92. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -6
  93. data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -1
  94. data/lib/rubocop/cop/style/missing_else.rb +7 -0
  95. data/lib/rubocop/cop/style/mutable_constant.rb +68 -6
  96. data/lib/rubocop/cop/style/negated_if.rb +1 -1
  97. data/lib/rubocop/cop/style/negated_unless.rb +1 -1
  98. data/lib/rubocop/cop/style/non_nil_check.rb +2 -2
  99. data/lib/rubocop/cop/style/not.rb +2 -2
  100. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -1
  101. data/lib/rubocop/cop/style/percent_q_literals.rb +2 -2
  102. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  103. data/lib/rubocop/cop/style/redundant_begin.rb +25 -0
  104. data/lib/rubocop/cop/style/redundant_condition.rb +2 -3
  105. data/lib/rubocop/cop/style/redundant_freeze.rb +4 -3
  106. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  107. data/lib/rubocop/cop/style/redundant_percent_q.rb +2 -3
  108. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +83 -0
  109. data/lib/rubocop/cop/style/redundant_sort.rb +19 -4
  110. data/lib/rubocop/cop/style/regexp_literal.rb +3 -3
  111. data/lib/rubocop/cop/style/return_nil.rb +2 -1
  112. data/lib/rubocop/cop/style/semicolon.rb +32 -24
  113. data/lib/rubocop/cop/style/single_line_block_params.rb +3 -1
  114. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -0
  115. data/lib/rubocop/cop/style/special_global_vars.rb +21 -0
  116. data/lib/rubocop/cop/style/static_class.rb +1 -2
  117. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  118. data/lib/rubocop/cop/style/struct_inheritance.rb +3 -0
  119. data/lib/rubocop/cop/style/symbol_array.rb +3 -3
  120. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  121. data/lib/rubocop/cop/style/word_array.rb +23 -5
  122. data/lib/rubocop/cop/style/yoda_condition.rb +4 -7
  123. data/lib/rubocop/cop/util.rb +7 -2
  124. data/lib/rubocop/magic_comment.rb +44 -15
  125. data/lib/rubocop/options.rb +1 -1
  126. data/lib/rubocop/result_cache.rb +1 -1
  127. data/lib/rubocop/runner.rb +1 -2
  128. data/lib/rubocop/version.rb +1 -1
  129. data/lib/rubocop.rb +9 -2
  130. metadata +14 -5
@@ -109,12 +109,13 @@ module RuboCop
109
109
  end
110
110
 
111
111
  def opposite_condition?(node)
112
- !node.unless? && node.if_branch.false_type? || node.unless? && node.if_branch.true_type?
112
+ (!node.unless? && node.if_branch.false_type?) ||
113
+ (node.unless? && node.if_branch.true_type?)
113
114
  end
114
115
 
115
116
  def require_parentheses?(condition)
116
117
  condition.and_type? || condition.or_type? ||
117
- condition.send_type? && condition.comparison_method?
118
+ (condition.send_type? && condition.comparison_method?)
118
119
  end
119
120
  end
120
121
  end
@@ -52,7 +52,7 @@ module RuboCop
52
52
  private
53
53
 
54
54
  def offense?(node)
55
- explicit_style? && node.implicit_call? || implicit_style? && !node.implicit_call?
55
+ (explicit_style? && node.implicit_call?) || (implicit_style? && !node.implicit_call?)
56
56
  end
57
57
 
58
58
  def message(_node)
@@ -93,8 +93,8 @@ module RuboCop
93
93
  parent = node.parent&.block_type? ? node.parent.parent : node.parent
94
94
  parent &&
95
95
  (logical_operator?(parent) ||
96
- parent.send_type? &&
97
- parent.arguments.any? { |argument| logical_operator?(argument) })
96
+ (parent.send_type? &&
97
+ parent.arguments.any? { |argument| logical_operator?(argument) }))
98
98
  end
99
99
 
100
100
  def call_in_optional_arguments?(node)
@@ -122,14 +122,14 @@ module RuboCop
122
122
 
123
123
  def call_as_argument_or_chain?(node)
124
124
  node.parent &&
125
- (node.parent.send_type? && !assigned_before?(node.parent, node) ||
125
+ ((node.parent.send_type? && !assigned_before?(node.parent, node)) ||
126
126
  node.parent.csend_type? || node.parent.super_type? || node.parent.yield_type?)
127
127
  end
128
128
 
129
129
  def hash_literal_in_arguments?(node)
130
130
  node.arguments.any? do |n|
131
131
  hash_literal?(n) ||
132
- n.send_type? && node.descendants.any? { |descendant| hash_literal?(descendant) }
132
+ (n.send_type? && node.descendants.any? { |descendant| hash_literal?(descendant) })
133
133
  end
134
134
  end
135
135
 
@@ -171,8 +171,8 @@ module RuboCop
171
171
  end
172
172
 
173
173
  def unary_literal?(node)
174
- node.numeric_type? && node.sign? ||
175
- node.parent&.send_type? && node.parent&.unary_operation?
174
+ (node.numeric_type? && node.sign?) ||
175
+ (node.parent&.send_type? && node.parent&.unary_operation?)
176
176
  end
177
177
 
178
178
  def assigned_before?(node, target)
@@ -96,7 +96,7 @@ module RuboCop
96
96
  MSG_MISSING = 'Use def with parentheses when there are parameters.'
97
97
 
98
98
  def on_def(node)
99
- return if node.endless?
99
+ return if forced_parentheses?(node)
100
100
 
101
101
  args = node.arguments
102
102
 
@@ -129,6 +129,15 @@ module RuboCop
129
129
  corrector.insert_after(arguments_range, ')')
130
130
  end
131
131
 
132
+ def forced_parentheses?(node)
133
+ # Regardless of style, parentheses are necessary for:
134
+ # 1. Endless methods
135
+ # 2. Argument lists containing a `forward-arg` (`...`)
136
+ # Removing the parens would be a syntax error here.
137
+
138
+ node.endless? || node.arguments.any?(&:forward_arg_type?)
139
+ end
140
+
132
141
  def require_parentheses?(args)
133
142
  style == :require_parentheses ||
134
143
  (style == :require_no_parentheses_except_multiline && args.multiline?)
@@ -5,6 +5,9 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for `if` expressions that do not have an `else` branch.
7
7
  #
8
+ # NOTE: Pattern matching is allowed to have no `else` branch because unlike `if` and `case`,
9
+ # it raises `NoMatchingPatternError` if the pattern doesn't match and without having `else`.
10
+ #
8
11
  # Supported styles are: if, case, both.
9
12
  #
10
13
  # @example EnforcedStyle: if
@@ -114,6 +117,10 @@ module RuboCop
114
117
  check(node)
115
118
  end
116
119
 
120
+ def on_case_match(node)
121
+ # do nothing.
122
+ end
123
+
117
124
  private
118
125
 
119
126
  def check(node)
@@ -14,8 +14,16 @@ module RuboCop
14
14
  # positives. Luckily, there is no harm in freezing an already
15
15
  # frozen object.
16
16
  #
17
+ # From Ruby 3.0, this cop honours the magic comment
18
+ # 'shareable_constant_value'. When this magic comment is set to any
19
+ # acceptable value other than none, it will suppress the offenses
20
+ # raised by this cop. It enforces frozen state.
21
+ #
17
22
  # NOTE: Regexp and Range literals are frozen objects since Ruby 3.0.
18
23
  #
24
+ # NOTE: From Ruby 3.0, this cop allows explicit freezing of interpolated
25
+ # string literals when `# frozen-string-literal: true` is used.
26
+ #
19
27
  # @example EnforcedStyle: literals (default)
20
28
  # # bad
21
29
  # CONST = [1, 2, 3]
@@ -52,7 +60,59 @@ module RuboCop
52
60
  # puts 1
53
61
  # end
54
62
  # end.freeze
63
+ #
64
+ # @example
65
+ # # Magic comment - shareable_constant_value: literal
66
+ #
67
+ # # bad
68
+ # CONST = [1, 2, 3]
69
+ #
70
+ # # good
71
+ # # shareable_constant_value: literal
72
+ # CONST = [1, 2, 3]
73
+ #
74
+ # NOTE: This special directive helps to create constants
75
+ # that hold only immutable objects, or Ractor-shareable
76
+ # constants. - ruby docs
77
+ #
55
78
  class MutableConstant < Base
79
+ # Handles magic comment shareable_constant_value with O(n ^ 2) complexity
80
+ # n - number of lines in the source
81
+ # Iterates over all lines before a CONSTANT
82
+ # until it reaches shareable_constant_value
83
+ module ShareableConstantValue
84
+ module_function
85
+
86
+ def recent_shareable_value?(node)
87
+ shareable_constant_comment = magic_comment_in_scope node
88
+ return false if shareable_constant_comment.nil?
89
+
90
+ shareable_constant_value = MagicComment.parse(shareable_constant_comment)
91
+ .shareable_constant_value
92
+ shareable_constant_value_enabled? shareable_constant_value
93
+ end
94
+
95
+ # Identifies the most recent magic comment with valid shareable constant values
96
+ # thats in scope for this node
97
+ def magic_comment_in_scope(node)
98
+ processed_source_till_node(node).reverse_each.find do |line|
99
+ MagicComment.parse(line).valid_shareable_constant_value?
100
+ end
101
+ end
102
+
103
+ private
104
+
105
+ def processed_source_till_node(node)
106
+ processed_source.lines[0..(node.last_line - 1)]
107
+ end
108
+
109
+ def shareable_constant_value_enabled?(value)
110
+ %w[literal experimental_everything experimental_copy].include? value
111
+ end
112
+ end
113
+ private_constant :ShareableConstantValue
114
+
115
+ include ShareableConstantValue
56
116
  include FrozenStringLiteral
57
117
  include ConfigurableEnforcedStyle
58
118
  extend AutoCorrector
@@ -85,18 +145,18 @@ module RuboCop
85
145
  return if immutable_literal?(value)
86
146
  return if operation_produces_immutable_object?(value)
87
147
  return if frozen_string_literal?(value)
148
+ return if shareable_constant_value?(value)
88
149
 
89
150
  add_offense(value) { |corrector| autocorrect(corrector, value) }
90
151
  end
91
152
 
92
153
  def check(value)
93
154
  range_enclosed_in_parentheses = range_enclosed_in_parentheses?(value)
94
-
95
155
  return unless mutable_literal?(value) ||
96
- target_ruby_version <= 2.7 && range_enclosed_in_parentheses
156
+ (target_ruby_version <= 2.7 && range_enclosed_in_parentheses)
97
157
 
98
- return if FROZEN_STRING_LITERAL_TYPES.include?(value.type) &&
99
- frozen_string_literals_enabled?
158
+ return if frozen_string_literal?(value)
159
+ return if shareable_constant_value?(value)
100
160
 
101
161
  add_offense(value) { |corrector| autocorrect(corrector, value) }
102
162
  end
@@ -126,8 +186,10 @@ module RuboCop
126
186
  frozen_regexp_or_range_literals?(node) || node.immutable_literal?
127
187
  end
128
188
 
129
- def frozen_string_literal?(node)
130
- FROZEN_STRING_LITERAL_TYPES.include?(node.type) && frozen_string_literals_enabled?
189
+ def shareable_constant_value?(node)
190
+ return false if target_ruby_version < 3.0
191
+
192
+ recent_shareable_value? node
131
193
  end
132
194
 
133
195
  def frozen_regexp_or_range_literals?(node)
@@ -90,7 +90,7 @@ module RuboCop
90
90
  end
91
91
 
92
92
  def correct_style?(node)
93
- style == :prefix && node.modifier_form? || style == :postfix && !node.modifier_form?
93
+ (style == :prefix && node.modifier_form?) || (style == :postfix && !node.modifier_form?)
94
94
  end
95
95
  end
96
96
  end
@@ -80,7 +80,7 @@ module RuboCop
80
80
  end
81
81
 
82
82
  def correct_style?(node)
83
- style == :prefix && node.modifier_form? || style == :postfix && !node.modifier_form?
83
+ (style == :prefix && node.modifier_form?) || (style == :postfix && !node.modifier_form?)
84
84
  end
85
85
  end
86
86
  end
@@ -63,7 +63,7 @@ module RuboCop
63
63
 
64
64
  def on_send(node)
65
65
  return if ignored_node?(node) ||
66
- !include_semantic_changes? && nil_comparison_style == 'comparison'
66
+ (!include_semantic_changes? && nil_comparison_style == 'comparison')
67
67
  return unless register_offense?(node)
68
68
 
69
69
  message = message(node)
@@ -87,7 +87,7 @@ module RuboCop
87
87
 
88
88
  def register_offense?(node)
89
89
  not_equal_to_nil?(node) ||
90
- include_semantic_changes? && (not_and_nil_check?(node) || unless_and_nil_check?(node))
90
+ (include_semantic_changes? && (not_and_nil_check?(node) || unless_and_nil_check?(node)))
91
91
  end
92
92
 
93
93
  def autocorrect(corrector, node)
@@ -53,8 +53,8 @@ module RuboCop
53
53
 
54
54
  def requires_parens?(child)
55
55
  child.and_type? || child.or_type? ||
56
- child.send_type? && child.binary_operation? ||
57
- child.if_type? && child.ternary?
56
+ (child.send_type? && child.binary_operation?) ||
57
+ (child.if_type? && child.ternary?)
58
58
  end
59
59
 
60
60
  def correct_opposite_method(corrector, range, child)
@@ -153,7 +153,7 @@ module RuboCop
153
153
 
154
154
  def dependency?(lhs, rhs)
155
155
  uses_var?(rhs, var_name(lhs)) ||
156
- lhs.send_type? && lhs.assignment_method? && accesses?(rhs, lhs)
156
+ (lhs.send_type? && lhs.assignment_method? && accesses?(rhs, lhs))
157
157
  end
158
158
 
159
159
  # `lhs` is an assignment method call like `obj.attr=` or `ary[idx]=`.
@@ -53,8 +53,8 @@ module RuboCop
53
53
  end
54
54
 
55
55
  def correct_literal_style?(node)
56
- style == :lower_case_q && type(node) == '%q' ||
57
- style == :upper_case_q && type(node) == '%Q'
56
+ (style == :lower_case_q && type(node) == '%q') ||
57
+ (style == :upper_case_q && type(node) == '%Q')
58
58
  end
59
59
 
60
60
  def message(_range)
@@ -141,7 +141,7 @@ module RuboCop
141
141
  end
142
142
 
143
143
  def requires_parens?(parent)
144
- parent.and_type? || parent.or_type? || parent.if_type? && parent.ternary?
144
+ parent.and_type? || parent.or_type? || (parent.if_type? && parent.ternary?)
145
145
  end
146
146
  end
147
147
  end
@@ -84,6 +84,7 @@ module RuboCop
84
84
 
85
85
  def on_kwbegin(node)
86
86
  return if empty_begin?(node) ||
87
+ begin_block_has_multiline_statements?(node) ||
87
88
  contain_rescue_or_ensure?(node) ||
88
89
  valid_context_using_only_begin?(node)
89
90
 
@@ -102,6 +103,9 @@ module RuboCop
102
103
  corrector.remove(offense_range)
103
104
  end
104
105
 
106
+ if use_modifier_form_after_multiline_begin_block?(node)
107
+ correct_modifier_form_after_multiline_begin_block(corrector, node)
108
+ end
105
109
  corrector.remove(node.loc.end)
106
110
  end
107
111
  end
@@ -127,10 +131,31 @@ module RuboCop
127
131
  corrector.insert_before(node.parent, comments) unless comments.blank?
128
132
  end
129
133
 
134
+ def use_modifier_form_after_multiline_begin_block?(node)
135
+ return unless (parent = node.parent)
136
+
137
+ node.multiline? && parent.if_type? && parent.modifier_form?
138
+ end
139
+
140
+ def correct_modifier_form_after_multiline_begin_block(corrector, node)
141
+ condition_range = condition_range(node.parent)
142
+
143
+ corrector.insert_after(node.children.first, " #{condition_range.source}")
144
+ corrector.remove(range_by_whole_lines(condition_range, include_final_newline: true))
145
+ end
146
+
147
+ def condition_range(node)
148
+ range_between(node.loc.keyword.begin_pos, node.condition.source_range.end_pos)
149
+ end
150
+
130
151
  def empty_begin?(node)
131
152
  node.children.empty?
132
153
  end
133
154
 
155
+ def begin_block_has_multiline_statements?(node)
156
+ node.children.count >= 2
157
+ end
158
+
134
159
  def contain_rescue_or_ensure?(node)
135
160
  first_child = node.children.first
136
161
 
@@ -120,11 +120,10 @@ module RuboCop
120
120
  end
121
121
 
122
122
  def require_parentheses?(node)
123
- node.basic_conditional? &&
124
- node.modifier_form? ||
123
+ (node.basic_conditional? && node.modifier_form?) ||
125
124
  node.range_type? ||
126
125
  node.rescue_type? ||
127
- node.respond_to?(:semantic_operator?) && node.semantic_operator?
126
+ (node.respond_to?(:semantic_operator?) && node.semantic_operator?)
128
127
  end
129
128
 
130
129
  def without_argument_parentheses_method?(node)
@@ -7,6 +7,9 @@ module RuboCop
7
7
  #
8
8
  # NOTE: Regexp and Range literals are frozen objects since Ruby 3.0.
9
9
  #
10
+ # NOTE: From Ruby 3.0, this cop allows explicit freezing of interpolated
11
+ # string literals when `# frozen-string-literal: true` is used.
12
+ #
10
13
  # @example
11
14
  # # bad
12
15
  # CONST = 1.freeze
@@ -37,9 +40,7 @@ module RuboCop
37
40
  node = strip_parenthesis(node)
38
41
 
39
42
  return true if node.immutable_literal?
40
-
41
- return true if FROZEN_STRING_LITERAL_TYPES.include?(node.type) &&
42
- frozen_string_literals_enabled?
43
+ return true if frozen_string_literal?(node)
43
44
 
44
45
  target_ruby_version >= 3.0 && (node.regexp_type? || node.range_type?)
45
46
  end
@@ -56,7 +56,7 @@ module RuboCop
56
56
  first_child = node.children.first
57
57
 
58
58
  variable_interpolation?(first_child) ||
59
- first_child.send_type? && !first_child.operator_method?
59
+ (first_child.send_type? && !first_child.operator_method?)
60
60
  end
61
61
 
62
62
  def interpolation?(node)
@@ -65,9 +65,8 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def allowed_percent_q?(node)
68
- node.source.start_with?(PERCENT_Q) && acceptable_q?(node) ||
69
- node.source.start_with?(PERCENT_CAPITAL_Q) &&
70
- acceptable_capital_q?(node)
68
+ (node.source.start_with?(PERCENT_Q) && acceptable_q?(node)) ||
69
+ (node.source.start_with?(PERCENT_CAPITAL_Q) && acceptable_capital_q?(node))
71
70
  end
72
71
 
73
72
  def message(node)
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # This cop checks for places where conditional branch makes redundant self-assignment.
7
+ #
8
+ # It only detects local variable because it may replace state of instance variable,
9
+ # class variable, and global variable that have state across methods with `nil`.
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ # foo = condition ? bar : foo
15
+ #
16
+ # # good
17
+ # foo = bar if condition
18
+ #
19
+ # # bad
20
+ # foo = condition ? foo : bar
21
+ #
22
+ # # good
23
+ # foo = bar unless condition
24
+ #
25
+ class RedundantSelfAssignmentBranch < Base
26
+ include RangeHelp
27
+ extend AutoCorrector
28
+
29
+ MSG = 'Remove the self-assignment branch.'
30
+
31
+ # @!method bad_method?(node)
32
+ def_node_matcher :bad_method?, <<~PATTERN
33
+ (send nil? :bad_method ...)
34
+ PATTERN
35
+
36
+ def on_lvasgn(node)
37
+ variable, expression = *node
38
+ return unless use_if_and_else_branch?(expression)
39
+
40
+ if_branch = expression.if_branch
41
+ else_branch = expression.else_branch
42
+ return if inconvertible_to_modifier?(if_branch, else_branch)
43
+
44
+ if self_assign?(variable, if_branch)
45
+ register_offense(expression, if_branch, else_branch, 'unless')
46
+ elsif self_assign?(variable, else_branch)
47
+ register_offense(expression, else_branch, if_branch, 'if')
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def use_if_and_else_branch?(expression)
54
+ return false unless expression&.if_type?
55
+
56
+ !expression.ternary? || !expression.else?
57
+ end
58
+
59
+ def inconvertible_to_modifier?(if_branch, else_branch)
60
+ multiple_statements?(if_branch) || multiple_statements?(else_branch) ||
61
+ (else_branch.respond_to?(:elsif?) && else_branch.elsif?)
62
+ end
63
+
64
+ def multiple_statements?(branch)
65
+ branch && branch.children.compact.count > 1
66
+ end
67
+
68
+ def self_assign?(variable, branch)
69
+ variable.to_s == branch&.source
70
+ end
71
+
72
+ def register_offense(if_node, offense_branch, opposite_branch, keyword)
73
+ add_offense(offense_branch) do |corrector|
74
+ assignment_value = opposite_branch ? opposite_branch.source : 'nil'
75
+ replacement = "#{assignment_value} #{keyword} #{if_node.condition.source}"
76
+
77
+ corrector.replace(if_node, replacement)
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end