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
@@ -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)