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
@@ -34,7 +34,7 @@ module RuboCop
34
34
  # interpolation should not be removed if the expanded value
35
35
  # contains a space character.
36
36
  expanded_value = autocorrected_value(final_node)
37
- return if in_array_percent_literal?(begin_node) && /\s/.match?(expanded_value)
37
+ return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
38
38
 
39
39
  add_offense(final_node) do |corrector|
40
40
  return if final_node.dstr_type? # nested, fixed in next iteration
@@ -95,7 +95,7 @@ module RuboCop
95
95
 
96
96
  def skip_range?(range_start, range_end)
97
97
  [range_start, range_end].any? do |bound|
98
- bound.type == :escape
98
+ bound.type != :literal
99
99
  end
100
100
  end
101
101
 
@@ -34,35 +34,20 @@ module RuboCop
34
34
  add_offense(void_next) if void_next
35
35
  end
36
36
  end
37
-
38
- def on_numblock(node)
39
- on_numblock_body_of_reduce(node) do |body|
40
- void_next = body.each_node(:next).find do |n|
41
- n.children.empty? && parent_numblock_node(n) == node
42
- end
43
-
44
- add_offense(void_next) if void_next
45
- end
46
- end
37
+ alias on_numblock on_block
47
38
 
48
39
  private
49
40
 
50
41
  # @!method on_block_body_of_reduce(node)
51
42
  def_node_matcher :on_block_body_of_reduce, <<~PATTERN
52
- (block (send _recv {:reduce :inject} !sym) _blockargs $(begin ...))
53
- PATTERN
54
-
55
- # @!method on_numblock_body_of_reduce(node)
56
- def_node_matcher :on_numblock_body_of_reduce, <<~PATTERN
57
- (numblock (send _recv {:reduce :inject} !sym) _argscount $(begin ...))
43
+ {
44
+ (block (call _recv {:reduce :inject} !sym) _blockargs $(begin ...))
45
+ (numblock (call _recv {:reduce :inject} !sym) _argscount $(begin ...))
46
+ }
58
47
  PATTERN
59
48
 
60
49
  def parent_block_node(node)
61
- node.each_ancestor(:block).first
62
- end
63
-
64
- def parent_numblock_node(node)
65
- node.each_ancestor(:numblock).first
50
+ node.each_ancestor(:block, :numblock).first
66
51
  end
67
52
  end
68
53
  end
@@ -43,7 +43,6 @@ module RuboCop
43
43
  #
44
44
  class NonAtomicFileOperation < Base
45
45
  extend AutoCorrector
46
- include Alignment
47
46
 
48
47
  MSG_REMOVE_FILE_EXIST_CHECK = 'Remove unnecessary existence check ' \
49
48
  '`%<receiver>s.%<method_name>s`.'
@@ -94,7 +94,7 @@ module RuboCop
94
94
  parent_node = node.parent
95
95
 
96
96
  add_offense(parent_node) do |corrector|
97
- if parent_node.arguments.last&.block_pass_type?
97
+ if parent_node.last_argument&.block_pass_type?
98
98
  correct_block_pass(corrector, parent_node)
99
99
  else
100
100
  correct_block(corrector, parent_node)
@@ -116,7 +116,7 @@ module RuboCop
116
116
 
117
117
  def correct_block_pass(corrector, node)
118
118
  if unsorted_dir_glob_pass?(node)
119
- block_arg = node.arguments.last
119
+ block_arg = node.last_argument
120
120
 
121
121
  corrector.remove(last_arg_range(node))
122
122
  corrector.insert_after(node, ".sort.each(#{block_arg.source})")
@@ -130,9 +130,7 @@ module RuboCop
130
130
  # @return [Parser::Source::Range]
131
131
  #
132
132
  def last_arg_range(node)
133
- node.arguments.last.source_range.with(
134
- begin_pos: node.arguments[-2].source_range.end_pos
135
- )
133
+ node.last_argument.source_range.with(begin_pos: node.arguments[-2].source_range.end_pos)
136
134
  end
137
135
 
138
136
  def unsorted_dir_loop?(node)
@@ -9,7 +9,7 @@ module RuboCop
9
9
  #
10
10
  # Conversion with `Integer`, `Float`, etc. will raise an `ArgumentError`
11
11
  # if given input that is not numeric (eg. an empty string), whereas
12
- # `to_i`, etc. will try to convert regardless of input (`''.to_i => 0`).
12
+ # `to_i`, etc. will try to convert regardless of input (``''.to_i => 0``).
13
13
  # As such, this cop is disabled by default because it's not necessarily
14
14
  # always correct to raise if a value is not numeric.
15
15
  #
@@ -91,19 +91,24 @@ module RuboCop
91
91
 
92
92
  # @!method to_method(node)
93
93
  def_node_matcher :to_method, <<~PATTERN
94
- (send $_ ${#{METHODS}})
94
+ (call $_ ${#{METHODS}})
95
95
  PATTERN
96
96
 
97
97
  # @!method to_method_symbol(node)
98
98
  def_node_matcher :to_method_symbol, <<~PATTERN
99
- {(send _ $_ ${(sym ${#{METHODS}})} ...)
100
- (send _ $_ ${(block_pass (sym ${#{METHODS}}))} ...)}
99
+ (call _ $_ ${
100
+ {
101
+ (sym ${#{METHODS}})
102
+ (block_pass (sym ${#{METHODS}}))
103
+ }
104
+ } ...)
101
105
  PATTERN
102
106
 
103
107
  def on_send(node)
104
108
  handle_conversion_method(node)
105
109
  handle_as_symbol(node)
106
110
  end
111
+ alias on_csend on_send
107
112
 
108
113
  private
109
114
 
@@ -24,6 +24,10 @@ module RuboCop
24
24
  #
25
25
  # This cop target those features.
26
26
  #
27
+ # @safety
28
+ # This cop's autocorrection is unsafe because if `require 'pp'` is removed from one file,
29
+ # `NameError` can be encountered when another file uses `PP.pp`.
30
+ #
27
31
  # @example
28
32
  # # bad
29
33
  # require 'unloaded_feature'
@@ -4,8 +4,12 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for redundant safe navigation calls.
7
- # `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`, and `equal?` methods
8
- # are checked by default. These are customizable with `AllowedMethods` option.
7
+ # Use cases where a constant, named in camel case for classes and modules is `nil` are rare,
8
+ # and an offense is not detected when the receiver is a snake case constant.
9
+ #
10
+ # For all receivers, the `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`,
11
+ # and `equal?` methods are checked by default.
12
+ # These are customizable with `AllowedMethods` option.
9
13
  #
10
14
  # The `AllowedMethods` option specifies nil-safe methods,
11
15
  # in other words, it is a method that is allowed to skip safe navigation.
@@ -22,6 +26,9 @@ module RuboCop
22
26
  #
23
27
  # @example
24
28
  # # bad
29
+ # CamelCaseConst&.do_something
30
+ #
31
+ # # bad
25
32
  # do_something if attrs&.respond_to?(:[])
26
33
  #
27
34
  # # good
@@ -33,6 +40,9 @@ module RuboCop
33
40
  # end
34
41
  #
35
42
  # # good
43
+ # CamelCaseConst.do_something
44
+ #
45
+ # # good
36
46
  # while node.is_a?(BeginNode)
37
47
  # node = node.parent
38
48
  # end
@@ -40,6 +50,22 @@ module RuboCop
40
50
  # # good - without `&.` this will always return `true`
41
51
  # foo&.respond_to?(:to_a)
42
52
  #
53
+ # # bad - for `nil`s conversion methods return default values for the type
54
+ # foo&.to_h || {}
55
+ # foo&.to_h { |k, v| [k, v] } || {}
56
+ # foo&.to_a || []
57
+ # foo&.to_i || 0
58
+ # foo&.to_f || 0.0
59
+ # foo&.to_s || ''
60
+ #
61
+ # # good
62
+ # foo.to_h
63
+ # foo.to_h { |k, v| [k, v] }
64
+ # foo.to_a
65
+ # foo.to_i
66
+ # foo.to_f
67
+ # foo.to_s
68
+ #
43
69
  # @example AllowedMethods: [nil_safe_method]
44
70
  # # bad
45
71
  # do_something if attrs&.nil_safe_method(:[])
@@ -54,22 +80,55 @@ module RuboCop
54
80
  extend AutoCorrector
55
81
 
56
82
  MSG = 'Redundant safe navigation detected.'
83
+ MSG_LITERAL = 'Redundant safe navigation with default literal detected.'
57
84
 
58
85
  NIL_SPECIFIC_METHODS = (nil.methods - Object.new.methods).to_set.freeze
59
86
 
87
+ SNAKE_CASE = /\A[[:digit:][:upper:]_]+\z/.freeze
88
+
60
89
  # @!method respond_to_nil_specific_method?(node)
61
90
  def_node_matcher :respond_to_nil_specific_method?, <<~PATTERN
62
91
  (csend _ :respond_to? (sym %NIL_SPECIFIC_METHODS))
63
92
  PATTERN
64
93
 
94
+ # @!method conversion_with_default?(node)
95
+ def_node_matcher :conversion_with_default?, <<~PATTERN
96
+ {
97
+ (or $(csend _ :to_h) (hash))
98
+ (or (block $(csend _ :to_h) ...) (hash))
99
+ (or $(csend _ :to_a) (array))
100
+ (or $(csend _ :to_i) (int 0))
101
+ (or $(csend _ :to_f) (float 0.0))
102
+ (or $(csend _ :to_s) (str empty?))
103
+ }
104
+ PATTERN
105
+
106
+ # rubocop:disable Metrics/AbcSize
65
107
  def on_csend(node)
66
- return unless check?(node) && allowed_method?(node.method_name)
67
- return if respond_to_nil_specific_method?(node)
108
+ unless node.receiver.const_type? && !node.receiver.source.match?(SNAKE_CASE)
109
+ return unless check?(node) && allowed_method?(node.method_name)
110
+ return if respond_to_nil_specific_method?(node)
111
+ end
68
112
 
69
113
  range = range_between(node.loc.dot.begin_pos, node.source_range.end_pos)
70
114
  add_offense(range) { |corrector| corrector.replace(node.loc.dot, '.') }
71
115
  end
72
116
 
117
+ def on_or(node)
118
+ conversion_with_default?(node) do |send_node|
119
+ range = range_between(send_node.loc.dot.begin_pos, node.source_range.end_pos)
120
+
121
+ add_offense(range, message: MSG_LITERAL) do |corrector|
122
+ corrector.replace(send_node.loc.dot, '.')
123
+
124
+ range_with_default = range_between(node.lhs.source_range.end.begin_pos,
125
+ node.source_range.end.end_pos)
126
+ corrector.remove(range_with_default)
127
+ end
128
+ end
129
+ end
130
+ # rubocop:enable Metrics/AbcSize
131
+
73
132
  private
74
133
 
75
134
  def check?(node)
@@ -56,10 +56,10 @@ module RuboCop
56
56
  def_node_matcher :redundant_with_index?, <<~PATTERN
57
57
  {
58
58
  (block
59
- $(send _ {:each_with_index :with_index} ...)
59
+ $(call _ {:each_with_index :with_index} ...)
60
60
  (args (arg _)) ...)
61
61
  (numblock
62
- $(send _ {:each_with_index :with_index} ...) 1 ...)
62
+ $(call _ {:each_with_index :with_index} ...) 1 ...)
63
63
  }
64
64
  PATTERN
65
65
 
@@ -56,9 +56,9 @@ module RuboCop
56
56
  def_node_matcher :redundant_with_object?, <<~PATTERN
57
57
  {
58
58
  (block
59
- $(send _ {:each_with_object :with_object} _) (args (arg _)) ...)
59
+ $(call _ {:each_with_object :with_object} _) (args (arg _)) ...)
60
60
  (numblock
61
- $(send _ {:each_with_object :with_object} _) 1 ...)
61
+ $(call _ {:each_with_object :with_object} _) 1 ...)
62
62
  }
63
63
  PATTERN
64
64
 
@@ -45,10 +45,9 @@ module RuboCop
45
45
  bad_method?(node) do |safe_nav, method|
46
46
  return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
47
47
 
48
- location =
49
- Parser::Source::Range.new(node.source_range.source_buffer,
50
- safe_nav.source_range.end_pos,
51
- node.source_range.end_pos)
48
+ begin_range = node.loc.dot || safe_nav.source_range.end
49
+ location = begin_range.join(node.source_range.end)
50
+
52
51
  add_offense(location) do |corrector|
53
52
  autocorrect(corrector, offense_range: location, send_node: node)
54
53
  end
@@ -10,11 +10,18 @@ module RuboCop
10
10
  # foo = foo
11
11
  # foo, bar = foo, bar
12
12
  # Foo = Foo
13
+ # hash['foo'] = hash['foo']
14
+ # obj.attr = obj.attr
13
15
  #
14
16
  # # good
15
17
  # foo = bar
16
18
  # foo, bar = bar, foo
17
19
  # Foo = Bar
20
+ # hash['foo'] = hash['bar']
21
+ # obj.attr = obj.attr2
22
+ #
23
+ # # good (method calls possibly can return different results)
24
+ # hash[foo] = hash[foo]
18
25
  #
19
26
  class SelfAssignment < Base
20
27
  MSG = 'Self-assignment detected.'
@@ -26,6 +33,15 @@ module RuboCop
26
33
  gvasgn: :gvar
27
34
  }.freeze
28
35
 
36
+ def on_send(node)
37
+ if node.method?(:[]=)
38
+ handle_key_assignment(node) if node.arguments.size == 2
39
+ elsif node.assignment_method?
40
+ handle_attribute_assignment(node) if node.arguments.size == 1
41
+ end
42
+ end
43
+ alias on_csend on_send
44
+
29
45
  def on_lvasgn(node)
30
46
  lhs, rhs = *node
31
47
  return unless rhs
@@ -72,6 +88,28 @@ module RuboCop
72
88
  rhs.type == ASSIGNMENT_TYPE_TO_RHS_TYPE[lhs.type] &&
73
89
  rhs.children.first == lhs.children.first
74
90
  end
91
+
92
+ def handle_key_assignment(node)
93
+ value_node = node.arguments[1]
94
+
95
+ if value_node.send_type? && value_node.method?(:[]) &&
96
+ node.receiver == value_node.receiver &&
97
+ !node.first_argument.call_type? &&
98
+ node.first_argument == value_node.first_argument
99
+ add_offense(node)
100
+ end
101
+ end
102
+
103
+ def handle_attribute_assignment(node)
104
+ first_argument = node.first_argument
105
+ return unless first_argument.respond_to?(:arguments) && first_argument.arguments.empty?
106
+
107
+ if first_argument.call_type? &&
108
+ node.receiver == first_argument.receiver &&
109
+ first_argument.method_name.to_s == node.method_name.to_s.delete_suffix('=')
110
+ add_offense(node)
111
+ end
112
+ end
75
113
  end
76
114
  end
77
115
  end
@@ -123,6 +123,7 @@ module RuboCop
123
123
 
124
124
  # Shorthand assignments always use their arguments
125
125
  next false if assignment_node.shorthand_asgn?
126
+ next false unless assignment_node.parent
126
127
 
127
128
  node_within_block_or_conditional =
128
129
  node_within_block_or_conditional?(assignment_node.parent, argument.scope.node)
@@ -19,6 +19,7 @@ module RuboCop
19
19
  # 'underscored_string'.to_sym
20
20
  # :'underscored_symbol'
21
21
  # 'hyphenated-string'.to_sym
22
+ # "string_#{interpolation}".to_sym
22
23
  #
23
24
  # # good
24
25
  # :string
@@ -26,6 +27,7 @@ module RuboCop
26
27
  # :underscored_string
27
28
  # :underscored_symbol
28
29
  # :'hyphenated-string'
30
+ # :"string_#{interpolation}"
29
31
  #
30
32
  # @example EnforcedStyle: strict (default)
31
33
  #
@@ -75,9 +77,12 @@ module RuboCop
75
77
 
76
78
  def on_send(node)
77
79
  return unless node.receiver
78
- return unless node.receiver.str_type? || node.receiver.sym_type?
79
80
 
80
- register_offense(node, correction: node.receiver.value.to_sym.inspect)
81
+ if node.receiver.str_type? || node.receiver.sym_type?
82
+ register_offense(node, correction: node.receiver.value.to_sym.inspect)
83
+ elsif node.receiver.dstr_type?
84
+ register_offense(node, correction: ":\"#{node.receiver.value.to_sym}\"")
85
+ end
81
86
  end
82
87
 
83
88
  def on_sym(node)
@@ -17,9 +17,12 @@ module RuboCop
17
17
  private
18
18
 
19
19
  def add_offense_from_diagnostic(diagnostic, ruby_version)
20
- message =
21
- "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
22
- 'configure using `TargetRubyVersion` parameter, under `AllCops`)'
20
+ message = if lsp_mode?
21
+ diagnostic.message
22
+ else
23
+ "#{diagnostic.message}\n(Using Ruby #{ruby_version} parser; " \
24
+ 'configure using `TargetRubyVersion` parameter, under `AllCops`)'
25
+ end
23
26
  add_offense(diagnostic.location, message: message, severity: diagnostic.level)
24
27
  end
25
28
 
@@ -34,7 +34,7 @@ module RuboCop
34
34
  MSG = 'Avoid leaving a trailing comma in attribute declarations.'
35
35
 
36
36
  def on_send(node)
37
- return unless node.attribute_accessor? && node.arguments.last.def_type?
37
+ return unless node.attribute_accessor? && node.last_argument.def_type?
38
38
 
39
39
  trailing_comma = trailing_comma_range(node)
40
40
 
@@ -69,8 +69,8 @@ module RuboCop
69
69
  # @!method reduce_with_block?(node)
70
70
  def_node_matcher :reduce_with_block?, <<~PATTERN
71
71
  {
72
- (block (send _recv {:reduce :inject} ...) args ...)
73
- (numblock (send _recv {:reduce :inject} ...) ...)
72
+ (block (call _recv {:reduce :inject} ...) args ...)
73
+ (numblock (call _recv {:reduce :inject} ...) ...)
74
74
  }
75
75
  PATTERN
76
76
 
@@ -256,7 +256,7 @@ module RuboCop
256
256
 
257
257
  def any_method_definition?(child)
258
258
  cop_config.fetch('MethodCreatingMethods', []).any? do |m|
259
- matcher_name = "#{m}_method?".to_sym
259
+ matcher_name = :"#{m}_method?"
260
260
  unless respond_to?(matcher_name)
261
261
  self.class.def_node_matcher matcher_name, <<~PATTERN
262
262
  {def (send nil? :#{m} ...)}
@@ -279,7 +279,7 @@ module RuboCop
279
279
 
280
280
  def any_context_creating_methods?(child)
281
281
  cop_config.fetch('ContextCreatingMethods', []).any? do |m|
282
- matcher_name = "#{m}_block?".to_sym
282
+ matcher_name = :"#{m}_block?"
283
283
  unless respond_to?(matcher_name)
284
284
  self.class.def_node_matcher matcher_name, <<~PATTERN
285
285
  ({block numblock} (send {nil? const} {:#{m}} ...) ...)
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Lint
6
6
  # Checks for uses of `Integer#times` that will never yield
7
- # (when the integer <= 0) or that will only ever yield once
7
+ # (when the integer ``<= 0``) or that will only ever yield once
8
8
  # (`1.times`).
9
9
  #
10
10
  # @safety
@@ -6,6 +6,16 @@ module RuboCop
6
6
  # Checks for operators, variables, literals, lambda, proc and nonmutating
7
7
  # methods used in void context.
8
8
  #
9
+ # `each` blocks are allowed to prevent false positives.
10
+ # For example, the expression inside the `each` block below.
11
+ # It's not void, especially when the receiver is an `Enumerator`:
12
+ #
13
+ # [source,ruby]
14
+ # ----
15
+ # enumerator = [1, 2, 3].filter
16
+ # enumerator.each { |item| item >= 2 } #=> [2, 3]
17
+ # ----
18
+ #
9
19
  # @example CheckForMethodsWithNoSideEffects: false (default)
10
20
  # # bad
11
21
  # def some_method
@@ -47,6 +57,7 @@ module RuboCop
47
57
 
48
58
  OP_MSG = 'Operator `%<op>s` used in void context.'
49
59
  VAR_MSG = 'Variable `%<var>s` used in void context.'
60
+ CONST_MSG = 'Constant `%<var>s` used in void context.'
50
61
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
51
62
  SELF_MSG = '`self` used in void context.'
52
63
  EXPRESSION_MSG = '`%<expression>s` used in void context.'
@@ -72,6 +83,7 @@ module RuboCop
72
83
  return unless node.body && !node.body.begin_type?
73
84
  return unless in_void_context?(node.body)
74
85
 
86
+ check_void_op(node.body) { node.method?(:each) }
75
87
  check_expression(node.body)
76
88
  end
77
89
 
@@ -87,11 +99,13 @@ module RuboCop
87
99
  def check_begin(node)
88
100
  expressions = *node
89
101
  expressions.pop unless in_void_context?(node)
90
- expressions.each { |expr| check_expression(expr) }
102
+ expressions.each do |expr|
103
+ check_void_op(expr)
104
+ check_expression(expr)
105
+ end
91
106
  end
92
107
 
93
108
  def check_expression(expr)
94
- check_void_op(expr)
95
109
  check_literal(expr)
96
110
  check_var(expr)
97
111
  check_self(expr)
@@ -101,8 +115,9 @@ module RuboCop
101
115
  check_nonmutating(expr)
102
116
  end
103
117
 
104
- def check_void_op(node)
118
+ def check_void_op(node, &block)
105
119
  return unless node.send_type? && OPERATORS.include?(node.method_name)
120
+ return if block && yield(node)
106
121
 
107
122
  add_offense(node.loc.selector,
108
123
  message: format(OP_MSG, op: node.method_name)) do |corrector|
@@ -113,20 +128,23 @@ module RuboCop
113
128
  def check_var(node)
114
129
  return unless node.variable? || node.const_type?
115
130
 
116
- if node.const_type? && node.special_keyword?
117
- add_offense(node, message: format(VAR_MSG, var: node.source)) do |corrector|
118
- autocorrect_void_expression(corrector, node)
119
- end
131
+ if node.const_type?
132
+ template = node.special_keyword? ? VAR_MSG : CONST_MSG
133
+
134
+ offense_range = node
135
+ message = format(template, var: node.source)
120
136
  else
121
- add_offense(node.loc.name,
122
- message: format(VAR_MSG, var: node.loc.name.source)) do |corrector|
123
- autocorrect_void_expression(corrector, node)
124
- end
137
+ offense_range = node.loc.name
138
+ message = format(VAR_MSG, var: node.loc.name.source)
139
+ end
140
+
141
+ add_offense(offense_range, message: message) do |corrector|
142
+ autocorrect_void_expression(corrector, node)
125
143
  end
126
144
  end
127
145
 
128
146
  def check_literal(node)
129
- return if !node.literal? || node.xstr_type? || node.range_type?
147
+ return if !entirely_literal?(node) || node.xstr_type? || node.range_type?
130
148
 
131
149
  add_offense(node, message: format(LIT_MSG, lit: node.source)) do |corrector|
132
150
  autocorrect_void_expression(corrector, node)
@@ -199,6 +217,19 @@ module RuboCop
199
217
  end
200
218
  corrector.replace(send_node.loc.selector, suggestion)
201
219
  end
220
+
221
+ def entirely_literal?(node)
222
+ case node.type
223
+ when :array
224
+ node.each_value.all? { |value| entirely_literal?(value) }
225
+ when :hash
226
+ return false unless node.each_key.all? { |key| entirely_literal?(key) }
227
+
228
+ node.each_value.all? { |value| entirely_literal?(value) }
229
+ else
230
+ node.literal?
231
+ end
232
+ end
202
233
  end
203
234
  end
204
235
  end
@@ -10,9 +10,9 @@ module RuboCop
10
10
  #
11
11
  # Interpreting ABC size:
12
12
  #
13
- # * <= 17 satisfactory
14
- # * 18..30 unsatisfactory
15
- # * > 30 dangerous
13
+ # * ``<= 17`` satisfactory
14
+ # * `18..30` unsatisfactory
15
+ # * `>` 30 dangerous
16
16
  #
17
17
  # You can have repeated "attributes" calls count as a single "branch".
18
18
  # For this purpose, attributes are any method with no argument; no attempt
@@ -12,6 +12,7 @@ module RuboCop
12
12
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
13
13
  # will be counted as one line regardless of its actual size.
14
14
  #
15
+ # NOTE: This cop does not apply for `Struct` definitions.
15
16
  #
16
17
  # NOTE: The `ExcludedMethods` configuration is deprecated and only kept
17
18
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
@@ -40,7 +41,6 @@ module RuboCop
40
41
  # )
41
42
  # end # 6 points
42
43
  #
43
- # NOTE: This cop does not apply for `Struct` definitions.
44
44
  class BlockLength < Base
45
45
  include CodeLength
46
46
  include AllowedMethods
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
+ # NOTE: This cop also applies for `Struct` definitions.
15
+ #
14
16
  # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
17
  #
16
18
  # class Foo
@@ -34,15 +36,18 @@ module RuboCop
34
36
  # )
35
37
  # end # 6 points
36
38
  #
37
- #
38
- # NOTE: This cop also applies for `Struct` definitions.
39
39
  class ClassLength < Base
40
40
  include CodeLength
41
41
 
42
42
  def on_class(node)
43
43
  check_code_length(node)
44
44
  end
45
- alias on_sclass on_class
45
+
46
+ def on_sclass(node)
47
+ return if node.each_ancestor(:class).any?
48
+
49
+ on_class(node)
50
+ end
46
51
 
47
52
  def on_casgn(node)
48
53
  parent = node.parent