rubocop 1.65.1 → 1.66.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) 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 +8 -4
  6. data/lib/rubocop/config.rb +20 -4
  7. data/lib/rubocop/config_loader_resolver.rb +1 -2
  8. data/lib/rubocop/config_validator.rb +9 -5
  9. data/lib/rubocop/cop/base.rb +4 -0
  10. data/lib/rubocop/cop/correctors/line_break_corrector.rb +2 -0
  11. data/lib/rubocop/cop/documentation.rb +18 -1
  12. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +2 -1
  13. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +11 -1
  14. data/lib/rubocop/cop/layout/block_alignment.rb +30 -12
  15. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  16. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +8 -3
  17. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +0 -3
  18. data/lib/rubocop/cop/layout/line_length.rb +14 -14
  19. data/lib/rubocop/cop/lint/empty_conditional_body.rb +27 -6
  20. data/lib/rubocop/cop/lint/float_comparison.rb +1 -3
  21. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +4 -2
  22. data/lib/rubocop/cop/lint/useless_assignment.rb +18 -11
  23. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +77 -0
  24. data/lib/rubocop/cop/lint/void.rb +30 -8
  25. data/lib/rubocop/cop/metrics/block_length.rb +6 -5
  26. data/lib/rubocop/cop/metrics/class_length.rb +6 -5
  27. data/lib/rubocop/cop/metrics/method_length.rb +6 -5
  28. data/lib/rubocop/cop/metrics/module_length.rb +6 -5
  29. data/lib/rubocop/cop/mixin/annotation_comment.rb +0 -2
  30. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +19 -9
  31. data/lib/rubocop/cop/mixin/line_length_help.rb +7 -2
  32. data/lib/rubocop/cop/mixin/string_literals_help.rb +12 -0
  33. data/lib/rubocop/cop/naming/accessor_method_name.rb +5 -0
  34. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  35. data/lib/rubocop/cop/style/alias.rb +1 -1
  36. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -3
  37. data/lib/rubocop/cop/style/empty_else.rb +5 -5
  38. data/lib/rubocop/cop/style/empty_heredoc.rb +1 -14
  39. data/lib/rubocop/cop/style/empty_literal.rb +32 -23
  40. data/lib/rubocop/cop/style/format_string_token.rb +2 -2
  41. data/lib/rubocop/cop/style/guard_clause.rb +2 -0
  42. data/lib/rubocop/cop/style/identical_conditional_branches.rb +1 -1
  43. data/lib/rubocop/cop/style/if_with_semicolon.rb +38 -6
  44. data/lib/rubocop/cop/style/in_pattern_then.rb +6 -2
  45. data/lib/rubocop/cop/style/magic_comment_format.rb +8 -3
  46. data/lib/rubocop/cop/style/map_into_array.rb +11 -2
  47. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -2
  48. data/lib/rubocop/cop/style/multiple_comparison.rb +3 -11
  49. data/lib/rubocop/cop/style/numeric_predicate.rb +2 -2
  50. data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
  51. data/lib/rubocop/cop/style/parallel_assignment.rb +5 -4
  52. data/lib/rubocop/cop/style/quoted_symbols.rb +0 -2
  53. data/lib/rubocop/cop/style/redundant_condition.rb +3 -2
  54. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +46 -0
  55. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -1
  56. data/lib/rubocop/cop/style/safe_navigation.rb +2 -2
  57. data/lib/rubocop/cop/team.rb +6 -2
  58. data/lib/rubocop/formatter/junit_formatter.rb +70 -23
  59. data/lib/rubocop/lockfile.rb +6 -4
  60. data/lib/rubocop/remote_config.rb +5 -1
  61. data/lib/rubocop/result_cache.rb +2 -8
  62. data/lib/rubocop/rspec/shared_contexts.rb +2 -2
  63. data/lib/rubocop/server/cache.rb +1 -1
  64. data/lib/rubocop/target_ruby.rb +7 -3
  65. data/lib/rubocop/version.rb +1 -1
  66. data/lib/rubocop.rb +2 -1
  67. 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,16 @@ 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
182
 
183
- processed_source.contains_comment?(loc.else.join(loc.end))
183
+ processed_source.contains_comment?(node.loc.else.join(node.source_range.end))
184
184
  end
185
185
 
186
186
  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,19 +21,20 @@ 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
- def_node_matcher :hash_node, '(send (const {nil? cbase} :Hash) :new)'
37
+ def_node_matcher :hash_node, '(send (const {nil? cbase} :Hash) :new (array)?)'
35
38
 
36
39
  # @!method str_node(node)
37
40
  def_node_matcher :str_node, '(send (const {nil? cbase} :String) :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,40 @@ 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
+ def message(node)
40
+ template = if node.if_branch&.begin_type?
41
+ MSG_NEWLINE
42
+ elsif node.else_branch&.if_type? || node.else_branch&.begin_type?
43
+ MSG_IF_ELSE
44
+ else
45
+ MSG_TERNARY
46
+ end
47
+
48
+ format(template, expr: node.condition.source)
49
+ end
50
+
51
+ def autocorrect(corrector, node)
52
+ if node.if_branch&.begin_type? || node.else_branch&.begin_type?
53
+ corrector.replace(node.loc.begin, "\n")
54
+ else
55
+ corrector.replace(node, replacement(node))
56
+ end
57
+ end
58
+
59
+ def replacement(node)
39
60
  return correct_elsif(node) if node.else_branch&.if_type?
40
61
 
41
- then_code = node.if_branch ? node.if_branch.source : 'nil'
42
- else_code = node.else_branch ? node.else_branch.source : 'nil'
62
+ then_code = node.if_branch ? build_expression(node.if_branch) : 'nil'
63
+ else_code = node.else_branch ? build_expression(node.else_branch) : 'nil'
43
64
 
44
65
  "#{node.condition.source} ? #{then_code} : #{else_code}"
45
66
  end
@@ -53,6 +74,17 @@ module RuboCop
53
74
  RUBY
54
75
  end
55
76
 
77
+ # rubocop:disable Metrics/AbcSize
78
+ def build_expression(expr)
79
+ return expr.source if !expr.call_type? || expr.parenthesized? || expr.arguments.empty?
80
+
81
+ method = expr.source_range.begin.join(expr.loc.selector.end)
82
+ arguments = expr.first_argument.source_range.begin.join(expr.source_range.end)
83
+
84
+ "#{method.source}(#{arguments.source})"
85
+ end
86
+ # rubocop:enable Metrics/AbcSize
87
+
56
88
  def build_else_branch(second_condition)
57
89
  result = <<~RUBY
58
90
  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,21 +105,26 @@ 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
109
-
110
108
  DIRECTIVE_REGEXP = Regexp.union(MagicComment::KEYWORDS.map do |_, v|
111
109
  Regexp.new(v, Regexp::IGNORECASE)
112
110
  end).freeze
113
111
 
114
112
  VALUE_REGEXP = Regexp.new("(?:#{DIRECTIVE_REGEXP}:\s*)(.*?)(?=;|$)")
115
113
 
116
- def_delegators :@comment, :text, :loc
117
114
  attr_reader :comment
118
115
 
119
116
  def initialize(comment)
120
117
  @comment = comment
121
118
  end
122
119
 
120
+ def text
121
+ @comment.text
122
+ end
123
+
124
+ def loc
125
+ @comment.loc
126
+ end
127
+
123
128
  # A magic comment can contain one directive (normal style) or
124
129
  # multiple directives (emacs style)
125
130
  def directives
@@ -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)'
@@ -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