rubocop 1.74.0 → 1.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +32 -6
  4. data/config/obsoletion.yml +3 -1
  5. data/lib/rubocop/cli.rb +1 -1
  6. data/lib/rubocop/config.rb +35 -6
  7. data/lib/rubocop/config_loader.rb +4 -0
  8. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  9. data/lib/rubocop/config_obsoletion.rb +46 -2
  10. data/lib/rubocop/config_validator.rb +1 -0
  11. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +1 -1
  12. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  13. data/lib/rubocop/cop/layout/block_alignment.rb +1 -0
  14. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  15. data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
  16. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  17. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  18. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
  19. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  20. data/lib/rubocop/cop/layout/line_length.rb +5 -1
  21. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
  22. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  23. data/lib/rubocop/cop/layout/redundant_line_break.rb +9 -5
  24. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  25. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  26. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
  27. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
  28. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  29. data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
  30. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  31. data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
  32. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
  33. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
  34. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
  35. data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
  36. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -0
  37. data/lib/rubocop/cop/lint/void.rb +1 -0
  38. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  39. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  40. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  41. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  42. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  43. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
  44. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
  45. data/lib/rubocop/cop/naming/method_name.rb +64 -8
  46. data/lib/rubocop/cop/naming/variable_name.rb +6 -19
  47. data/lib/rubocop/cop/registry.rb +9 -6
  48. data/lib/rubocop/cop/style/array_intersect.rb +39 -28
  49. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  50. data/lib/rubocop/cop/style/collection_methods.rb +1 -0
  51. data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
  52. data/lib/rubocop/cop/style/for.rb +1 -0
  53. data/lib/rubocop/cop/style/guard_clause.rb +2 -1
  54. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -2
  55. data/lib/rubocop/cop/style/hash_fetch_chain.rb +105 -0
  56. data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
  57. data/lib/rubocop/cop/style/inverse_methods.rb +1 -0
  58. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  59. data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
  60. data/lib/rubocop/cop/style/it_block_parameter.rb +100 -0
  61. data/lib/rubocop/cop/style/lambda.rb +1 -0
  62. data/lib/rubocop/cop/style/map_into_array.rb +1 -0
  63. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +1 -0
  64. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -0
  65. data/lib/rubocop/cop/style/next.rb +44 -0
  66. data/lib/rubocop/cop/style/object_then.rb +1 -0
  67. data/lib/rubocop/cop/style/proc.rb +1 -0
  68. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  69. data/lib/rubocop/cop/style/redundant_format.rb +10 -3
  70. data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -1
  71. data/lib/rubocop/cop/style/redundant_self.rb +1 -0
  72. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  73. data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
  74. data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
  75. data/lib/rubocop/cop/style/sole_nested_conditional.rb +41 -100
  76. data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
  77. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -0
  78. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  79. data/lib/rubocop/cop/variable_force/variable.rb +1 -6
  80. data/lib/rubocop/cop/variable_force.rb +1 -1
  81. data/lib/rubocop/lsp/runtime.rb +4 -4
  82. data/lib/rubocop/lsp/stdin_runner.rb +3 -1
  83. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  84. data/lib/rubocop/rspec/shared_contexts.rb +20 -0
  85. data/lib/rubocop/rspec/support.rb +2 -0
  86. data/lib/rubocop/runner.rb +5 -1
  87. data/lib/rubocop/target_ruby.rb +1 -1
  88. data/lib/rubocop/version.rb +14 -7
  89. data/lib/rubocop.rb +4 -0
  90. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +20 -2
  91. metadata +10 -6
@@ -3,15 +3,18 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for `raise` or `fail` statements which are
7
- # raising `Exception` class.
6
+ # Checks for `raise` or `fail` statements which raise `Exception` or
7
+ # `Exception.new`. Use `StandardError` or a specific exception class instead.
8
8
  #
9
- # You can specify a module name that will be an implicit namespace
10
- # using `AllowedImplicitNamespaces` option. The cop cause a false positive
11
- # for namespaced `Exception` when a namespace is omitted. This option can
12
- # prevent the false positive by specifying a namespace to be omitted for
13
- # `Exception`. Alternatively, make `Exception` a fully qualified class
14
- # name with an explicit namespace.
9
+ # If you have defined your own namespaced `Exception` class, it is possible
10
+ # to configure the cop to allow it by setting `AllowedImplicitNamespaces` to
11
+ # an array with the names of the namespaces to allow. By default, this is set to
12
+ # `['Gem']`, which allows `Gem::Exception` to be raised without an explicit namespace.
13
+ # If not allowed, a false positive may be registered if `raise Exception` is called
14
+ # within the namespace.
15
+ #
16
+ # Alternatively, use a fully qualified name with `raise`/`fail`
17
+ # (eg. `raise Namespace::Exception`).
15
18
  #
16
19
  # @safety
17
20
  # This cop is unsafe because it will change the exception class being
@@ -20,15 +23,31 @@ module RuboCop
20
23
  # @example
21
24
  # # bad
22
25
  # raise Exception, 'Error message here'
26
+ # raise Exception.new('Error message here')
23
27
  #
24
28
  # # good
25
29
  # raise StandardError, 'Error message here'
30
+ # raise MyError.new, 'Error message here'
31
+ #
32
+ # @example AllowedImplicitNamespaces: ['Gem'] (default)
33
+ # # bad - `Foo` is not an allowed implicit namespace
34
+ # module Foo
35
+ # def self.foo
36
+ # raise Exception # This is qualified to `Foo::Exception`.
37
+ # end
38
+ # end
26
39
  #
27
- # @example AllowedImplicitNamespaces: ['Gem']
28
40
  # # good
29
41
  # module Gem
30
42
  # def self.foo
31
- # raise Exception # This exception means `Gem::Exception`.
43
+ # raise Exception # This is qualified to `Gem::Exception`.
44
+ # end
45
+ # end
46
+ #
47
+ # # good
48
+ # module Foo
49
+ # def self.foo
50
+ # raise Foo::Exception
32
51
  # end
33
52
  # end
34
53
  class RaiseException < Base
@@ -53,6 +53,7 @@ module RuboCop
53
53
  # rubocop:enable Metrics/AbcSize
54
54
 
55
55
  alias on_numblock on_block
56
+ alias on_itblock on_block
56
57
 
57
58
  private
58
59
 
@@ -64,6 +65,8 @@ module RuboCop
64
65
  (args (arg _)) ...)
65
66
  (numblock
66
67
  $(call _ {:each_with_index :with_index} ...) 1 ...)
68
+ (itblock
69
+ $(call _ {:each_with_index :with_index} ...) _ ...)
67
70
  }
68
71
  PATTERN
69
72
 
@@ -49,6 +49,7 @@ module RuboCop
49
49
  end
50
50
 
51
51
  alias on_numblock on_block
52
+ alias on_itblock on_block
52
53
 
53
54
  private
54
55
 
@@ -59,6 +60,8 @@ module RuboCop
59
60
  $(call _ {:each_with_object :with_object} _) (args (arg _)) ...)
60
61
  (numblock
61
62
  $(call _ {:each_with_object :with_object} _) 1 ...)
63
+ (itblock
64
+ $(call _ {:each_with_object :with_object} _) _ ...)
62
65
  }
63
66
  PATTERN
64
67
 
@@ -56,19 +56,26 @@ module RuboCop
56
56
 
57
57
  outer_local_variable = variable_table.find_variable(variable.name)
58
58
  return unless outer_local_variable
59
+ return if variable_used_in_declaration_of_outer?(variable, outer_local_variable)
59
60
  return if same_conditions_node_different_branch?(variable, outer_local_variable)
60
61
 
61
62
  message = format(MSG, variable: variable.name)
62
63
  add_offense(variable.declaration_node, message: message)
63
64
  end
64
65
 
66
+ private
67
+
68
+ def variable_used_in_declaration_of_outer?(variable, outer_local_variable)
69
+ variable.scope.node.each_ancestor.any?(outer_local_variable.declaration_node)
70
+ end
71
+
65
72
  def same_conditions_node_different_branch?(variable, outer_local_variable)
66
73
  variable_node = variable_node(variable)
67
74
  return false unless node_or_its_ascendant_conditional?(variable_node)
68
75
 
69
76
  outer_local_variable_node =
70
77
  find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
71
- return true unless outer_local_variable_node
78
+ return false unless outer_local_variable_node
72
79
  return false unless outer_local_variable_node.conditional?
73
80
  return true if variable_node == outer_local_variable_node
74
81
 
@@ -53,6 +53,7 @@ module RuboCop
53
53
  end
54
54
 
55
55
  alias on_numblock on_block
56
+ alias on_itblock on_block
56
57
 
57
58
  private
58
59
 
@@ -74,6 +75,7 @@ module RuboCop
74
75
 
75
76
  def arg_count(node)
76
77
  return node.children[1] if node.numblock_type? # the maximum numbered param for the block
78
+ return 1 if node.itblock_type? # `it` block parameter is always one
77
79
 
78
80
  # Only `arg`, `optarg` and `mlhs` (destructuring) count as arguments that
79
81
  # can be used. Keyword arguments are not used for these methods so are
@@ -43,6 +43,7 @@ module RuboCop
43
43
  end
44
44
 
45
45
  alias on_numblock on_block
46
+ alias on_itblock on_block
46
47
 
47
48
  def after_block(node)
48
49
  @instance_eval_count -= 1 if instance_eval_block?(node)
@@ -101,9 +101,8 @@ module RuboCop
101
101
  check(node) if loop_method?(node)
102
102
  end
103
103
 
104
- def on_numblock(node)
105
- check(node) if loop_method?(node)
106
- end
104
+ alias on_numblock on_block
105
+ alias on_itblock on_block
107
106
 
108
107
  private
109
108
 
@@ -189,8 +188,9 @@ module RuboCop
189
188
 
190
189
  def preceded_by_continue_statement?(break_statement)
191
190
  break_statement.left_siblings.any? do |sibling|
192
- # Numblocks have the arguments count as a number in the AST.
193
- next if sibling.is_a?(Integer)
191
+ # Numblocks have the arguments count as a number or,
192
+ # itblocks have `:it` symbol in the AST.
193
+ next if sibling.is_a?(Integer) || sibling.is_a?(Symbol)
194
194
  next if sibling.loop_keyword? || loop_method?(sibling)
195
195
 
196
196
  sibling.each_descendant(*CONTINUE_KEYWORDS).any?
@@ -143,6 +143,7 @@ module RuboCop
143
143
  end
144
144
 
145
145
  alias on_numblock on_block
146
+ alias on_itblock on_block
146
147
 
147
148
  private
148
149
 
@@ -86,6 +86,7 @@ module RuboCop
86
86
  check_expression(node.body)
87
87
  end
88
88
  alias on_numblock on_block
89
+ alias on_itblock on_block
89
90
 
90
91
  def on_begin(node)
91
92
  check_begin(node)
@@ -57,6 +57,7 @@ module RuboCop
57
57
  check_code_length(node)
58
58
  end
59
59
  alias on_numblock on_block
60
+ alias on_itblock on_block
60
61
 
61
62
  private
62
63
 
@@ -63,6 +63,7 @@ module RuboCop
63
63
  check_code_length(node)
64
64
  end
65
65
  alias on_numblock on_block
66
+ alias on_itblock on_block
66
67
 
67
68
  private
68
69
 
@@ -145,7 +145,7 @@ module RuboCop
145
145
 
146
146
  def extract_body(node)
147
147
  case node.type
148
- when :class, :module, :sclass, :block, :numblock, :def, :defs
148
+ when :class, :module, :sclass, :block, :numblock, :itblock, :def, :defs
149
149
  node.body
150
150
  when :casgn
151
151
  extract_body(node.expression)
@@ -48,7 +48,7 @@ module RuboCop
48
48
 
49
49
  args = process_args(node.arguments)
50
50
  return extract_breakable_node_from_elements(node, args, max)
51
- elsif node.def_type?
51
+ elsif node.type?(:def, :defs)
52
52
  return extract_breakable_node_from_elements(node, node.arguments, max)
53
53
  elsif node.type?(:array, :hash)
54
54
  return extract_breakable_node_from_elements(node, node.children, max)
@@ -220,7 +220,7 @@ module RuboCop
220
220
 
221
221
  # @api private
222
222
  def already_on_multiple_lines?(node)
223
- return node.first_line != node.last_argument.last_line if node.def_type?
223
+ return node.first_line != node.last_argument.last_line if node.type?(:def, :defs)
224
224
 
225
225
  !node.single_line?
226
226
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This module encapsulates the ability to forbid certain identifiers in a cop.
6
+ module ForbiddenIdentifiers
7
+ SIGILS = '@$' # if a variable starts with a sigil it will be removed
8
+
9
+ def forbidden_identifier?(name)
10
+ name = name.to_s.delete(SIGILS)
11
+
12
+ forbidden_identifiers.any? && forbidden_identifiers.include?(name)
13
+ end
14
+
15
+ def forbidden_identifiers
16
+ cop_config.fetch('ForbiddenIdentifiers', [])
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This module encapsulates the ability to forbid certain patterns in a cop.
6
+ module ForbiddenPattern
7
+ def forbidden_pattern?(name)
8
+ forbidden_patterns.any? { |pattern| Regexp.new(pattern).match?(name) }
9
+ end
10
+
11
+ def forbidden_patterns
12
+ cop_config.fetch('ForbiddenPatterns', [])
13
+ end
14
+ end
15
+ end
16
+ end
@@ -30,6 +30,7 @@ module RuboCop
30
30
  end
31
31
 
32
32
  alias on_numblock on_block
33
+ alias on_itblock on_block
33
34
 
34
35
  private
35
36
 
@@ -6,14 +6,31 @@ module RuboCop
6
6
  # Makes sure that all methods use the configured style,
7
7
  # snake_case or camelCase, for their names.
8
8
  #
9
- # This cop has `AllowedPatterns` configuration option.
9
+ # Method names matching patterns are always allowed.
10
10
  #
11
- # Naming/MethodName:
12
- # AllowedPatterns:
13
- # - '\AonSelectionBulkChange\z'
14
- # - '\AonSelectionCleared\z'
11
+ # The cop can be configured with `AllowedPatterns` to allow certain regexp patterns:
15
12
  #
16
- # Method names matching patterns are always allowed.
13
+ # [source,yaml]
14
+ # ----
15
+ # Naming/MethodName:
16
+ # AllowedPatterns:
17
+ # - '\AonSelectionBulkChange\z'
18
+ # - '\AonSelectionCleared\z'
19
+ # ----
20
+ #
21
+ # As well, you can also forbid specific method names or regexp patterns
22
+ # using `ForbiddenIdentifiers` or `ForbiddenPatterns`:
23
+ #
24
+ # [source,yaml]
25
+ # ----
26
+ # Naming/MethodName:
27
+ # ForbiddenIdentifiers:
28
+ # - 'def'
29
+ # - 'super'
30
+ # ForbiddenPatterns:
31
+ # - '_v1\z'
32
+ # - '_gen1\z'
33
+ # ----
17
34
  #
18
35
  # @example EnforcedStyle: snake_case (default)
19
36
  # # bad
@@ -28,12 +45,26 @@ module RuboCop
28
45
  #
29
46
  # # good
30
47
  # def fooBar; end
48
+ #
49
+ # @example ForbiddenIdentifiers: ['def', 'super']
50
+ # # bad
51
+ # def def; end
52
+ # def super; end
53
+ #
54
+ # @example ForbiddenPatterns: ['_v1\z', '_gen1\z']
55
+ # # bad
56
+ # def release_v1; end
57
+ # def api_gen1; end
58
+ #
31
59
  class MethodName < Base
32
60
  include ConfigurableNaming
33
61
  include AllowedPattern
34
62
  include RangeHelp
63
+ include ForbiddenIdentifiers
64
+ include ForbiddenPattern
35
65
 
36
66
  MSG = 'Use %<style>s for method names.'
67
+ MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
37
68
 
38
69
  # @!method sym_name(node)
39
70
  def_node_matcher :sym_name, '(sym $_name)'
@@ -48,19 +79,44 @@ module RuboCop
48
79
  name = attr_name(name_item)
49
80
  next if !name || matches_allowed_pattern?(name)
50
81
 
51
- check_name(node, name, range_position(node))
82
+ if forbidden_name?(name.to_s)
83
+ register_forbidden_name(node)
84
+ else
85
+ check_name(node, name, range_position(node))
86
+ end
52
87
  end
53
88
  end
54
89
 
55
90
  def on_def(node)
56
91
  return if node.operator_method? || matches_allowed_pattern?(node.method_name)
57
92
 
58
- check_name(node, node.method_name, node.loc.name)
93
+ if forbidden_name?(node.method_name.to_s)
94
+ register_forbidden_name(node)
95
+ else
96
+ check_name(node, node.method_name, node.loc.name)
97
+ end
59
98
  end
60
99
  alias on_defs on_def
61
100
 
62
101
  private
63
102
 
103
+ def forbidden_name?(name)
104
+ forbidden_identifier?(name) || forbidden_pattern?(name)
105
+ end
106
+
107
+ def register_forbidden_name(node)
108
+ if node.type?(:def, :defs)
109
+ name_node = node.loc.name
110
+ method_name = node.method_name
111
+ else
112
+ attrs = node.attribute_accessor?
113
+ name_node = attrs.last.last
114
+ method_name = attr_name(name_node)
115
+ end
116
+ message = format(MSG_FORBIDDEN, identifier: method_name)
117
+ add_offense(name_node, message: message)
118
+ end
119
+
64
120
  def attr_name(name_item)
65
121
  sym_name(name_item) || str_name(name_item)
66
122
  end
@@ -53,6 +53,8 @@ module RuboCop
53
53
  include AllowedIdentifiers
54
54
  include ConfigurableNaming
55
55
  include AllowedPattern
56
+ include ForbiddenIdentifiers
57
+ include ForbiddenPattern
56
58
 
57
59
  MSG = 'Use %<style>s for variable names.'
58
60
  MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another name instead.'
@@ -92,27 +94,12 @@ module RuboCop
92
94
 
93
95
  private
94
96
 
95
- def message(style)
96
- format(MSG, style: style)
97
- end
98
-
99
- def forbidden_identifiers
100
- cop_config.fetch('ForbiddenIdentifiers', [])
101
- end
102
-
103
- def forbidden_patterns
104
- cop_config.fetch('ForbiddenPatterns', [])
105
- end
106
-
107
- def matches_forbidden_pattern?(name)
108
- forbidden_patterns.any? { |pattern| Regexp.new(pattern).match?(name) }
109
- end
110
-
111
97
  def forbidden_name?(name)
112
- name = name.to_s.delete(SIGILS)
98
+ forbidden_identifier?(name) || forbidden_pattern?(name)
99
+ end
113
100
 
114
- (forbidden_identifiers.any? && forbidden_identifiers.include?(name)) ||
115
- (forbidden_patterns.any? && matches_forbidden_pattern?(name))
101
+ def message(style)
102
+ format(MSG, style: style)
116
103
  end
117
104
 
118
105
  def register_forbidden_name(node)
@@ -23,8 +23,8 @@ module RuboCop
23
23
  global.without_department(:Test).cops
24
24
  end
25
25
 
26
- def self.qualified_cop_name(name, origin)
27
- global.qualified_cop_name(name, origin)
26
+ def self.qualified_cop_name(name, origin, warn: true)
27
+ global.qualified_cop_name(name, origin, warn: warn)
28
28
  end
29
29
 
30
30
  # Changes momentarily the global registry
@@ -139,7 +139,7 @@ module RuboCop
139
139
 
140
140
  case potential_badges.size
141
141
  when 0 then name # No namespace found. Deal with it later in caller.
142
- when 1 then resolve_badge(badge, potential_badges.first, path)
142
+ when 1 then resolve_badge(badge, potential_badges.first, path, warn: warn)
143
143
  else raise AmbiguousCopName.new(badge, path, potential_badges)
144
144
  end
145
145
  end
@@ -296,11 +296,14 @@ module RuboCop
296
296
  self.class.new(cops)
297
297
  end
298
298
 
299
- def resolve_badge(given_badge, real_badge, source_path)
299
+ def resolve_badge(given_badge, real_badge, source_path, warn: true)
300
300
  unless given_badge.match?(real_badge)
301
301
  path = PathUtil.smart_path(source_path)
302
- warn "#{path}: #{given_badge} has the wrong namespace - " \
303
- "replace it with #{given_badge.with_department(real_badge.department)}"
302
+
303
+ if warn
304
+ warn("#{path}: #{given_badge} has the wrong namespace - " \
305
+ "replace it with #{given_badge.with_department(real_badge.department)}")
306
+ end
304
307
  end
305
308
 
306
309
  real_badge.to_s
@@ -6,7 +6,8 @@ module RuboCop
6
6
  # In Ruby 3.1, `Array#intersect?` has been added.
7
7
  #
8
8
  # This cop identifies places where `(array1 & array2).any?`
9
- # can be replaced by `array1.intersect?(array2)`.
9
+ # or `(array1.intersection(array2)).any?` can be replaced by
10
+ # `array1.intersect?(array2)`.
10
11
  #
11
12
  # The `array1.intersect?(array2)` method is faster than
12
13
  # `(array1 & array2).any?` and is more readable.
@@ -20,6 +21,10 @@ module RuboCop
20
21
  # [1].intersect?([1,2]) { |x| false } # => true
21
22
  # ----
22
23
  #
24
+ # NOTE: Although `Array#intersection` can take zero or multiple arguments,
25
+ # only cases where exactly one argument is provided can be replaced with
26
+ # `Array#intersect?` and are handled by this cop.
27
+ #
23
28
  # @safety
24
29
  # This cop cannot guarantee that `array1` and `array2` are
25
30
  # actually arrays while method `intersect?` is for arrays only.
@@ -30,6 +35,11 @@ module RuboCop
30
35
  # (array1 & array2).empty?
31
36
  # (array1 & array2).none?
32
37
  #
38
+ # # bad
39
+ # array1.intersection(array2).any?
40
+ # array1.intersection(array2).empty?
41
+ # array1.intersection(array2).none?
42
+ #
33
43
  # # good
34
44
  # array1.intersect?(array2)
35
45
  # !array1.intersect?(array2)
@@ -53,65 +63,66 @@ module RuboCop
53
63
 
54
64
  minimum_target_ruby_version 3.1
55
65
 
56
- # @!method regular_bad_intersection_check?(node)
57
- def_node_matcher :regular_bad_intersection_check?, <<~PATTERN
58
- (send
59
- (begin
60
- (send $(...) :& $(...))
61
- ) ${:any? :empty? :none?}
62
- )
63
- PATTERN
66
+ PREDICATES = %i[any? empty? none?].to_set.freeze
67
+ ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
64
68
 
65
- # @!method active_support_bad_intersection_check?(node)
66
- def_node_matcher :active_support_bad_intersection_check?, <<~PATTERN
67
- (send
68
- (begin
69
- (send $(...) :& $(...))
70
- ) ${:present? :any? :blank? :empty? :none?}
69
+ # @!method bad_intersection_check?(node, predicates)
70
+ def_node_matcher :bad_intersection_check?, <<~PATTERN
71
+ (call
72
+ {
73
+ (begin (send $_ :& $_))
74
+ (call $_ :intersection $_)
75
+ }
76
+ $%1
71
77
  )
72
78
  PATTERN
73
79
 
74
- MSG = 'Use `%<negated>s%<receiver>s.intersect?(%<argument>s)` ' \
75
- 'instead of `(%<receiver>s & %<argument>s).%<method_name>s`.'
80
+ MSG = 'Use `%<negated>s%<receiver>s%<dot>sintersect?(%<argument>s)` ' \
81
+ 'instead of `%<existing>s`.'
76
82
  STRAIGHT_METHODS = %i[present? any?].freeze
77
83
  NEGATED_METHODS = %i[blank? empty? none?].freeze
78
84
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
79
85
 
80
86
  def on_send(node)
81
87
  return if node.block_literal?
82
- return unless (receiver, argument, method_name = bad_intersection_check?(node))
88
+ return unless (receiver, argument, method_name = bad_intersection?(node))
83
89
 
84
- message = message(receiver.source, argument.source, method_name)
90
+ dot = node.loc.dot.source
91
+ message = message(receiver.source, argument.source, method_name, dot, node.source)
85
92
 
86
93
  add_offense(node, message: message) do |corrector|
87
94
  bang = straight?(method_name) ? '' : '!'
88
95
 
89
- corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
96
+ corrector.replace(node, "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})")
90
97
  end
91
98
  end
99
+ alias on_csend on_send
92
100
 
93
101
  private
94
102
 
95
- def bad_intersection_check?(node)
96
- if active_support_extensions_enabled?
97
- active_support_bad_intersection_check?(node)
98
- else
99
- regular_bad_intersection_check?(node)
100
- end
103
+ def bad_intersection?(node)
104
+ predicates = if active_support_extensions_enabled?
105
+ ACTIVE_SUPPORT_PREDICATES
106
+ else
107
+ PREDICATES
108
+ end
109
+
110
+ bad_intersection_check?(node, predicates)
101
111
  end
102
112
 
103
113
  def straight?(method_name)
104
114
  STRAIGHT_METHODS.include?(method_name.to_sym)
105
115
  end
106
116
 
107
- def message(receiver, argument, method_name)
117
+ def message(receiver, argument, method_name, dot, existing)
108
118
  negated = straight?(method_name) ? '' : '!'
109
119
  format(
110
120
  MSG,
111
121
  negated: negated,
112
122
  receiver: receiver,
113
123
  argument: argument,
114
- method_name: method_name
124
+ dot: dot,
125
+ existing: existing
115
126
  )
116
127
  end
117
128
  end
@@ -208,6 +208,7 @@ module RuboCop
208
208
  end
209
209
 
210
210
  alias on_numblock on_block
211
+ alias on_itblock on_block
211
212
 
212
213
  private
213
214
 
@@ -347,7 +348,7 @@ module RuboCop
347
348
  # rubocop:disable Metrics/CyclomaticComplexity
348
349
  def get_blocks(node, &block)
349
350
  case node.type
350
- when :block, :numblock
351
+ when :block, :numblock, :itblock
351
352
  yield node
352
353
  when :send, :csend
353
354
  # When a method has an argument which is another method with a block,
@@ -50,6 +50,7 @@ module RuboCop
50
50
  check_method_node(node.send_node)
51
51
  end
52
52
  alias on_numblock on_block
53
+ alias on_itblock on_block
53
54
 
54
55
  def on_send(node)
55
56
  return unless implicit_block?(node)
@@ -80,6 +80,7 @@ module RuboCop
80
80
  # rubocop:enable Metrics/CyclomaticComplexity
81
81
 
82
82
  alias on_numblock on_block
83
+ alias on_itblock on_block
83
84
 
84
85
  def on_for(node)
85
86
  return unless node.parent&.begin_type?
@@ -77,6 +77,7 @@ module RuboCop
77
77
  end
78
78
 
79
79
  alias on_numblock on_block
80
+ alias on_itblock on_block
80
81
 
81
82
  private
82
83
 
@@ -135,6 +135,7 @@ module RuboCop
135
135
  on_def(node)
136
136
  end
137
137
  alias on_numblock on_block
138
+ alias on_itblock on_block
138
139
 
139
140
  def on_if(node)
140
141
  return if accepted_form?(node)
@@ -213,7 +214,7 @@ module RuboCop
213
214
  if_branch = node.if_branch
214
215
  else_branch = node.else_branch
215
216
 
216
- corrector.replace(node.loc.begin, "\n") if node.loc.begin&.is?('then')
217
+ corrector.replace(node.loc.begin, "\n") if node.then?
217
218
 
218
219
  if if_branch&.send_type? && heredoc?(if_branch.last_argument)
219
220
  autocorrect_heredoc_argument(corrector, node, if_branch, else_branch, guard)