rubocop-rspec 2.22.0 → 2.25.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -9
  3. data/README.md +1 -1
  4. data/config/default.yml +87 -17
  5. data/lib/rubocop/cop/rspec/duplicated_metadata.rb +1 -1
  6. data/lib/rubocop/cop/rspec/empty_example_group.rb +3 -0
  7. data/lib/rubocop/cop/rspec/empty_metadata.rb +46 -0
  8. data/lib/rubocop/cop/rspec/eq.rb +47 -0
  9. data/lib/rubocop/cop/rspec/example_length.rb +11 -5
  10. data/lib/rubocop/cop/rspec/excessive_docstring_spacing.rb +13 -4
  11. data/lib/rubocop/cop/rspec/expect_actual.rb +2 -2
  12. data/lib/rubocop/cop/rspec/file_path.rb +6 -0
  13. data/lib/rubocop/cop/rspec/focus.rb +15 -0
  14. data/lib/rubocop/cop/rspec/indexed_let.rb +32 -1
  15. data/lib/rubocop/cop/rspec/instance_variable.rb +1 -1
  16. data/lib/rubocop/cop/rspec/leaky_constant_declaration.rb +1 -1
  17. data/lib/rubocop/cop/rspec/let_before_examples.rb +4 -0
  18. data/lib/rubocop/cop/rspec/metadata_style.rb +202 -0
  19. data/lib/rubocop/cop/rspec/mixin/file_help.rb +14 -0
  20. data/lib/rubocop/cop/rspec/mixin/metadata.rb +21 -7
  21. data/lib/rubocop/cop/rspec/named_subject.rb +1 -1
  22. data/lib/rubocop/cop/rspec/pending.rb +12 -2
  23. data/lib/rubocop/cop/rspec/predicate_matcher.rb +3 -3
  24. data/lib/rubocop/cop/rspec/rails/http_status.rb +28 -17
  25. data/lib/rubocop/cop/rspec/rails/negation_be_valid.rb +102 -0
  26. data/lib/rubocop/cop/rspec/receive_messages.rb +161 -0
  27. data/lib/rubocop/cop/rspec/sort_metadata.rb +2 -1
  28. data/lib/rubocop/cop/rspec/spec_file_path_format.rb +133 -0
  29. data/lib/rubocop/cop/rspec/spec_file_path_suffix.rb +40 -0
  30. data/lib/rubocop/cop/rspec/variable_definition.rb +2 -2
  31. data/lib/rubocop/cop/rspec/verified_double_reference.rb +6 -6
  32. data/lib/rubocop/cop/rspec/verified_doubles.rb +1 -1
  33. data/lib/rubocop/cop/rspec/void_expect.rb +2 -1
  34. data/lib/rubocop/cop/rspec_cops.rb +7 -0
  35. data/lib/rubocop/rspec/version.rb +1 -1
  36. data/lib/rubocop-rspec.rb +1 -0
  37. metadata +13 -5
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # It makes reading the test harder because it's not clear what exactly
9
9
  # is tested by this particular example.
10
10
  #
11
+ # The configurable options `AllowedIdentifiers` and `AllowedPatterns`
12
+ # will also read those set in `Naming/VariableNumber`.
13
+ #
11
14
  # @example `Max: 1 (default)`
12
15
  # # bad
13
16
  # let(:item_1) { create(:item) }
@@ -31,7 +34,20 @@ module RuboCop
31
34
  # let(:item_1) { create(:item) }
32
35
  # let(:item_2) { create(:item) }
33
36
  #
37
+ # @example `AllowedIdentifiers: ['item_1', 'item_2']`
38
+ # # good
39
+ # let(:item_1) { create(:item) }
40
+ # let(:item_2) { create(:item) }
41
+ #
42
+ # @example `AllowedPatterns: ['item']`
43
+ # # good
44
+ # let(:item_1) { create(:item) }
45
+ # let(:item_2) { create(:item) }
46
+ #
34
47
  class IndexedLet < Base
48
+ include AllowedIdentifiers
49
+ include AllowedPattern
50
+
35
51
  MSG = 'This `let` statement uses index in its name. Please give it ' \
36
52
  'a meaningful name.'
37
53
 
@@ -69,12 +85,27 @@ module RuboCop
69
85
  end
70
86
 
71
87
  def indexed_let?(node)
72
- let?(node) && SUFFIX_INDEX_REGEX.match?(let_name(node))
88
+ let?(node) &&
89
+ SUFFIX_INDEX_REGEX.match?(let_name(node)) &&
90
+ !allowed_identifier?(let_name(node).to_s) &&
91
+ !matches_allowed_pattern?(let_name(node).to_s)
73
92
  end
74
93
 
75
94
  def let_name_stripped_index(node)
76
95
  let_name(node).to_s.gsub(INDEX_REGEX, '')
77
96
  end
97
+
98
+ def cop_config_patterns_values
99
+ Array(config.for_cop('Naming/VariableNumber')
100
+ .fetch('AllowedPatterns', [])) +
101
+ Array(cop_config.fetch('AllowedPatterns', []))
102
+ end
103
+
104
+ def allowed_identifiers
105
+ Array(config.for_cop('Naming/VariableNumber')
106
+ .fetch('AllowedIdentifiers', [])) +
107
+ Array(cop_config.fetch('AllowedIdentifiers', []))
108
+ end
78
109
  end
79
110
  end
80
111
  end
@@ -48,7 +48,7 @@ module RuboCop
48
48
  class InstanceVariable < Base
49
49
  include TopLevelGroup
50
50
 
51
- MSG = 'Avoid instance variables use let, ' \
51
+ MSG = 'Avoid instance variables - use let, ' \
52
52
  'a method call, or a local variable (if possible).'
53
53
 
54
54
  # @!method dynamic_class?(node)
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # Anonymous classes are fine, since they don't result in global
18
18
  # namespace name clashes.
19
19
  #
20
- # @see https://relishapp.com/rspec/rspec-mocks/docs/mutating-constants
20
+ # @see https://rspec.info/features/3-12/rspec-mocks/mutating-constants
21
21
  #
22
22
  # @example Constants leak between examples
23
23
  # # bad
@@ -51,6 +51,10 @@ module RuboCop
51
51
  }
52
52
  PATTERN
53
53
 
54
+ def self.autocorrect_incompatible_with
55
+ [RSpec::ScatteredLet]
56
+ end
57
+
54
58
  def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
55
59
  return unless example_group_with_body?(node)
56
60
 
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Use consistent metadata style.
7
+ #
8
+ # This cop does not support autocorrection in the case of
9
+ # `EnforcedStyle: hash` where the trailing metadata type is ambiguous.
10
+ # (e.g. `describe 'Something', :a, b`)
11
+ #
12
+ # @example EnforcedStyle: symbol (default)
13
+ # # bad
14
+ # describe 'Something', a: true
15
+ #
16
+ # # good
17
+ # describe 'Something', :a
18
+ #
19
+ # @example EnforcedStyle: hash
20
+ # # bad
21
+ # describe 'Something', :a
22
+ #
23
+ # # good
24
+ # describe 'Something', a: true
25
+ class MetadataStyle < Base # rubocop:disable Metrics/ClassLength
26
+ extend AutoCorrector
27
+
28
+ include ConfigurableEnforcedStyle
29
+ include Metadata
30
+ include RangeHelp
31
+
32
+ # @!method extract_metadata_hash(node)
33
+ def_node_matcher :extract_metadata_hash, <<~PATTERN
34
+ (send _ _ _ ... $hash)
35
+ PATTERN
36
+
37
+ # @!method match_boolean_metadata_pair?(node)
38
+ def_node_matcher :match_boolean_metadata_pair?, <<~PATTERN
39
+ (pair sym true)
40
+ PATTERN
41
+
42
+ # @!method match_ambiguous_trailing_metadata?(node)
43
+ def_node_matcher :match_ambiguous_trailing_metadata?, <<~PATTERN
44
+ (send _ _ _ ... !{hash sym})
45
+ PATTERN
46
+
47
+ def on_metadata(symbols, hash)
48
+ # RSpec example groups accept two string arguments. In such a case,
49
+ # the rspec_metadata matcher will interpret the second string
50
+ # argument as a metadata symbol.
51
+ symbols.shift if symbols.first&.str_type?
52
+
53
+ symbols.each do |symbol|
54
+ on_metadata_symbol(symbol)
55
+ end
56
+
57
+ return unless hash
58
+
59
+ hash.pairs.each do |pair|
60
+ on_metadata_pair(pair)
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ def autocorrect_pair(corrector, node)
67
+ remove_pair(corrector, node)
68
+ insert_symbol(corrector, node)
69
+ end
70
+
71
+ def autocorrect_symbol(corrector, node)
72
+ return if match_ambiguous_trailing_metadata?(node.parent)
73
+
74
+ remove_symbol(corrector, node)
75
+ insert_pair(corrector, node)
76
+ end
77
+
78
+ def bad_metadata_pair?(node)
79
+ style == :symbol && match_boolean_metadata_pair?(node)
80
+ end
81
+
82
+ def bad_metadata_symbol?(_node)
83
+ style == :hash
84
+ end
85
+
86
+ def format_symbol_to_pair_source(node)
87
+ "#{node.value}: true"
88
+ end
89
+
90
+ def insert_pair(corrector, node)
91
+ hash_node = extract_metadata_hash(node.parent)
92
+ if hash_node.nil?
93
+ insert_pair_as_last_argument(corrector, node)
94
+ elsif hash_node.pairs.any?
95
+ insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
96
+ else
97
+ insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
98
+ end
99
+ end
100
+
101
+ def insert_pair_as_last_argument(corrector, node)
102
+ corrector.insert_before(
103
+ node.parent.location.end || node.parent.source_range.with(
104
+ begin_pos: node.parent.source_range.end_pos
105
+ ),
106
+ ", #{format_symbol_to_pair_source(node)}"
107
+ )
108
+ end
109
+
110
+ def insert_pair_to_empty_hash_metadata(corrector, node, hash_node)
111
+ corrector.insert_after(
112
+ hash_node.location.begin,
113
+ " #{format_symbol_to_pair_source(node)} "
114
+ )
115
+ end
116
+
117
+ def insert_pair_to_non_empty_hash_metadata(corrector, node, hash_node)
118
+ corrector.insert_after(
119
+ hash_node.children.last,
120
+ ", #{format_symbol_to_pair_source(node)}"
121
+ )
122
+ end
123
+
124
+ def insert_symbol(corrector, node)
125
+ corrector.insert_after(
126
+ node.parent.left_sibling,
127
+ ", #{node.key.value.inspect}"
128
+ )
129
+ end
130
+
131
+ def message_for_style
132
+ format(
133
+ 'Use %<style>s style for metadata.',
134
+ style: style
135
+ )
136
+ end
137
+
138
+ def on_metadata_pair(node)
139
+ return unless bad_metadata_pair?(node)
140
+
141
+ add_offense(node, message: message_for_style) do |corrector|
142
+ autocorrect_pair(corrector, node)
143
+ end
144
+ end
145
+
146
+ def on_metadata_symbol(node)
147
+ return unless bad_metadata_symbol?(node)
148
+
149
+ add_offense(node, message: message_for_style) do |corrector|
150
+ autocorrect_symbol(corrector, node)
151
+ end
152
+ end
153
+
154
+ def remove_pair(corrector, node)
155
+ if !node.parent.braces? || node.left_siblings.any?
156
+ remove_pair_following(corrector, node)
157
+ elsif node.right_siblings.any?
158
+ remove_pair_preceding(corrector, node)
159
+ else
160
+ corrector.remove(node)
161
+ end
162
+ end
163
+
164
+ def remove_pair_following(corrector, node)
165
+ corrector.remove(
166
+ range_with_surrounding_comma(
167
+ range_with_surrounding_space(
168
+ node.source_range,
169
+ side: :left
170
+ ),
171
+ :left
172
+ )
173
+ )
174
+ end
175
+
176
+ def remove_pair_preceding(corrector, node)
177
+ corrector.remove(
178
+ range_with_surrounding_space(
179
+ range_with_surrounding_comma(
180
+ node.source_range,
181
+ :right
182
+ ),
183
+ side: :right
184
+ )
185
+ )
186
+ end
187
+
188
+ def remove_symbol(corrector, node)
189
+ corrector.remove(
190
+ range_with_surrounding_comma(
191
+ range_with_surrounding_space(
192
+ node.source_range,
193
+ side: :left
194
+ ),
195
+ :left
196
+ )
197
+ )
198
+ end
199
+ end
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ # Help methods for file.
7
+ module FileHelp
8
+ def expanded_file_path
9
+ File.expand_path(processed_source.file_path)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -13,7 +13,7 @@ module RuboCop
13
13
  def_node_matcher :rspec_metadata, <<~PATTERN
14
14
  (block
15
15
  (send
16
- #rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ ${send str sym}* (hash $...)?)
16
+ #rspec? {#Examples.all #ExampleGroups.all #SharedGroups.all #Hooks.all} _ $...)
17
17
  ...)
18
18
  PATTERN
19
19
 
@@ -24,25 +24,39 @@ module RuboCop
24
24
 
25
25
  # @!method metadata_in_block(node)
26
26
  def_node_search :metadata_in_block, <<~PATTERN
27
- (send (lvar %) #Hooks.all _ ${send str sym}* (hash $...)?)
27
+ (send (lvar %) #Hooks.all _ $...)
28
28
  PATTERN
29
29
 
30
30
  def on_block(node)
31
31
  rspec_configure(node) do |block_var|
32
- metadata_in_block(node, block_var) do |symbols, pairs|
33
- on_metadata(symbols, pairs.flatten)
32
+ metadata_in_block(node, block_var) do |metadata_arguments|
33
+ on_metadata_arguments(metadata_arguments)
34
34
  end
35
35
  end
36
36
 
37
- rspec_metadata(node) do |symbols, pairs|
38
- on_metadata(symbols, pairs.flatten)
37
+ rspec_metadata(node) do |metadata_arguments|
38
+ on_metadata_arguments(metadata_arguments)
39
39
  end
40
40
  end
41
41
  alias on_numblock on_block
42
42
 
43
- def on_metadata(_symbols, _pairs)
43
+ def on_metadata(_symbols, _hash)
44
44
  raise ::NotImplementedError
45
45
  end
46
+
47
+ private
48
+
49
+ def on_metadata_arguments(metadata_arguments)
50
+ *symbols, last = metadata_arguments
51
+ hash = nil
52
+ case last&.type
53
+ when :hash
54
+ hash = last
55
+ when :sym
56
+ symbols << last
57
+ end
58
+ on_metadata(symbols, hash)
59
+ end
46
60
  end
47
61
  end
48
62
  end
@@ -145,7 +145,7 @@ module RuboCop
145
145
  end
146
146
 
147
147
  def find_subject(block_node)
148
- block_node.body.child_nodes.find { |send_node| subject?(send_node) }
148
+ block_node.body&.child_nodes&.find { |send_node| subject?(send_node) }
149
149
  end
150
150
  end
151
151
  end
@@ -41,10 +41,15 @@ module RuboCop
41
41
  def_node_matcher :skippable?, <<~PATTERN
42
42
  {
43
43
  (send #rspec? #ExampleGroups.regular ...)
44
- (send nil? #Examples.regular ...)
44
+ #skippable_example?
45
45
  }
46
46
  PATTERN
47
47
 
48
+ # @!method skippable_example?(node)
49
+ def_node_matcher :skippable_example?, <<~PATTERN
50
+ (send nil? #Examples.regular ...)
51
+ PATTERN
52
+
48
53
  # @!method pending_block?(node)
49
54
  def_node_matcher :pending_block?, <<~PATTERN
50
55
  {
@@ -62,7 +67,12 @@ module RuboCop
62
67
  private
63
68
 
64
69
  def skipped?(node)
65
- skippable?(node) && skipped_in_metadata?(node)
70
+ skippable?(node) && skipped_in_metadata?(node) ||
71
+ skipped_regular_example_without_body?(node)
72
+ end
73
+
74
+ def skipped_regular_example_without_body?(node)
75
+ skippable_example?(node) && !node.block_node
66
76
  end
67
77
  end
68
78
  end
@@ -74,7 +74,7 @@ module RuboCop
74
74
  name[0..-2]
75
75
  when 'exist?', 'exists?'
76
76
  'exist'
77
- when /^has_/
77
+ when /\Ahas_/
78
78
  name.sub('has_', 'have_')[0..-2]
79
79
  else
80
80
  "be_#{name[0..-2]}"
@@ -240,10 +240,10 @@ module RuboCop
240
240
  'include?'
241
241
  when 'respond_to'
242
242
  'respond_to?'
243
- when /^have_(.+)/
243
+ when /\Ahave_(.+)/
244
244
  "has_#{Regexp.last_match(1)}?"
245
245
  else
246
- "#{matcher[/^be_(.+)/, 1]}?"
246
+ "#{matcher[/\Abe_(.+)/, 1]}?"
247
247
  end
248
248
  end
249
249
  # rubocop:enable Metrics/MethodLength
@@ -17,10 +17,12 @@ module RuboCop
17
17
  # # bad
18
18
  # it { is_expected.to have_http_status 200 }
19
19
  # it { is_expected.to have_http_status 404 }
20
+ # it { is_expected.to have_http_status "403" }
20
21
  #
21
22
  # # good
22
23
  # it { is_expected.to have_http_status :ok }
23
24
  # it { is_expected.to have_http_status :not_found }
25
+ # it { is_expected.to have_http_status :forbidden }
24
26
  # it { is_expected.to have_http_status :success }
25
27
  # it { is_expected.to have_http_status :error }
26
28
  #
@@ -28,10 +30,12 @@ module RuboCop
28
30
  # # bad
29
31
  # it { is_expected.to have_http_status :ok }
30
32
  # it { is_expected.to have_http_status :not_found }
33
+ # it { is_expected.to have_http_status "forbidden" }
31
34
  #
32
35
  # # good
33
36
  # it { is_expected.to have_http_status 200 }
34
37
  # it { is_expected.to have_http_status 404 }
38
+ # it { is_expected.to have_http_status 403 }
35
39
  # it { is_expected.to have_http_status :success }
36
40
  # it { is_expected.to have_http_status :error }
37
41
  #
@@ -39,8 +43,10 @@ module RuboCop
39
43
  # # bad
40
44
  # it { is_expected.to have_http_status :ok }
41
45
  # it { is_expected.to have_http_status :not_found }
46
+ # it { is_expected.to have_http_status "forbidden" }
42
47
  # it { is_expected.to have_http_status 200 }
43
48
  # it { is_expected.to have_http_status 404 }
49
+ # it { is_expected.to have_http_status "403" }
44
50
  #
45
51
  # # good
46
52
  # it { is_expected.to be_ok }
@@ -55,11 +61,13 @@ module RuboCop
55
61
 
56
62
  # @!method http_status(node)
57
63
  def_node_matcher :http_status, <<-PATTERN
58
- (send nil? :have_http_status ${int sym})
64
+ (send nil? :have_http_status ${int sym str})
59
65
  PATTERN
60
66
 
61
67
  def on_send(node)
62
68
  http_status(node) do |arg|
69
+ return if arg.str_type? && arg.heredoc?
70
+
63
71
  checker = checker_class.new(arg)
64
72
  return unless checker.offensive?
65
73
 
@@ -99,6 +107,10 @@ module RuboCop
99
107
  format(MSG, prefer: prefer, current: current)
100
108
  end
101
109
 
110
+ def current
111
+ offense_range.source
112
+ end
113
+
102
114
  def offense_range
103
115
  node
104
116
  end
@@ -123,10 +135,6 @@ module RuboCop
123
135
  symbol.inspect
124
136
  end
125
137
 
126
- def current
127
- number.inspect
128
- end
129
-
130
138
  private
131
139
 
132
140
  def symbol
@@ -134,7 +142,7 @@ module RuboCop
134
142
  end
135
143
 
136
144
  def number
137
- node.source.to_i
145
+ node.value.to_i
138
146
  end
139
147
  end
140
148
 
@@ -148,10 +156,6 @@ module RuboCop
148
156
  number.to_s
149
157
  end
150
158
 
151
- def current
152
- symbol.inspect
153
- end
154
-
155
159
  private
156
160
 
157
161
  def symbol
@@ -159,7 +163,7 @@ module RuboCop
159
163
  end
160
164
 
161
165
  def number
162
- ::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol]
166
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE[symbol.to_sym]
163
167
  end
164
168
  end
165
169
 
@@ -177,15 +181,13 @@ module RuboCop
177
181
  def prefer
178
182
  if node.sym_type?
179
183
  "be_#{node.value}"
180
- else
184
+ elsif node.int_type?
181
185
  "be_#{symbol}"
186
+ elsif node.str_type?
187
+ "be_#{normalize_str}"
182
188
  end
183
189
  end
184
190
 
185
- def current
186
- offense_range.source
187
- end
188
-
189
191
  private
190
192
 
191
193
  def symbol
@@ -193,7 +195,16 @@ module RuboCop
193
195
  end
194
196
 
195
197
  def number
196
- node.source.to_i
198
+ node.value.to_i
199
+ end
200
+
201
+ def normalize_str
202
+ str = node.value.to_s
203
+ if str.match?(/\A\d+\z/)
204
+ ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(str.to_i)
205
+ else
206
+ str
207
+ end
197
208
  end
198
209
  end
199
210
  end
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module RSpec
6
+ module Rails
7
+ # Enforces use of `be_invalid` or `not_to` for negated be_valid.
8
+ #
9
+ # @safety
10
+ # This cop is unsafe because it cannot guarantee that
11
+ # the test target is an instance of `ActiveModel::Validations``.
12
+ #
13
+ # @example EnforcedStyle: not_to (default)
14
+ # # bad
15
+ # expect(foo).to be_invalid
16
+ #
17
+ # # good
18
+ # expect(foo).not_to be_valid
19
+ #
20
+ # # good (with method chain)
21
+ # expect(foo).to be_invalid.and be_odd
22
+ #
23
+ # @example EnforcedStyle: be_invalid
24
+ # # bad
25
+ # expect(foo).not_to be_valid
26
+ #
27
+ # # good
28
+ # expect(foo).to be_invalid
29
+ #
30
+ # # good (with method chain)
31
+ # expect(foo).to be_invalid.or be_even
32
+ #
33
+ class NegationBeValid < Base
34
+ extend AutoCorrector
35
+ include ConfigurableEnforcedStyle
36
+
37
+ MSG = 'Use `expect(...).%<runner>s %<matcher>s`.'
38
+ RESTRICT_ON_SEND = %i[be_valid be_invalid].freeze
39
+
40
+ # @!method not_to?(node)
41
+ def_node_matcher :not_to?, <<~PATTERN
42
+ (send ... :not_to (send nil? :be_valid ...))
43
+ PATTERN
44
+
45
+ # @!method be_invalid?(node)
46
+ def_node_matcher :be_invalid?, <<~PATTERN
47
+ (send ... :to (send nil? :be_invalid ...))
48
+ PATTERN
49
+
50
+ def on_send(node)
51
+ return unless offense?(node.parent)
52
+
53
+ add_offense(offense_range(node),
54
+ message: message(node.method_name)) do |corrector|
55
+ corrector.replace(node.parent.loc.selector, replaced_runner)
56
+ corrector.replace(node.loc.selector, replaced_matcher)
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def offense?(node)
63
+ case style
64
+ when :not_to
65
+ be_invalid?(node)
66
+ when :be_invalid
67
+ not_to?(node)
68
+ end
69
+ end
70
+
71
+ def offense_range(node)
72
+ node.parent.loc.selector.with(end_pos: node.loc.selector.end_pos)
73
+ end
74
+
75
+ def message(_matcher)
76
+ format(MSG,
77
+ runner: replaced_runner,
78
+ matcher: replaced_matcher)
79
+ end
80
+
81
+ def replaced_runner
82
+ case style
83
+ when :not_to
84
+ 'not_to'
85
+ when :be_invalid
86
+ 'to'
87
+ end
88
+ end
89
+
90
+ def replaced_matcher
91
+ case style
92
+ when :not_to
93
+ 'be_valid'
94
+ when :be_invalid
95
+ 'be_invalid'
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end