rubocop-minitest 0.6.2 → 0.10.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.
- checksums.yaml +5 -5
- data/.circleci/config.yml +0 -3
- data/.gitattributes +1 -0
- data/.rubocop.yml +2 -1
- data/.rubocop_todo.yml +0 -7
- data/CHANGELOG.md +65 -0
- data/Gemfile +1 -1
- data/README.md +5 -1
- data/Rakefile +29 -0
- data/config/default.yml +103 -18
- data/docs/antora.yml +7 -0
- data/docs/modules/ROOT/nav.adoc +6 -0
- data/docs/modules/ROOT/pages/cops.adoc +37 -0
- data/docs/modules/ROOT/pages/cops_minitest.adoc +1014 -0
- data/docs/modules/ROOT/pages/index.adoc +5 -0
- data/docs/modules/ROOT/pages/installation.adoc +15 -0
- data/docs/modules/ROOT/pages/usage.adoc +32 -0
- data/{manual → legacy-docs}/cops.md +1 -0
- data/{manual → legacy-docs}/cops_minitest.md +64 -41
- data/{manual → legacy-docs}/index.md +0 -0
- data/{manual → legacy-docs}/installation.md +0 -0
- data/{manual → legacy-docs}/usage.md +0 -0
- data/lib/rubocop/cop/generator.rb +56 -0
- data/lib/rubocop/cop/minitest/assert_empty.rb +4 -30
- data/lib/rubocop/cop/minitest/assert_empty_literal.rb +15 -0
- data/lib/rubocop/cop/minitest/assert_equal.rb +2 -31
- data/lib/rubocop/cop/minitest/assert_in_delta.rb +27 -0
- data/lib/rubocop/cop/minitest/assert_includes.rb +4 -4
- data/lib/rubocop/cop/minitest/assert_instance_of.rb +4 -38
- data/lib/rubocop/cop/minitest/assert_kind_of.rb +25 -0
- data/lib/rubocop/cop/minitest/assert_match.rb +4 -39
- data/lib/rubocop/cop/minitest/assert_nil.rb +2 -2
- data/lib/rubocop/cop/minitest/assert_output.rb +49 -0
- data/lib/rubocop/cop/minitest/assert_path_exists.rb +58 -0
- data/lib/rubocop/cop/minitest/assert_respond_to.rb +10 -45
- data/lib/rubocop/cop/minitest/assert_silent.rb +45 -0
- data/lib/rubocop/cop/minitest/assert_truthy.rb +2 -2
- data/lib/rubocop/cop/minitest/assertion_in_lifecycle_hook.rb +43 -0
- data/lib/rubocop/cop/minitest/global_expectations.rb +95 -0
- data/lib/rubocop/cop/minitest/literal_as_actual_argument.rb +52 -0
- data/lib/rubocop/cop/minitest/multiple_assertions.rb +63 -0
- data/lib/rubocop/cop/minitest/refute_empty.rb +4 -30
- data/lib/rubocop/cop/minitest/refute_false.rb +3 -3
- data/lib/rubocop/cop/minitest/refute_in_delta.rb +27 -0
- data/lib/rubocop/cop/minitest/refute_includes.rb +4 -4
- data/lib/rubocop/cop/minitest/refute_instance_of.rb +4 -38
- data/lib/rubocop/cop/minitest/refute_kind_of.rb +25 -0
- data/lib/rubocop/cop/minitest/refute_match.rb +4 -39
- data/lib/rubocop/cop/minitest/refute_nil.rb +2 -2
- data/lib/rubocop/cop/minitest/refute_path_exists.rb +58 -0
- data/lib/rubocop/cop/minitest/refute_respond_to.rb +10 -45
- data/lib/rubocop/cop/minitest/test_method_name.rb +70 -0
- data/lib/rubocop/cop/minitest/unspecified_exception.rb +36 -0
- data/lib/rubocop/cop/minitest_cops.rb +17 -1
- data/lib/rubocop/cop/mixin/argument_range_helper.rb +10 -0
- data/lib/rubocop/cop/mixin/in_delta_mixin.rb +50 -0
- data/lib/rubocop/cop/mixin/minitest_cop_rule.rb +102 -0
- data/lib/rubocop/cop/mixin/minitest_exploration_helpers.rb +84 -0
- data/lib/rubocop/minitest/version.rb +1 -1
- data/mkdocs.yml +2 -2
- data/relnotes/v0.10.0.md +21 -0
- data/relnotes/v0.6.2.md +5 -0
- data/relnotes/v0.7.0.md +13 -0
- data/relnotes/v0.8.0.md +12 -0
- data/relnotes/v0.8.1.md +5 -0
- data/relnotes/v0.9.0.md +10 -0
- data/rubocop-minitest.gemspec +4 -4
- data/tasks/cops_documentation.rake +83 -52
- data/tasks/cut_release.rake +16 -0
- metadata +45 -15
- data/lib/rubocop/cop/mixin/includes_cop_rule.rb +0 -78
@@ -9,51 +9,16 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# refute(matcher.match(string))
|
12
|
-
# refute(matcher.match(string), '
|
12
|
+
# refute(matcher.match(string), 'message')
|
13
13
|
#
|
14
14
|
# # good
|
15
15
|
# refute_match(matcher, string)
|
16
|
-
# refute_match(matcher, string, '
|
16
|
+
# refute_match(matcher, string, 'message')
|
17
17
|
#
|
18
18
|
class RefuteMatch < Cop
|
19
|
-
|
19
|
+
extend MinitestCopRule
|
20
20
|
|
21
|
-
|
22
|
-
'`refute(%<receiver>s)`.'
|
23
|
-
|
24
|
-
def_node_matcher :refute_with_match, <<~PATTERN
|
25
|
-
(send nil? :refute $(send $_ :match $_) $...)
|
26
|
-
PATTERN
|
27
|
-
|
28
|
-
def on_send(node)
|
29
|
-
refute_with_match(node) do
|
30
|
-
|first_receiver_arg, matcher, actual, rest_receiver_arg|
|
31
|
-
message = rest_receiver_arg.first
|
32
|
-
arguments = node_arguments(matcher, actual, message)
|
33
|
-
receiver = [first_receiver_arg.source, message&.source].compact.join(', ')
|
34
|
-
|
35
|
-
offense_message = format(MSG, arguments: arguments, receiver: receiver)
|
36
|
-
|
37
|
-
add_offense(node, message: offense_message)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def autocorrect(node)
|
42
|
-
lambda do |corrector|
|
43
|
-
refute_with_match(node) do |_, matcher, actual|
|
44
|
-
corrector.replace(node.loc.selector, 'refute_match')
|
45
|
-
|
46
|
-
replacement = [matcher, actual].map(&:source).join(', ')
|
47
|
-
corrector.replace(first_argument_range(node), replacement)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def node_arguments(matcher, actual, message)
|
55
|
-
[matcher.source, actual.source, message&.source].compact.join(', ')
|
56
|
-
end
|
21
|
+
define_rule :refute, target_method: :match
|
57
22
|
end
|
58
23
|
end
|
59
24
|
end
|
@@ -9,11 +9,11 @@ module RuboCop
|
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
11
|
# refute_equal(nil, actual)
|
12
|
-
# refute_equal(nil, actual, '
|
12
|
+
# refute_equal(nil, actual, 'message')
|
13
13
|
#
|
14
14
|
# # good
|
15
15
|
# refute_nil(actual)
|
16
|
-
# refute_nil(actual, '
|
16
|
+
# refute_nil(actual, 'message')
|
17
17
|
#
|
18
18
|
class RefuteNil < Cop
|
19
19
|
include ArgumentRangeHelper
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# This cop enforces the test to use `refute_path_exists`
|
7
|
+
# instead of using `refute(File.exist?(path))`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# # bad
|
11
|
+
# refute(File.exist?(path))
|
12
|
+
# refute(File.exist?(path), 'message')
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# refute_path_exists(path)
|
16
|
+
# refute_path_exists(path, 'message')
|
17
|
+
#
|
18
|
+
class RefutePathExists < Cop
|
19
|
+
MSG = 'Prefer using `%<good_method>s` over `%<bad_method>s`.'
|
20
|
+
|
21
|
+
def_node_matcher :refute_file_exists, <<~PATTERN
|
22
|
+
(send nil? :refute
|
23
|
+
(send
|
24
|
+
(const _ :File) {:exist? :exists?} $_)
|
25
|
+
$...)
|
26
|
+
PATTERN
|
27
|
+
|
28
|
+
def on_send(node)
|
29
|
+
refute_file_exists(node) do |path, failure_message|
|
30
|
+
failure_message = failure_message.first
|
31
|
+
good_method = build_good_method(path, failure_message)
|
32
|
+
message = format(MSG, good_method: good_method, bad_method: node.source)
|
33
|
+
|
34
|
+
add_offense(node, message: message)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def autocorrect(node)
|
39
|
+
refute_file_exists(node) do |path, failure_message|
|
40
|
+
failure_message = failure_message.first
|
41
|
+
|
42
|
+
lambda do |corrector|
|
43
|
+
replacement = build_good_method(path, failure_message)
|
44
|
+
corrector.replace(node, replacement)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def build_good_method(path, message)
|
52
|
+
args = [path.source, message&.source].compact.join(', ')
|
53
|
+
"refute_path_exists(#{args})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -3,59 +3,24 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Minitest
|
6
|
-
# This cop enforces the test to use `refute_respond_to(object, :
|
7
|
-
# over `refute(object.respond_to?(:
|
6
|
+
# This cop enforces the test to use `refute_respond_to(object, :do_something)`
|
7
|
+
# over `refute(object.respond_to?(:do_something))`.
|
8
8
|
#
|
9
9
|
# @example
|
10
10
|
# # bad
|
11
|
-
# refute(object.respond_to?(:
|
12
|
-
# refute(object.respond_to?(:
|
13
|
-
# refute(respond_to?(:
|
11
|
+
# refute(object.respond_to?(:do_something))
|
12
|
+
# refute(object.respond_to?(:do_something), 'message')
|
13
|
+
# refute(respond_to?(:do_something))
|
14
14
|
#
|
15
15
|
# # good
|
16
|
-
# refute_respond_to(object, :
|
17
|
-
# refute_respond_to(object, :
|
18
|
-
# refute_respond_to(self, :
|
16
|
+
# refute_respond_to(object, :do_something)
|
17
|
+
# refute_respond_to(object, :do_something, 'message')
|
18
|
+
# refute_respond_to(self, :do_something)
|
19
19
|
#
|
20
20
|
class RefuteRespondTo < Cop
|
21
|
-
|
21
|
+
extend MinitestCopRule
|
22
22
|
|
23
|
-
|
24
|
-
'`refute(%<over>s)`.'
|
25
|
-
|
26
|
-
def_node_matcher :refute_with_respond_to, <<~PATTERN
|
27
|
-
(send nil? :refute $(send $_ :respond_to? $_) $...)
|
28
|
-
PATTERN
|
29
|
-
|
30
|
-
def on_send(node)
|
31
|
-
refute_with_respond_to(node) do |over, object, method, rest_args|
|
32
|
-
custom_message = rest_args.first
|
33
|
-
preferred = build_preferred_arguments(object, method, custom_message)
|
34
|
-
over = [over, custom_message].compact.map(&:source).join(', ')
|
35
|
-
message = format(MSG, preferred: preferred, over: over)
|
36
|
-
add_offense(node, message: message)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def autocorrect(node)
|
41
|
-
lambda do |corrector|
|
42
|
-
refute_with_respond_to(node) do |_, object, method|
|
43
|
-
corrector.replace(node.loc.selector, 'refute_respond_to')
|
44
|
-
|
45
|
-
object = object ? object.source : 'self'
|
46
|
-
replacement = [object, method.source].join(', ')
|
47
|
-
corrector.replace(first_argument_range(node), replacement)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
def build_preferred_arguments(receiver, method, message)
|
55
|
-
receiver = receiver ? receiver.source : 'self'
|
56
|
-
|
57
|
-
[receiver, method.source, message&.source].compact.join(', ')
|
58
|
-
end
|
23
|
+
define_rule :refute, target_method: :respond_to?
|
59
24
|
end
|
60
25
|
end
|
61
26
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# This cop enforces that test method names start with `test_` prefix.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# class FooTest < Minitest::Test
|
11
|
+
# def does_something
|
12
|
+
# assert_equal 42, do_something
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# class FooTest < Minitest::Test
|
18
|
+
# def test_does_something
|
19
|
+
# assert_equal 42, do_something
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
class TestMethodName < Cop
|
24
|
+
include MinitestExplorationHelpers
|
25
|
+
include DefNode
|
26
|
+
|
27
|
+
MSG = 'Test method name should start with `test_` prefix.'
|
28
|
+
|
29
|
+
def on_class(class_node)
|
30
|
+
return unless test_class?(class_node)
|
31
|
+
|
32
|
+
class_elements(class_node).each do |node|
|
33
|
+
add_offense(node, location: :name) if offense?(node)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def autocorrect(node)
|
38
|
+
lambda do |corrector|
|
39
|
+
corrector.replace(node.loc.name, "test_#{node.method_name}")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def class_elements(class_node)
|
46
|
+
class_def = class_node.body
|
47
|
+
return [] unless class_def
|
48
|
+
|
49
|
+
if class_def.def_type?
|
50
|
+
[class_def]
|
51
|
+
else
|
52
|
+
class_def.each_child_node(:def).to_a
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def offense?(node)
|
57
|
+
public?(node) && !test_method_name?(node) && !lifecycle_hook_method?(node)
|
58
|
+
end
|
59
|
+
|
60
|
+
def public?(node)
|
61
|
+
!non_public?(node)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_method_name?(node)
|
65
|
+
node.method_name.to_s.start_with?('test_')
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Minitest
|
6
|
+
# This cop checks for a specified error in `assert_raises`.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# assert_raises { raise FooException }
|
11
|
+
# assert_raises('This should have raised') { raise FooException }
|
12
|
+
#
|
13
|
+
# # good
|
14
|
+
# assert_raises(FooException) { raise FooException }
|
15
|
+
# assert_raises(FooException, 'This should have raised') { raise FooException }
|
16
|
+
#
|
17
|
+
class UnspecifiedException < Cop
|
18
|
+
MSG = 'Specify the exception being captured.'
|
19
|
+
|
20
|
+
def on_block(block_node)
|
21
|
+
node = block_node.send_node
|
22
|
+
return unless node.method?(:assert_raises)
|
23
|
+
|
24
|
+
add_offense(node) if unspecified_exception?(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def unspecified_exception?(node)
|
30
|
+
args = node.arguments
|
31
|
+
args.empty? || (args.size == 1 && args[0].str_type?)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,21 +1,37 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative 'mixin/argument_range_helper'
|
4
|
-
require_relative 'mixin/
|
4
|
+
require_relative 'mixin/in_delta_mixin'
|
5
|
+
require_relative 'mixin/minitest_cop_rule'
|
6
|
+
require_relative 'mixin/minitest_exploration_helpers'
|
5
7
|
require_relative 'minitest/assert_empty'
|
6
8
|
require_relative 'minitest/assert_empty_literal'
|
7
9
|
require_relative 'minitest/assert_equal'
|
10
|
+
require_relative 'minitest/assert_in_delta'
|
11
|
+
require_relative 'minitest/assertion_in_lifecycle_hook'
|
12
|
+
require_relative 'minitest/assert_kind_of'
|
8
13
|
require_relative 'minitest/assert_nil'
|
9
14
|
require_relative 'minitest/assert_includes'
|
10
15
|
require_relative 'minitest/assert_instance_of'
|
11
16
|
require_relative 'minitest/assert_match'
|
17
|
+
require_relative 'minitest/assert_output'
|
18
|
+
require_relative 'minitest/assert_path_exists'
|
12
19
|
require_relative 'minitest/assert_respond_to'
|
20
|
+
require_relative 'minitest/assert_silent'
|
13
21
|
require_relative 'minitest/assert_truthy'
|
22
|
+
require_relative 'minitest/global_expectations'
|
23
|
+
require_relative 'minitest/literal_as_actual_argument'
|
24
|
+
require_relative 'minitest/multiple_assertions'
|
14
25
|
require_relative 'minitest/refute_empty'
|
15
26
|
require_relative 'minitest/refute_false'
|
16
27
|
require_relative 'minitest/refute_equal'
|
28
|
+
require_relative 'minitest/refute_in_delta'
|
29
|
+
require_relative 'minitest/refute_kind_of'
|
17
30
|
require_relative 'minitest/refute_nil'
|
18
31
|
require_relative 'minitest/refute_includes'
|
19
32
|
require_relative 'minitest/refute_match'
|
20
33
|
require_relative 'minitest/refute_instance_of'
|
34
|
+
require_relative 'minitest/refute_path_exists'
|
21
35
|
require_relative 'minitest/refute_respond_to'
|
36
|
+
require_relative 'minitest/test_method_name'
|
37
|
+
require_relative 'minitest/unspecified_exception'
|
@@ -26,6 +26,16 @@ module RuboCop
|
|
26
26
|
second_argument.source_range.end_pos
|
27
27
|
)
|
28
28
|
end
|
29
|
+
|
30
|
+
def all_arguments_range(node)
|
31
|
+
first_argument = node.first_argument
|
32
|
+
last_argument = node.arguments.last
|
33
|
+
|
34
|
+
range_between(
|
35
|
+
first_argument.source_range.begin_pos,
|
36
|
+
last_argument.source_range.end_pos
|
37
|
+
)
|
38
|
+
end
|
29
39
|
end
|
30
40
|
end
|
31
41
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common functionality for `AssertInDelta` and `RefuteInDelta` cops.
|
6
|
+
module InDeltaMixin
|
7
|
+
MSG = 'Prefer using `%<good_method>s` over `%<bad_method>s`.'
|
8
|
+
|
9
|
+
def on_send(node)
|
10
|
+
equal_floats_call(node) do |expected, actual, message|
|
11
|
+
message = message.first
|
12
|
+
|
13
|
+
if expected.float_type? || actual.float_type?
|
14
|
+
message = format(MSG,
|
15
|
+
good_method: build_good_method(expected, actual, message),
|
16
|
+
bad_method: node.source)
|
17
|
+
|
18
|
+
add_offense(node, message: message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def autocorrect(node)
|
24
|
+
equal_floats_call(node) do |expected, actual, message|
|
25
|
+
message = message.first
|
26
|
+
replacement = build_good_method(expected, actual, message)
|
27
|
+
|
28
|
+
lambda do |corrector|
|
29
|
+
corrector.replace(node, replacement)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def build_good_method(expected, actual, message)
|
37
|
+
if message
|
38
|
+
"#{assertion_method}_in_delta(#{expected.source}, #{actual.source}, 0.001, #{message.source})"
|
39
|
+
else
|
40
|
+
"#{assertion_method}_in_delta(#{expected.source}, #{actual.source})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def assertion_method
|
45
|
+
class_name = self.class.name.split('::').last
|
46
|
+
class_name[/\A[[:upper:]][[:lower:]]+/].downcase
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Provide a method to define offense rule for Minitest cops.
|
6
|
+
module MinitestCopRule
|
7
|
+
#
|
8
|
+
# Define offense rule for Minitest cops.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# define_rule :assert, target_method: :match
|
12
|
+
# define_rule :refute, target_method: :match
|
13
|
+
# define_rule :assert, target_method: :include?, preferred_method: :assert_includes
|
14
|
+
# define_rule :assert, target_method: :instance_of?, inverse: true
|
15
|
+
#
|
16
|
+
# @param assertion_method [Symbol] Assertion method like `assert` or `refute`.
|
17
|
+
# @param target_method [Symbol] Method name offensed by assertion method arguments.
|
18
|
+
# @param preferred_method [Symbol] An optional param. Custom method name replaced by
|
19
|
+
# auto-correction. The preferred method name that connects
|
20
|
+
# `assertion_method` and `target_method` with `_` is
|
21
|
+
# the default name.
|
22
|
+
# @param inverse [Boolean] An optional param. Order of arguments replaced by auto-correction.
|
23
|
+
#
|
24
|
+
def define_rule(assertion_method, target_method:, preferred_method: nil, inverse: false)
|
25
|
+
preferred_method = "#{assertion_method}_#{target_method.to_s.delete('?')}" if preferred_method.nil?
|
26
|
+
|
27
|
+
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
28
|
+
include ArgumentRangeHelper
|
29
|
+
|
30
|
+
MSG = 'Prefer using `#{preferred_method}(%<new_arguments>s)` over ' \
|
31
|
+
'`#{assertion_method}(%<original_arguments>s)`.'
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
return unless node.method?(:#{assertion_method})
|
35
|
+
return unless (arguments = peel_redundant_parentheses_from(node.arguments))
|
36
|
+
return unless arguments.first.respond_to?(:method?) && arguments.first.method?(:#{target_method})
|
37
|
+
|
38
|
+
add_offense(node, message: offense_message(arguments))
|
39
|
+
end
|
40
|
+
|
41
|
+
def autocorrect(node)
|
42
|
+
lambda do |corrector|
|
43
|
+
corrector.replace(node.loc.selector, '#{preferred_method}')
|
44
|
+
|
45
|
+
arguments = peel_redundant_parentheses_from(node.arguments)
|
46
|
+
|
47
|
+
new_arguments = new_arguments(arguments).join(', ')
|
48
|
+
|
49
|
+
if enclosed_in_redundant_parentheses?(node)
|
50
|
+
new_arguments = '(' + new_arguments + ')'
|
51
|
+
end
|
52
|
+
|
53
|
+
corrector.replace(first_argument_range(node), new_arguments)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def peel_redundant_parentheses_from(arguments)
|
60
|
+
return arguments unless arguments.first.begin_type?
|
61
|
+
|
62
|
+
peel_redundant_parentheses_from(arguments.first.children)
|
63
|
+
end
|
64
|
+
|
65
|
+
def offense_message(arguments)
|
66
|
+
message_argument = arguments.last if arguments.first != arguments.last
|
67
|
+
|
68
|
+
new_arguments = [
|
69
|
+
new_arguments(arguments),
|
70
|
+
message_argument&.source
|
71
|
+
].flatten.compact.join(', ')
|
72
|
+
|
73
|
+
original_arguments = arguments.map(&:source).join(', ')
|
74
|
+
|
75
|
+
format(
|
76
|
+
MSG,
|
77
|
+
new_arguments: new_arguments,
|
78
|
+
original_arguments: original_arguments
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
def new_arguments(arguments)
|
83
|
+
receiver = correct_receiver(arguments.first.receiver)
|
84
|
+
method_argument = arguments.first.arguments.first&.source
|
85
|
+
|
86
|
+
new_arguments = [receiver, method_argument].compact
|
87
|
+
new_arguments.reverse! if #{inverse}
|
88
|
+
new_arguments
|
89
|
+
end
|
90
|
+
|
91
|
+
def enclosed_in_redundant_parentheses?(node)
|
92
|
+
node.arguments.first.begin_type?
|
93
|
+
end
|
94
|
+
|
95
|
+
def correct_receiver(receiver)
|
96
|
+
receiver ? receiver.source : 'self'
|
97
|
+
end
|
98
|
+
RUBY
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|