rubocop 1.71.2 → 1.75.2

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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +118 -21
  4. data/config/internal_affairs.yml +20 -0
  5. data/config/obsoletion.yml +3 -1
  6. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  7. data/lib/rubocop/cli.rb +1 -1
  8. data/lib/rubocop/comment_config.rb +1 -1
  9. data/lib/rubocop/config.rb +39 -6
  10. data/lib/rubocop/config_loader.rb +48 -9
  11. data/lib/rubocop/config_loader_resolver.rb +24 -9
  12. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
  13. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  14. data/lib/rubocop/config_obsoletion.rb +46 -2
  15. data/lib/rubocop/config_validator.rb +2 -1
  16. data/lib/rubocop/cop/internal_affairs/example_description.rb +7 -3
  17. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  18. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
  19. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -1
  20. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +91 -0
  21. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  22. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  23. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  24. data/lib/rubocop/cop/internal_affairs.rb +2 -16
  25. data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
  26. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  27. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
  28. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  29. data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
  30. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  31. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +3 -3
  32. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +27 -1
  33. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
  34. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  35. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  36. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  37. data/lib/rubocop/cop/layout/line_length.rb +8 -4
  38. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
  39. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  40. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  41. data/lib/rubocop/cop/layout/redundant_line_break.rb +9 -5
  42. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -5
  43. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  44. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  45. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
  46. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
  47. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  48. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  49. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  50. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -17
  51. data/lib/rubocop/cop/lint/empty_conditional_body.rb +14 -64
  52. data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
  53. data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
  54. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  55. data/lib/rubocop/cop/lint/literal_as_condition.rb +103 -9
  56. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
  57. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  58. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -3
  59. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  60. data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
  61. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  62. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +261 -0
  63. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  64. data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
  65. data/lib/rubocop/cop/lint/return_in_void_context.rb +9 -11
  66. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
  67. data/lib/rubocop/cop/lint/shared_mutable_default.rb +12 -1
  68. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  69. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  70. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  71. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  72. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
  73. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
  74. data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
  75. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -0
  76. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +71 -0
  77. data/lib/rubocop/cop/lint/void.rb +7 -0
  78. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  79. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  80. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  81. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  82. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  83. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  84. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  85. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  86. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  87. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  88. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  89. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  90. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
  91. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +0 -1
  92. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
  93. data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
  94. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  95. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
  96. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  97. data/lib/rubocop/cop/mixin/range_help.rb +15 -3
  98. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  99. data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
  100. data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
  101. data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
  102. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  103. data/lib/rubocop/cop/naming/method_name.rb +64 -8
  104. data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
  105. data/lib/rubocop/cop/naming/variable_name.rb +51 -6
  106. data/lib/rubocop/cop/registry.rb +9 -6
  107. data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
  108. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
  109. data/lib/rubocop/cop/style/array_intersect.rb +39 -28
  110. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  111. data/lib/rubocop/cop/style/class_and_module_children.rb +29 -7
  112. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  113. data/lib/rubocop/cop/style/collection_methods.rb +1 -0
  114. data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
  115. data/lib/rubocop/cop/style/commented_keyword.rb +10 -3
  116. data/lib/rubocop/cop/style/comparable_between.rb +75 -0
  117. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -0
  118. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  119. data/lib/rubocop/cop/style/empty_literal.rb +4 -0
  120. data/lib/rubocop/cop/style/endless_method.rb +163 -18
  121. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
  122. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  123. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  124. data/lib/rubocop/cop/style/for.rb +1 -0
  125. data/lib/rubocop/cop/style/format_string_token.rb +38 -11
  126. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  127. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  128. data/lib/rubocop/cop/style/guard_clause.rb +2 -1
  129. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -2
  130. data/lib/rubocop/cop/style/hash_fetch_chain.rb +105 -0
  131. data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
  132. data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
  133. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -2
  134. data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
  135. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  136. data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
  137. data/lib/rubocop/cop/style/it_block_parameter.rb +100 -0
  138. data/lib/rubocop/cop/style/keyword_parameters_order.rb +13 -7
  139. data/lib/rubocop/cop/style/lambda.rb +1 -0
  140. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  141. data/lib/rubocop/cop/style/map_into_array.rb +1 -0
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -4
  143. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -1
  144. data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -1
  145. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
  146. data/lib/rubocop/cop/style/next.rb +44 -0
  147. data/lib/rubocop/cop/style/object_then.rb +1 -0
  148. data/lib/rubocop/cop/style/proc.rb +1 -0
  149. data/lib/rubocop/cop/style/raise_args.rb +8 -8
  150. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  151. data/lib/rubocop/cop/style/redundant_condition.rb +57 -0
  152. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +14 -4
  153. data/lib/rubocop/cop/style/redundant_format.rb +257 -0
  154. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  155. data/lib/rubocop/cop/style/redundant_parentheses.rb +20 -5
  156. data/lib/rubocop/cop/style/redundant_self.rb +1 -0
  157. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  158. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  159. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -0
  160. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  161. data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
  162. data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
  163. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  164. data/lib/rubocop/cop/style/sole_nested_conditional.rb +41 -106
  165. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  166. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  167. data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
  168. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -0
  169. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  170. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  171. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  172. data/lib/rubocop/cop/util.rb +2 -2
  173. data/lib/rubocop/cop/utils/format_string.rb +10 -5
  174. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  175. data/lib/rubocop/cop/variable_force/variable.rb +2 -7
  176. data/lib/rubocop/cop/variable_force.rb +1 -1
  177. data/lib/rubocop/cops_documentation_generator.rb +12 -1
  178. data/lib/rubocop/directive_comment.rb +36 -3
  179. data/lib/rubocop/ext/regexp_node.rb +0 -1
  180. data/lib/rubocop/lsp/runtime.rb +6 -4
  181. data/lib/rubocop/lsp/server.rb +0 -2
  182. data/lib/rubocop/lsp/stdin_runner.rb +3 -1
  183. data/lib/rubocop/magic_comment.rb +8 -0
  184. data/lib/rubocop/options.rb +26 -11
  185. data/lib/rubocop/path_util.rb +4 -0
  186. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  187. data/lib/rubocop/plugin/load_error.rb +26 -0
  188. data/lib/rubocop/plugin/loader.rb +100 -0
  189. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  190. data/lib/rubocop/plugin.rb +46 -0
  191. data/lib/rubocop/rake_task.rb +4 -1
  192. data/lib/rubocop/rspec/cop_helper.rb +13 -1
  193. data/lib/rubocop/rspec/shared_contexts.rb +35 -0
  194. data/lib/rubocop/rspec/support.rb +3 -0
  195. data/lib/rubocop/runner.rb +5 -1
  196. data/lib/rubocop/server/cache.rb +47 -11
  197. data/lib/rubocop/server/cli.rb +2 -2
  198. data/lib/rubocop/target_finder.rb +1 -1
  199. data/lib/rubocop/target_ruby.rb +1 -1
  200. data/lib/rubocop/version.rb +30 -8
  201. data/lib/rubocop.rb +10 -1
  202. data/lib/ruby_lsp/rubocop/addon.rb +7 -10
  203. data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +25 -10
  204. metadata +43 -12
  205. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -20,20 +20,30 @@ module RuboCop
20
20
 
21
21
  MSG = 'Remove the redundant current directory path.'
22
22
  RESTRICT_ON_SEND = %i[require_relative].freeze
23
- CURRENT_DIRECTORY_PATH = './'
23
+ CURRENT_DIRECTORY_PREFIX = %r{./+}.freeze
24
+ REDUNDANT_CURRENT_DIRECTORY_PREFIX = /\A#{CURRENT_DIRECTORY_PREFIX}/.freeze
24
25
 
25
26
  def on_send(node)
26
27
  return unless (first_argument = node.first_argument)
27
- return unless first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
28
- return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PATH))
28
+ return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PREFIX))
29
+ return unless (redundant_length = redundant_path_length(first_argument.str_content))
29
30
 
30
31
  begin_pos = first_argument.source_range.begin.begin_pos + index
31
- range = range_between(begin_pos, begin_pos + 2)
32
+ end_pos = begin_pos + redundant_length
33
+ range = range_between(begin_pos, end_pos)
32
34
 
33
35
  add_offense(range) do |corrector|
34
36
  corrector.remove(range)
35
37
  end
36
38
  end
39
+
40
+ private
41
+
42
+ def redundant_path_length(path)
43
+ return unless (match = path&.match(REDUNDANT_CURRENT_DIRECTORY_PREFIX))
44
+
45
+ match[0].length
46
+ end
37
47
  end
38
48
  end
39
49
  end
@@ -0,0 +1,257 @@
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 or constant argument is redundant,
9
+ # as it can be replaced by the string or constant 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(MESSAGE)
42
+ # sprintf(MESSAGE)
43
+ #
44
+ # # good
45
+ # MESSAGE
46
+ #
47
+ # # bad
48
+ # format('%s %s', 'foo', 'bar')
49
+ # sprintf('%s %s', 'foo', 'bar')
50
+ #
51
+ # # good
52
+ # 'foo bar'
53
+ #
54
+ class RedundantFormat < Base
55
+ extend AutoCorrector
56
+
57
+ MSG = 'Use `%<prefer>s` directly instead of `%<method_name>s`.'
58
+
59
+ RESTRICT_ON_SEND = %i[format sprintf].to_set.freeze
60
+ ACCEPTABLE_LITERAL_TYPES = %i[str dstr sym dsym numeric boolean nil].freeze
61
+
62
+ # @!method format_without_additional_args?(node)
63
+ def_node_matcher :format_without_additional_args?, <<~PATTERN
64
+ (send {(const {nil? cbase} :Kernel) nil?} %RESTRICT_ON_SEND ${str dstr const})
65
+ PATTERN
66
+
67
+ # @!method rational_number?(node)
68
+ def_node_matcher :rational_number?, <<~PATTERN
69
+ {rational (send int :/ rational) (begin rational) (begin (send int :/ rational))}
70
+ PATTERN
71
+
72
+ # @!method complex_number?(node)
73
+ def_node_matcher :complex_number?, <<~PATTERN
74
+ {complex (send int :+ complex) (begin complex) (begin (send int :+ complex))}
75
+ PATTERN
76
+
77
+ # @!method find_hash_value_node(node, name)
78
+ def_node_search :find_hash_value_node, <<~PATTERN
79
+ (pair (sym %1) $_)
80
+ PATTERN
81
+
82
+ # @!method splatted_arguments?(node)
83
+ def_node_matcher :splatted_arguments?, <<~PATTERN
84
+ (send _ %RESTRICT_ON_SEND <{
85
+ splat
86
+ (hash <kwsplat ...>)
87
+ } ...>)
88
+ PATTERN
89
+
90
+ def on_send(node)
91
+ format_without_additional_args?(node) do |value|
92
+ replacement = value.source
93
+
94
+ add_offense(node, message: message(node, replacement)) do |corrector|
95
+ corrector.replace(node, replacement)
96
+ end
97
+ return
98
+ end
99
+
100
+ detect_unnecessary_fields(node)
101
+ end
102
+
103
+ private
104
+
105
+ def message(node, prefer)
106
+ format(MSG, prefer: prefer, method_name: node.method_name)
107
+ end
108
+
109
+ def detect_unnecessary_fields(node)
110
+ return unless node.first_argument&.str_type?
111
+
112
+ string = node.first_argument.value
113
+ arguments = node.arguments[1..]
114
+
115
+ return unless string && arguments.any?
116
+ return if splatted_arguments?(node)
117
+
118
+ register_all_fields_literal(node, string, arguments)
119
+ end
120
+
121
+ def register_all_fields_literal(node, string, arguments)
122
+ return unless all_fields_literal?(string, arguments.dup)
123
+
124
+ formatted_string = format(string, *argument_values(arguments))
125
+ replacement = quote(formatted_string, node)
126
+
127
+ add_offense(node, message: message(node, replacement)) do |corrector|
128
+ corrector.replace(node, replacement)
129
+ end
130
+ end
131
+
132
+ def all_fields_literal?(string, arguments)
133
+ count = 0
134
+ sequences = RuboCop::Cop::Utils::FormatString.new(string).format_sequences
135
+ return false unless sequences.any?
136
+
137
+ sequences.each do |sequence|
138
+ next if sequence.percent?
139
+
140
+ hash = arguments.detect(&:hash_type?)
141
+ next unless (argument = find_argument(sequence, arguments, hash))
142
+ next unless matching_argument?(sequence, argument)
143
+
144
+ count += 1
145
+ end
146
+
147
+ sequences.size == count
148
+ end
149
+
150
+ def find_argument(sequence, arguments, hash)
151
+ if hash && (sequence.annotated? || sequence.template?)
152
+ find_hash_value_node(hash, sequence.name.to_sym).first
153
+ elsif sequence.arg_number
154
+ arguments[sequence.arg_number.to_i - 1]
155
+ else
156
+ # If the specifier contains `*`, the following arguments will be used
157
+ # to specify the width and can be ignored.
158
+ (sequence.arity - 1).times { arguments.shift }
159
+ arguments.shift
160
+ end
161
+ end
162
+
163
+ def matching_argument?(sequence, argument)
164
+ # Template specifiers don't give a type, any acceptable literal type is ok.
165
+ return argument.type?(*ACCEPTABLE_LITERAL_TYPES) if sequence.template?
166
+
167
+ # An argument matches a specifier if it can be easily converted
168
+ # to that type.
169
+ case sequence.type
170
+ when 's'
171
+ argument.type?(*ACCEPTABLE_LITERAL_TYPES)
172
+ when 'd', 'i', 'u'
173
+ integer?(argument)
174
+ when 'f'
175
+ float?(argument)
176
+ else
177
+ false
178
+ end
179
+ end
180
+
181
+ def numeric?(argument)
182
+ argument.type?(:numeric, :str) ||
183
+ rational_number?(argument) ||
184
+ complex_number?(argument)
185
+ end
186
+
187
+ def integer?(argument)
188
+ numeric?(argument) && Integer(argument_value(argument), exception: false)
189
+ end
190
+
191
+ def float?(argument)
192
+ numeric?(argument) && Float(argument_value(argument), exception: false)
193
+ end
194
+
195
+ # Add correct quotes to the formatted string, preferring retaining the existing
196
+ # quotes if possible.
197
+ def quote(string, node)
198
+ str_node = node.first_argument
199
+ start_delimiter = str_node.loc.begin.source
200
+ end_delimiter = str_node.loc.end.source
201
+
202
+ # If there is any interpolation, the delimiters need to be changed potentially
203
+ if node.each_descendant(:dstr, :dsym).any?
204
+ case start_delimiter
205
+ when "'"
206
+ start_delimiter = end_delimiter = '"'
207
+ when /\A%q(.)/
208
+ start_delimiter = "%Q#{Regexp.last_match[1]}"
209
+ end
210
+ end
211
+
212
+ "#{start_delimiter}#{string}#{end_delimiter}"
213
+ end
214
+
215
+ def argument_values(arguments)
216
+ arguments.map { |argument| argument_value(argument) }
217
+ end
218
+
219
+ def argument_value(argument)
220
+ argument = argument.children.first if argument.begin_type?
221
+
222
+ if argument.dsym_type?
223
+ dsym_value(argument)
224
+ elsif argument.hash_type?
225
+ hash_value(argument)
226
+ elsif rational_number?(argument)
227
+ rational_value(argument)
228
+ elsif complex_number?(argument)
229
+ complex_value(argument)
230
+ elsif argument.respond_to?(:value)
231
+ argument.value
232
+ else
233
+ argument.source
234
+ end
235
+ end
236
+
237
+ def dsym_value(dsym_node)
238
+ dsym_node.children.first.source
239
+ end
240
+
241
+ def hash_value(hash_node)
242
+ hash_node.each_pair.with_object({}) do |pair, hash|
243
+ hash[pair.key.value] = argument_value(pair.value)
244
+ end
245
+ end
246
+
247
+ def rational_value(rational_node)
248
+ rational_node.source.to_r
249
+ end
250
+
251
+ def complex_value(complex_node)
252
+ Complex(complex_node.source)
253
+ end
254
+ end
255
+ end
256
+ end
257
+ end
@@ -60,7 +60,7 @@ module RuboCop
60
60
  (begin (send !{(str _) array} {:+ :- :* :** :/ :%} {float int}))
61
61
  (begin (send _ {:== :=== :!= :<= :>= :< :>} _))
62
62
  (send _ {:count :length :size} ...)
63
- (block (send _ {:count :length :size} ...) ...)
63
+ (any_block (send _ {:count :length :size} ...) ...)
64
64
  }
65
65
  PATTERN
66
66
  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
- ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
20
+ ALLOWED_NODE_TYPES = %i[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?)
@@ -154,6 +162,7 @@ module RuboCop
154
162
  return if node.semantic_operator? && begin_node.parent
155
163
  return if node.multiline? && allow_in_multiline_conditions?
156
164
  return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
165
+ return if !node.and_type? && begin_node.parent&.and_type?
157
166
  return if begin_node.parent&.if_type? && begin_node.parent.ternary?
158
167
 
159
168
  'a logical expression'
@@ -207,7 +216,13 @@ module RuboCop
207
216
  end
208
217
 
209
218
  def disallowed_literal?(begin_node, node)
210
- node.literal? && !node.range_type? && !raised_to_power_negative_numeric?(begin_node, node)
219
+ if node.range_type?
220
+ return false unless (parent = begin_node.parent)
221
+
222
+ parent.begin_type? && parent.children.one?
223
+ else
224
+ !raised_to_power_negative_numeric?(begin_node, node)
225
+ end
211
226
  end
212
227
 
213
228
  def raised_to_power_negative_numeric?(begin_node, node)
@@ -118,6 +118,7 @@ module RuboCop
118
118
  end
119
119
 
120
120
  alias on_numblock on_block
121
+ alias on_itblock on_block
121
122
 
122
123
  def on_if(node)
123
124
  # Allow conditional nodes to use `self` in the condition if that variable
@@ -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]
@@ -21,6 +21,7 @@ module RuboCop
21
21
 
22
22
  MSG_BLOCK = 'Use `sort` instead of `sort_by { |%<var>s| %<var>s }`.'
23
23
  MSG_NUMBLOCK = 'Use `sort` instead of `sort_by { _1 }`.'
24
+ MSG_ITBLOCK = 'Use `sort` instead of `sort_by { it }`.'
24
25
 
25
26
  def on_block(node)
26
27
  redundant_sort_by_block(node) do |send, var_name|
@@ -36,7 +37,17 @@ module RuboCop
36
37
  redundant_sort_by_numblock(node) do |send|
37
38
  range = sort_by_range(send, node)
38
39
 
39
- add_offense(range, message: format(MSG_NUMBLOCK)) do |corrector|
40
+ add_offense(range, message: MSG_NUMBLOCK) do |corrector|
41
+ corrector.replace(range, 'sort')
42
+ end
43
+ end
44
+ end
45
+
46
+ def on_itblock(node)
47
+ redundant_sort_by_itblock(node) do |send|
48
+ range = sort_by_range(send, node)
49
+
50
+ add_offense(range, message: MSG_ITBLOCK) do |corrector|
40
51
  corrector.replace(range, 'sort')
41
52
  end
42
53
  end
@@ -54,6 +65,11 @@ module RuboCop
54
65
  (numblock $(call _ :sort_by) 1 (lvar :_1))
55
66
  PATTERN
56
67
 
68
+ # @!method redundant_sort_by_itblock(node)
69
+ def_node_matcher :redundant_sort_by_itblock, <<~PATTERN
70
+ (itblock $(call _ :sort_by) _ (lvar :it))
71
+ PATTERN
72
+
57
73
  def sort_by_range(send, node)
58
74
  range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
59
75
  end
@@ -67,11 +67,13 @@ module RuboCop
67
67
  node.parent && parentheses?(node.parent)
68
68
  end
69
69
 
70
+ # rubocop:disable Metrics/AbcSize
70
71
  def correct_rescue_block(corrector, node, parenthesized)
71
72
  operation = node.body
72
73
 
73
74
  node_indentation, node_offset = indentation_and_offset(node, parenthesized)
74
75
 
76
+ corrector.wrap(operation, '[', ']') if operation.array_type? && !operation.bracketed?
75
77
  corrector.remove(range_between(operation.source_range.end_pos, node.source_range.end_pos))
76
78
  corrector.insert_before(operation, "begin\n#{node_indentation}")
77
79
  corrector.insert_after(heredoc_end(operation) || operation, <<~RESCUE_CLAUSE.chop)
@@ -81,6 +83,7 @@ module RuboCop
81
83
  #{node_offset}end
82
84
  RESCUE_CLAUSE
83
85
  end
86
+ # rubocop:enable Metrics/AbcSize
84
87
 
85
88
  def indentation_and_offset(node, parenthesized)
86
89
  node_indentation = indentation(node)
@@ -47,7 +47,7 @@ module RuboCop
47
47
 
48
48
  def on_return(node)
49
49
  # Check Lint/NonLocalExitFromIterator first before this cop
50
- node.each_ancestor(:block, :def, :defs) do |n|
50
+ node.each_ancestor(:block, :any_def) do |n|
51
51
  break if scoped_node?(n)
52
52
 
53
53
  send_node, args_node, _body_node = *n
@@ -83,7 +83,7 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def scoped_node?(node)
86
- node.type?(:def, :defs) || node.lambda?
86
+ node.any_def_type? || node.lambda?
87
87
  end
88
88
 
89
89
  # @!method chained_send?(node)
@@ -59,6 +59,7 @@ module RuboCop
59
59
  {
60
60
  (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
61
61
  (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
62
+ (itblock call $_ ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
62
63
  }
63
64
  PATTERN
64
65
 
@@ -137,6 +138,7 @@ module RuboCop
137
138
  return unless (block_arg_name, regexp_method_send_node = regexp_match?(block_node))
138
139
 
139
140
  block_arg_name = :"_#{block_arg_name}" if block_node.numblock_type?
141
+
140
142
  return unless calls_lvar?(regexp_method_send_node, block_arg_name)
141
143
 
142
144
  regexp_method_send_node
@@ -150,7 +152,8 @@ module RuboCop
150
152
  return node.child_nodes.first if node.match_with_lvasgn_type?
151
153
 
152
154
  if node.receiver.lvar_type? &&
153
- (block.numblock_type? || node.receiver.source == block.first_argument.source)
155
+ (block.type?(:numblock, :itblock) ||
156
+ node.receiver.source == block.first_argument.source)
154
157
  node.first_argument
155
158
  elsif node.first_argument.lvar_type?
156
159
  node.receiver
@@ -56,11 +56,13 @@ module RuboCop
56
56
  end
57
57
  # rubocop:enable Metrics/AbcSize
58
58
  alias on_numblock on_block
59
+ alias on_itblock on_block
59
60
 
60
61
  private
61
62
 
62
63
  def do_line(node)
63
- if node.numblock_type? || node.arguments.children.empty? || node.send_node.lambda_literal?
64
+ if node.type?(:numblock, :itblock) ||
65
+ node.arguments.children.empty? || node.send_node.lambda_literal?
64
66
  node.loc.begin
65
67
  else
66
68
  node.arguments
@@ -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