rubocop 1.66.1 → 1.67.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +15 -6
  4. data/config/internal_affairs.yml +11 -0
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
  6. data/lib/rubocop/cli/command/lsp.rb +2 -2
  7. data/lib/rubocop/config_loader_resolver.rb +3 -3
  8. data/lib/rubocop/config_validator.rb +2 -1
  9. data/lib/rubocop/cop/base.rb +6 -2
  10. data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
  11. data/lib/rubocop/cop/cop.rb +8 -0
  12. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  13. data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
  14. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
  15. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
  16. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
  17. data/lib/rubocop/cop/internal_affairs.rb +16 -0
  18. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
  19. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  21. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
  22. data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
  23. data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
  24. data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
  25. data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
  26. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  27. data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
  28. data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
  29. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  30. data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
  31. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
  32. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
  33. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
  34. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
  35. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
  36. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
  37. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  38. data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
  39. data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
  40. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
  41. data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
  42. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  43. data/lib/rubocop/cop/offense.rb +2 -2
  44. data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
  45. data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
  46. data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
  47. data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
  48. data/lib/rubocop/cop/style/collection_compact.rb +10 -10
  49. data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
  50. data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
  51. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
  52. data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
  53. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  54. data/lib/rubocop/cop/style/guard_clause.rb +1 -1
  55. data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
  56. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  57. data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
  58. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -3
  59. data/lib/rubocop/cop/style/lambda.rb +1 -1
  60. data/lib/rubocop/cop/style/map_into_array.rb +53 -7
  61. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
  62. data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
  63. data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
  64. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  65. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
  66. data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
  67. data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
  68. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  69. data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
  70. data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
  71. data/lib/rubocop/cop/style/require_order.rb +1 -1
  72. data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
  73. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
  74. data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
  75. data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
  76. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  77. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  78. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  79. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  80. data/lib/rubocop/cop/team.rb +8 -1
  81. data/lib/rubocop/cop/util.rb +1 -1
  82. data/lib/rubocop/cops_documentation_generator.rb +73 -34
  83. data/lib/rubocop/file_finder.rb +9 -4
  84. data/lib/rubocop/lsp/runtime.rb +2 -0
  85. data/lib/rubocop/lsp/server.rb +0 -1
  86. data/lib/rubocop/rspec/expect_offense.rb +1 -0
  87. data/lib/rubocop/runner.rb +3 -0
  88. data/lib/rubocop/server/cache.rb +6 -1
  89. data/lib/rubocop/server/core.rb +1 -0
  90. data/lib/rubocop/target_ruby.rb +12 -12
  91. data/lib/rubocop/version.rb +3 -1
  92. data/lib/rubocop/yaml_duplication_checker.rb +20 -27
  93. data/lib/rubocop.rb +2 -0
  94. metadata +10 -8
@@ -91,7 +91,7 @@ module RuboCop
91
91
  next if !argument || argument.hash_type?
92
92
 
93
93
  add_offense(
94
- argument.source_range, message: message(i, argument.source)
94
+ argument, message: message(i, argument.source)
95
95
  ) do |corrector|
96
96
  autocorrect(corrector, node)
97
97
  end
@@ -18,7 +18,7 @@ module RuboCop
18
18
  # # good - using BigDecimal
19
19
  # x.to_d == 0.1.to_d
20
20
  #
21
- # # good - comparing against zero
21
+ # # good - comparing against zero
22
22
  # x == 0.0
23
23
  # x != 0.0
24
24
  #
@@ -28,7 +28,7 @@ module RuboCop
28
28
  FOR_METHOD = ' Or, if they were intended to be separate method ' \
29
29
  'arguments, separate them with a comma.'
30
30
 
31
- # rubocop:disable Metrics/AbcSize
31
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
32
32
  def on_dstr(node)
33
33
  each_bad_cons(node) do |lhs_node, rhs_node|
34
34
  range = lhs_node.source_range.join(rhs_node.source_range)
@@ -40,13 +40,19 @@ module RuboCop
40
40
  end
41
41
 
42
42
  add_offense(range, message: message) do |corrector|
43
- range = lhs_node.source_range.end.join(rhs_node.source_range.begin)
43
+ if lhs_node.value == ''
44
+ corrector.remove(lhs_node)
45
+ elsif rhs_node.value == ''
46
+ corrector.remove(rhs_node)
47
+ else
48
+ range = lhs_node.source_range.end.join(rhs_node.source_range.begin)
44
49
 
45
- corrector.replace(range, ' + ')
50
+ corrector.replace(range, ' + ')
51
+ end
46
52
  end
47
53
  end
48
54
  end
49
- # rubocop:enable Metrics/AbcSize
55
+ # rubocop:enable Metrics/AbcSize, Metrics/MethodLength, Metrics/PerceivedComplexity
50
56
 
51
57
  private
52
58
 
@@ -29,25 +29,16 @@ module RuboCop
29
29
 
30
30
  MSG = '`it` calls without arguments will refer to the first block param in Ruby 3.4; ' \
31
31
  'use `it()` or `self.it`.'
32
+ RESTRICT_ON_SEND = %i[it].freeze
32
33
 
33
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
34
- return unless (body = node.body)
35
- return unless node.arguments.empty_and_without_delimiters?
34
+ def on_send(node)
35
+ return unless (block_node = node.each_ancestor(:block).first)
36
+ return unless block_node.arguments.empty_and_without_delimiters?
36
37
 
37
- if body.send_type? && deprecated_it_method?(body)
38
- add_offense(body)
39
- else
40
- body.each_descendant(:send).each do |send_node|
41
- next unless deprecated_it_method?(send_node)
42
-
43
- add_offense(send_node)
44
- end
45
- end
38
+ add_offense(node) if deprecated_it_method?(node)
46
39
  end
47
40
 
48
41
  def deprecated_it_method?(node)
49
- return false unless node.method?(:it)
50
-
51
42
  !node.receiver && node.arguments.empty? && !node.parenthesized? && !node.block_literal?
52
43
  end
53
44
  end
@@ -30,6 +30,8 @@ module RuboCop
30
30
  # interpolation should not be removed if the expanded value
31
31
  # contains a space character.
32
32
  expanded_value = autocorrected_value(final_node)
33
+ expanded_value = handle_special_regexp_chars(begin_node, expanded_value)
34
+
33
35
  return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
34
36
 
35
37
  add_offense(final_node) do |corrector|
@@ -77,6 +79,27 @@ module RuboCop
77
79
  end
78
80
  # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
79
81
 
82
+ def handle_special_regexp_chars(begin_node, value)
83
+ parent_node = begin_node.parent
84
+
85
+ return value unless parent_node.regexp_type? && parent_node.slash_literal? && value['/']
86
+
87
+ # When a literal string containing a forward slash preceded by backslashes
88
+ # is interpolated inside a regexp, the number of resultant backslashes in the
89
+ # compiled Regexp is `(2(n+1) / 4)+1`, where `n` is the number of backslashes
90
+ # inside the interpolation.
91
+ # ie. 0-2 backslashes is compiled to 1, 3-6 is compiled to 3, etc.
92
+ # This maintains that same behavior in order to ensure the Regexp behavior
93
+ # does not change upon removing the interpolation.
94
+ value.gsub(%r{(\\*)/}) do
95
+ backslashes = Regexp.last_match[1]
96
+ backslash_count = backslashes.length
97
+ needed_backslashes = (2 * ((backslash_count + 1) / 4)) + 1
98
+
99
+ "#{'\\' * needed_backslashes}/"
100
+ end
101
+ end
102
+
80
103
  def autocorrected_value_for_string(node)
81
104
  if node.source.start_with?("'", '%q')
82
105
  node.children.last.inspect[1..-2]
@@ -150,7 +173,7 @@ module RuboCop
150
173
 
151
174
  def ends_heredoc_line?(node)
152
175
  grandparent = node.parent.parent
153
- return false unless grandparent&.dstr_type? && grandparent&.heredoc?
176
+ return false unless grandparent&.dstr_type? && grandparent.heredoc?
154
177
 
155
178
  line = processed_source.lines[node.last_line - 1]
156
179
  line.size == node.loc.last_column + 1
@@ -161,7 +184,7 @@ module RuboCop
161
184
  return false unless parent.dstr_type? || parent.dsym_type?
162
185
 
163
186
  grandparent = parent.parent
164
- grandparent&.array_type? && grandparent&.percent_literal?
187
+ grandparent&.array_type? && grandparent.percent_literal?
165
188
  end
166
189
  end
167
190
  end
@@ -37,9 +37,7 @@ module RuboCop
37
37
  private
38
38
 
39
39
  def valid_context?(node)
40
- unless node.arguments.one? && first_argument_starts_with_left_parenthesis?(node)
41
- return true
42
- end
40
+ return true unless node.arguments.one? && node.first_argument.parenthesized_call?
43
41
  return true if first_argument_block_type?(node.first_argument)
44
42
 
45
43
  node.operator_method? || node.setter_method? || chained_calls?(node) ||
@@ -51,11 +49,12 @@ module RuboCop
51
49
  end
52
50
 
53
51
  def valid_first_argument?(first_arg)
54
- first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg)
52
+ first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg) ||
53
+ compound_range?(first_arg)
55
54
  end
56
55
 
57
- def first_argument_starts_with_left_parenthesis?(node)
58
- node.first_argument.source.start_with?('(')
56
+ def compound_range?(first_arg)
57
+ first_arg.range_type? && first_arg.parenthesized_call?
59
58
  end
60
59
 
61
60
  def chained_calls?(node)
@@ -131,7 +131,7 @@ module RuboCop
131
131
  private
132
132
 
133
133
  def assume_receiver_instance_exists?(receiver)
134
- return true if receiver.const_type? && !receiver.source.match?(SNAKE_CASE)
134
+ return true if receiver.const_type? && !receiver.short_name.match?(SNAKE_CASE)
135
135
 
136
136
  receiver.literal? && !receiver.nil_type?
137
137
  end
@@ -3,88 +3,152 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Check to make sure that if safe navigation is used for a method
7
- # call in an `&&` or `||` condition that safe navigation is used for all
8
- # method calls on that same object.
6
+ # Check to make sure that if safe navigation is used in an `&&` or `||` condition,
7
+ # consistent and appropriate safe navigation, without excess or deficiency,
8
+ # is used for all method calls on the same object.
9
9
  #
10
10
  # @example
11
11
  # # bad
12
- # foo&.bar && foo.baz
12
+ # foo&.bar && foo&.baz
13
13
  #
14
- # # bad
15
- # foo.bar || foo&.baz
14
+ # # good
15
+ # foo&.bar && foo.baz
16
16
  #
17
17
  # # bad
18
- # foo&.bar && (foobar.baz || foo.baz)
18
+ # foo.bar && foo&.baz
19
19
  #
20
20
  # # good
21
21
  # foo.bar && foo.baz
22
22
  #
23
+ # # bad
24
+ # foo&.bar || foo.baz
25
+ #
23
26
  # # good
24
27
  # foo&.bar || foo&.baz
25
28
  #
29
+ # # bad
30
+ # foo.bar || foo&.baz
31
+ #
26
32
  # # good
33
+ # foo.bar || foo.baz
34
+ #
35
+ # # bad
27
36
  # foo&.bar && (foobar.baz || foo&.baz)
28
37
  #
38
+ # # good
39
+ # foo&.bar && (foobar.baz || foo.baz)
40
+ #
29
41
  class SafeNavigationConsistency < Base
30
- include IgnoredNode
31
42
  include NilMethods
32
43
  extend AutoCorrector
33
44
 
34
- MSG = 'Ensure that safe navigation is used consistently inside of `&&` and `||`.'
45
+ USE_DOT_MSG = 'Use `.` instead of unnecessary `&.`.'
46
+ USE_SAFE_NAVIGATION_MSG = 'Use `&.` for consistency with safe navigation.'
47
+
48
+ def on_and(node)
49
+ all_operands = collect_operands(node, [])
50
+ operand_groups = all_operands.group_by { |operand| receiver_name_as_key(operand, +'') }
51
+
52
+ operand_groups.each_value do |grouped_operands|
53
+ next unless (dot_op, begin_of_rest_operands = find_consistent_parts(grouped_operands))
35
54
 
36
- def on_csend(node)
37
- return unless node.parent&.operator_keyword?
55
+ rest_operands = grouped_operands[begin_of_rest_operands..]
56
+ rest_operands.each do |operand|
57
+ next if already_appropriate_call?(operand, dot_op)
38
58
 
39
- check(node)
59
+ register_offense(operand, dot_op)
60
+ end
61
+ end
40
62
  end
63
+ alias on_or on_and
41
64
 
42
- def check(node)
43
- ancestor = top_conditional_ancestor(node)
44
- conditions = ancestor.conditions
45
- safe_nav_receiver = node.receiver
65
+ private
46
66
 
47
- method_calls = conditions.select(&:send_type?)
48
- unsafe_method_calls = unsafe_method_calls(method_calls, safe_nav_receiver)
67
+ def collect_operands(node, operand_nodes)
68
+ operand_nodes(node.lhs, operand_nodes)
69
+ operand_nodes(node.rhs, operand_nodes)
49
70
 
50
- unsafe_method_calls.each do |unsafe_method_call|
51
- location = location(node, unsafe_method_call)
71
+ operand_nodes
72
+ end
52
73
 
53
- add_offense(location) { |corrector| autocorrect(corrector, unsafe_method_call) }
74
+ def receiver_name_as_key(method, fully_receivers)
75
+ if method.parent.call_type?
76
+ receiver(method.parent, fully_receivers)
77
+ else
78
+ fully_receivers << method.receiver&.source.to_s
79
+ end
80
+ end
54
81
 
55
- ignore_node(unsafe_method_call)
82
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
+ def find_consistent_parts(grouped_operands)
84
+ csend_in_and, csend_in_or, send_in_and, send_in_or = most_left_indices(grouped_operands)
85
+
86
+ if csend_in_and
87
+ ['.', (send_in_and ? [send_in_and, csend_in_and].min : csend_in_and) + 1]
88
+ elsif send_in_or && csend_in_or
89
+ send_in_or < csend_in_or ? ['.', send_in_or + 1] : ['&.', csend_in_or + 1]
90
+ elsif send_in_and && csend_in_or && send_in_and < csend_in_or
91
+ ['.', csend_in_or]
56
92
  end
57
93
  end
94
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
58
95
 
59
- private
96
+ def already_appropriate_call?(operand, dot_op)
97
+ (operand.safe_navigation? && dot_op == '&.') || (operand.dot? && dot_op == '.')
98
+ end
99
+
100
+ def register_offense(operand, dot_operator)
101
+ offense_range = operand.operator_method? ? operand : operand.loc.dot
102
+ message = dot_operator == '.' ? USE_DOT_MSG : USE_SAFE_NAVIGATION_MSG
60
103
 
61
- def autocorrect(corrector, node)
62
- return unless node.dot?
104
+ add_offense(offense_range, message: message) do |corrector|
105
+ next if operand.operator_method?
63
106
 
64
- corrector.insert_before(node.loc.dot, '&')
107
+ corrector.replace(operand.loc.dot, dot_operator)
108
+ end
65
109
  end
66
110
 
67
- def location(node, unsafe_method_call)
68
- node.source_range.join(unsafe_method_call.source_range)
111
+ def operand_nodes(operand, operand_nodes)
112
+ if operand.operator_keyword?
113
+ collect_operands(operand, operand_nodes)
114
+ elsif operand.call_type?
115
+ operand_nodes << operand
116
+ end
69
117
  end
70
118
 
71
- def top_conditional_ancestor(node)
72
- parent = node.parent
73
- unless parent &&
74
- (parent.operator_keyword? ||
75
- (parent.begin_type? && parent.parent && parent.parent.operator_keyword?))
76
- return node
119
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
120
+ def most_left_indices(grouped_operands)
121
+ indices = { csend_in_and: nil, csend_in_or: nil, send_in_and: nil, send_in_or: nil }
122
+
123
+ grouped_operands.each_with_index do |operand, index|
124
+ indices[:csend_in_and] ||= index if operand_in_and?(operand) && operand.csend_type?
125
+ indices[:csend_in_or] ||= index if operand_in_or?(operand) && operand.csend_type?
126
+ indices[:send_in_and] ||= index if operand_in_and?(operand) && !nilable?(operand)
127
+ indices[:send_in_or] ||= index if operand_in_or?(operand) && !nilable?(operand)
77
128
  end
78
129
 
79
- top_conditional_ancestor(parent)
130
+ indices.values
80
131
  end
132
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
81
133
 
82
- def unsafe_method_calls(method_calls, safe_nav_receiver)
83
- method_calls.select do |method_call|
84
- safe_nav_receiver == method_call.receiver &&
85
- !nil_methods.include?(method_call.method_name) &&
86
- !ignored_node?(method_call)
87
- end
134
+ def operand_in_and?(node)
135
+ return true if node.parent.and_type?
136
+
137
+ parent = node.parent.parent while node.parent.begin_type?
138
+
139
+ parent&.and_type?
140
+ end
141
+
142
+ def operand_in_or?(node)
143
+ return true if node.parent.or_type?
144
+
145
+ parent = node.parent.parent while node.parent.begin_type?
146
+
147
+ parent&.or_type?
148
+ end
149
+
150
+ def nilable?(node)
151
+ node.csend_type? || nil_methods.include?(node.method_name)
88
152
  end
89
153
  end
90
154
  end
@@ -141,7 +141,7 @@ module RuboCop
141
141
  end
142
142
 
143
143
  def in_percent_literal_array?(node)
144
- node.parent&.array_type? && node.parent&.percent_literal?
144
+ node.parent&.array_type? && node.parent.percent_literal?
145
145
  end
146
146
 
147
147
  def correct_hash_key(node)
@@ -3,29 +3,47 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Identifies places where `URI.regexp` is obsolete and should
7
- # not be used. Instead, use `URI::DEFAULT_PARSER.make_regexp`.
6
+ # Identifies places where `URI.regexp` is obsolete and should not be used.
7
+ #
8
+ # For Ruby 3.3 or lower, use `URI::DEFAULT_PARSER.make_regexp`.
9
+ # For Ruby 3.4 or higher, use `URI::RFC2396_PARSER.make_regexp`.
10
+ #
11
+ # NOTE: If you need to support both Ruby 3.3 and lower as well as Ruby 3.4 and higher,
12
+ # consider manually changing the code as follows:
13
+ #
14
+ # [source,ruby]
15
+ # ----
16
+ # defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::DEFAULT_PARSER
17
+ # ----
8
18
  #
9
19
  # @example
10
20
  # # bad
11
21
  # URI.regexp('http://example.com')
12
22
  #
13
- # # good
23
+ # # good - Ruby 3.3 or lower
14
24
  # URI::DEFAULT_PARSER.make_regexp('http://example.com')
15
25
  #
26
+ # # good - Ruby 3.4 or higher
27
+ # URI::RFC2396_PARSER.make_regexp('http://example.com')
28
+ #
16
29
  class UriRegexp < Base
17
30
  extend AutoCorrector
18
31
 
19
32
  MSG = '`%<current>s` is obsolete and should not be used. Instead, use `%<preferred>s`.'
20
- URI_CONSTANTS = ['URI', '::URI'].freeze
21
33
  RESTRICT_ON_SEND = %i[regexp].freeze
22
34
 
35
+ # @!method uri_constant?(node)
36
+ def_node_matcher :uri_constant?, <<~PATTERN
37
+ (const {cbase nil?} :URI)
38
+ PATTERN
39
+
23
40
  def on_send(node)
24
- return unless node.receiver
25
- return unless URI_CONSTANTS.include?(node.receiver.source)
41
+ return unless uri_constant?(node.receiver)
26
42
 
43
+ parser = target_ruby_version >= 3.4 ? 'RFC2396_PARSER' : 'DEFAULT_PARSER'
27
44
  argument = node.first_argument ? "(#{node.first_argument.source})" : ''
28
- preferred_method = "#{node.receiver.source}::DEFAULT_PARSER.make_regexp#{argument}"
45
+
46
+ preferred_method = "#{node.receiver.source}::#{parser}.make_regexp#{argument}"
29
47
  message = format(MSG, current: node.source, preferred: preferred_method)
30
48
 
31
49
  add_offense(node.loc.selector, message: message) do |corrector|
@@ -15,7 +15,7 @@ module RuboCop
15
15
  parent = node.parent
16
16
 
17
17
  parent&.send_type? && parent.arguments.include?(node) &&
18
- !parent.parenthesized? && parent&.block_literal?
18
+ !parent.parenthesized? && parent.block_literal?
19
19
  end
20
20
 
21
21
  # Override to determine values that are invalid in a percent array
@@ -5,7 +5,6 @@ module RuboCop
5
5
  # Common functionality for modifier cops.
6
6
  module StatementModifier
7
7
  include LineLengthHelp
8
- include RangeHelp
9
8
 
10
9
  private
11
10
 
@@ -65,7 +64,9 @@ module RuboCop
65
64
  end
66
65
 
67
66
  def method_source(if_body)
68
- range_between(if_body.source_range.begin_pos, if_body.loc.selector.end_pos).source
67
+ end_range = if_body.implicit_call? ? if_body.loc.dot.end : if_body.loc.selector
68
+
69
+ if_body.source_range.begin.join(end_range).source
69
70
  end
70
71
 
71
72
  def first_line_comment(node)
@@ -116,9 +116,9 @@ module RuboCop
116
116
  add_offense(range, message: create_message(word)) do |corrector|
117
117
  suggestions = find_flagged_term(word)['Suggestions']
118
118
 
119
- next unless suggestions.is_a?(String)
120
-
121
- corrector.replace(range, suggestions)
119
+ if (preferred_term = preferred_sole_term(suggestions))
120
+ corrector.replace(range, preferred_term)
121
+ end
122
122
  end
123
123
  end
124
124
  end
@@ -157,6 +157,15 @@ module RuboCop
157
157
  set_regexes(flagged_term_strings, allowed_strings)
158
158
  end
159
159
 
160
+ def preferred_sole_term(suggestions)
161
+ case suggestions
162
+ when Array
163
+ suggestions.one? && preferred_sole_term(suggestions.first)
164
+ when String
165
+ suggestions
166
+ end
167
+ end
168
+
160
169
  def extract_regexp(term, term_definition)
161
170
  return term_definition['Regex'] if term_definition['Regex']
162
171
  return /(?:\b|(?<=[\W_]))#{term}(?:\b|(?=[\W_]))/ if term_definition['WholeWord']
@@ -88,7 +88,7 @@ module RuboCop
88
88
  next if allowed_method_name?(method_name.to_s, prefix)
89
89
 
90
90
  add_offense(
91
- node.first_argument.source_range,
91
+ node.first_argument,
92
92
  message: message(method_name, expected_name(method_name.to_s, prefix))
93
93
  )
94
94
  end
@@ -97,8 +97,8 @@ module RuboCop
97
97
  # @!attribute [r] correctable?
98
98
  #
99
99
  # @return [Boolean]
100
- # whether this offense can be automatically corrected via
101
- # autocorrect or a todo.
100
+ # whether this offense can be automatically corrected via autocorrect.
101
+ # This includes todo comments, for example when requested with `--disable-uncorrectable`.
102
102
  def correctable?
103
103
  @status != :unsupported
104
104
  end
@@ -68,6 +68,8 @@ module RuboCop
68
68
  # class Foo
69
69
  #
70
70
  # private :bar, :baz
71
+ # private *%i[qux quux]
72
+ # private *METHOD_NAMES
71
73
  #
72
74
  # end
73
75
  #
@@ -76,6 +78,8 @@ module RuboCop
76
78
  # class Foo
77
79
  #
78
80
  # private :bar, :baz
81
+ # private *%i[qux quux]
82
+ # private *METHOD_NAMES
79
83
  #
80
84
  # end
81
85
  #
@@ -128,7 +132,9 @@ module RuboCop
128
132
 
129
133
  # @!method access_modifier_with_symbol?(node)
130
134
  def_node_matcher :access_modifier_with_symbol?, <<~PATTERN
131
- (send nil? {:private :protected :public :module_function} (sym _))
135
+ (send nil? {:private :protected :public :module_function}
136
+ {(sym _) (splat {#percent_symbol_array? const})}
137
+ )
132
138
  PATTERN
133
139
 
134
140
  # @!method access_modifier_with_attr?(node)
@@ -170,6 +176,10 @@ module RuboCop
170
176
  end
171
177
  end
172
178
 
179
+ def percent_symbol_array?(node)
180
+ node.array_type? && node.percent_literal?(:symbol)
181
+ end
182
+
173
183
  def allow_modifiers_on_symbols?(node)
174
184
  cop_config['AllowModifiersOnSymbols'] && access_modifier_with_symbol?(node)
175
185
  end
@@ -218,7 +228,7 @@ module RuboCop
218
228
 
219
229
  def find_corresponding_def_node(node)
220
230
  if access_modifier_with_symbol?(node)
221
- method_name = node.first_argument.value
231
+ method_name = node.first_argument.respond_to?(:value) && node.first_argument.value
222
232
  node.parent.each_child_node(:def).find do |child|
223
233
  child.method?(method_name)
224
234
  end
@@ -10,6 +10,9 @@ module RuboCop
10
10
  # NOTE: If there is a method call before the accessor method it is always allowed
11
11
  # as it might be intended like Sorbet.
12
12
  #
13
+ # NOTE: If there is a RBS::Inline annotation comment just after the accessor method
14
+ # it is always allowed.
15
+ #
13
16
  # @example EnforcedStyle: grouped (default)
14
17
  # # bad
15
18
  # class Foo
@@ -92,7 +95,7 @@ module RuboCop
92
95
  comment_line?(processed_source[node.first_line - 2])
93
96
  end
94
97
 
95
- # rubocop:disable Metrics/CyclomaticComplexity
98
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
96
99
  def groupable_accessor?(node)
97
100
  return true unless (previous_expression = node.left_siblings.last)
98
101
 
@@ -105,11 +108,16 @@ module RuboCop
105
108
 
106
109
  return true unless previous_expression.send_type?
107
110
 
111
+ # Accessors with RBS::Inline annotations shouldn't be groupable.
112
+ return false if processed_source.comments.any? do |c|
113
+ same_line?(c, previous_expression) && c.text.start_with?('#:')
114
+ end
115
+
108
116
  previous_expression.attribute_accessor? ||
109
117
  previous_expression.access_modifier? ||
110
118
  node.first_line - previous_expression.last_line > 1 # there is a space between nodes
111
119
  end
112
- # rubocop:enable Metrics/CyclomaticComplexity
120
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
113
121
 
114
122
  def class_send_elements(class_node)
115
123
  class_def = class_node.body