rubocop 1.64.1 → 1.65.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +11 -1
  4. data/lib/rubocop/config_loader.rb +1 -1
  5. data/lib/rubocop/config_loader_resolver.rb +9 -3
  6. data/lib/rubocop/cop/cop.rb +22 -4
  7. data/lib/rubocop/cop/gemspec/add_runtime_dependency.rb +38 -0
  8. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  9. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  10. data/lib/rubocop/cop/layout/assignment_indentation.rb +3 -2
  11. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  12. data/lib/rubocop/cop/layout/condition_position.rb +0 -4
  13. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +1 -1
  14. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  15. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  17. data/lib/rubocop/cop/layout/line_length.rb +20 -20
  18. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -0
  19. data/lib/rubocop/cop/legacy/corrector.rb +12 -2
  20. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +0 -2
  21. data/lib/rubocop/cop/lint/ambiguous_operator.rb +0 -2
  22. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +0 -2
  23. data/lib/rubocop/cop/lint/boolean_symbol.rb +0 -2
  24. data/lib/rubocop/cop/lint/circular_argument_reference.rb +0 -13
  25. data/lib/rubocop/cop/lint/debugger.rb +0 -4
  26. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +0 -10
  27. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +1 -5
  28. data/lib/rubocop/cop/lint/duplicate_hash_key.rb +0 -4
  29. data/lib/rubocop/cop/lint/duplicate_methods.rb +0 -10
  30. data/lib/rubocop/cop/lint/each_with_object_argument.rb +0 -4
  31. data/lib/rubocop/cop/lint/else_layout.rb +0 -2
  32. data/lib/rubocop/cop/lint/empty_ensure.rb +1 -11
  33. data/lib/rubocop/cop/lint/empty_interpolation.rb +0 -4
  34. data/lib/rubocop/cop/lint/empty_when.rb +1 -3
  35. data/lib/rubocop/cop/lint/ensure_return.rb +1 -6
  36. data/lib/rubocop/cop/lint/float_comparison.rb +3 -1
  37. data/lib/rubocop/cop/lint/float_out_of_range.rb +0 -4
  38. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +0 -10
  39. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +15 -12
  40. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +0 -7
  41. data/lib/rubocop/cop/lint/interpolation_check.rb +0 -4
  42. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  43. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +1 -1
  44. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +0 -4
  45. data/lib/rubocop/cop/lint/loop.rb +6 -12
  46. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -7
  47. data/lib/rubocop/cop/lint/next_without_accumulator.rb +0 -4
  48. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +0 -5
  49. data/lib/rubocop/cop/lint/percent_string_array.rb +0 -4
  50. data/lib/rubocop/cop/lint/percent_symbol_array.rb +0 -4
  51. data/lib/rubocop/cop/lint/rand_one.rb +0 -4
  52. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +3 -1
  53. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  54. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +0 -4
  55. data/lib/rubocop/cop/lint/require_parentheses.rb +0 -4
  56. data/lib/rubocop/cop/lint/rescue_exception.rb +0 -4
  57. data/lib/rubocop/cop/lint/return_in_void_context.rb +0 -2
  58. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +0 -4
  59. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +6 -10
  60. data/lib/rubocop/cop/lint/to_enum_arguments.rb +2 -9
  61. data/lib/rubocop/cop/lint/unified_integer.rb +0 -4
  62. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  63. data/lib/rubocop/cop/lint/unreachable_code.rb +0 -5
  64. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -5
  65. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +0 -4
  66. data/lib/rubocop/cop/lint/useless_setter_call.rb +0 -4
  67. data/lib/rubocop/cop/lint/void.rb +5 -0
  68. data/lib/rubocop/cop/metrics/block_nesting.rb +19 -7
  69. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  70. data/lib/rubocop/cop/mixin/allowed_methods.rb +7 -1
  71. data/lib/rubocop/cop/mixin/allowed_pattern.rb +15 -3
  72. data/lib/rubocop/cop/mixin/configurable_max.rb +5 -1
  73. data/lib/rubocop/cop/mixin/rescue_node.rb +4 -0
  74. data/lib/rubocop/cop/naming/predicate_name.rb +52 -26
  75. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +10 -1
  76. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  77. data/lib/rubocop/cop/style/def_with_parentheses.rb +0 -2
  78. data/lib/rubocop/cop/style/each_for_simple_loop.rb +0 -1
  79. data/lib/rubocop/cop/style/eval_with_location.rb +12 -11
  80. data/lib/rubocop/cop/style/file_read.rb +2 -5
  81. data/lib/rubocop/cop/style/file_write.rb +2 -5
  82. data/lib/rubocop/cop/style/global_std_stream.rb +7 -1
  83. data/lib/rubocop/cop/style/hash_except.rb +8 -5
  84. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +0 -1
  85. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  86. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +77 -43
  87. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +5 -0
  88. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
  89. data/lib/rubocop/cop/style/missing_else.rb +0 -4
  90. data/lib/rubocop/cop/style/multiline_when_then.rb +0 -4
  91. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  92. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  93. data/lib/rubocop/cop/style/redundant_condition.rb +0 -1
  94. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -1
  95. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -24
  96. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +15 -1
  97. data/lib/rubocop/cop/style/sole_nested_conditional.rb +21 -2
  98. data/lib/rubocop/cop/style/super_arguments.rb +28 -10
  99. data/lib/rubocop/cop/style/symbol_proc.rb +8 -1
  100. data/lib/rubocop/cop/style/while_until_do.rb +0 -2
  101. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  102. data/lib/rubocop/cop/style/zero_length_predicate.rb +32 -24
  103. data/lib/rubocop/cop/team.rb +8 -0
  104. data/lib/rubocop/cop/util.rb +7 -1
  105. data/lib/rubocop/cop/variable_force.rb +13 -1
  106. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  107. data/lib/rubocop/core_ext/string.rb +2 -6
  108. data/lib/rubocop/ext/regexp_node.rb +9 -31
  109. data/lib/rubocop/ext/regexp_parser.rb +4 -21
  110. data/lib/rubocop/formatter/html_formatter.rb +3 -1
  111. data/lib/rubocop/options.rb +3 -1
  112. data/lib/rubocop/rspec/shared_contexts.rb +20 -0
  113. data/lib/rubocop/rspec/support.rb +1 -0
  114. data/lib/rubocop/server/cache.rb +10 -0
  115. data/lib/rubocop/server/client_command/exec.rb +2 -2
  116. data/lib/rubocop/server/client_command/start.rb +1 -1
  117. data/lib/rubocop/server/core.rb +4 -0
  118. data/lib/rubocop/version.rb +1 -1
  119. data/lib/rubocop.rb +6 -0
  120. metadata +7 -6
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Prefer `select` or `reject` over `map { ... }.compact`.
7
+ # This cop also handles `filter_map { ... }`, similar to `map { ... }.compact`.
7
8
  #
8
9
  # @example
9
10
  #
@@ -11,6 +12,9 @@ module RuboCop
11
12
  # array.map { |e| some_condition? ? e : next }.compact
12
13
  #
13
14
  # # bad
15
+ # array.filter_map { |e| some_condition? ? e : next }
16
+ #
17
+ # # bad
14
18
  # array.map do |e|
15
19
  # if some_condition?
16
20
  # e
@@ -40,55 +44,73 @@ module RuboCop
40
44
  class MapCompactWithConditionalBlock < Base
41
45
  extend AutoCorrector
42
46
 
43
- MSG = 'Replace `map { ... }.compact` with `%<method>s`.'
44
-
45
- # @!method map_and_compact?(node)
46
- def_node_matcher :map_and_compact?, <<~RUBY
47
- (call
48
- (block
49
- (call _ :map)
50
- (args
51
- $(arg _))
52
- {
53
- (if $_ $(lvar _) {next nil?})
54
- (if $_ {next nil?} $(lvar _))
55
- (if $_ (next $(lvar _)) {next nil nil?})
56
- (if $_ {next nil nil?} (next $(lvar _)))
57
- (begin
58
- {
59
- (if $_ next nil?)
60
- (if $_ nil? next)
61
- }
62
- $(lvar _))
63
- (begin
64
- {
65
- (if $_ (next $(lvar _)) nil?)
66
- (if $_ nil? (next $(lvar _)))
67
- }
68
- (nil))
69
- }) :compact)
47
+ MSG = 'Replace `%<current>s` with `%<method>s`.'
48
+ RESTRICT_ON_SEND = %i[compact filter_map].freeze
49
+
50
+ # @!method conditional_block(node)
51
+ def_node_matcher :conditional_block, <<~RUBY
52
+ (block
53
+ (call _ {:map :filter_map})
54
+ (args
55
+ $(arg _))
56
+ {
57
+ (if $_ $(lvar _) {next nil?})
58
+ (if $_ {next nil?} $(lvar _))
59
+ (if $_ (next $(lvar _)) {next nil nil?})
60
+ (if $_ {next nil nil?} (next $(lvar _)))
61
+ (begin
62
+ {
63
+ (if $_ next nil?)
64
+ (if $_ nil? next)
65
+ }
66
+ $(lvar _))
67
+ (begin
68
+ {
69
+ (if $_ (next $(lvar _)) nil?)
70
+ (if $_ nil? (next $(lvar _)))
71
+ }
72
+ (nil))
73
+ })
70
74
  RUBY
71
75
 
72
76
  def on_send(node)
73
- map_and_compact?(node) do |block_argument_node, condition_node, return_value_node|
74
- return unless returns_block_argument?(block_argument_node, return_value_node)
75
- return if condition_node.parent.elsif?
76
-
77
- method = truthy_branch?(return_value_node) ? 'select' : 'reject'
78
- range = range(node)
79
-
80
- add_offense(range, message: format(MSG, method: method)) do |corrector|
81
- corrector.replace(
82
- range,
83
- "#{method} { |#{block_argument_node.source}| #{condition_node.source} }"
84
- )
85
- end
77
+ map_candidate = node.children.first
78
+ if (block_argument, condition, return_value = conditional_block(map_candidate))
79
+ return unless node.method?(:compact) && node.arguments.empty?
80
+
81
+ range = map_with_compact_range(node)
82
+ elsif (block_argument, condition, return_value = conditional_block(node.parent))
83
+ return unless node.method?(:filter_map)
84
+
85
+ range = filter_map_range(node)
86
+ else
87
+ return
86
88
  end
89
+
90
+ inspect(node, block_argument, condition, return_value, range)
87
91
  end
88
92
  alias on_csend on_send
89
93
 
90
94
  private
91
95
 
96
+ def inspect(node, block_argument_node, condition_node, return_value_node, range)
97
+ return unless returns_block_argument?(block_argument_node, return_value_node)
98
+ return if condition_node.parent.elsif?
99
+
100
+ method = truthy_branch?(return_value_node) ? 'select' : 'reject'
101
+ current = current(node)
102
+
103
+ add_offense(range, message: format(MSG, current: current, method: method)) do |corrector|
104
+ return if part_of_ignored_node?(node) || ignored_node?(node)
105
+
106
+ corrector.replace(
107
+ range, "#{method} { |#{block_argument_node.source}| #{condition_node.source} }"
108
+ )
109
+
110
+ ignore_node(node)
111
+ end
112
+ end
113
+
92
114
  def returns_block_argument?(block_argument_node, return_value_node)
93
115
  block_argument_node.name == return_value_node.children.first
94
116
  end
@@ -123,10 +145,22 @@ module RuboCop
123
145
  end
124
146
  end
125
147
 
126
- def range(node)
127
- map_node = node.receiver.send_node
148
+ def current(node)
149
+ if node.method?(:compact)
150
+ map_or_filter_map_method = node.children.first
151
+
152
+ "#{map_or_filter_map_method.method_name} { ... }.compact"
153
+ else
154
+ 'filter_map { ... }'
155
+ end
156
+ end
157
+
158
+ def map_with_compact_range(node)
159
+ node.receiver.send_node.loc.selector.begin.join(node.source_range.end)
160
+ end
128
161
 
129
- map_node.loc.selector.join(node.source_range.end)
162
+ def filter_map_range(node)
163
+ node.loc.selector.join(node.parent.source_range.end)
130
164
  end
131
165
  end
132
166
  end
@@ -18,6 +18,7 @@ module RuboCop
18
18
  return if inside_endless_method_def?(node)
19
19
  return if require_parentheses_for_hash_value_omission?(node)
20
20
  return if syntax_like_method_call?(node)
21
+ return if method_call_before_constant_resolution?(node)
21
22
  return if super_call_without_arguments?(node)
22
23
  return if legitimate_call_with_parentheses?(node)
23
24
  return if allowed_camel_case_method_call?(node)
@@ -63,6 +64,10 @@ module RuboCop
63
64
  node.implicit_call? || node.operator_method?
64
65
  end
65
66
 
67
+ def method_call_before_constant_resolution?(node)
68
+ node.parent&.const_type?
69
+ end
70
+
66
71
  def super_call_without_arguments?(node)
67
72
  node.super_type? && node.arguments.none?
68
73
  end
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Enforces the presence (default) or absence of parentheses in
7
- # method calls containing parameters.
7
+ # method calls containing arguments.
8
8
  #
9
9
  # In the default style (require_parentheses), macro methods are allowed.
10
10
  # Additional methods can be added to the `AllowedMethods` or
@@ -168,10 +168,6 @@ module RuboCop
168
168
  config.for_cop('Style/UnlessElse')
169
169
  end
170
170
 
171
- def empty_else_cop_enabled?
172
- empty_else_config.fetch('Enabled')
173
- end
174
-
175
171
  def empty_else_style
176
172
  return unless empty_else_config.key?('EnforcedStyle')
177
173
 
@@ -54,10 +54,6 @@ module RuboCop
54
54
 
55
55
  same_line?(when_node, when_node.body)
56
56
  end
57
-
58
- def accept_node_type?(node)
59
- node&.array_type? || node&.hash_type?
60
- end
61
57
  end
62
58
  end
63
59
  end
@@ -9,7 +9,7 @@ module RuboCop
9
9
  #
10
10
  # String interpolation is always kept in double quotes.
11
11
  #
12
- # Note: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
12
+ # NOTE: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
13
13
  # are not quoted that don't need to be. This cop is for configuring the quoting
14
14
  # style to use for symbols that require quotes.
15
15
  #
@@ -75,7 +75,7 @@ module RuboCop
75
75
 
76
76
  def on_def(node)
77
77
  return unless node.body&.kwbegin_type?
78
- return if node.endless? && !node.body.children.one?
78
+ return if node.endless?
79
79
 
80
80
  register_offense(node.body)
81
81
  end
@@ -12,7 +12,6 @@ module RuboCop
12
12
  # # good
13
13
  # a = b || c
14
14
  #
15
- # @example
16
15
  # # bad
17
16
  # if b
18
17
  # b
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for the presence of superfluous `.rb` extension in
7
7
  # the filename provided to `require` and `require_relative`.
8
8
  #
9
- # Note: If the extension is omitted, Ruby tries adding '.rb', '.so',
9
+ # NOTE: If the extension is omitted, Ruby tries adding '.rb', '.so',
10
10
  # and so on to the name until found. If the file named cannot be found,
11
11
  # a `LoadError` will be raised.
12
12
  # There is an edge case where `foo.so` file is loaded instead of a `LoadError`
@@ -95,30 +95,14 @@ module RuboCop
95
95
  delimiters.include?(char)
96
96
  end
97
97
 
98
- if Gem::Version.new(Regexp::Parser::VERSION) >= Gem::Version.new('2.0')
99
- def each_escape(node)
100
- node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
101
- yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
102
-
103
- if expr.type == :set
104
- char_class_depth + (event == :enter ? 1 : -1)
105
- else
106
- char_class_depth
107
- end
108
- end
109
- end
110
- # Please remove this `else` branch when support for regexp_parser 1.8 will be dropped.
111
- # It's for compatibility with regexp_parser 1.8 and will never be maintained.
112
- else
113
- def each_escape(node)
114
- node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
115
- yield(expr.text[1], expr.start_index, !char_class_depth.zero?) if expr.type == :escape
116
-
117
- if expr.type == :set
118
- char_class_depth + (event == :enter ? 1 : -1)
119
- else
120
- char_class_depth
121
- end
98
+ def each_escape(node)
99
+ node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
100
+ yield(expr.text[1], expr.ts, !char_class_depth.zero?) if expr.type == :escape
101
+
102
+ if expr.type == :set
103
+ char_class_depth + (event == :enter ? 1 : -1)
104
+ else
105
+ char_class_depth
122
106
  end
123
107
  end
124
108
  end
@@ -7,6 +7,20 @@ module RuboCop
7
7
  # Since the `send` method can be used to call private methods, by default,
8
8
  # only the `public_send` method is detected.
9
9
  #
10
+ # NOTE: Writer methods with names ending in `=` are always permitted because their
11
+ # behavior differs as follows:
12
+ #
13
+ # [source,ruby]
14
+ # ----
15
+ # def foo=(foo)
16
+ # @foo = foo
17
+ # 42
18
+ # end
19
+ #
20
+ # self.foo = 1 # => 1
21
+ # send(:foo=, 1) # => 42
22
+ # ----
23
+ #
10
24
  # @safety
11
25
  # This cop is not safe because it can incorrectly detect based on the receiver.
12
26
  # Additionally, when `AllowSend` is set to `true`, it cannot determine whether
@@ -43,7 +57,7 @@ module RuboCop
43
57
  MSG = 'Use `%<method_name>s` method call directly instead.'
44
58
  RESTRICT_ON_SEND = %i[public_send send __send__].freeze
45
59
  STATIC_METHOD_NAME_NODE_TYPES = %i[sym str].freeze
46
- METHOD_NAME_PATTERN = /\A[a-zA-Z_][a-zA-Z0-9_]*[!?=]?\z/.freeze
60
+ METHOD_NAME_PATTERN = /\A[a-zA-Z_][a-zA-Z0-9_]*[!?]?\z/.freeze
47
61
  RESERVED_WORDS = %i[
48
62
  BEGIN END alias and begin break case class def defined? do else elsif end ensure
49
63
  false for if in module next nil not or redo rescue retry return self super then true
@@ -159,7 +159,13 @@ module RuboCop
159
159
  node_to_check = condition&.block_type? ? condition.send_node : condition
160
160
  return unless wrap_condition?(node_to_check)
161
161
 
162
- corrector.wrap(condition, '(', ')')
162
+ if condition.call_type?
163
+ source = parenthesized_method_arguments(condition)
164
+
165
+ corrector.replace(condition, source)
166
+ else
167
+ corrector.wrap(condition, '(', ')')
168
+ end
163
169
  end
164
170
 
165
171
  def correct_for_outer_condition_modify_form_style(corrector, node, if_branch)
@@ -236,7 +242,20 @@ module RuboCop
236
242
  end
237
243
 
238
244
  def replace_condition(condition)
239
- wrap_condition?(condition) ? "(#{condition.source})" : condition.source
245
+ return condition.source unless wrap_condition?(condition)
246
+
247
+ if condition.call_type?
248
+ parenthesized_method_arguments(condition)
249
+ else
250
+ "(#{condition.source})"
251
+ end
252
+ end
253
+
254
+ def parenthesized_method_arguments(node)
255
+ method_call = node.source_range.begin.join(node.loc.selector.end).source
256
+ arguments = node.first_argument.source_range.begin.join(node.source_range.end).source
257
+
258
+ "#{method_call}(#{arguments})"
240
259
  end
241
260
 
242
261
  def allow_modifier?
@@ -3,8 +3,25 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for redundant argument forwarding when calling super
7
- # with arguments identical to the method definition.
6
+ # Checks for redundant argument forwarding when calling super with arguments identical to
7
+ # the method definition.
8
+ #
9
+ # Using zero arity `super` within a `define_method` block results in `RuntimeError`:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # def m
14
+ # define_method(:foo) { super() } # => OK
15
+ # end
16
+ #
17
+ # def m
18
+ # define_method(:foo) { super } # => RuntimeError
19
+ # end
20
+ # ----
21
+ #
22
+ # Furthermore, any arguments accompanied by a block may potentially be delegating to
23
+ # `define_method`, therefore, `super` used within these blocks will be allowed.
24
+ # This approach might result in false negatives, yet ensuring safe detection takes precedence.
8
25
  #
9
26
  # @example
10
27
  # # bad
@@ -44,8 +61,10 @@ module RuboCop
44
61
 
45
62
  def on_super(super_node)
46
63
  def_node = super_node.ancestors.find do |node|
47
- # You can't implicitly call super when dynamically defining methods
48
- break if define_method?(node)
64
+ # When defining dynamic methods, implicitly calling `super` is not possible.
65
+ # Since there is a possibility of delegation to `define_method`,
66
+ # `super` used within the block is always allowed.
67
+ break if node.block_type?
49
68
 
50
69
  break node if DEF_TYPES.include?(node.type)
51
70
  end
@@ -127,6 +146,11 @@ module RuboCop
127
146
  # https://bugs.ruby-lang.org/issues/20505
128
147
  def block_reassigned?(def_node, block_arg_name)
129
148
  def_node.each_node(*ASSIGN_TYPES).any? do |assign_node|
149
+ # TODO: Since `Symbol#name` is supported from Ruby 3.0, the inheritance check for
150
+ # `AST::Node` can be removed when requiring Ruby 3.0+.
151
+ lhs = assign_node.node_parts[0]
152
+ next if lhs.is_a?(AST::Node) && !lhs.respond_to?(:name)
153
+
130
154
  assign_node.name == block_arg_name
131
155
  end
132
156
  end
@@ -135,12 +159,6 @@ module RuboCop
135
159
  def_arg.forward_arg_type? && super_arg.forwarded_args_type?
136
160
  end
137
161
 
138
- def define_method?(node)
139
- return false unless node.block_type?
140
-
141
- node.method?(:define_method) || node.method?(:define_singleton_method)
142
- end
143
-
144
162
  def preprocess_super_args(super_args)
145
163
  super_args.flat_map do |node|
146
164
  if node.hash_type? && !node.braces?
@@ -223,7 +223,14 @@ module RuboCop
223
223
  end
224
224
 
225
225
  def autocorrect_without_args(corrector, node)
226
- autocorrect_lambda_block(corrector, node) if node.send_node.lambda_literal?
226
+ if node.send_node.lambda_literal?
227
+ if node.send_node.loc.selector.source == '->'
228
+ corrector.replace(node, "lambda(&:#{node.body.method_name})")
229
+ return
230
+ else
231
+ autocorrect_lambda_block(corrector, node)
232
+ end
233
+ end
227
234
 
228
235
  corrector.replace(block_range_with_space(node), "(&:#{node.body.method_name})")
229
236
  end
@@ -17,8 +17,6 @@ module RuboCop
17
17
  # do_something(x.pop)
18
18
  # end
19
19
  #
20
- # @example
21
- #
22
20
  # # bad
23
21
  # until x.empty? do
24
22
  # do_something(x.pop)
@@ -16,7 +16,6 @@ module RuboCop
16
16
  # # good
17
17
  # x += 1 while x < 10
18
18
  #
19
- # @example
20
19
  # # bad
21
20
  # until x > 10
22
21
  # x += 1
@@ -48,14 +48,19 @@ module RuboCop
48
48
  check_nonzero_length_comparison(node)
49
49
  end
50
50
 
51
+ def on_csend(node)
52
+ check_zero_length_predicate(node)
53
+ check_zero_length_comparison(node)
54
+ end
55
+
51
56
  private
52
57
 
53
58
  def check_zero_length_predicate(node)
54
- return unless (length_method = zero_length_predicate(node.parent))
59
+ return unless zero_length_predicate?(node.parent)
55
60
  return if non_polymorphic_collection?(node.parent)
56
61
 
57
62
  offense = node.loc.selector.join(node.parent.source_range.end)
58
- message = format(ZERO_MSG, current: "#{length_method}.zero?")
63
+ message = format(ZERO_MSG, current: offense.source)
59
64
 
60
65
  add_offense(offense, message: message) do |corrector|
61
66
  corrector.replace(offense, 'empty?')
@@ -92,44 +97,47 @@ module RuboCop
92
97
  end
93
98
  end
94
99
 
95
- # @!method zero_length_predicate(node)
96
- def_node_matcher :zero_length_predicate, <<~PATTERN
97
- (send (send (...) ${:length :size}) :zero?)
100
+ # @!method zero_length_predicate?(node)
101
+ def_node_matcher :zero_length_predicate?, <<~PATTERN
102
+ (call (call (...) {:length :size}) :zero?)
98
103
  PATTERN
99
104
 
100
105
  # @!method zero_length_comparison(node)
101
106
  def_node_matcher :zero_length_comparison, <<~PATTERN
102
- {(send (send (...) ${:length :size}) $:== (int $0))
103
- (send (int $0) $:== (send (...) ${:length :size}))
104
- (send (send (...) ${:length :size}) $:< (int $1))
105
- (send (int $1) $:> (send (...) ${:length :size}))}
107
+ {(call (call (...) ${:length :size}) $:== (int $0))
108
+ (call (int $0) $:== (call (...) ${:length :size}))
109
+ (call (call (...) ${:length :size}) $:< (int $1))
110
+ (call (int $1) $:> (call (...) ${:length :size}))}
106
111
  PATTERN
107
112
 
108
113
  # @!method nonzero_length_comparison(node)
109
114
  def_node_matcher :nonzero_length_comparison, <<~PATTERN
110
- {(send (send (...) ${:length :size}) ${:> :!=} (int $0))
111
- (send (int $0) ${:< :!=} (send (...) ${:length :size}))}
115
+ {(call (call (...) ${:length :size}) ${:> :!=} (int $0))
116
+ (call (int $0) ${:< :!=} (call (...) ${:length :size}))}
112
117
  PATTERN
113
118
 
114
119
  def replacement(node)
115
- receiver = zero_length_receiver(node)
116
- return "#{receiver.source}.empty?" if receiver
120
+ length_node = zero_length_node(node)
121
+ if length_node&.receiver
122
+ return "#{length_node.receiver.source}#{length_node.loc.dot.source}empty?"
123
+ end
117
124
 
118
- "!#{other_receiver(node).source}.empty?"
125
+ other_length_node = other_length_node(node)
126
+ "!#{other_length_node.receiver.source}#{other_length_node.loc.dot.source}empty?"
119
127
  end
120
128
 
121
- # @!method zero_length_receiver(node)
122
- def_node_matcher :zero_length_receiver, <<~PATTERN
123
- {(send (send $_ _) :== (int 0))
124
- (send (int 0) :== (send $_ _))
125
- (send (send $_ _) :< (int 1))
126
- (send (int 1) :> (send $_ _))}
129
+ # @!method zero_length_node(node)
130
+ def_node_matcher :zero_length_node, <<~PATTERN
131
+ {(send $(call _ _) :== (int 0))
132
+ (send (int 0) :== $(call _ _))
133
+ (send $(call _ _) :< (int 1))
134
+ (send (int 1) :> $(call _ _))}
127
135
  PATTERN
128
136
 
129
- # @!method other_receiver(node)
130
- def_node_matcher :other_receiver, <<~PATTERN
131
- {(send (send $_ _) _ _)
132
- (send _ _ (send $_ _))}
137
+ # @!method other_length_node(node)
138
+ def_node_matcher :other_length_node, <<~PATTERN
139
+ {(call $(call _ _) _ _)
140
+ (call _ _ $(call _ _))}
133
141
  PATTERN
134
142
 
135
143
  # Some collection like objects in the Ruby standard library
@@ -74,6 +74,10 @@ module RuboCop
74
74
  # @deprecated. Use investigate
75
75
  # @return Array<offenses>
76
76
  def inspect_file(processed_source)
77
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
78
+ `inspect_file` is deprecated. Use `investigate` instead.
79
+ WARNING
80
+
77
81
  investigate(processed_source).offenses
78
82
  end
79
83
 
@@ -108,6 +112,10 @@ module RuboCop
108
112
 
109
113
  # @deprecated
110
114
  def forces
115
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
116
+ `forces` is deprecated.
117
+ WARNING
118
+
111
119
  @forces ||= self.class.forces_for(cops)
112
120
  end
113
121
 
@@ -20,6 +20,10 @@ module RuboCop
20
20
 
21
21
  # @deprecated Use `ProcessedSource#line_with_comment?`, `contains_comment?` or similar
22
22
  def comment_lines?(node)
23
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
24
+ `comment_lines?` is deprecated. Use `ProcessedSource#line_with_comment?`, `contains_comment?` or similar instead.
25
+ WARNING
26
+
23
27
  processed_source[line_range(node)].any? { |line| comment_line?(line) }
24
28
  end
25
29
 
@@ -173,7 +177,9 @@ module RuboCop
173
177
  def same_line?(node1, node2)
174
178
  line1 = line(node1)
175
179
  line2 = line(node2)
176
- line1 && line2 && line1 == line2
180
+ return false unless line1 && line2
181
+
182
+ line1 == line2
177
183
  end
178
184
 
179
185
  def indent(node, offset: 0)
@@ -27,7 +27,10 @@ module RuboCop
27
27
  class VariableForce < Force # rubocop:disable Metrics/ClassLength
28
28
  VARIABLE_ASSIGNMENT_TYPE = :lvasgn
29
29
  REGEXP_NAMED_CAPTURE_TYPE = :match_with_lvasgn
30
- VARIABLE_ASSIGNMENT_TYPES = [VARIABLE_ASSIGNMENT_TYPE, REGEXP_NAMED_CAPTURE_TYPE].freeze
30
+ PATTERN_MATCH_VARIABLE_TYPE = :match_var
31
+ VARIABLE_ASSIGNMENT_TYPES = [
32
+ VARIABLE_ASSIGNMENT_TYPE, REGEXP_NAMED_CAPTURE_TYPE, PATTERN_MATCH_VARIABLE_TYPE
33
+ ].freeze
31
34
 
32
35
  ARGUMENT_DECLARATION_TYPES = [
33
36
  :arg, :optarg, :restarg,
@@ -112,6 +115,7 @@ module RuboCop
112
115
  NODE_HANDLER_METHOD_NAMES = [
113
116
  [VARIABLE_ASSIGNMENT_TYPE, :process_variable_assignment],
114
117
  [REGEXP_NAMED_CAPTURE_TYPE, :process_regexp_named_captures],
118
+ [PATTERN_MATCH_VARIABLE_TYPE, :process_pattern_match_variable],
115
119
  [MULTIPLE_ASSIGNMENT_TYPE, :process_variable_multiple_assignment],
116
120
  [VARIABLE_REFERENCE_TYPE, :process_variable_referencing],
117
121
  [RESCUE_TYPE, :process_rescue],
@@ -175,6 +179,14 @@ module RuboCop
175
179
  skip_children!
176
180
  end
177
181
 
182
+ def process_pattern_match_variable(node)
183
+ name = node.children.first
184
+
185
+ variable_table.declare_variable(name, node) unless variable_table.variable_exist?(name)
186
+
187
+ skip_children!
188
+ end
189
+
178
190
  def regexp_captured_names(node)
179
191
  regexp = node.to_regexp
180
192
 
@@ -306,7 +306,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
306
306
  filename = "#{department_to_basename(department)}.adoc"
307
307
  content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
308
308
  cops_of_department(department).each do |cop|
309
- anchor = cop.cop_name.sub('/', '').downcase
309
+ anchor = cop.cop_name.delete('/').downcase
310
310
  content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
311
311
  end
312
312
 
@@ -9,12 +9,8 @@ class String
9
9
  # @return [Boolean] true is the string is blank, false otherwise
10
10
  #
11
11
  # @example
12
- # ''.blank? #=> true
13
- #
14
- # @example
15
- # ' '.blank? #=> true
16
- #
17
- # @example
12
+ # ''.blank? #=> true
13
+ # ' '.blank? #=> true
18
14
  # ' test'.blank? #=> false
19
15
  def blank?
20
16
  empty? || lstrip.empty?