rubocop 1.56.4 → 1.60.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +4 -4
  4. data/config/default.yml +57 -3
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +10 -5
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/config.rb +0 -2
  8. data/lib/rubocop/config_loader.rb +0 -1
  9. data/lib/rubocop/config_obsoletion.rb +11 -8
  10. data/lib/rubocop/config_validator.rb +0 -2
  11. data/lib/rubocop/cop/base.rb +6 -0
  12. data/lib/rubocop/cop/bundler/gem_comment.rb +2 -2
  13. data/lib/rubocop/cop/exclude_limit.rb +1 -1
  14. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +2 -2
  15. data/lib/rubocop/cop/internal_affairs/method_name_equal.rb +19 -20
  16. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +53 -0
  17. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -2
  18. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  19. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  20. data/lib/rubocop/cop/layout/end_alignment.rb +12 -2
  21. data/lib/rubocop/cop/layout/extra_spacing.rb +4 -10
  22. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +22 -7
  23. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +1 -1
  24. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +2 -2
  25. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  26. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  27. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -1
  28. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +16 -1
  29. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -1
  30. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +4 -4
  31. data/lib/rubocop/cop/layout/single_line_block_chain.rb +5 -0
  32. data/lib/rubocop/cop/layout/space_around_operators.rb +50 -20
  33. data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
  34. data/lib/rubocop/cop/lint/assignment_in_condition.rb +4 -4
  35. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +2 -2
  36. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  37. data/lib/rubocop/cop/lint/debugger.rb +11 -1
  38. data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
  39. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  40. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -3
  41. data/lib/rubocop/cop/lint/float_comparison.rb +10 -0
  42. data/lib/rubocop/cop/lint/hash_compare_by_identity.rb +2 -1
  43. data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +56 -0
  44. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +85 -0
  45. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  46. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  47. data/lib/rubocop/cop/lint/next_without_accumulator.rb +6 -21
  48. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
  49. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +3 -5
  50. data/lib/rubocop/cop/lint/number_conversion.rb +9 -4
  51. data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
  52. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +63 -4
  53. data/lib/rubocop/cop/lint/redundant_with_index.rb +2 -2
  54. data/lib/rubocop/cop/lint/redundant_with_object.rb +2 -2
  55. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -4
  56. data/lib/rubocop/cop/lint/self_assignment.rb +38 -0
  57. data/lib/rubocop/cop/lint/shadowed_argument.rb +1 -0
  58. data/lib/rubocop/cop/lint/symbol_conversion.rb +7 -2
  59. data/lib/rubocop/cop/lint/syntax.rb +6 -3
  60. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  61. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +2 -2
  62. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  63. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  64. data/lib/rubocop/cop/lint/void.rb +43 -12
  65. data/lib/rubocop/cop/metrics/abc_size.rb +3 -3
  66. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  67. data/lib/rubocop/cop/metrics/class_length.rb +8 -3
  68. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
  69. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  70. data/lib/rubocop/cop/mixin/comments_help.rb +16 -12
  71. data/lib/rubocop/cop/mixin/configurable_formatting.rb +1 -0
  72. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +14 -11
  73. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  74. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  75. data/lib/rubocop/cop/naming/block_forwarding.rb +12 -4
  76. data/lib/rubocop/cop/naming/constant_name.rb +1 -2
  77. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  78. data/lib/rubocop/cop/security/open.rb +2 -2
  79. data/lib/rubocop/cop/style/access_modifier_declarations.rb +2 -2
  80. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  81. data/lib/rubocop/cop/style/arguments_forwarding.rb +120 -17
  82. data/lib/rubocop/cop/style/array_first_last.rb +64 -0
  83. data/lib/rubocop/cop/style/auto_resource_cleanup.rb +21 -14
  84. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +2 -2
  85. data/lib/rubocop/cop/style/case_like_if.rb +4 -4
  86. data/lib/rubocop/cop/style/class_check.rb +1 -0
  87. data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
  88. data/lib/rubocop/cop/style/collection_compact.rb +18 -8
  89. data/lib/rubocop/cop/style/combinable_loops.rb +13 -7
  90. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -0
  91. data/lib/rubocop/cop/style/conditional_assignment.rb +2 -2
  92. data/lib/rubocop/cop/style/date_time.rb +5 -4
  93. data/lib/rubocop/cop/style/each_for_simple_loop.rb +7 -7
  94. data/lib/rubocop/cop/style/each_with_object.rb +2 -2
  95. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  96. data/lib/rubocop/cop/style/eval_with_location.rb +3 -14
  97. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  98. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  99. data/lib/rubocop/cop/style/format_string.rb +24 -3
  100. data/lib/rubocop/cop/style/guard_clause.rb +26 -0
  101. data/lib/rubocop/cop/style/hash_each_methods.rb +83 -10
  102. data/lib/rubocop/cop/style/hash_except.rb +2 -1
  103. data/lib/rubocop/cop/style/identical_conditional_branches.rb +28 -3
  104. data/lib/rubocop/cop/style/inverse_methods.rb +6 -5
  105. data/lib/rubocop/cop/style/invertible_unless_condition.rb +39 -2
  106. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +3 -2
  107. data/lib/rubocop/cop/style/map_to_hash.rb +17 -7
  108. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +14 -5
  109. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -4
  110. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +20 -0
  111. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  112. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +2 -2
  113. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  114. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -3
  115. data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
  116. data/lib/rubocop/cop/style/next.rb +1 -1
  117. data/lib/rubocop/cop/style/numeric_literal_prefix.rb +1 -1
  118. data/lib/rubocop/cop/style/operator_method_call.rb +2 -2
  119. data/lib/rubocop/cop/style/parallel_assignment.rb +2 -2
  120. data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -0
  121. data/lib/rubocop/cop/style/redundant_argument.rb +3 -2
  122. data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
  123. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +93 -5
  124. data/lib/rubocop/cop/style/redundant_each.rb +7 -4
  125. data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
  126. data/lib/rubocop/cop/style/redundant_fetch_block.rb +3 -3
  127. data/lib/rubocop/cop/style/redundant_filter_chain.rb +22 -5
  128. data/lib/rubocop/cop/style/redundant_line_continuation.rb +10 -1
  129. data/lib/rubocop/cop/style/redundant_parentheses.rb +68 -21
  130. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  131. data/lib/rubocop/cop/style/redundant_self.rb +17 -2
  132. data/lib/rubocop/cop/style/redundant_sort.rb +9 -8
  133. data/lib/rubocop/cop/style/redundant_sort_by.rb +2 -2
  134. data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -1
  135. data/lib/rubocop/cop/style/sample.rb +2 -1
  136. data/lib/rubocop/cop/style/select_by_regexp.rb +7 -6
  137. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  138. data/lib/rubocop/cop/style/semicolon.rb +8 -0
  139. data/lib/rubocop/cop/style/single_argument_dig.rb +7 -3
  140. data/lib/rubocop/cop/style/single_line_do_end_block.rb +67 -0
  141. data/lib/rubocop/cop/style/slicing_with_range.rb +76 -10
  142. data/lib/rubocop/cop/style/string_chars.rb +1 -0
  143. data/lib/rubocop/cop/style/strip.rb +7 -4
  144. data/lib/rubocop/cop/style/super_with_args_parentheses.rb +35 -0
  145. data/lib/rubocop/cop/style/symbol_proc.rb +36 -0
  146. data/lib/rubocop/cop/style/unpack_first.rb +11 -14
  147. data/lib/rubocop/cops_documentation_generator.rb +11 -1
  148. data/lib/rubocop/ext/regexp_node.rb +9 -4
  149. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  150. data/lib/rubocop/formatter/html_formatter.rb +5 -4
  151. data/lib/rubocop/formatter/json_formatter.rb +0 -1
  152. data/lib/rubocop/formatter.rb +1 -1
  153. data/lib/rubocop/lsp/routes.rb +1 -1
  154. data/lib/rubocop/options.rb +0 -8
  155. data/lib/rubocop/result_cache.rb +0 -1
  156. data/lib/rubocop/rspec/shared_contexts.rb +6 -0
  157. data/lib/rubocop/rspec/support.rb +1 -0
  158. data/lib/rubocop/runner.rb +1 -1
  159. data/lib/rubocop/server/cache.rb +1 -1
  160. data/lib/rubocop/version.rb +1 -1
  161. data/lib/rubocop.rb +5 -0
  162. metadata +16 -24
  163. /data/lib/rubocop/formatter/{git_hub_actions_formatter.rb → github_actions_formatter.rb} +0 -0
@@ -5,17 +5,21 @@ module RuboCop
5
5
  module Style
6
6
  # Checks for RuntimeError as the argument of raise/fail.
7
7
  #
8
- # It checks for code like this:
9
- #
10
8
  # @example
11
- # # Bad
9
+ # # bad
12
10
  # raise RuntimeError, 'message'
13
- #
14
- # # Bad
15
11
  # raise RuntimeError.new('message')
16
12
  #
17
- # # Good
13
+ # # good
18
14
  # raise 'message'
15
+ #
16
+ # # bad - message is not a string
17
+ # raise RuntimeError, Object.new
18
+ # raise RuntimeError.new(Object.new)
19
+ #
20
+ # # good
21
+ # raise Object.new.to_s
22
+ #
19
23
  class RedundantException < Base
20
24
  extend AutoCorrector
21
25
 
@@ -30,26 +34,42 @@ module RuboCop
30
34
  fix_exploded(node) || fix_compact(node)
31
35
  end
32
36
 
37
+ private
38
+
33
39
  def fix_exploded(node)
34
40
  exploded?(node) do |command, message|
35
41
  add_offense(node, message: MSG_1) do |corrector|
36
- if node.parenthesized?
37
- corrector.replace(node, "#{command}(#{message.source})")
38
- else
39
- corrector.replace(node, "#{command} #{message.source}")
40
- end
42
+ corrector.replace(node, replaced_exploded(node, command, message))
41
43
  end
42
44
  end
43
45
  end
44
46
 
47
+ def replaced_exploded(node, command, message)
48
+ arg = string_message?(message) ? message.source : "#{message.source}.to_s"
49
+ arg = node.parenthesized? ? "(#{arg})" : " #{arg}"
50
+ "#{command}#{arg}"
51
+ end
52
+
53
+ def string_message?(message)
54
+ message.str_type? || message.dstr_type? || message.xstr_type?
55
+ end
56
+
45
57
  def fix_compact(node)
46
58
  compact?(node) do |new_call, message|
47
59
  add_offense(node, message: MSG_2) do |corrector|
48
- corrector.replace(new_call, message.source)
60
+ corrector.replace(new_call, replaced_compact(message))
49
61
  end
50
62
  end
51
63
  end
52
64
 
65
+ def replaced_compact(message)
66
+ if string_message?(message)
67
+ message.source
68
+ else
69
+ "#{message.source}.to_s"
70
+ end
71
+ end
72
+
53
73
  # @!method exploded?(node)
54
74
  def_node_matcher :exploded?, <<~PATTERN
55
75
  (send nil? ${:raise :fail} (const {nil? cbase} :RuntimeError) $_)
@@ -47,7 +47,7 @@ module RuboCop
47
47
  # @!method redundant_fetch_block_candidate?(node)
48
48
  def_node_matcher :redundant_fetch_block_candidate?, <<~PATTERN
49
49
  (block
50
- $(send _ :fetch _)
50
+ $(call _ :fetch _)
51
51
  (args)
52
52
  ${nil? #basic_literal? #const_type?})
53
53
  PATTERN
@@ -61,10 +61,10 @@ module RuboCop
61
61
  bad = build_bad_method(send, body)
62
62
 
63
63
  add_offense(range, message: format(MSG, good: good, bad: bad)) do |corrector|
64
- receiver, _, key = send.children
64
+ _, _, key = send.children
65
65
  default_value = body ? body.source : 'nil'
66
66
 
67
- corrector.replace(node, "#{receiver.source}.fetch(#{key.source}, #{default_value})")
67
+ corrector.replace(range, "fetch(#{key.source}, #{default_value})")
68
68
  end
69
69
  end
70
70
  end
@@ -6,6 +6,12 @@ module RuboCop
6
6
  # Identifies usages of `any?`, `empty?` or `none?` predicate methods
7
7
  # chained to `select`/`filter`/`find_all` and change them to use predicate method instead.
8
8
  #
9
+ # @safety
10
+ # This cop's autocorrection is unsafe because `array.select.any?` evaluates all elements
11
+ # through the `select` method, while `array.any?` uses short-circuit evaluation.
12
+ # In other words, `array.select.any?` guarantees the evaluation of every element,
13
+ # but `array.any?` does not necessarily evaluate all of them.
14
+ #
9
15
  # @example
10
16
  # # bad
11
17
  # arr.select { |x| x > 1 }.any?
@@ -28,6 +34,9 @@ module RuboCop
28
34
  # # good
29
35
  # arr.select { |x| x > 1 }.many?
30
36
  #
37
+ # # good
38
+ # arr.select { |x| x > 1 }.present?
39
+ #
31
40
  # @example AllCops:ActiveSupportExtensionsEnabled: true
32
41
  # # bad
33
42
  # arr.select { |x| x > 1 }.many?
@@ -35,20 +44,26 @@ module RuboCop
35
44
  # # good
36
45
  # arr.many? { |x| x > 1 }
37
46
  #
47
+ # # bad
48
+ # arr.select { |x| x > 1 }.present?
49
+ #
50
+ # # good
51
+ # arr.any? { |x| x > 1 }
52
+ #
38
53
  class RedundantFilterChain < Base
39
54
  extend AutoCorrector
40
55
 
41
56
  MSG = 'Use `%<prefer>s` instead of `%<first_method>s.%<second_method>s`.'
42
57
 
43
- RAILS_METHODS = %i[many?].freeze
58
+ RAILS_METHODS = %i[many? present?].freeze
44
59
  RESTRICT_ON_SEND = (%i[any? empty? none? one?] + RAILS_METHODS).freeze
45
60
 
46
61
  # @!method select_predicate?(node)
47
62
  def_node_matcher :select_predicate?, <<~PATTERN
48
- (send
63
+ (call
49
64
  {
50
- (block $(send _ {:select :filter :find_all}) ...)
51
- $(send _ {:select :filter :find_all} block_pass_type?)
65
+ (block $(call _ {:select :filter :find_all}) ...)
66
+ $(call _ {:select :filter :find_all} block_pass_type?)
52
67
  }
53
68
  ${:#{RESTRICT_ON_SEND.join(' :')}})
54
69
  PATTERN
@@ -58,7 +73,8 @@ module RuboCop
58
73
  empty?: :none?,
59
74
  none?: :none?,
60
75
  one?: :one?,
61
- many?: :many?
76
+ many?: :many?,
77
+ present?: :any?
62
78
  }.freeze
63
79
  private_constant :REPLACEMENT_METHODS
64
80
 
@@ -71,6 +87,7 @@ module RuboCop
71
87
  register_offense(select_node, node)
72
88
  end
73
89
  end
90
+ alias on_csend on_send
74
91
 
75
92
  private
76
93
 
@@ -94,7 +94,8 @@ module RuboCop
94
94
  !ends_with_backslash_without_comment?(range.source_line) ||
95
95
  string_concatenation?(range.source_line) ||
96
96
  start_with_arithmetic_operator?(processed_source[range.line]) ||
97
- inside_string_literal_or_method_with_argument?(range)
97
+ inside_string_literal_or_method_with_argument?(range) ||
98
+ leading_dot_method_chain_with_blank_line?(range)
98
99
  end
99
100
 
100
101
  def ends_with_backslash_without_comment?(source_line)
@@ -107,10 +108,18 @@ module RuboCop
107
108
 
108
109
  def inside_string_literal_or_method_with_argument?(range)
109
110
  processed_source.tokens.each_cons(2).any? do |token, next_token|
111
+ next if token.line == next_token.line
112
+
110
113
  inside_string_literal?(range, token) || method_with_argument?(token, next_token)
111
114
  end
112
115
  end
113
116
 
117
+ def leading_dot_method_chain_with_blank_line?(range)
118
+ return false unless range.source_line.strip.start_with?('.', '&.')
119
+
120
+ processed_source[range.line].strip.empty?
121
+ end
122
+
114
123
  def redundant_line_continuation?(range)
115
124
  return true unless (node = find_node_for_line(range.line))
116
125
  return false if argument_newline?(node)
@@ -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,15 +53,16 @@ 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? ||
58
- like_method_argument_parentheses?(parent)
56
+ parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
57
+ like_method_argument_parentheses?(parent) || multiline_control_flow_statements?(node)
59
58
  end
60
59
 
61
60
  def allowed_expression?(node)
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)
@@ -104,6 +104,13 @@ module RuboCop
104
104
  !node.arithmetic_operation? && node.first_argument.begin_type?
105
105
  end
106
106
 
107
+ def multiline_control_flow_statements?(node)
108
+ return false unless (parent = node.parent)
109
+ return false if parent.single_line?
110
+
111
+ parent.return_type? || parent.next_type? || parent.break_type?
112
+ end
113
+
107
114
  def empty_parentheses?(node)
108
115
  # Don't flag `()`
109
116
  node.children.empty?
@@ -111,34 +118,69 @@ module RuboCop
111
118
 
112
119
  def first_arg_begins_with_hash_literal?(node)
113
120
  # Don't flag `method ({key: value})` or `method ({key: value}.method)`
114
- method_chain_begins_with_hash_literal?(node.children.first) &&
115
- first_argument?(node) &&
116
- !parentheses?(node.parent)
121
+ hash_literal = method_chain_begins_with_hash_literal(node.children.first)
122
+ if (root_method = node.each_ancestor(:send).to_a.last)
123
+ parenthesized = root_method.parenthesized_call?
124
+ end
125
+ hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
117
126
  end
118
127
 
119
- def method_chain_begins_with_hash_literal?(node)
120
- return false if node.nil?
121
- return true if node.hash_type?
122
- return false unless node.send_type?
128
+ def method_chain_begins_with_hash_literal(node)
129
+ return if node.nil?
130
+ return node if node.hash_type?
131
+ return unless node.send_type?
123
132
 
124
- method_chain_begins_with_hash_literal?(node.children.first)
133
+ method_chain_begins_with_hash_literal(node.children.first)
125
134
  end
126
135
 
127
136
  def check(begin_node)
128
137
  node = begin_node.children.first
129
- return offense(begin_node, 'a keyword') if keyword_with_redundant_parentheses?(node)
130
- return offense(begin_node, 'a literal') if disallowed_literal?(begin_node, node)
131
- return offense(begin_node, 'a variable') if node.variable?
132
- return offense(begin_node, 'a constant') if node.const_type?
133
138
 
134
- return offense(begin_node, 'an interpolated expression') if interpolation?(begin_node)
139
+ if (message = find_offense_message(begin_node, node))
140
+ return offense(begin_node, message)
141
+ end
135
142
 
136
143
  check_send(begin_node, node) if node.call_type?
137
144
  end
138
145
 
146
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
147
+ def find_offense_message(begin_node, node)
148
+ return 'a keyword' if keyword_with_redundant_parentheses?(node)
149
+ return 'a literal' if disallowed_literal?(begin_node, node)
150
+ return 'a variable' if node.variable?
151
+ return 'a constant' if node.const_type?
152
+ if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
153
+ return 'an expression'
154
+ end
155
+ return 'an interpolated expression' if interpolation?(begin_node)
156
+
157
+ return if begin_node.chained?
158
+
159
+ if node.and_type? || node.or_type?
160
+ return if node.semantic_operator? && begin_node.parent
161
+ return if node.multiline? && allow_in_multiline_conditions?
162
+ return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
163
+ return if begin_node.parent&.if_type? && begin_node.parent&.ternary?
164
+
165
+ 'a logical expression'
166
+ elsif node.respond_to?(:comparison_method?) && node.comparison_method?
167
+ return unless begin_node.parent.nil?
168
+
169
+ 'a comparison expression'
170
+ end
171
+ end
172
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
173
+
139
174
  # @!method interpolation?(node)
140
175
  def_node_matcher :interpolation?, '[^begin ^^dstr]'
141
176
 
177
+ def allow_in_multiline_conditions?
178
+ parentheses_around_condition_config = config.for_cop('Style/ParenthesesAroundCondition')
179
+ return false unless parentheses_around_condition_config['Enabled']
180
+
181
+ !!parentheses_around_condition_config['AllowInMultilineConditions']
182
+ end
183
+
142
184
  def check_send(begin_node, node)
143
185
  return check_unary(begin_node, node) if node.unary_operation?
144
186
 
@@ -203,7 +245,6 @@ module RuboCop
203
245
  def method_call_with_redundant_parentheses?(node)
204
246
  return false unless node.call_type?
205
247
  return false if node.prefix_not?
206
- return false if range_end?(node)
207
248
 
208
249
  send_node, args = method_node_and_args(node)
209
250
 
@@ -215,7 +256,13 @@ module RuboCop
215
256
  end
216
257
 
217
258
  def first_argument?(node)
218
- first_send_argument?(node) || first_super_argument?(node) || first_yield_argument?(node)
259
+ if first_send_argument?(node) ||
260
+ first_super_argument?(node) ||
261
+ first_yield_argument?(node)
262
+ return true
263
+ end
264
+
265
+ node.each_ancestor.any? { |ancestor| first_argument?(ancestor) }
219
266
  end
220
267
 
221
268
  # @!method first_send_argument?(node)
@@ -58,7 +58,7 @@ module RuboCop
58
58
 
59
59
  MSG = 'Redundant `return` detected.'
60
60
  MULTI_RETURN_MSG = 'To return multiple values, use an array.'
61
- RESTRICT_ON_SEND = %i[define_method define_singleton_method].freeze
61
+ RESTRICT_ON_SEND = %i[define_method define_singleton_method lambda].freeze
62
62
 
63
63
  def on_send(node)
64
64
  return unless (parent = node.parent) && parent.block_type?
@@ -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) ||
@@ -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)
@@ -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]
@@ -80,6 +80,7 @@ module RuboCop
80
80
  processed_source.tokens.group_by(&:line)
81
81
  end
82
82
 
83
+ # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
83
84
  def semicolon_position(tokens)
84
85
  if tokens.last.semicolon?
85
86
  -1
@@ -90,10 +91,13 @@ module RuboCop
90
91
  elsif exist_semicolon_after_left_curly_brace?(tokens) ||
91
92
  exist_semicolon_after_left_string_interpolation_brace?(tokens)
92
93
  2
94
+ elsif exist_semicolon_after_left_lambda_curly_brace?(tokens)
95
+ 3
93
96
  elsif exist_semicolon_before_right_string_interpolation_brace?(tokens)
94
97
  -4
95
98
  end
96
99
  end
100
+ # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
97
101
 
98
102
  def exist_semicolon_before_right_curly_brace?(tokens)
99
103
  tokens[-2]&.right_curly_brace? && tokens[-3]&.semicolon?
@@ -103,6 +107,10 @@ module RuboCop
103
107
  tokens[1]&.left_curly_brace? && tokens[2]&.semicolon?
104
108
  end
105
109
 
110
+ def exist_semicolon_after_left_lambda_curly_brace?(tokens)
111
+ tokens[2]&.type == :tLAMBEG && tokens[3]&.semicolon?
112
+ end
113
+
106
114
  def exist_semicolon_before_right_string_interpolation_brace?(tokens)
107
115
  tokens[-3]&.type == :tSTRING_DEND && tokens[-4]&.semicolon?
108
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