rubocop 1.71.2 → 1.73.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +54 -11
  4. data/config/internal_affairs.yml +16 -0
  5. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  6. data/lib/rubocop/comment_config.rb +1 -1
  7. data/lib/rubocop/config.rb +4 -0
  8. data/lib/rubocop/config_loader.rb +44 -8
  9. data/lib/rubocop/config_loader_resolver.rb +23 -9
  10. data/lib/rubocop/config_validator.rb +1 -1
  11. data/lib/rubocop/cop/internal_affairs/example_description.rb +4 -2
  12. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  13. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  15. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  16. data/lib/rubocop/cop/internal_affairs.rb +1 -16
  17. data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
  18. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
  19. data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  21. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +26 -1
  22. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  23. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_length.rb +3 -3
  25. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  26. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  27. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  28. data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -14
  29. data/lib/rubocop/cop/lint/empty_conditional_body.rb +10 -5
  30. data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
  31. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  32. data/lib/rubocop/cop/lint/literal_as_condition.rb +99 -9
  33. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
  34. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  35. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +252 -0
  36. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  37. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +80 -0
  38. data/lib/rubocop/cop/lint/void.rb +6 -0
  39. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  40. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  41. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  42. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  43. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
  44. data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
  45. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  46. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  47. data/lib/rubocop/cop/mixin/range_help.rb +3 -3
  48. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  49. data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
  50. data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
  51. data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
  52. data/lib/rubocop/cop/naming/variable_name.rb +64 -6
  53. data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
  54. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
  55. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  56. data/lib/rubocop/cop/style/endless_method.rb +163 -18
  57. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  58. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -1
  59. data/lib/rubocop/cop/style/redundant_condition.rb +46 -0
  60. data/lib/rubocop/cop/style/redundant_format.rb +250 -0
  61. data/lib/rubocop/cop/style/redundant_parentheses.rb +18 -4
  62. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  63. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  64. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  65. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  66. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  67. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  68. data/lib/rubocop/cop/util.rb +1 -1
  69. data/lib/rubocop/cop/utils/format_string.rb +7 -5
  70. data/lib/rubocop/cops_documentation_generator.rb +12 -1
  71. data/lib/rubocop/directive_comment.rb +35 -2
  72. data/lib/rubocop/lsp/runtime.rb +2 -0
  73. data/lib/rubocop/lsp/server.rb +0 -2
  74. data/lib/rubocop/options.rb +26 -11
  75. data/lib/rubocop/path_util.rb +4 -0
  76. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  77. data/lib/rubocop/plugin/load_error.rb +26 -0
  78. data/lib/rubocop/plugin/loader.rb +100 -0
  79. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  80. data/lib/rubocop/plugin.rb +46 -0
  81. data/lib/rubocop/rake_task.rb +4 -1
  82. data/lib/rubocop/rspec/cop_helper.rb +9 -0
  83. data/lib/rubocop/rspec/shared_contexts.rb +15 -0
  84. data/lib/rubocop/rspec/support.rb +1 -0
  85. data/lib/rubocop/server/cache.rb +35 -2
  86. data/lib/rubocop/server/cli.rb +2 -2
  87. data/lib/rubocop/version.rb +17 -2
  88. data/lib/rubocop.rb +5 -1
  89. data/lib/ruby_lsp/rubocop/addon.rb +7 -10
  90. data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +5 -8
  91. metadata +35 -10
  92. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -5,8 +5,11 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for endless methods.
7
7
  #
8
- # It can enforce either the use of endless methods definitions
9
- # for single-lined method bodies, or disallow endless methods.
8
+ # It can enforce endless method definitions whenever possible or with single line methods.
9
+ # It can also disallow multiline endless method definitions or all endless definitions.
10
+ #
11
+ # `require_single_line` style enforces endless method definitions for single line methods.
12
+ # `require_always` style enforces endless method definitions for single statement methods.
10
13
  #
11
14
  # Other method definition types are not considered by this cop.
12
15
  #
@@ -15,36 +18,116 @@ module RuboCop
15
18
  # * allow_single_line (default) - only single line endless method definitions are allowed.
16
19
  # * allow_always - all endless method definitions are allowed.
17
20
  # * disallow - all endless method definitions are disallowed.
21
+ # * require_single_line - endless method definitions are required for single line methods.
22
+ # * require_always - all endless method definitions are required.
18
23
  #
19
24
  # NOTE: Incorrect endless method definitions will always be
20
25
  # corrected to a multi-line definition.
21
26
  #
22
27
  # @example EnforcedStyle: allow_single_line (default)
28
+ # # bad, multi-line endless method
29
+ # def my_method = x.foo
30
+ # .bar
31
+ # .baz
32
+ #
23
33
  # # good
24
- # def my_method() = x
34
+ # def my_method
35
+ # x
36
+ # end
25
37
  #
26
- # # bad, multi-line endless method
27
- # def my_method() = x.foo
28
- # .bar
29
- # .baz
38
+ # # good
39
+ # def my_method = x
40
+ #
41
+ # # good
42
+ # def my_method
43
+ # x.foo
44
+ # .bar
45
+ # .baz
46
+ # end
30
47
  #
31
48
  # @example EnforcedStyle: allow_always
32
49
  # # good
33
- # def my_method() = x
50
+ # def my_method
51
+ # x
52
+ # end
34
53
  #
35
54
  # # good
36
- # def my_method() = x.foo
37
- # .bar
38
- # .baz
55
+ # def my_method = x
56
+ #
57
+ # # good
58
+ # def my_method = x.foo
59
+ # .bar
60
+ # .baz
61
+ #
62
+ # # good
63
+ # def my_method
64
+ # x.foo
65
+ # .bar
66
+ # .baz
67
+ # end
39
68
  #
40
69
  # @example EnforcedStyle: disallow
41
70
  # # bad
42
- # def my_method() = x
71
+ # def my_method = x
72
+ #
73
+ # # bad
74
+ # def my_method = x.foo
75
+ # .bar
76
+ # .baz
77
+ #
78
+ # # good
79
+ # def my_method
80
+ # x
81
+ # end
82
+ #
83
+ # # good
84
+ # def my_method
85
+ # x.foo
86
+ # .bar
87
+ # .baz
88
+ # end
89
+ #
90
+ # @example EnforcedStyle: require_single_line
91
+ # # bad
92
+ # def my_method
93
+ # x
94
+ # end
95
+ #
96
+ # # bad
97
+ # def my_method = x.foo
98
+ # .bar
99
+ # .baz
100
+ #
101
+ # # good
102
+ # def my_method = x
103
+ #
104
+ # # good
105
+ # def my_method
106
+ # x.foo
107
+ # .bar
108
+ # .baz
109
+ # end
110
+ #
111
+ # @example EnforcedStyle: require_always
112
+ # # bad
113
+ # def my_method
114
+ # x
115
+ # end
43
116
  #
44
117
  # # bad
45
- # def my_method() = x.foo
46
- # .bar
47
- # .baz
118
+ # def my_method
119
+ # x.foo
120
+ # .bar
121
+ # .baz
122
+ # end
123
+ #
124
+ # # good
125
+ # def my_method = x
126
+ #
127
+ # # good
128
+ # def my_method = x.foo
129
+ # .bar
130
+ # .baz
48
131
  #
49
132
  class EndlessMethod < Base
50
133
  include ConfigurableEnforcedStyle
@@ -57,12 +140,21 @@ module RuboCop
57
140
  CORRECTION_STYLES = %w[multiline single_line].freeze
58
141
  MSG = 'Avoid endless method definitions.'
59
142
  MSG_MULTI_LINE = 'Avoid endless method definitions with multiple lines.'
143
+ MSG_REQUIRE_SINGLE = 'Use endless method definitions for single line methods.'
144
+ MSG_REQUIRE_ALWAYS = 'Use endless method definitions.'
60
145
 
61
146
  def on_def(node)
62
- if style == :disallow
63
- handle_disallow_style(node)
64
- else
147
+ return if node.assignment_method?
148
+
149
+ case style
150
+ when :allow_single_line, :allow_always
65
151
  handle_allow_style(node)
152
+ when :disallow
153
+ handle_disallow_style(node)
154
+ when :require_single_line
155
+ handle_require_single_line_style(node)
156
+ when :require_always
157
+ handle_require_always_style(node)
66
158
  end
67
159
  end
68
160
 
@@ -77,11 +169,64 @@ module RuboCop
77
169
  end
78
170
  end
79
171
 
172
+ def handle_require_single_line_style(node)
173
+ if node.endless? && !node.single_line?
174
+ add_offense(node, message: MSG_MULTI_LINE) do |corrector|
175
+ correct_to_multiline(corrector, node)
176
+ end
177
+ elsif !node.endless? && can_be_made_endless?(node) && node.body.single_line?
178
+ return if too_long_when_made_endless?(node)
179
+
180
+ add_offense(node, message: MSG_REQUIRE_SINGLE) do |corrector|
181
+ corrector.replace(node, endless_replacement(node))
182
+ end
183
+ end
184
+ end
185
+
186
+ def handle_require_always_style(node)
187
+ return if node.endless? || !can_be_made_endless?(node)
188
+ return if too_long_when_made_endless?(node)
189
+
190
+ add_offense(node, message: MSG_REQUIRE_ALWAYS) do |corrector|
191
+ corrector.replace(node, endless_replacement(node))
192
+ end
193
+ end
194
+
80
195
  def handle_disallow_style(node)
81
196
  return unless node.endless?
82
197
 
83
198
  add_offense(node) { |corrector| correct_to_multiline(corrector, node) }
84
199
  end
200
+
201
+ def correct_to_multiline(corrector, node)
202
+ replacement = <<~RUBY.strip
203
+ def #{node.method_name}#{arguments(node)}
204
+ #{node.body.source}
205
+ end
206
+ RUBY
207
+
208
+ corrector.replace(node, replacement)
209
+ end
210
+
211
+ def endless_replacement(node)
212
+ <<~RUBY.strip
213
+ def #{node.method_name}#{arguments(node)} = #{node.body.source}
214
+ RUBY
215
+ end
216
+
217
+ def arguments(node, missing = '')
218
+ node.arguments.any? ? node.arguments.source : missing
219
+ end
220
+
221
+ def can_be_made_endless?(node)
222
+ node.body && !node.body.begin_type? && !node.body.kwbegin_type?
223
+ end
224
+
225
+ def too_long_when_made_endless?(node)
226
+ return false unless config.cop_enabled?('Layout/LineLength')
227
+
228
+ endless_replacement(node).length > config.for_cop('Layout/LineLength')['Max']
229
+ end
85
230
  end
86
231
  end
87
232
  end
@@ -36,7 +36,7 @@ module RuboCop
36
36
  include RangeHelp
37
37
  extend AutoCorrector
38
38
 
39
- MSG = 'Use `\\` instead of `+` or `<<` to concatenate those strings.'
39
+ MSG = 'Use `\\` instead of `%<operator>s` to concatenate multiline strings.'
40
40
  CONCAT_TOKEN_TYPES = %i[tPLUS tLSHFT].freeze
41
41
  SIMPLE_STRING_TOKEN_TYPE = :tSTRING
42
42
  COMPLEX_STRING_BEGIN_TOKEN = :tSTRING_BEG
@@ -61,14 +61,20 @@ module RuboCop
61
61
  successor = tokens[index + 2]
62
62
 
63
63
  return unless eligible_token_set?(predecessor, operator, successor)
64
-
65
64
  return if same_line?(operator, successor)
66
65
 
67
66
  next_successor = token_after_last_string(successor, index)
68
-
69
67
  return unless eligible_next_successor?(next_successor)
70
68
 
71
- add_offense(operator.pos) { |corrector| autocorrect(corrector, operator.pos) }
69
+ register_offense(operator)
70
+ end
71
+
72
+ def register_offense(operator)
73
+ message = format(MSG, operator: operator.text)
74
+
75
+ add_offense(operator.pos, message: message) do |corrector|
76
+ autocorrect(corrector, operator.pos)
77
+ end
72
78
  end
73
79
 
74
80
  def autocorrect(corrector, operator_range)
@@ -41,7 +41,7 @@ module RuboCop
41
41
  return if ignored_node?(node)
42
42
 
43
43
  return unless (receiver = node.receiver)
44
- return unless receiver.any_block_type? && receiver.loc.end.is?('end')
44
+ return unless receiver.any_block_type? && receiver.keywords?
45
45
 
46
46
  range = range_between(receiver.loc.end.begin_pos, node.source_range.end_pos)
47
47
 
@@ -42,7 +42,28 @@ module RuboCop
42
42
  # c
43
43
  # end
44
44
  #
45
+ # # bad
46
+ # a.nil? ? true : a
47
+ #
48
+ # # good
49
+ # a.nil? || a
50
+ #
51
+ # # bad
52
+ # if a.nil?
53
+ # true
54
+ # else
55
+ # a
56
+ # end
57
+ #
58
+ # # good
59
+ # a.nil? || a
60
+ #
61
+ # @example AllowedMethods: ['nonzero?'] (default)
62
+ # # good
63
+ # num.nonzero? ? true : false
64
+ #
45
65
  class RedundantCondition < Base
66
+ include AllowedMethods
46
67
  include CommentsHelp
47
68
  include RangeHelp
48
69
  extend AutoCorrector
@@ -128,6 +149,16 @@ module RuboCop
128
149
  # end
129
150
  return true if condition == if_branch
130
151
 
152
+ # e.g.
153
+ # a.nil? ? true : a
154
+ # or
155
+ # if a.nil?
156
+ # true
157
+ # else
158
+ # a
159
+ # end
160
+ return true if if_branch_is_true_type_and_else_is_not?(node)
161
+
131
162
  # e.g.
132
163
  # if foo
133
164
  # @value = foo
@@ -146,6 +177,19 @@ module RuboCop
146
177
  !use_hash_key_access?(if_branch)
147
178
  end
148
179
 
180
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
181
+ def if_branch_is_true_type_and_else_is_not?(node)
182
+ return false unless node.ternary? || node.if?
183
+
184
+ cond = node.condition
185
+ if cond.call_type? && (!cond.predicate_method? || allowed_method?(cond.method_name))
186
+ return false
187
+ end
188
+
189
+ node.if_branch&.true_type? && node.else_branch && !node.else_branch.true_type?
190
+ end
191
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
192
+
149
193
  def branches_have_assignment?(node)
150
194
  _condition, if_branch, else_branch = *node # rubocop:disable InternalAffairs/NodeDestructuring
151
195
 
@@ -194,6 +238,8 @@ module RuboCop
194
238
  argument_source = if_branch.first_argument.source
195
239
 
196
240
  "#{if_branch.receiver.source} #{if_branch.method_name} (#{argument_source}"
241
+ elsif if_branch.true_type?
242
+ if_branch.parent.condition.source
197
243
  else
198
244
  if_branch.source
199
245
  end
@@ -0,0 +1,250 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for calls to `Kernel#format` or `Kernel#sprintf` that are redundant.
7
+ #
8
+ # Calling `format` with only a single string argument is redundant, as it can be
9
+ # replaced by the string itself.
10
+ #
11
+ # Also looks for `format` calls where the arguments are literals that can be
12
+ # inlined into a string easily. This applies to the `%s`, `%d`, `%i`, `%u`, and
13
+ # `%f` format specifiers.
14
+ #
15
+ # @safety
16
+ # This cop's autocorrection is unsafe because string object returned by
17
+ # `format` and `sprintf` are never frozen. If `format('string')` is autocorrected to
18
+ # `'string'`, `FrozenError` may occur when calling a destructive method like `String#<<`.
19
+ # Consider using `'string'.dup` instead of `format('string')`.
20
+ # Additionally, since the necessity of `dup` cannot be determined automatically,
21
+ # this autocorrection is inherently unsafe.
22
+ #
23
+ # [source,ruby]
24
+ # ----
25
+ # # frozen_string_literal: true
26
+ #
27
+ # format('template').frozen? # => false
28
+ # 'template'.frozen? # => true
29
+ # ----
30
+ #
31
+ # @example
32
+ #
33
+ # # bad
34
+ # format('the quick brown fox jumps over the lazy dog.')
35
+ # sprintf('the quick brown fox jumps over the lazy dog.')
36
+ #
37
+ # # good
38
+ # 'the quick brown fox jumps over the lazy dog.'
39
+ #
40
+ # # bad
41
+ # format('%s %s', 'foo', 'bar')
42
+ # sprintf('%s %s', 'foo', 'bar')
43
+ #
44
+ # # good
45
+ # 'foo bar'
46
+ #
47
+ class RedundantFormat < Base
48
+ extend AutoCorrector
49
+
50
+ MSG = 'Use `%<prefer>s` directly instead of `%<method_name>s`.'
51
+
52
+ RESTRICT_ON_SEND = %i[format sprintf].to_set.freeze
53
+ ACCEPTABLE_LITERAL_TYPES = %i[str dstr sym dsym numeric boolean nil].freeze
54
+
55
+ # @!method format_without_additional_args?(node)
56
+ def_node_matcher :format_without_additional_args?, <<~PATTERN
57
+ (send {(const {nil? cbase} :Kernel) nil?} %RESTRICT_ON_SEND ${str dstr})
58
+ PATTERN
59
+
60
+ # @!method rational_number?(node)
61
+ def_node_matcher :rational_number?, <<~PATTERN
62
+ {rational (send int :/ rational) (begin rational) (begin (send int :/ rational))}
63
+ PATTERN
64
+
65
+ # @!method complex_number?(node)
66
+ def_node_matcher :complex_number?, <<~PATTERN
67
+ {complex (send int :+ complex) (begin complex) (begin (send int :+ complex))}
68
+ PATTERN
69
+
70
+ # @!method find_hash_value_node(node, name)
71
+ def_node_search :find_hash_value_node, <<~PATTERN
72
+ (pair (sym %1) $_)
73
+ PATTERN
74
+
75
+ # @!method splatted_arguments?(node)
76
+ def_node_matcher :splatted_arguments?, <<~PATTERN
77
+ (send _ %RESTRICT_ON_SEND <{
78
+ splat
79
+ (hash <kwsplat ...>)
80
+ } ...>)
81
+ PATTERN
82
+
83
+ def on_send(node)
84
+ format_without_additional_args?(node) do |value|
85
+ replacement = value.source
86
+
87
+ add_offense(node, message: message(node, replacement)) do |corrector|
88
+ corrector.replace(node, replacement)
89
+ end
90
+ return
91
+ end
92
+
93
+ detect_unnecessary_fields(node)
94
+ end
95
+
96
+ private
97
+
98
+ def message(node, prefer)
99
+ format(MSG, prefer: prefer, method_name: node.method_name)
100
+ end
101
+
102
+ def detect_unnecessary_fields(node)
103
+ return unless node.first_argument&.str_type?
104
+
105
+ string = node.first_argument.value
106
+ arguments = node.arguments[1..]
107
+
108
+ return unless string && arguments.any?
109
+ return if splatted_arguments?(node)
110
+
111
+ register_all_fields_literal(node, string, arguments)
112
+ end
113
+
114
+ def register_all_fields_literal(node, string, arguments)
115
+ return unless all_fields_literal?(string, arguments.dup)
116
+
117
+ formatted_string = format(string, *argument_values(arguments))
118
+ replacement = quote(formatted_string, node)
119
+
120
+ add_offense(node, message: message(node, replacement)) do |corrector|
121
+ corrector.replace(node, replacement)
122
+ end
123
+ end
124
+
125
+ def all_fields_literal?(string, arguments)
126
+ count = 0
127
+ sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
128
+ return false unless sequences.any?
129
+
130
+ sequences.each do |sequence|
131
+ next if sequence.percent?
132
+
133
+ hash = arguments.detect(&:hash_type?)
134
+ next unless (argument = find_argument(sequence, arguments, hash))
135
+ next unless matching_argument?(sequence, argument)
136
+
137
+ count += 1
138
+ end
139
+
140
+ sequences.size == count
141
+ end
142
+
143
+ def find_argument(sequence, arguments, hash)
144
+ if hash && (sequence.annotated? || sequence.template?)
145
+ find_hash_value_node(hash, sequence.name.to_sym).first
146
+ elsif sequence.arg_number
147
+ arguments[sequence.arg_number.to_i - 1]
148
+ else
149
+ # If the specifier contains `*`, the following arguments will be used
150
+ # to specify the width and can be ignored.
151
+ (sequence.arity - 1).times { arguments.shift }
152
+ arguments.shift
153
+ end
154
+ end
155
+
156
+ def matching_argument?(sequence, argument)
157
+ # Template specifiers don't give a type, any acceptable literal type is ok.
158
+ return argument.type?(*ACCEPTABLE_LITERAL_TYPES) if sequence.template?
159
+
160
+ # An argument matches a specifier if it can be easily converted
161
+ # to that type.
162
+ case sequence.type
163
+ when 's'
164
+ argument.type?(*ACCEPTABLE_LITERAL_TYPES)
165
+ when 'd', 'i', 'u'
166
+ integer?(argument)
167
+ when 'f'
168
+ float?(argument)
169
+ else
170
+ false
171
+ end
172
+ end
173
+
174
+ def numeric?(argument)
175
+ argument.type?(:numeric, :str) ||
176
+ rational_number?(argument) ||
177
+ complex_number?(argument)
178
+ end
179
+
180
+ def integer?(argument)
181
+ numeric?(argument) && Integer(argument_value(argument), exception: false)
182
+ end
183
+
184
+ def float?(argument)
185
+ numeric?(argument) && Float(argument_value(argument), exception: false)
186
+ end
187
+
188
+ # Add correct quotes to the formatted string, preferring retaining the existing
189
+ # quotes if possible.
190
+ def quote(string, node)
191
+ str_node = node.first_argument
192
+ start_delimiter = str_node.loc.begin.source
193
+ end_delimiter = str_node.loc.end.source
194
+
195
+ # If there is any interpolation, the delimiters need to be changed potentially
196
+ if node.each_descendant(:dstr, :dsym).any?
197
+ case start_delimiter
198
+ when "'"
199
+ start_delimiter = end_delimiter = '"'
200
+ when /\A%q(.)/
201
+ start_delimiter = "%Q#{Regexp.last_match[1]}"
202
+ end
203
+ end
204
+
205
+ "#{start_delimiter}#{string}#{end_delimiter}"
206
+ end
207
+
208
+ def argument_values(arguments)
209
+ arguments.map { |argument| argument_value(argument) }
210
+ end
211
+
212
+ def argument_value(argument)
213
+ argument = argument.children.first if argument.begin_type?
214
+
215
+ if argument.dsym_type?
216
+ dsym_value(argument)
217
+ elsif argument.hash_type?
218
+ hash_value(argument)
219
+ elsif rational_number?(argument)
220
+ rational_value(argument)
221
+ elsif complex_number?(argument)
222
+ complex_value(argument)
223
+ elsif argument.respond_to?(:value)
224
+ argument.value
225
+ else
226
+ argument.source
227
+ end
228
+ end
229
+
230
+ def dsym_value(dsym_node)
231
+ dsym_node.children.first.source
232
+ end
233
+
234
+ def hash_value(hash_node)
235
+ hash_node.each_pair.with_object({}) do |pair, hash|
236
+ hash[pair.key.value] = argument_value(pair.value)
237
+ end
238
+ end
239
+
240
+ def rational_value(rational_node)
241
+ rational_node.source.to_r
242
+ end
243
+
244
+ def complex_value(complex_node)
245
+ Complex(complex_node.source)
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
@@ -13,14 +13,16 @@ module RuboCop
13
13
  # # good
14
14
  # x if y.z.nil?
15
15
  #
16
- class RedundantParentheses < Base
16
+ class RedundantParentheses < Base # rubocop:disable Metrics/ClassLength
17
17
  include Parentheses
18
18
  extend AutoCorrector
19
19
 
20
20
  ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
21
21
 
22
22
  # @!method square_brackets?(node)
23
- def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
23
+ def_node_matcher :square_brackets?, <<~PATTERN
24
+ (send `{(send _recv _msg) str array hash const #variable?} :[] ...)
25
+ PATTERN
24
26
 
25
27
  # @!method method_node_and_args(node)
26
28
  def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
@@ -39,6 +41,10 @@ module RuboCop
39
41
 
40
42
  private
41
43
 
44
+ def variable?(node)
45
+ node.respond_to?(:variable?) && node.variable?
46
+ end
47
+
42
48
  def parens_allowed?(node)
43
49
  empty_parentheses?(node) ||
44
50
  first_arg_begins_with_hash_literal?(node) ||
@@ -128,6 +134,8 @@ module RuboCop
128
134
  node = begin_node.children.first
129
135
 
130
136
  if (message = find_offense_message(begin_node, node))
137
+ begin_node = begin_node.parent if node.range_type?
138
+
131
139
  return offense(begin_node, message)
132
140
  end
133
141
 
@@ -137,7 +145,7 @@ module RuboCop
137
145
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
138
146
  def find_offense_message(begin_node, node)
139
147
  return 'a keyword' if keyword_with_redundant_parentheses?(node)
140
- return 'a literal' if disallowed_literal?(begin_node, node)
148
+ return 'a literal' if node.literal? && disallowed_literal?(begin_node, node)
141
149
  return 'a variable' if node.variable?
142
150
  return 'a constant' if node.const_type?
143
151
  if node.assignment? && (begin_node.parent.nil? || begin_node.parent.begin_type?)
@@ -207,7 +215,13 @@ module RuboCop
207
215
  end
208
216
 
209
217
  def disallowed_literal?(begin_node, node)
210
- node.literal? && !node.range_type? && !raised_to_power_negative_numeric?(begin_node, node)
218
+ if node.range_type?
219
+ return false unless (parent = begin_node.parent)
220
+
221
+ parent.begin_type? && parent.children.one?
222
+ else
223
+ !raised_to_power_negative_numeric?(begin_node, node)
224
+ end
211
225
  end
212
226
 
213
227
  def raised_to_power_negative_numeric?(begin_node, node)
@@ -58,7 +58,7 @@ module RuboCop
58
58
  # rubocop:disable Metrics/AbcSize
59
59
  def on_lvasgn(node)
60
60
  return unless (rhs = node.rhs)
61
- return unless rhs.call_type? && method_returning_self?(rhs.method_name)
61
+ return unless rhs.type?(:any_block, :call) && method_returning_self?(rhs.method_name)
62
62
  return unless (receiver = rhs.receiver)
63
63
 
64
64
  receiver_type = ASSIGNMENT_TYPE_TO_RECEIVER_TYPE[node.type]
@@ -8,9 +8,9 @@ module RuboCop
8
8
  #
9
9
  # Endless methods added in Ruby 3.0 are also accepted by this cop.
10
10
  #
11
- # If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line` or
12
- # `allow_always`, single-line methods will be autocorrected to endless
13
- # methods if there is only one statement in the body.
11
+ # If `Style/EndlessMethod` is enabled with `EnforcedStyle: allow_single_line`, `allow_always`,
12
+ # `require_single_line`, or `require_always`, single-line methods will be autocorrected
13
+ # to endless methods if there is only one statement in the body.
14
14
  #
15
15
  # @example
16
16
  # # bad
@@ -128,7 +128,7 @@ module RuboCop
128
128
  end
129
129
 
130
130
  def uncorrectable?(part)
131
- part.multiline? || heredoc?(part) || part.each_descendant(:block).any?
131
+ part.multiline? || heredoc?(part) || part.each_descendant(:any_block).any?
132
132
  end
133
133
 
134
134
  def heredoc?(node)