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
@@ -51,7 +51,6 @@ module RuboCop
51
51
  # # defined inside a method call.
52
52
  #
53
53
  # # bad
54
- # # consistent
55
54
  # array = [
56
55
  # :value
57
56
  # ]
@@ -72,13 +71,11 @@ module RuboCop
72
71
  # # brackets are indented to the same position.
73
72
  #
74
73
  # # bad
75
- # # align_brackets
76
74
  # and_now_for_something = [
77
75
  # :completely_different
78
76
  # ]
79
77
  #
80
78
  # # good
81
- # # align_brackets
82
79
  # and_now_for_something = [
83
80
  # :completely_different
84
81
  # ]
@@ -25,20 +25,20 @@ module RuboCop
25
25
  # * `Layout/ArrayAlignment`
26
26
  # * `Layout/BlockAlignment`
27
27
  # * `Layout/BlockEndNewline`
28
- # * `LayoutClosingParenthesisIndentation`
29
- # * `LayoutFirstArgumentIndentation`
30
- # * `LayoutFirstArrayElementIndentation`
31
- # * `LayoutFirstHashElementIndentation`
32
- # * `LayoutFirstParameterIndentation`
33
- # * `LayoutHashAlignment`
34
- # * `LayoutIndentationWidth`
35
- # * `LayoutMultilineArrayLineBreaks`
36
- # * `LayoutMultilineBlockLayout`
37
- # * `LayoutMultilineHashBraceLayout`
38
- # * `LayoutMultilineHashKeyLineBreaks`
39
- # * `LayoutMultilineMethodArgumentLineBreaks`
40
- # * `LayoutMultilineMethodParameterLineBreaks`
41
- # * `Layout/ParameterAlignment`
28
+ # * `Layout/ClosingParenthesisIndentation`
29
+ # * `Layout/FirstArgumentIndentation`
30
+ # * `Layout/FirstArrayElementIndentation`
31
+ # * `Layout/FirstHashElementIndentation`
32
+ # * `Layout/FirstParameterIndentation`
33
+ # * `Layout/HashAlignment`
34
+ # * `Layout/IndentationWidth`
35
+ # * `Layout/MultilineArrayLineBreaks`
36
+ # * `Layout/MultilineBlockLayout`
37
+ # * `Layout/MultilineHashBraceLayout`
38
+ # * `Layout/MultilineHashKeyLineBreaks`
39
+ # * `Layout/MultilineMethodArgumentLineBreaks`
40
+ # * `Layout/MultilineMethodParameterLineBreaks`
41
+ # * `Layout//ParameterAlignment`
42
42
  # * `Style/BlockDelimiters`
43
43
  #
44
44
  # Together, these cops will pretty print hashes, arrays,
@@ -67,19 +67,31 @@ module RuboCop
67
67
 
68
68
  MSG = 'Avoid `%<keyword>s` branches without a body.'
69
69
 
70
+ # rubocop:disable Metrics/AbcSize
70
71
  def on_if(node)
71
72
  return if node.body || same_line?(node.loc.begin, node.loc.end)
72
73
  return if cop_config['AllowComments'] && contains_comments?(node)
73
74
 
74
- add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
75
+ range = offense_range(node)
76
+
77
+ add_offense(range, message: format(MSG, keyword: node.keyword)) do |corrector|
75
78
  next if node.parent&.call_type?
76
79
 
77
80
  autocorrect(corrector, node)
78
81
  end
79
82
  end
83
+ # rubocop:enable Metrics/AbcSize
80
84
 
81
85
  private
82
86
 
87
+ def offense_range(node)
88
+ if node.loc.else
89
+ node.source_range.begin.join(node.loc.else.begin)
90
+ else
91
+ node.source_range
92
+ end
93
+ end
94
+
83
95
  def autocorrect(corrector, node)
84
96
  remove_comments(corrector, node)
85
97
  remove_empty_branch(corrector, node)
@@ -93,13 +105,22 @@ module RuboCop
93
105
  end
94
106
  end
95
107
 
108
+ # rubocop:disable Metrics/AbcSize
96
109
  def remove_empty_branch(corrector, node)
97
- if empty_if_branch?(node) && else_branch?(node)
98
- corrector.remove(branch_range(node))
99
- else
100
- corrector.remove(deletion_range(branch_range(node)))
101
- end
110
+ range = if empty_if_branch?(node) && else_branch?(node)
111
+ branch_range(node)
112
+ elsif same_line?(node, else_kw_loc = node.loc.else)
113
+ node.source_range.begin.join(else_kw_loc.begin)
114
+ elsif node.parent&.loc.respond_to?(:end) &&
115
+ same_line?(node, end_loc = node.parent.loc.end)
116
+ node.source_range.begin.join(end_loc.begin)
117
+ else
118
+ deletion_range(branch_range(node))
119
+ end
120
+
121
+ corrector.remove(range)
102
122
  end
123
+ # rubocop:enable Metrics/AbcSize
103
124
 
104
125
  def correct_other_branches(corrector, node)
105
126
  return unless require_other_branches_correction?(node)
@@ -66,9 +66,7 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def literal_zero?(node)
69
- # TODO: https://github.com/rubocop/rubocop-ast/pull/304 is released,
70
- # replace this condition with `node&.numeric_type? && node.value.zero?`.
71
- node&.numeric_type? && node.node_parts[0].zero?
69
+ node&.numeric_type? && node.value.zero?
72
70
  end
73
71
 
74
72
  # rubocop:disable Metrics/PerceivedComplexity
@@ -40,9 +40,9 @@ module RuboCop
40
40
  end
41
41
 
42
42
  add_offense(range, message: message) do |corrector|
43
- preferred = "#{lhs_node.source} + #{rhs_node.source}"
43
+ range = lhs_node.source_range.end.join(rhs_node.source_range.begin)
44
44
 
45
- corrector.replace(range, preferred)
45
+ corrector.replace(range, ' + ')
46
46
  end
47
47
  end
48
48
  end
@@ -92,6 +92,8 @@ module RuboCop
92
92
  end
93
93
 
94
94
  def str_content(node)
95
+ return unless node.respond_to?(:str_type?)
96
+
95
97
  if node.str_type?
96
98
  node.children[0]
97
99
  else
@@ -16,15 +16,14 @@ module RuboCop
16
16
  # reassignments and properly handles varied cases such as branch, loop,
17
17
  # rescue, ensure, etc.
18
18
  #
19
+ # This cop's autocorrection avoids cases like `a ||= 1` because removing assignment from
20
+ # operator assignment can cause NameError if this assignment has been used to declare
21
+ # a local variable. For example, replacing `a ||= 1` with `a || 1` may cause
22
+ # "undefined local variable or method `a' for main:Object (NameError)".
23
+ #
19
24
  # NOTE: Given the assignment `foo = 1, bar = 2`, removing unused variables
20
25
  # can lead to a syntax error, so this case is not autocorrected.
21
26
  #
22
- # @safety
23
- # This cop's autocorrection is unsafe because removing assignment from
24
- # operator assignment can cause NameError if this assignment has been used to declare
25
- # local variable. For example, replacing `a ||= 1` to `a || 1` may cause
26
- # "undefined local variable or method `a' for main:Object (NameError)".
27
- #
28
27
  # @example
29
28
  #
30
29
  # # bad
@@ -53,24 +52,32 @@ module RuboCop
53
52
  scope.variables.each_value { |variable| check_for_unused_assignments(variable) }
54
53
  end
55
54
 
56
- # rubocop:disable Metrics/AbcSize
55
+ # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
57
56
  def check_for_unused_assignments(variable)
58
57
  return if variable.should_be_unused?
59
58
 
60
59
  variable.assignments.reverse_each do |assignment|
61
- next if assignment.used? || part_of_ignored_node?(assignment.node)
60
+ assignment_node = assignment.node
61
+ next if assignment.used? || part_of_ignored_node?(assignment_node)
62
62
 
63
63
  message = message_for_useless_assignment(assignment)
64
64
  range = offense_range(assignment)
65
65
 
66
66
  add_offense(range, message: message) do |corrector|
67
- autocorrect(corrector, assignment) unless sequential_assignment?(assignment.node)
67
+ # In cases like `x = 1, y = 2`, where removing a variable would cause a syntax error,
68
+ # and where changing `x ||= 1` to `x = 1` would cause `NameError`,
69
+ # the autocorrect will be skipped, even if the variable is unused.
70
+ if sequential_assignment?(assignment_node) || assignment_node.parent&.or_asgn_type?
71
+ next
72
+ end
73
+
74
+ autocorrect(corrector, assignment)
68
75
  end
69
76
 
70
- ignore_node(assignment.node) if chained_assignment?(assignment.node)
77
+ ignore_node(assignment_node) if chained_assignment?(assignment_node)
71
78
  end
72
79
  end
73
- # rubocop:enable Metrics/AbcSize
80
+ # rubocop:enable Metrics/AbcSize, Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
74
81
 
75
82
  def message_for_useless_assignment(assignment)
76
83
  variable = assignment.variable
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Certain numeric operations have no impact, being:
7
+ # Adding or subtracting 0, multiplying or dividing by 1 or raising to the power of 1.
8
+ # These are probably leftover from debugging, or are mistakes.
9
+ #
10
+ # @example
11
+ #
12
+ # # bad
13
+ # x + 0
14
+ # x - 0
15
+ # x * 1
16
+ # x / 1
17
+ # x ** 1
18
+ #
19
+ # # good
20
+ # x
21
+ #
22
+ # # bad
23
+ # x += 0
24
+ # x -= 0
25
+ # x *= 1
26
+ # x /= 1
27
+ # x **= 1
28
+ #
29
+ # # good
30
+ # x = x
31
+ #
32
+ class UselessNumericOperation < Base
33
+ extend AutoCorrector
34
+ MSG = 'Do not apply inconsequential numeric operations to variables.'
35
+ RESTRICT_ON_SEND = %i[+ - * / **].freeze
36
+
37
+ # @!method useless_operation?(node)
38
+ def_node_matcher :useless_operation?, '(send (send nil? $_) $_ (int $_))'
39
+
40
+ # @!method useless_abbreviated_assignment?(node)
41
+ def_node_matcher :useless_abbreviated_assignment?, '(op-asgn (lvasgn $_) $_ (int $_))'
42
+
43
+ def on_send(node)
44
+ return unless useless_operation?(node)
45
+
46
+ variable, operation, number = useless_operation?(node)
47
+ return unless useless?(operation, number)
48
+
49
+ add_offense(node) do |corrector|
50
+ corrector.replace(node, variable)
51
+ end
52
+ end
53
+
54
+ def on_op_asgn(node)
55
+ return unless useless_abbreviated_assignment?(node)
56
+
57
+ variable, operation, number = useless_abbreviated_assignment?(node)
58
+ return unless useless?(operation, number)
59
+
60
+ add_offense(node) do |corrector|
61
+ corrector.replace(node, "#{variable} = #{variable}")
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def useless?(operation, number)
68
+ if number.zero?
69
+ true if %i[+ -].include?(operation)
70
+ elsif number == 1
71
+ true if %i[* / **].include?(operation)
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -66,7 +66,6 @@ module RuboCop
66
66
  BINARY_OPERATORS = %i[* / % + - == === != < > <= >= <=>].freeze
67
67
  UNARY_OPERATORS = %i[+@ -@ ~ !].freeze
68
68
  OPERATORS = (BINARY_OPERATORS + UNARY_OPERATORS).freeze
69
- VOID_CONTEXT_TYPES = %i[def for block].freeze
70
69
  NONMUTATING_METHODS_WITH_BANG_VERSION = %i[capitalize chomp chop compact
71
70
  delete_prefix delete_suffix downcase
72
71
  encode flatten gsub lstrip merge next
@@ -86,7 +85,6 @@ module RuboCop
86
85
  check_void_op(node.body) { node.method?(:each) }
87
86
  check_expression(node.body)
88
87
  end
89
-
90
88
  alias on_numblock on_block
91
89
 
92
90
  def on_begin(node)
@@ -94,6 +92,10 @@ module RuboCop
94
92
  end
95
93
  alias on_kwbegin on_begin
96
94
 
95
+ def on_ensure(node)
96
+ check_ensure(node)
97
+ end
98
+
97
99
  private
98
100
 
99
101
  def check_begin(node)
@@ -193,12 +195,24 @@ module RuboCop
193
195
  end
194
196
  end
195
197
 
198
+ def check_ensure(node)
199
+ return unless (body = node.body)
200
+ # NOTE: the `begin` node case is already handled via `on_begin`
201
+ return if body.begin_type?
202
+
203
+ check_void_op(body) do
204
+ block_node = node.each_ancestor(:block).first
205
+ block_node&.method?(:each)
206
+ end
207
+
208
+ check_expression(body)
209
+ end
210
+
196
211
  def in_void_context?(node)
197
212
  parent = node.parent
198
-
199
213
  return false unless parent && parent.children.last == node
200
214
 
201
- VOID_CONTEXT_TYPES.include?(parent.type) && parent.void_context?
215
+ parent.respond_to?(:void_context?) && parent.void_context?
202
216
  end
203
217
 
204
218
  def autocorrect_void_op(corrector, node)
@@ -231,15 +245,23 @@ module RuboCop
231
245
  def entirely_literal?(node)
232
246
  case node.type
233
247
  when :array
234
- node.each_value.all? { |value| entirely_literal?(value) }
248
+ all_values_entirely_literal?(node)
235
249
  when :hash
236
- return false unless node.each_key.all? { |key| entirely_literal?(key) }
237
-
238
- node.each_value.all? { |value| entirely_literal?(value) }
250
+ all_keys_entirely_literal?(node) && all_values_entirely_literal?(node)
251
+ when :send, :csend
252
+ node.method?(:freeze) && node.receiver && entirely_literal?(node.receiver)
239
253
  else
240
254
  node.literal?
241
255
  end
242
256
  end
257
+
258
+ def all_keys_entirely_literal?(node)
259
+ node.each_key.all? { |key| entirely_literal?(key) }
260
+ end
261
+
262
+ def all_values_entirely_literal?(node)
263
+ node.each_value.all? { |value| entirely_literal?(value) }
264
+ end
243
265
  end
244
266
  end
245
267
  end
@@ -9,8 +9,9 @@ module RuboCop
9
9
  # The cop can be configured to ignore blocks passed to certain methods.
10
10
  #
11
11
  # You can set constructs you want to fold with `CountAsOne`.
12
- # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
13
- # will be counted as one line regardless of its actual size.
12
+ #
13
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'.
14
+ # Each construct will be counted as one line regardless of its actual size.
14
15
  #
15
16
  # NOTE: This cop does not apply for `Struct` definitions.
16
17
  #
@@ -18,7 +19,7 @@ module RuboCop
18
19
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
19
20
  # instead. By default, there are no methods to allowed.
20
21
  #
21
- # @example CountAsOne: ['array', 'heredoc', 'method_call']
22
+ # @example CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
22
23
  #
23
24
  # something do
24
25
  # array = [ # +1
@@ -26,7 +27,7 @@ module RuboCop
26
27
  # 2
27
28
  # ]
28
29
  #
29
- # hash = { # +3
30
+ # hash = { # +1
30
31
  # key: 'value'
31
32
  # }
32
33
  #
@@ -39,7 +40,7 @@ module RuboCop
39
40
  # 1,
40
41
  # 2
41
42
  # )
42
- # end # 6 points
43
+ # end # 4 points
43
44
  #
44
45
  class BlockLength < Base
45
46
  include CodeLength
@@ -8,12 +8,13 @@ module RuboCop
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
10
  # You can set constructs you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
- # will be counted as one line regardless of its actual size.
11
+ #
12
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'.
13
+ # Each construct will be counted as one line regardless of its actual size.
13
14
  #
14
15
  # NOTE: This cop also applies for `Struct` definitions.
15
16
  #
16
- # @example CountAsOne: ['array', 'heredoc', 'method_call']
17
+ # @example CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
17
18
  #
18
19
  # class Foo
19
20
  # ARRAY = [ # +1
@@ -21,7 +22,7 @@ module RuboCop
21
22
  # 2
22
23
  # ]
23
24
  #
24
- # HASH = { # +3
25
+ # HASH = { # +1
25
26
  # key: 'value'
26
27
  # }
27
28
  #
@@ -34,7 +35,7 @@ module RuboCop
34
35
  # 1,
35
36
  # 2
36
37
  # )
37
- # end # 6 points
38
+ # end # 4 points
38
39
  #
39
40
  class ClassLength < Base
40
41
  include CodeLength
@@ -8,15 +8,16 @@ module RuboCop
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
10
  # You can set constructs you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
- # will be counted as one line regardless of its actual size.
11
+ #
12
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'.
13
+ # Each construct will be counted as one line regardless of its actual size.
13
14
  #
14
15
  # NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is
15
16
  # deprecated and only kept for backwards compatibility.
16
17
  # Please use `AllowedMethods` and `AllowedPatterns` instead.
17
18
  # By default, there are no methods to allowed.
18
19
  #
19
- # @example CountAsOne: ['array', 'heredoc', 'method_call']
20
+ # @example CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
20
21
  #
21
22
  # def m
22
23
  # array = [ # +1
@@ -24,7 +25,7 @@ module RuboCop
24
25
  # 2
25
26
  # ]
26
27
  #
27
- # hash = { # +3
28
+ # hash = { # +1
28
29
  # key: 'value'
29
30
  # }
30
31
  #
@@ -37,7 +38,7 @@ module RuboCop
37
38
  # 1,
38
39
  # 2
39
40
  # )
40
- # end # 6 points
41
+ # end # 4 points
41
42
  #
42
43
  class MethodLength < Base
43
44
  include CodeLength
@@ -8,10 +8,11 @@ module RuboCop
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
10
  # You can set constructs you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
- # will be counted as one line regardless of its actual size.
13
11
  #
14
- # @example CountAsOne: ['array', 'heredoc', 'method_call']
12
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'.
13
+ # Each construct will be counted as one line regardless of its actual size.
14
+ #
15
+ # @example CountAsOne: ['array', 'hash', 'heredoc', 'method_call']
15
16
  #
16
17
  # module M
17
18
  # ARRAY = [ # +1
@@ -19,7 +20,7 @@ module RuboCop
19
20
  # 2
20
21
  # ]
21
22
  #
22
- # HASH = { # +3
23
+ # HASH = { # +1
23
24
  # key: 'value'
24
25
  # }
25
26
  #
@@ -32,7 +33,7 @@ module RuboCop
32
33
  # 1,
33
34
  # 2
34
35
  # )
35
- # end # 6 points
36
+ # end # 4 points
36
37
  #
37
38
  class ModuleLength < Base
38
39
  include CodeLength
@@ -4,8 +4,6 @@ module RuboCop
4
4
  module Cop
5
5
  # Representation of an annotation comment in source code (eg. `# TODO: blah blah blah`).
6
6
  class AnnotationComment
7
- extend Forwardable
8
-
9
7
  attr_reader :comment, :margin, :keyword, :colon, :space, :note
10
8
 
11
9
  # @param [Parser::Source::Comment] comment
@@ -20,7 +20,7 @@ module RuboCop
20
20
 
21
21
  def frozen_string_literal?(node)
22
22
  frozen_string = if target_ruby_version >= 3.0
23
- uninterpolated_string?(node) || frozen_heredoc?(node)
23
+ uninterpolated_string?(node) || uninterpolated_heredoc?(node)
24
24
  else
25
25
  FROZEN_STRING_LITERAL_TYPES_RUBY27.include?(node.type)
26
26
  end
@@ -32,29 +32,39 @@ module RuboCop
32
32
  node.str_type? || (node.dstr_type? && node.each_descendant(:begin).none?)
33
33
  end
34
34
 
35
- def frozen_heredoc?(node)
35
+ def uninterpolated_heredoc?(node)
36
36
  return false unless node.dstr_type? && node.heredoc?
37
37
 
38
38
  node.children.all?(&:str_type?)
39
39
  end
40
+ alias frozen_heredoc? uninterpolated_heredoc?
40
41
 
41
42
  def frozen_string_literals_enabled?
42
43
  ruby_version = processed_source.ruby_version
43
44
  return false unless ruby_version
44
45
 
46
+ # Check if a magic string literal comment specifies what to do
47
+ magic_comments = leading_comment_lines.filter_map { |line| MagicComment.parse(line) }
48
+ if (literal_magic_comment = magic_comments.find(&:frozen_string_literal_specified?))
49
+ return literal_magic_comment.frozen_string_literal?
50
+ end
51
+
45
52
  # TODO: Ruby officially abandon making frozen string literals default
46
53
  # for Ruby 3.0.
47
54
  # https://bugs.ruby-lang.org/issues/11473#note-53
48
- # Whether frozen string literals will be the default after Ruby 3.0
49
- # or not is still unclear as of January 2019.
55
+ # Whether frozen string literals will be the default after Ruby 4.0
56
+ # or not is still unclear as of July 2024.
50
57
  # It may be necessary to add this code in the future.
51
58
  #
52
- # return true if ruby_version >= 3.1
59
+ # return ruby_version >= 4.0 if string_literals_frozen_by_default?.nil?
53
60
  #
54
- # And the above `ruby_version >= 3.1` is undecided whether it will be
55
- # Ruby 3.1, 3.2, 4.0 or others.
56
- # See https://bugs.ruby-lang.org/issues/8976#note-41 for details.
57
- leading_comment_lines.any? { |line| MagicComment.parse(line).frozen_string_literal? }
61
+ # And the above `ruby_version >= 4.0` is undecided whether it will be
62
+ # Ruby 4.0 or others.
63
+ # See https://bugs.ruby-lang.org/issues/20205 for details.
64
+ # For now, offer a configuration value to override behavior is using RUBYOPT.
65
+ return false if string_literals_frozen_by_default?.nil?
66
+
67
+ string_literals_frozen_by_default?
58
68
  end
59
69
 
60
70
  def frozen_string_literals_disabled?
@@ -91,8 +91,13 @@ module RuboCop
91
91
  end
92
92
 
93
93
  def uri_regexp
94
- @uri_regexp ||=
95
- URI::DEFAULT_PARSER.make_regexp(config.for_cop('Layout/LineLength')['URISchemes'])
94
+ @uri_regexp ||= begin
95
+ # Ruby 3.4 changes the default parser to RFC3986 which warns on make_regexp.
96
+ # Additionally, the RFC2396_PARSER alias is only available on 3.4 for now.
97
+ # Extra info at https://github.com/ruby/uri/issues/118
98
+ parser = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
99
+ parser.make_regexp(config.for_cop('Layout/LineLength')['URISchemes'])
100
+ end
96
101
  end
97
102
 
98
103
  def valid_uri?(uri_ish_string)
@@ -16,6 +16,18 @@ module RuboCop
16
16
  !/" | \\[^'\\] | \#[@{$]/x.match?(src)
17
17
  end
18
18
  end
19
+
20
+ def preferred_string_literal
21
+ enforce_double_quotes? ? '""' : "''"
22
+ end
23
+
24
+ def enforce_double_quotes?
25
+ string_literals_config['EnforcedStyle'] == 'double_quotes'
26
+ end
27
+
28
+ def string_literals_config
29
+ config.for_cop('Style/StringLiterals')
30
+ end
19
31
  end
20
32
  end
21
33
  end
@@ -40,6 +40,7 @@ module RuboCop
40
40
  MSG_WRITER = 'Do not prefix writer method names with `set_`.'
41
41
 
42
42
  def on_def(node)
43
+ return unless proper_attribute_name?(node)
43
44
  return unless bad_reader_name?(node) || bad_writer_name?(node)
44
45
 
45
46
  message = message(node)
@@ -58,6 +59,10 @@ module RuboCop
58
59
  end
59
60
  end
60
61
 
62
+ def proper_attribute_name?(node)
63
+ !node.method_name.to_s.end_with?('!', '?', '=')
64
+ end
65
+
61
66
  def bad_reader_name?(node)
62
67
  node.method_name.to_s.start_with?('get_') && !node.arguments?
63
68
  end
@@ -70,7 +70,7 @@ module RuboCop
70
70
  # def_node_matcher(:is_even) { |value| }
71
71
  #
72
72
  # # good
73
- # # def_node_matcher(:even?) { |value| }
73
+ # def_node_matcher(:even?) { |value| }
74
74
  #
75
75
  class PredicateName < Base
76
76
  include AllowedMethods
@@ -80,7 +80,7 @@ module RuboCop
80
80
  def alias_method_possible?(node)
81
81
  scope_type(node) != :instance_eval &&
82
82
  node.children.none?(&:gvar_type?) &&
83
- node&.parent&.type != :def
83
+ node.each_ancestor(:def).none?
84
84
  end
85
85
 
86
86
  def add_offense_for_args(node, &block)