rubocop 1.53.0 → 1.60.0

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 (236) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +5 -3
  4. data/config/default.yml +80 -11
  5. data/config/obsoletion.yml +5 -0
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  7. data/lib/rubocop/cli.rb +1 -1
  8. data/lib/rubocop/config.rb +0 -2
  9. data/lib/rubocop/config_finder.rb +2 -2
  10. data/lib/rubocop/config_loader.rb +0 -1
  11. data/lib/rubocop/config_obsoletion/parameter_rule.rb +9 -1
  12. data/lib/rubocop/config_obsoletion.rb +11 -8
  13. data/lib/rubocop/config_validator.rb +0 -2
  14. data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
  15. data/lib/rubocop/cop/base.rb +7 -1
  16. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -0
  17. data/lib/rubocop/cop/bundler/duplicated_group.rb +127 -0
  18. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  19. data/lib/rubocop/cop/bundler/ordered_gems.rb +9 -1
  20. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -4
  21. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  22. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  23. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +9 -1
  24. data/lib/rubocop/cop/generator/require_file_injector.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -21
  26. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +3 -1
  27. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  28. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  29. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  30. data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
  31. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
  32. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  33. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  34. data/lib/rubocop/cop/layout/dot_position.rb +1 -5
  35. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +42 -9
  36. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +26 -3
  37. data/lib/rubocop/cop/layout/end_alignment.rb +12 -2
  38. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  39. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  40. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  41. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  42. data/lib/rubocop/cop/layout/heredoc_indentation.rb +4 -1
  43. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  44. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  45. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +17 -9
  46. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  47. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -0
  48. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
  49. data/lib/rubocop/cop/layout/redundant_line_break.rb +15 -4
  50. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  51. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  52. data/lib/rubocop/cop/layout/space_after_comma.rb +9 -1
  53. data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
  54. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +2 -2
  55. data/lib/rubocop/cop/layout/space_around_operators.rb +53 -21
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  57. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +5 -0
  58. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  59. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  60. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  61. data/lib/rubocop/cop/lint/debugger.rb +18 -4
  62. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  63. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  64. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  65. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  66. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  67. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  68. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  69. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  70. data/lib/rubocop/cop/lint/mixed_case_range.rb +3 -1
  71. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  72. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -7
  73. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  74. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  75. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +10 -0
  76. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  77. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +63 -4
  78. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  79. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  80. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +14 -8
  81. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  82. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  83. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -1
  84. data/lib/rubocop/cop/lint/struct_new_override.rb +12 -12
  85. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  86. data/lib/rubocop/cop/lint/symbol_conversion.rb +8 -3
  87. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  88. data/lib/rubocop/cop/lint/to_enum_arguments.rb +5 -3
  89. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  92. data/lib/rubocop/cop/lint/useless_assignment.rb +38 -12
  93. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  94. data/lib/rubocop/cop/lint/void.rb +46 -21
  95. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  96. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  97. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  98. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  99. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +32 -4
  100. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  101. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  102. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  103. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  104. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  105. data/lib/rubocop/cop/mixin/heredoc.rb +6 -2
  106. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
  107. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +6 -8
  108. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  109. data/lib/rubocop/cop/mixin/string_help.rb +4 -2
  110. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  111. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  112. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  113. data/lib/rubocop/cop/naming/file_name.rb +1 -1
  114. data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +3 -1
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  116. data/lib/rubocop/cop/registry.rb +4 -4
  117. data/lib/rubocop/cop/security/open.rb +2 -2
  118. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  119. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  120. data/lib/rubocop/cop/style/alias.rb +9 -8
  121. data/lib/rubocop/cop/style/arguments_forwarding.rb +355 -63
  122. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  123. data/lib/rubocop/cop/style/array_intersect.rb +13 -5
  124. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  125. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  126. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  127. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  128. data/lib/rubocop/cop/style/class_check.rb +1 -0
  129. data/lib/rubocop/cop/style/class_equality_comparison.rb +7 -0
  130. data/lib/rubocop/cop/style/collection_compact.rb +7 -6
  131. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  132. data/lib/rubocop/cop/style/combinable_loops.rb +17 -9
  133. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -1
  134. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  135. data/lib/rubocop/cop/style/date_time.rb +5 -4
  136. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  137. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  138. data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
  139. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  140. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  141. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  142. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  143. data/lib/rubocop/cop/style/for.rb +1 -1
  144. data/lib/rubocop/cop/style/format_string.rb +24 -3
  145. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -1
  146. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  147. data/lib/rubocop/cop/style/hash_conversion.rb +10 -0
  148. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  149. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  150. data/lib/rubocop/cop/style/identical_conditional_branches.rb +28 -3
  151. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  152. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  153. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  154. data/lib/rubocop/cop/style/lambda.rb +3 -3
  155. data/lib/rubocop/cop/style/lambda_call.rb +5 -0
  156. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  157. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  158. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +22 -6
  159. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  160. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  161. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  162. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  163. data/lib/rubocop/cop/style/mixin_grouping.rb +1 -1
  164. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  165. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +2 -4
  166. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  167. data/lib/rubocop/cop/style/next.rb +1 -1
  168. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  169. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  170. data/lib/rubocop/cop/style/operator_method_call.rb +8 -2
  171. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  172. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  173. data/lib/rubocop/cop/style/redundant_argument.rb +9 -3
  174. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  175. data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
  176. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  177. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  178. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  179. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  180. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  181. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  182. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  183. data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -21
  184. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +18 -4
  185. data/lib/rubocop/cop/style/redundant_return.rb +8 -3
  186. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  187. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +5 -0
  188. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  189. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  190. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  191. data/lib/rubocop/cop/style/return_nil.rb +6 -2
  192. data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +23 -9
  193. data/lib/rubocop/cop/style/sample.rb +2 -1
  194. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  195. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  196. data/lib/rubocop/cop/style/semicolon.rb +8 -3
  197. data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
  198. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  199. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  200. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
  201. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  202. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +30 -5
  203. data/lib/rubocop/cop/style/strip.rb +7 -4
  204. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  205. data/lib/rubocop/cop/style/symbol_array.rb +35 -15
  206. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  207. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  208. data/lib/rubocop/cop/style/yoda_condition.rb +4 -2
  209. data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
  210. data/lib/rubocop/cop/utils/regexp_ranges.rb +26 -13
  211. data/lib/rubocop/cop/variable_force/assignment.rb +14 -5
  212. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  213. data/lib/rubocop/ext/regexp_node.rb +9 -4
  214. data/lib/rubocop/file_finder.rb +4 -7
  215. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  216. data/lib/rubocop/formatter/html_formatter.rb +5 -4
  217. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  218. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  219. data/lib/rubocop/formatter.rb +1 -1
  220. data/lib/rubocop/lsp/routes.rb +42 -19
  221. data/lib/rubocop/lsp/runtime.rb +22 -2
  222. data/lib/rubocop/lsp/server.rb +10 -4
  223. data/lib/rubocop/magic_comment.rb +12 -10
  224. data/lib/rubocop/options.rb +0 -8
  225. data/lib/rubocop/result_cache.rb +4 -1
  226. data/lib/rubocop/rspec/shared_contexts.rb +8 -3
  227. data/lib/rubocop/rspec/support.rb +1 -0
  228. data/lib/rubocop/runner.rb +6 -4
  229. data/lib/rubocop/server/client_command/exec.rb +1 -1
  230. data/lib/rubocop/string_interpreter.rb +3 -3
  231. data/lib/rubocop/target_finder.rb +7 -3
  232. data/lib/rubocop/target_ruby.rb +9 -5
  233. data/lib/rubocop/version.rb +1 -1
  234. data/lib/rubocop.rb +6 -0
  235. metadata +20 -13
  236. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -17,12 +17,11 @@ module RuboCop
17
17
  include Parentheses
18
18
  extend AutoCorrector
19
19
 
20
+ ALLOWED_NODE_TYPES = %i[and or send splat kwsplat].freeze
21
+
20
22
  # @!method square_brackets?(node)
21
23
  def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'
22
24
 
23
- # @!method range_end?(node)
24
- def_node_matcher :range_end?, '^^{irange erange}'
25
-
26
25
  # @!method method_node_and_args(node)
27
26
  def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'
28
27
 
@@ -54,7 +53,7 @@ module RuboCop
54
53
  def ignore_syntax?(node)
55
54
  return false unless (parent = node.parent)
56
55
 
57
- parent.while_post_type? || parent.until_post_type? ||
56
+ parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
58
57
  like_method_argument_parentheses?(parent)
59
58
  end
60
59
 
@@ -62,7 +61,8 @@ module RuboCop
62
61
  allowed_ancestor?(node) ||
63
62
  allowed_method_call?(node) ||
64
63
  allowed_multiple_expression?(node) ||
65
- allowed_ternary?(node)
64
+ allowed_ternary?(node) ||
65
+ node.parent&.range_type?
66
66
  end
67
67
 
68
68
  def allowed_ancestor?(node)
@@ -98,7 +98,9 @@ module RuboCop
98
98
  end
99
99
 
100
100
  def like_method_argument_parentheses?(node)
101
- node.send_type? && node.arguments.one? && !node.parenthesized? &&
101
+ return false if !node.send_type? && !node.super_type? && !node.yield_type?
102
+
103
+ node.arguments.one? && !node.parenthesized? &&
102
104
  !node.arithmetic_operation? && node.first_argument.begin_type?
103
105
  end
104
106
 
@@ -109,34 +111,69 @@ module RuboCop
109
111
 
110
112
  def first_arg_begins_with_hash_literal?(node)
111
113
  # Don't flag `method ({key: value})` or `method ({key: value}.method)`
112
- method_chain_begins_with_hash_literal?(node.children.first) &&
113
- first_argument?(node) &&
114
- !parentheses?(node.parent)
114
+ hash_literal = method_chain_begins_with_hash_literal(node.children.first)
115
+ if (root_method = node.each_ancestor(:send).to_a.last)
116
+ parenthesized = root_method.parenthesized_call?
117
+ end
118
+ hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
115
119
  end
116
120
 
117
- def method_chain_begins_with_hash_literal?(node)
118
- return false if node.nil?
119
- return true if node.hash_type?
120
- return false unless node.send_type?
121
+ def method_chain_begins_with_hash_literal(node)
122
+ return if node.nil?
123
+ return node if node.hash_type?
124
+ return unless node.send_type?
121
125
 
122
- method_chain_begins_with_hash_literal?(node.children.first)
126
+ method_chain_begins_with_hash_literal(node.children.first)
123
127
  end
124
128
 
125
129
  def check(begin_node)
126
130
  node = begin_node.children.first
127
- return offense(begin_node, 'a keyword') if keyword_with_redundant_parentheses?(node)
128
- return offense(begin_node, 'a literal') if disallowed_literal?(begin_node, node)
129
- return offense(begin_node, 'a variable') if node.variable?
130
- return offense(begin_node, 'a constant') if node.const_type?
131
131
 
132
- return offense(begin_node, 'an interpolated expression') if interpolation?(begin_node)
132
+ if (message = find_offense_message(begin_node, node))
133
+ return offense(begin_node, message)
134
+ end
133
135
 
134
136
  check_send(begin_node, node) if node.call_type?
135
137
  end
136
138
 
139
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
140
+ def find_offense_message(begin_node, node)
141
+ return 'a keyword' if keyword_with_redundant_parentheses?(node)
142
+ return 'a literal' if disallowed_literal?(begin_node, node)
143
+ return 'a variable' if node.variable?
144
+ return 'a constant' if node.const_type?
145
+ if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
146
+ return 'an expression'
147
+ end
148
+ return 'an interpolated expression' if interpolation?(begin_node)
149
+
150
+ return if begin_node.chained?
151
+
152
+ if node.and_type? || node.or_type?
153
+ return if node.semantic_operator? && begin_node.parent
154
+ return if node.multiline? && allow_in_multiline_conditions?
155
+ return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
156
+ return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
157
+
158
+ 'a logical expression'
159
+ elsif node.respond_to?(:comparison_method?) && node.comparison_method?
160
+ return unless begin_node.parent.nil?
161
+
162
+ 'a comparison expression'
163
+ end
164
+ end
165
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
166
+
137
167
  # @!method interpolation?(node)
138
168
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
139
169
 
170
+ def allow_in_multiline_conditions?
171
+ parentheses_around_condition_config = config.for_cop('Style/ParenthesesAroundCondition')
172
+ return false unless parentheses_around_condition_config['Enabled']
173
+
174
+ !!parentheses_around_condition_config['AllowInMultilineConditions']
175
+ end
176
+
140
177
  def check_send(begin_node, node)
141
178
  return check_unary(begin_node, node) if node.unary_operation?
142
179
 
@@ -201,7 +238,6 @@ module RuboCop
201
238
  def method_call_with_redundant_parentheses?(node)
202
239
  return false unless node.call_type?
203
240
  return false if node.prefix_not?
204
- return false if range_end?(node)
205
241
 
206
242
  send_node, args = method_node_and_args(node)
207
243
 
@@ -213,7 +249,13 @@ module RuboCop
213
249
  end
214
250
 
215
251
  def first_argument?(node)
216
- first_send_argument?(node) || first_super_argument?(node) || first_yield_argument?(node)
252
+ if first_send_argument?(node) ||
253
+ first_super_argument?(node) ||
254
+ first_yield_argument?(node)
255
+ return true
256
+ end
257
+
258
+ node.each_ancestor.any? { |ancestor| first_argument?(ancestor) }
217
259
  end
218
260
 
219
261
  # @!method first_send_argument?(node)
@@ -40,7 +40,9 @@ module RuboCop
40
40
  byteindex byterindex gsub gsub! partition rpartition scan split start_with? sub sub!
41
41
  ].freeze
42
42
  DETERMINISTIC_REGEX = /\A(?:#{LITERAL_REGEX})+\Z/.freeze
43
- STR_SPECIAL_CHARS = %w[\n \" \' \\\\ \t \b \f \r].freeze
43
+ STR_SPECIAL_CHARS = %w[
44
+ \a \c \C \e \f \M \n \" \' \\\\ \t \b \f \r \u \v \x \0 \1 \2 \3 \4 \5 \6 \7
45
+ ].freeze
44
46
 
45
47
  def on_send(node)
46
48
  return unless (regexp_node = node.first_argument)
@@ -48,15 +50,14 @@ module RuboCop
48
50
  return if !regexp_node.regopt.children.empty? || regexp_node.content == ' '
49
51
  return unless determinist_regexp?(regexp_node)
50
52
 
51
- new_argument = replacement(regexp_node)
52
- quote = new_argument.include?('"') ? "'" : '"'
53
- prefer = "#{quote}#{new_argument}#{quote}"
53
+ prefer = preferred_argument(regexp_node)
54
54
  message = format(MSG, prefer: prefer, current: regexp_node.source)
55
55
 
56
56
  add_offense(regexp_node, message: message) do |corrector|
57
57
  corrector.replace(regexp_node, prefer)
58
58
  end
59
59
  end
60
+ alias on_csend on_send
60
61
 
61
62
  private
62
63
 
@@ -64,6 +65,19 @@ module RuboCop
64
65
  DETERMINISTIC_REGEX.match?(regexp_node.source)
65
66
  end
66
67
 
68
+ def preferred_argument(regexp_node)
69
+ new_argument = replacement(regexp_node)
70
+
71
+ if new_argument.include?('"')
72
+ new_argument.gsub!("'", "\\\\'")
73
+ quote = "'"
74
+ else
75
+ quote = '"'
76
+ end
77
+
78
+ "#{quote}#{new_argument}#{quote}"
79
+ end
80
+
67
81
  def replacement(regexp_node)
68
82
  regexp_content = regexp_node.content
69
83
  stack = []
@@ -22,13 +22,18 @@ module RuboCop
22
22
  # return something
23
23
  # end
24
24
  #
25
- # # good
25
+ # # bad
26
26
  # def test
27
27
  # return something if something_else
28
28
  # end
29
29
  #
30
30
  # # good
31
31
  # def test
32
+ # something if something_else
33
+ # end
34
+ #
35
+ # # good
36
+ # def test
32
37
  # if x
33
38
  # elsif y
34
39
  # else
@@ -53,7 +58,7 @@ module RuboCop
53
58
 
54
59
  MSG = 'Redundant `return` detected.'
55
60
  MULTI_RETURN_MSG = 'To return multiple values, use an array.'
56
- RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
61
+ RESTRICT_ON_SEND = %i[define_method define_singleton_method lambda].freeze
57
62
 
58
63
  def on_send(node)
59
64
  return unless (parent = node.parent) && parent.block_type?
@@ -136,7 +141,7 @@ module RuboCop
136
141
  end
137
142
 
138
143
  def check_if_node(node)
139
- return if node.modifier_form? || node.ternary?
144
+ return if node.ternary?
140
145
 
141
146
  check_branch(node.if_branch)
142
147
  check_branch(node.else_branch)
@@ -17,7 +17,8 @@ module RuboCop
17
17
  # protected scope, you cannot send private messages this way.
18
18
  #
19
19
  # Note we allow uses of `self` with operators because it would be awkward
20
- # otherwise.
20
+ # otherwise. Also allows the use of `self.it` without arguments in blocks,
21
+ # as in `0.times { self.it }`, following `Lint/ItWithoutArgumentsInBlock` cop.
21
22
  #
22
23
  # @example
23
24
  #
@@ -107,8 +108,8 @@ module RuboCop
107
108
  def on_send(node)
108
109
  return unless node.self_receiver? && regular_method_call?(node)
109
110
  return if node.parent&.mlhs_type?
110
-
111
111
  return if allowed_send_node?(node)
112
+ return if it_method_in_block?(node)
112
113
 
113
114
  add_offense(node.receiver) do |corrector|
114
115
  corrector.remove(node.receiver)
@@ -155,6 +156,20 @@ module RuboCop
155
156
  KERNEL_METHODS.include?(node.method_name)
156
157
  end
157
158
 
159
+ # Respects `Lint/ItWithoutArgumentsInBlock` cop and the following Ruby 3.3's warning:
160
+ #
161
+ # $ ruby -e '0.times { begin; it; end }'
162
+ # -e:1: warning: `it` calls without arguments will refer to the first block param in
163
+ # Ruby 3.4; use it() or self.it
164
+ #
165
+ def it_method_in_block?(node)
166
+ return false unless node.method?(:it)
167
+ return false unless (block_node = node.each_ancestor(:block).first)
168
+ return false unless block_node.arguments.empty_and_without_delimiters?
169
+
170
+ node.arguments.empty? && !node.block_literal?
171
+ end
172
+
158
173
  def regular_method_call?(node)
159
174
  !(node.operator_method? ||
160
175
  KEYWORDS.include?(node.method_name) ||
@@ -75,6 +75,11 @@ module RuboCop
75
75
  add_offense(offense_branch) do |corrector|
76
76
  assignment_value = opposite_branch ? opposite_branch.source : 'nil'
77
77
  replacement = "#{assignment_value} #{keyword} #{if_node.condition.source}"
78
+ if opposite_branch.respond_to?(:heredoc?) && opposite_branch.heredoc?
79
+ replacement += opposite_branch.loc.heredoc_end.with(
80
+ begin_pos: opposite_branch.source_range.end_pos
81
+ ).source
82
+ end
78
83
 
79
84
  corrector.replace(if_node, replacement)
80
85
  end
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # This cop is unsafe, because `sort...last` and `max` may not return the
17
17
  # same element in all cases.
18
18
  #
19
- # In an enumerable where there are multiple elements where `a <=> b == 0`,
19
+ # In an enumerable where there are multiple elements where ``a <=> b == 0``,
20
20
  # or where the transformation done by the `sort_by` block has the
21
21
  # same result, `sort.last` and `max` (or `sort_by.last` and `max_by`)
22
22
  # will return different elements. `sort.last` will return the last
@@ -87,15 +87,15 @@ module RuboCop
87
87
  # @!method redundant_sort?(node)
88
88
  def_node_matcher :redundant_sort?, <<~MATCHER
89
89
  {
90
- (send $(send _ $:sort) ${:last :first})
91
- (send $(send _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
90
+ (call $(call _ $:sort) ${:last :first})
91
+ (call $(call _ $:sort) ${:[] :at :slice} {(int 0) (int -1)})
92
92
 
93
- (send $(send _ $:sort_by _) ${:last :first})
93
+ (call $(call _ $:sort_by _) ${:last :first})
94
94
  (send $(send _ $:sort_by _) ${:[] :at :slice} {(int 0) (int -1)})
95
95
 
96
- (send ({block numblock} $(send _ ${:sort_by :sort}) ...) ${:last :first})
97
- (send
98
- ({block numblock} $(send _ ${:sort_by :sort}) ...)
96
+ (call ({block numblock} $(call _ ${:sort_by :sort}) ...) ${:last :first})
97
+ (call
98
+ ({block numblock} $(call _ ${:sort_by :sort}) ...)
99
99
  ${:[] :at :slice} {(int 0) (int -1)}
100
100
  )
101
101
  }
@@ -108,6 +108,7 @@ module RuboCop
108
108
 
109
109
  register_offense(ancestor, sort_node, sorter, accessor)
110
110
  end
111
+ alias on_csend on_send
111
112
 
112
113
  private
113
114
 
@@ -180,7 +181,7 @@ module RuboCop
180
181
  end
181
182
 
182
183
  def arg_node(node)
183
- node.arguments.first
184
+ node.first_argument
184
185
  end
185
186
 
186
187
  def arg_value(node)
@@ -46,12 +46,12 @@ module RuboCop
46
46
 
47
47
  # @!method redundant_sort_by_block(node)
48
48
  def_node_matcher :redundant_sort_by_block, <<~PATTERN
49
- (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
49
+ (block $(call _ :sort_by) (args (arg $_x)) (lvar _x))
50
50
  PATTERN
51
51
 
52
52
  # @!method redundant_sort_by_numblock(node)
53
53
  def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
54
- (numblock $(send _ :sort_by) 1 (lvar :_1))
54
+ (numblock $(call _ :sort_by) 1 (lvar :_1))
55
55
  PATTERN
56
56
 
57
57
  def sort_by_range(send, node)
@@ -133,7 +133,7 @@ module RuboCop
133
133
  end
134
134
 
135
135
  def percent_array_literal?(node)
136
- (percent_w_literal?(node) || percent_w_upper_literal?(node))
136
+ percent_w_literal?(node) || percent_w_upper_literal?(node)
137
137
  end
138
138
 
139
139
  def heredoc_with_disabled_interpolation?(node)
@@ -3,9 +3,13 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Enforces consistency between 'return nil' and 'return'.
6
+ # Enforces consistency between `return nil` and `return`.
7
7
  #
8
- # Supported styles are: return, return_nil.
8
+ # This cop is disabled by default. Because there seems to be a perceived semantic difference
9
+ # between `return` and `return nil`. The former can be seen as just halting evaluation,
10
+ # while the latter might be used when the return value is of specific concern.
11
+ #
12
+ # Supported styles are `return` and `return_nil`.
9
13
  #
10
14
  # @example EnforcedStyle: return (default)
11
15
  # # bad
@@ -31,7 +31,7 @@ module RuboCop
31
31
  # do_something?
32
32
  # end
33
33
  #
34
- # @example AllowedMethod: ['foo?']
34
+ # @example AllowedMethods: ['foo?']
35
35
  # # good
36
36
  # def foo?
37
37
  # return if condition
@@ -39,7 +39,7 @@ module RuboCop
39
39
  # do_something?
40
40
  # end
41
41
  #
42
- # @example AllowedPattern: [/foo/]
42
+ # @example AllowedPatterns: [/foo/]
43
43
  # # good
44
44
  # def foo?
45
45
  # return if condition
@@ -52,7 +52,7 @@ module RuboCop
52
52
  include AllowedMethods
53
53
  include AllowedPattern
54
54
 
55
- MSG = 'Use `return false` instead of `%<prefer>s` in the predicate method.'
55
+ MSG = 'Return `false` instead of `nil` in predicate methods.'
56
56
 
57
57
  # @!method return_nil?(node)
58
58
  def_node_matcher :return_nil?, <<~PATTERN
@@ -65,16 +65,30 @@ module RuboCop
65
65
  return unless (body = node.body)
66
66
 
67
67
  body.each_descendant(:return) do |return_node|
68
- next unless return_nil?(return_node)
68
+ register_offense(return_node, 'return false') if return_nil?(return_node)
69
+ end
69
70
 
70
- message = format(MSG, prefer: return_node.source)
71
+ return unless (nil_node = nil_node_at_the_end_of_method_body(body))
71
72
 
72
- add_offense(return_node, message: message) do |corrector|
73
- corrector.replace(return_node, 'return false')
74
- end
75
- end
73
+ register_offense(nil_node, 'false')
76
74
  end
77
75
  alias on_defs on_def
76
+
77
+ private
78
+
79
+ def nil_node_at_the_end_of_method_body(body)
80
+ return body if body.nil_type?
81
+ return unless body.begin_type?
82
+ return unless (last_child = body.children.last)
83
+
84
+ last_child if last_child.is_a?(AST::Node) && last_child.nil_type?
85
+ end
86
+
87
+ def register_offense(offense_node, replacement)
88
+ add_offense(offense_node) do |corrector|
89
+ corrector.replace(offense_node, replacement)
90
+ end
91
+ end
78
92
  end
79
93
  end
80
94
  end
@@ -35,7 +35,7 @@ module RuboCop
35
35
 
36
36
  # @!method sample_candidate?(node)
37
37
  def_node_matcher :sample_candidate?, <<~PATTERN
38
- (send $(send _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
38
+ (call $(call _ :shuffle $...) ${:#{RESTRICT_ON_SEND.join(' :')}} $...)
39
39
  PATTERN
40
40
 
41
41
  def on_send(node)
@@ -52,6 +52,7 @@ module RuboCop
52
52
  end
53
53
  end
54
54
  end
55
+ alias on_csend on_send
55
56
 
56
57
  private
57
58
 
@@ -55,8 +55,8 @@ module RuboCop
55
55
  # @!method regexp_match?(node)
56
56
  def_node_matcher :regexp_match?, <<~PATTERN
57
57
  {
58
- (block send (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
- (numblock send $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
58
+ (block call (args (arg $_)) ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
59
+ (numblock call $1 ${(send _ %REGEXP_METHODS _) match-with-lvasgn})
60
60
  }
61
61
  PATTERN
62
62
 
@@ -64,9 +64,9 @@ module RuboCop
64
64
  # @!method creates_hash?(node)
65
65
  def_node_matcher :creates_hash?, <<~PATTERN
66
66
  {
67
- (send (const _ :Hash) {:new :[]} ...)
68
- (block (send (const _ :Hash) :new ...) ...)
69
- (send _ { :to_h :to_hash } ...)
67
+ (call (const _ :Hash) {:new :[]} ...)
68
+ (block (call (const _ :Hash) :new ...) ...)
69
+ (call _ { :to_h :to_hash } ...)
70
70
  }
71
71
  PATTERN
72
72
 
@@ -100,6 +100,7 @@ module RuboCop
100
100
  register_offense(node, block_node, regexp, replacement)
101
101
  end
102
102
  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
103
+ alias on_csend on_send
103
104
 
104
105
  private
105
106
 
@@ -146,7 +147,7 @@ module RuboCop
146
147
  return node.child_nodes.first if node.match_with_lvasgn_type?
147
148
 
148
149
  if node.receiver.lvar_type? &&
149
- (block.numblock_type? || node.receiver.source == block.arguments.first.source)
150
+ (block.numblock_type? || node.receiver.source == block.first_argument.source)
150
151
  node.first_argument
151
152
  elsif node.first_argument.lvar_type?
152
153
  node.receiver
@@ -16,7 +16,7 @@ module RuboCop
16
16
  extend AutoCorrector
17
17
 
18
18
  MSG = 'Use self-assignment shorthand `%<method>s=`.'
19
- OPS = %i[+ - * ** / | &].freeze
19
+ OPS = %i[+ - * ** / % ^ << >> | &].freeze
20
20
 
21
21
  def self.autocorrect_incompatible_with
22
22
  [Layout::SpaceAroundOperators]
@@ -62,9 +62,6 @@ module RuboCop
62
62
  private
63
63
 
64
64
  def check_for_line_terminator_or_opener
65
- # Make the obvious check first
66
- return unless processed_source.raw_source.include?(';')
67
-
68
65
  each_semicolon do |line, column, token_before_semicolon|
69
66
  register_semicolon(line, column, false, token_before_semicolon)
70
67
  end
@@ -83,6 +80,7 @@ module RuboCop
83
80
  processed_source.tokens.group_by(&:line)
84
81
  end
85
82
 
83
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
86
84
  def semicolon_position(tokens)
87
85
  if tokens.last.semicolon?
88
86
  -1
@@ -93,10 +91,13 @@ module RuboCop
93
91
  elsif exist_semicolon_after_left_curly_brace?(tokens) ||
94
92
  exist_semicolon_after_left_string_interpolation_brace?(tokens)
95
93
  2
94
+ elsif exist_semicolon_after_left_lambda_curly_brace?(tokens)
95
+ 3
96
96
  elsif exist_semicolon_before_right_string_interpolation_brace?(tokens)
97
97
  -4
98
98
  end
99
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
100
101
 
101
102
  def exist_semicolon_before_right_curly_brace?(tokens)
102
103
  tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
@@ -106,6 +107,10 @@ module RuboCop
106
107
  tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
107
108
  end
108
109
 
110
+ def exist_semicolon_after_left_lambda_curly_brace?(tokens)
111
+ tokens[2]&.type == :tLAMBEG && tokens[3]&.semicolon?
112
+ end
113
+
109
114
  def exist_semicolon_before_right_string_interpolation_brace?(tokens)
110
115
  tokens[-3]&.type == :tSTRING_DEND && tokens[-4]&.semicolon?
111
116
  end
@@ -3,8 +3,11 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Sometimes using dig method ends up with just a single
7
- # argument. In such cases, dig should be replaced with [].
6
+ # Sometimes using `dig` method ends up with just a single
7
+ # argument. In such cases, dig should be replaced with `[]`.
8
+ #
9
+ # Since replacing `hash&.dig(:key)` with `hash[:key]` could potentially lead to error,
10
+ # calls to the `dig` method using safe navigation will be ignored.
8
11
  #
9
12
  # @safety
10
13
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -33,6 +36,7 @@ module RuboCop
33
36
 
34
37
  MSG = 'Use `%<receiver>s[%<argument>s]` instead of `%<original>s`.'
35
38
  RESTRICT_ON_SEND = %i[dig].freeze
39
+ IGNORED_ARGUMENT_TYPES = %i[block_pass forwarded_restarg forwarded_args hash].freeze
36
40
 
37
41
  # @!method single_argument_dig?(node)
38
42
  def_node_matcher :single_argument_dig?, <<~PATTERN
@@ -44,7 +48,7 @@ module RuboCop
44
48
 
45
49
  expression = single_argument_dig?(node)
46
50
  return unless expression
47
- return if expression.forwarded_args_type?
51
+ return if IGNORED_ARGUMENT_TYPES.include?(expression.type)
48
52
 
49
53
  receiver = node.receiver.source
50
54
  argument = expression.source
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for single-line `do`...`end` block.
7
+ #
8
+ # In practice a single line `do`...`end` is autocorrected when `EnforcedStyle: semantic`
9
+ # in `Style/BlockDelimiters`. The autocorrection maintains the `do` ... `end` syntax to
10
+ # preserve semantics and does not change it to `{`...`}` block.
11
+ #
12
+ # @example
13
+ #
14
+ # # bad
15
+ # foo do |arg| bar(arg) end
16
+ #
17
+ # # good
18
+ # foo do |arg|
19
+ # bar(arg)
20
+ # end
21
+ #
22
+ # # bad
23
+ # ->(arg) do bar(arg) end
24
+ #
25
+ # # good
26
+ # ->(arg) { bar(arg) }
27
+ #
28
+ class SingleLineDoEndBlock < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Prefer multiline `do`...`end` block.'
32
+
33
+ # rubocop:disable Metrics/AbcSize
34
+ def on_block(node)
35
+ return if !node.single_line? || node.braces?
36
+
37
+ add_offense(node) do |corrector|
38
+ corrector.insert_after(do_line(node), "\n")
39
+
40
+ node_body = node.body
41
+
42
+ if node_body.respond_to?(:heredoc?) && node_body.heredoc?
43
+ corrector.remove(node.loc.end)
44
+ corrector.insert_after(node_body.loc.heredoc_end, "\nend")
45
+ else
46
+ corrector.insert_before(node.loc.end, "\n")
47
+ end
48
+ end
49
+ end
50
+ # rubocop:enable Metrics/AbcSize
51
+ alias on_numblock on_block
52
+
53
+ private
54
+
55
+ def do_line(node)
56
+ if node.numblock_type? || node.arguments.children.empty? || node.send_node.lambda_literal?
57
+ node.loc.begin
58
+ else
59
+ node.arguments
60
+ end
61
+ end
62
+
63
+ def x(corrector, node); end
64
+ end
65
+ end
66
+ end
67
+ end