rubocop-rspec 2.22.0 → 2.25.0

Sign up to get free protection for your applications and to get access to all the features.
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