rubocop 1.65.1 → 1.66.1

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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +67 -67
  3. data/config/default.yml +18 -2
  4. data/exe/rubocop +4 -3
  5. data/lib/rubocop/comment_config.rb +1 -1
  6. data/lib/rubocop/config.rb +5 -1
  7. data/lib/rubocop/config_loader.rb +14 -8
  8. data/lib/rubocop/config_loader_resolver.rb +1 -2
  9. data/lib/rubocop/config_validator.rb +1 -1
  10. data/lib/rubocop/cop/base.rb +4 -0
  11. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -0
  12. data/lib/rubocop/cop/documentation.rb +18 -1
  13. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +2 -1
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  15. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +11 -1
  16. data/lib/rubocop/cop/layout/block_alignment.rb +30 -12
  17. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  18. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +8 -3
  19. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +0 -3
  20. data/lib/rubocop/cop/layout/line_length.rb +14 -14
  21. data/lib/rubocop/cop/lint/empty_conditional_body.rb +27 -6
  22. data/lib/rubocop/cop/lint/float_comparison.rb +1 -3
  23. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +4 -2
  24. data/lib/rubocop/cop/lint/useless_assignment.rb +18 -11
  25. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +77 -0
  26. data/lib/rubocop/cop/lint/void.rb +30 -8
  27. data/lib/rubocop/cop/metrics/block_length.rb +6 -5
  28. data/lib/rubocop/cop/metrics/class_length.rb +6 -5
  29. data/lib/rubocop/cop/metrics/method_length.rb +6 -5
  30. data/lib/rubocop/cop/metrics/module_length.rb +6 -5
  31. data/lib/rubocop/cop/mixin/annotation_comment.rb +0 -2
  32. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +19 -9
  33. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -2
  34. data/lib/rubocop/cop/mixin/string_literals_help.rb +12 -0
  35. data/lib/rubocop/cop/naming/accessor_method_name.rb +5 -0
  36. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  37. data/lib/rubocop/cop/style/alias.rb +1 -1
  38. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -3
  39. data/lib/rubocop/cop/style/empty_else.rb +6 -5
  40. data/lib/rubocop/cop/style/empty_heredoc.rb +1 -14
  41. data/lib/rubocop/cop/style/empty_literal.rb +31 -22
  42. data/lib/rubocop/cop/style/format_string_token.rb +2 -2
  43. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  44. data/lib/rubocop/cop/style/identical_conditional_branches.rb +1 -1
  45. data/lib/rubocop/cop/style/if_with_semicolon.rb +45 -6
  46. data/lib/rubocop/cop/style/in_pattern_then.rb +6 -2
  47. data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
  48. data/lib/rubocop/cop/style/map_into_array.rb +12 -5
  49. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -2
  50. data/lib/rubocop/cop/style/multiple_comparison.rb +3 -11
  51. data/lib/rubocop/cop/style/numeric_predicate.rb +2 -2
  52. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  53. data/lib/rubocop/cop/style/parallel_assignment.rb +5 -4
  54. data/lib/rubocop/cop/style/quoted_symbols.rb +0 -2
  55. data/lib/rubocop/cop/style/redundant_condition.rb +3 -2
  56. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +46 -0
  57. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  58. data/lib/rubocop/cop/style/safe_navigation.rb +2 -2
  59. data/lib/rubocop/cop/team.rb +6 -2
  60. data/lib/rubocop/formatter/junit_formatter.rb +70 -23
  61. data/lib/rubocop/lockfile.rb +6 -4
  62. data/lib/rubocop/remote_config.rb +5 -1
  63. data/lib/rubocop/result_cache.rb +2 -8
  64. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  65. data/lib/rubocop/server/cache.rb +1 -1
  66. data/lib/rubocop/target_ruby.rb +7 -3
  67. data/lib/rubocop/version.rb +1 -1
  68. data/lib/rubocop/yaml_duplication_checker.rb +1 -0
  69. data/lib/rubocop.rb +2 -1
  70. metadata +12 -30
@@ -334,13 +334,18 @@ module RuboCop
334
334
  end
335
335
  end
336
336
 
337
+ # rubocop:disable Metrics/AbcSize
337
338
  def arguments_range(node, first_node)
338
- arguments = node.arguments.reject { |arg| ADDITIONAL_ARG_TYPES.include?(arg.type) }
339
+ arguments = node.arguments.reject do |arg|
340
+ next true if ADDITIONAL_ARG_TYPES.include?(arg.type) || arg.variable? || arg.call_type?
339
341
 
340
- start_node = first_node || arguments.first
342
+ arg.literal? && arg.each_descendant(:kwsplat).none?
343
+ end
341
344
 
342
- range_between(start_node.source_range.begin_pos, arguments.last.source_range.end_pos)
345
+ start_node = first_node || arguments.first
346
+ start_node.source_range.begin.join(arguments.last.source_range.end)
343
347
  end
348
+ # rubocop:enable Metrics/AbcSize
344
349
 
345
350
  def allow_only_rest_arguments?
346
351
  cop_config.fetch('AllowOnlyRestArgument', true)
@@ -143,7 +143,7 @@ module RuboCop
143
143
  private
144
144
 
145
145
  def check(node)
146
- return if cop_config['AllowComments'] && comment_in_else?(node.loc)
146
+ return if cop_config['AllowComments'] && comment_in_else?(node)
147
147
 
148
148
  empty_check(node) if empty_style?
149
149
  nil_check(node) if nil_style?
@@ -171,16 +171,17 @@ module RuboCop
171
171
 
172
172
  def autocorrect(corrector, node)
173
173
  return false if autocorrect_forbidden?(node.type.to_s)
174
- return false if comment_in_else?(node.loc)
174
+ return false if comment_in_else?(node)
175
175
 
176
176
  end_pos = base_node(node).loc.end.begin_pos
177
177
  corrector.remove(range_between(node.loc.else.begin_pos, end_pos))
178
178
  end
179
179
 
180
- def comment_in_else?(loc)
181
- return false if loc.else.nil? || loc.end.nil?
180
+ def comment_in_else?(node)
181
+ node = node.parent while node.if_type? && node.elsif?
182
+ return false unless node.else?
182
183
 
183
- processed_source.contains_comment?(loc.else.join(loc.end))
184
+ processed_source.contains_comment?(node.loc.else.join(node.source_range.end))
184
185
  end
185
186
 
186
187
  def base_node(node)
@@ -36,6 +36,7 @@ module RuboCop
36
36
  class EmptyHeredoc < Base
37
37
  include Heredoc
38
38
  include RangeHelp
39
+ include StringLiteralsHelp
39
40
  extend AutoCorrector
40
41
 
41
42
  MSG = 'Use an empty string literal instead of heredoc.'
@@ -53,20 +54,6 @@ module RuboCop
53
54
  corrector.remove(range_by_whole_lines(heredoc_end, include_final_newline: true))
54
55
  end
55
56
  end
56
-
57
- private
58
-
59
- def preferred_string_literal
60
- enforce_double_quotes? ? '""' : "''"
61
- end
62
-
63
- def enforce_double_quotes?
64
- string_literals_config['EnforcedStyle'] == 'double_quotes'
65
- end
66
-
67
- def string_literals_config
68
- config.for_cop('Style/StringLiterals')
69
- end
70
57
  end
71
58
  end
72
59
  end
@@ -9,7 +9,9 @@ module RuboCop
9
9
  # @example
10
10
  # # bad
11
11
  # a = Array.new
12
+ # a = Array[]
12
13
  # h = Hash.new
14
+ # h = Hash[]
13
15
  # s = String.new
14
16
  #
15
17
  # # good
@@ -19,16 +21,17 @@ module RuboCop
19
21
  class EmptyLiteral < Base
20
22
  include FrozenStringLiteral
21
23
  include RangeHelp
24
+ include StringLiteralsHelp
22
25
  extend AutoCorrector
23
26
 
24
- ARR_MSG = 'Use array literal `[]` instead of `Array.new`.'
25
- HASH_MSG = 'Use hash literal `{}` instead of `Hash.new`.'
27
+ ARR_MSG = 'Use array literal `[]` instead of `%<current>s`.'
28
+ HASH_MSG = 'Use hash literal `{}` instead of `%<current>s`.'
26
29
  STR_MSG = 'Use string literal `%<prefer>s` instead of `String.new`.'
27
30
 
28
- RESTRICT_ON_SEND = %i[new].freeze
31
+ RESTRICT_ON_SEND = %i[new [] Array Hash].freeze
29
32
 
30
33
  # @!method array_node(node)
31
- def_node_matcher :array_node, '(send (const {nil? cbase} :Array) :new)'
34
+ def_node_matcher :array_node, '(send (const {nil? cbase} :Array) :new (array)?)'
32
35
 
33
36
  # @!method hash_node(node)
34
37
  def_node_matcher :hash_node, '(send (const {nil? cbase} :Hash) :new)'
@@ -47,6 +50,22 @@ module RuboCop
47
50
  }
48
51
  PATTERN
49
52
 
53
+ # @!method array_with_index(node)
54
+ def_node_matcher :array_with_index, <<~PATTERN
55
+ {
56
+ (send (const {nil? cbase} :Array) :[])
57
+ (send nil? :Array (array))
58
+ }
59
+ PATTERN
60
+
61
+ # @!method hash_with_index(node)
62
+ def_node_matcher :hash_with_index, <<~PATTERN
63
+ {
64
+ (send (const {nil? cbase} :Hash) :[])
65
+ (send nil? :Hash (array))
66
+ }
67
+ PATTERN
68
+
50
69
  def on_send(node)
51
70
  return unless (message = offense_message(node))
52
71
 
@@ -59,26 +78,14 @@ module RuboCop
59
78
 
60
79
  def offense_message(node)
61
80
  if offense_array_node?(node)
62
- ARR_MSG
81
+ format(ARR_MSG, current: node.source)
63
82
  elsif offense_hash_node?(node)
64
- HASH_MSG
83
+ format(HASH_MSG, current: node.source)
65
84
  elsif str_node(node) && !frozen_strings?
66
85
  format(STR_MSG, prefer: preferred_string_literal)
67
86
  end
68
87
  end
69
88
 
70
- def preferred_string_literal
71
- enforce_double_quotes? ? '""' : "''"
72
- end
73
-
74
- def enforce_double_quotes?
75
- string_literals_config['EnforcedStyle'] == 'double_quotes'
76
- end
77
-
78
- def string_literals_config
79
- config.for_cop('Style/StringLiterals')
80
- end
81
-
82
89
  def first_argument_unparenthesized?(node)
83
90
  parent = node.parent
84
91
  return false unless parent && %i[send super zsuper].include?(parent.type)
@@ -100,12 +107,12 @@ module RuboCop
100
107
  end
101
108
 
102
109
  def offense_array_node?(node)
103
- array_node(node) && !array_with_block(node.parent)
110
+ (array_node(node) && !array_with_block(node.parent)) || array_with_index(node)
104
111
  end
105
112
 
106
113
  def offense_hash_node?(node)
107
114
  # If Hash.new takes a block, it can't be changed to {}.
108
- hash_node(node) && !hash_with_block(node.parent)
115
+ (hash_node(node) && !hash_with_block(node.parent)) || hash_with_index(node)
109
116
  end
110
117
 
111
118
  def correction(node)
@@ -129,8 +136,10 @@ module RuboCop
129
136
  def frozen_strings?
130
137
  return true if frozen_string_literals_enabled?
131
138
 
132
- frozen_string_cop_enabled = config.for_cop('Style/FrozenStringLiteral')['Enabled']
133
- frozen_string_cop_enabled && !frozen_string_literals_disabled?
139
+ frozen_string_cop_enabled = config.for_cop('Style/FrozenStringLiteralComment')['Enabled']
140
+ frozen_string_cop_enabled &&
141
+ !frozen_string_literals_disabled? &&
142
+ string_literals_frozen_by_default?.nil?
134
143
  end
135
144
  end
136
145
  end
@@ -11,8 +11,8 @@ module RuboCop
11
11
  # The reason is that _unannotated_ format is very similar
12
12
  # to encoded URLs or Date/Time formatting strings.
13
13
  #
14
- # This cop can be customized allowed methods with `AllowedMethods`.
15
- # By default, there are no methods to allowed.
14
+ # This cop's allowed methods can be customized with `AllowedMethods`.
15
+ # By default, there are no allowed methods.
16
16
  #
17
17
  # @example EnforcedStyle: annotated (default)
18
18
  #
@@ -277,6 +277,8 @@ module RuboCop
277
277
  end
278
278
 
279
279
  def trivial?(node)
280
+ return false unless node.if_branch
281
+
280
282
  node.branches.one? && !node.if_branch.if_type? && !node.if_branch.begin_type?
281
283
  end
282
284
 
@@ -155,7 +155,7 @@ module RuboCop
155
155
  condition_variable = assignable_condition_value(node)
156
156
 
157
157
  head = heads.first
158
- if head.assignment?
158
+ if head.respond_to?(:assignment?) && head.assignment?
159
159
  # The `send` node is used instead of the `indexasgn` node, so `name` cannot be used.
160
160
  # https://github.com/rubocop/rubocop-ast/blob/v1.29.0/lib/rubocop/ast/node/indexasgn_node.rb
161
161
  #
@@ -18,6 +18,7 @@ module RuboCop
18
18
  extend AutoCorrector
19
19
 
20
20
  MSG_IF_ELSE = 'Do not use `if %<expr>s;` - use `if/else` instead.'
21
+ MSG_NEWLINE = 'Do not use `if %<expr>s;` - use a newline instead.'
21
22
  MSG_TERNARY = 'Do not use `if %<expr>s;` - use a ternary operator instead.'
22
23
 
23
24
  def on_normal_if_unless(node)
@@ -26,20 +27,47 @@ module RuboCop
26
27
  beginning = node.loc.begin
27
28
  return unless beginning&.is?(';')
28
29
 
29
- message = node.else_branch&.if_type? ? MSG_IF_ELSE : MSG_TERNARY
30
+ message = message(node)
30
31
 
31
- add_offense(node, message: format(message, expr: node.condition.source)) do |corrector|
32
- corrector.replace(node, autocorrect(node))
32
+ add_offense(node, message: message) do |corrector|
33
+ autocorrect(corrector, node)
33
34
  end
34
35
  end
35
36
 
36
37
  private
37
38
 
38
- def autocorrect(node)
39
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
40
+ def message(node)
41
+ template = if node.if_branch&.begin_type?
42
+ MSG_NEWLINE
43
+ elsif node.else_branch&.if_type? || node.else_branch&.begin_type? ||
44
+ use_block_in_branches?(node)
45
+ MSG_IF_ELSE
46
+ else
47
+ MSG_TERNARY
48
+ end
49
+
50
+ format(template, expr: node.condition.source)
51
+ end
52
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
53
+
54
+ def autocorrect(corrector, node)
55
+ if node.branches.compact.any?(&:begin_type?) || use_block_in_branches?(node)
56
+ corrector.replace(node.loc.begin, "\n")
57
+ else
58
+ corrector.replace(node, replacement(node))
59
+ end
60
+ end
61
+
62
+ def use_block_in_branches?(node)
63
+ node.branches.compact.any? { |branch| branch.block_type? || branch.numblock_type? }
64
+ end
65
+
66
+ def replacement(node)
39
67
  return correct_elsif(node) if node.else_branch&.if_type?
40
68
 
41
- then_code = node.if_branch ? node.if_branch.source : 'nil'
42
- else_code = node.else_branch ? node.else_branch.source : 'nil'
69
+ then_code = node.if_branch ? build_expression(node.if_branch) : 'nil'
70
+ else_code = node.else_branch ? build_expression(node.else_branch) : 'nil'
43
71
 
44
72
  "#{node.condition.source} ? #{then_code} : #{else_code}"
45
73
  end
@@ -53,6 +81,17 @@ module RuboCop
53
81
  RUBY
54
82
  end
55
83
 
84
+ # rubocop:disable Metrics/AbcSize
85
+ def build_expression(expr)
86
+ return expr.source if !expr.call_type? || expr.parenthesized? || expr.arguments.empty?
87
+
88
+ method = expr.source_range.begin.join(expr.loc.selector.end)
89
+ arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
90
+
91
+ "#{method.source}(#{arguments.source})"
92
+ end
93
+ # rubocop:enable Metrics/AbcSize
94
+
56
95
  def build_else_branch(second_condition)
57
96
  result = <<~RUBY
58
97
  elsif #{second_condition.condition.source}
@@ -44,11 +44,15 @@ module RuboCop
44
44
  private
45
45
 
46
46
  def alternative_pattern_source(pattern)
47
+ collect_alternative_patterns(pattern).join(' | ')
48
+ end
49
+
50
+ def collect_alternative_patterns(pattern)
47
51
  return pattern.children.map(&:source) unless pattern.children.first.match_alt_type?
48
52
 
49
- pattern_sources = alternative_pattern_source(pattern.children.first)
53
+ pattern_sources = collect_alternative_patterns(pattern.children.first)
50
54
 
51
- (pattern_sources << pattern.children[1].source).join(' | ')
55
+ pattern_sources << pattern.children[1].source
52
56
  end
53
57
  end
54
58
  end
@@ -105,7 +105,7 @@ module RuboCop
105
105
 
106
106
  # Value object to extract source ranges for the different parts of a magic comment
107
107
  class CommentRange
108
- extend Forwardable
108
+ extend SimpleForwardable
109
109
 
110
110
  DIRECTIVE_REGEXP = Regexp.union(MagicComment::KEYWORDS.map do |_, v|
111
111
  Regexp.new(v, Regexp::IGNORECASE)
@@ -58,12 +58,21 @@ module RuboCop
58
58
  [
59
59
  ^({begin kwbegin} ...)
60
60
  ({block numblock} (send !{nil? self} :each) _
61
- (send (lvar _) {:<< :push :append} _))
61
+ (send (lvar _) {:<< :push :append} {send lvar begin}))
62
62
  ]
63
63
  PATTERN
64
64
 
65
65
  # @!method empty_array_asgn?(node)
66
- def_node_matcher :empty_array_asgn?, '(lvasgn _ (array))'
66
+ def_node_matcher :empty_array_asgn?, <<~PATTERN
67
+ (
68
+ lvasgn _ {
69
+ (array)
70
+ (send (const {nil? cbase} :Array) :[])
71
+ (send (const {nil? cbase} :Array) :new (array)?)
72
+ (send nil? :Array (array))
73
+ }
74
+ )
75
+ PATTERN
67
76
 
68
77
  # @!method lvar_ref?(node, name)
69
78
  def_node_matcher :lvar_ref?, '(lvar %1)'
@@ -138,10 +147,8 @@ module RuboCop
138
147
  false
139
148
  when :begin, :kwbegin
140
149
  !node.right_sibling && return_value_used?(parent)
141
- when :block, :numblock
142
- !parent.void_context?
143
150
  else
144
- true
151
+ !parent.respond_to?(:void_context?) || !parent.void_context?
145
152
  end
146
153
  end
147
154
 
@@ -5,8 +5,8 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for unwanted parentheses in parameterless method calls.
7
7
  #
8
- # This cop can be customized allowed methods with `AllowedMethods`.
9
- # By default, there are no methods to allowed.
8
+ # This cop's allowed methods can be customized with `AllowedMethods`.
9
+ # By default, there are no allowed methods.
10
10
  #
11
11
  # NOTE: This cop allows the use of `it()` without arguments in blocks,
12
12
  # as in `0.times { it() }`, following `Lint/ItWithoutArgumentsInBlock` cop.
@@ -56,12 +56,10 @@ module RuboCop
56
56
  'in a conditional, use `Array#include?` instead.'
57
57
 
58
58
  def on_new_investigation
59
- @last_comparison = nil
59
+ reset_comparison
60
60
  end
61
61
 
62
62
  def on_or(node)
63
- reset_comparison if switch_comparison?(node)
64
-
65
63
  root_of_or_node = root_of_or_node(node)
66
64
 
67
65
  return unless node == root_of_or_node
@@ -74,9 +72,9 @@ module RuboCop
74
72
  prefer_method = "[#{elements}].include?(#{variables_in_node(node).first})"
75
73
 
76
74
  corrector.replace(node, prefer_method)
77
- end
78
75
 
79
- @last_comparison = node
76
+ reset_comparison
77
+ end
80
78
  end
81
79
 
82
80
  private
@@ -147,12 +145,6 @@ module RuboCop
147
145
  end
148
146
  end
149
147
 
150
- def switch_comparison?(node)
151
- return true if @last_comparison.nil?
152
-
153
- @last_comparison.descendants.none?(node)
154
- end
155
-
156
148
  def reset_comparison
157
149
  @compared_elements = []
158
150
  @allowed_method_comparison = false
@@ -8,8 +8,8 @@ module RuboCop
8
8
  # These can be replaced by their respective predicate methods.
9
9
  # This cop can also be configured to do the reverse.
10
10
  #
11
- # This cop can be customized allowed methods with `AllowedMethods`.
12
- # By default, there are no methods to allowed.
11
+ # This cop's allowed methods can be customized with `AllowedMethods`.
12
+ # By default, there are no allowed methods.
13
13
  #
14
14
  # This cop disregards `#nonzero?` as its value is truthy or falsey,
15
15
  # but not `true` and `false`, and thus not always interchangeable with
@@ -42,7 +42,7 @@ module RuboCop
42
42
  def on_normal_if_unless(node)
43
43
  return unless node.single_line?
44
44
  return unless node.else_branch
45
- return if node.elsif?
45
+ return if node.elsif? || node.if_branch&.begin_type?
46
46
 
47
47
  message = message(node)
48
48
  add_offense(node, message: message) do |corrector|
@@ -211,15 +211,16 @@ module RuboCop
211
211
  protected
212
212
 
213
213
  def assignment
214
- @new_elements.map { |lhs, rhs| "#{lhs.source} = #{source(rhs)}" }
214
+ @new_elements.map { |lhs, rhs| "#{lhs.source} = #{source(rhs, rhs.loc)}" }
215
215
  end
216
216
 
217
217
  private
218
218
 
219
- def source(node)
220
- if node.str_type? && node.loc.begin.nil?
219
+ def source(node, loc)
220
+ # __FILE__ is treated as a StrNode but has no begin
221
+ if node.str_type? && loc.respond_to?(:begin) && loc.begin.nil?
221
222
  "'#{node.source}'"
222
- elsif node.sym_type? && node.loc.begin.nil?
223
+ elsif node.sym_type? && loc.begin.nil?
223
224
  ":#{node.source}"
224
225
  else
225
226
  node.source
@@ -98,8 +98,6 @@ module RuboCop
98
98
 
99
99
  def style
100
100
  return super unless super == :same_as_string_literals
101
-
102
- string_literals_config = config.for_cop('Style/StringLiterals')
103
101
  return :single_quotes unless string_literals_config['Enabled']
104
102
 
105
103
  string_literals_config['EnforcedStyle'].to_sym
@@ -39,9 +39,9 @@ module RuboCop
39
39
  splat block_pass forwarded_restarg forwarded_kwrestarg forwarded_args
40
40
  ].freeze
41
41
 
42
+ # rubocop:disable Metrics/AbcSize
42
43
  def on_if(node)
43
- return if node.elsif_conditional?
44
- return unless offense?(node)
44
+ return if node.modifier_form? || node.elsif_conditional? || !offense?(node)
45
45
 
46
46
  message = message(node)
47
47
 
@@ -57,6 +57,7 @@ module RuboCop
57
57
  end
58
58
  end
59
59
  end
60
+ # rubocop:enable Metrics/AbcSize
60
61
 
61
62
  private
62
63
 
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Before Ruby 3.0, interpolated strings followed the frozen string literal
7
+ # magic comment which sometimes made it necessary to explicitly unfreeze them.
8
+ # Ruby 3.0 changed interpolated strings to always be unfrozen which makes
9
+ # unfreezing them redundant.
10
+ #
11
+ # @example
12
+ # # bad
13
+ # +"#{foo} bar"
14
+ #
15
+ # # bad
16
+ # "#{foo} bar".dup
17
+ #
18
+ # # good
19
+ # "#{foo} bar"
20
+ #
21
+ class RedundantInterpolationUnfreeze < Base
22
+ include FrozenStringLiteral
23
+ extend AutoCorrector
24
+ extend TargetRubyVersion
25
+
26
+ MSG = "Don't unfreeze interpolated strings as they are already unfrozen."
27
+
28
+ RESTRICT_ON_SEND = %i[+@ dup].freeze
29
+
30
+ minimum_target_ruby_version 3.0
31
+
32
+ def on_send(node)
33
+ return if node.arguments?
34
+ return unless (receiver = node.receiver)
35
+ return unless receiver.dstr_type?
36
+ return if uninterpolated_string?(receiver) || uninterpolated_heredoc?(receiver)
37
+
38
+ add_offense(node.loc.selector) do |corrector|
39
+ corrector.remove(node.loc.selector)
40
+ corrector.remove(node.loc.dot) unless node.unary_operation?
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -33,6 +33,7 @@ module RuboCop
33
33
  # 'foo'.sub('f', 'x')
34
34
  # 'foo'.sub!('f', 'x')
35
35
  class RedundantRegexpArgument < Base
36
+ include StringLiteralsHelp
36
37
  extend AutoCorrector
37
38
 
38
39
  MSG = 'Use string `%<prefer>s` as argument instead of regexp `%<current>s`.'
@@ -71,8 +72,10 @@ module RuboCop
71
72
  if new_argument.include?('"')
72
73
  new_argument.gsub!("'", "\\\\'")
73
74
  quote = "'"
74
- else
75
+ elsif new_argument.include?('\\')
75
76
  quote = '"'
77
+ else
78
+ quote = enforce_double_quotes? ? '"' : "'"
76
79
  end
77
80
 
78
81
  "#{quote}#{new_argument}#{quote}"
@@ -17,9 +17,9 @@ module RuboCop
17
17
  # `foo&.bar` can start returning `nil` as well as what the method
18
18
  # returns.
19
19
  #
20
- # The default for `MaxChainLength` is `2`
20
+ # The default for `MaxChainLength` is `2`.
21
21
  # We have limited the cop to not register an offense for method chains
22
- # that exceed this option is set.
22
+ # that exceed this option's value.
23
23
  #
24
24
  # @safety
25
25
  # Autocorrection is unsafe because if a value is `false`, the resulting
@@ -120,8 +120,12 @@ module RuboCop
120
120
  end
121
121
 
122
122
  def external_dependency_checksum
123
- keys = cops.filter_map(&:external_dependency_checksum)
124
- Digest::SHA1.hexdigest(keys.join)
123
+ # The external dependency checksums are cached per RuboCop team so that
124
+ # the checksums don't need to be recomputed for each file.
125
+ @external_dependency_checksum ||= begin
126
+ keys = cops.filter_map(&:external_dependency_checksum)
127
+ Digest::SHA1.hexdigest(keys.join)
128
+ end
125
129
  end
126
130
 
127
131
  private