forked from rubocop/rubocop-minitest
/
minitest_cop_rule.rb
106 lines (87 loc) · 3.94 KB
/
minitest_cop_rule.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# frozen_string_literal: true
module RuboCop
module Cop
# Provide a method to define offense rule for Minitest cops.
module MinitestCopRule
#
# Define offense rule for Minitest cops.
#
# @example
# define_rule :assert, target_method: :match
# define_rule :refute, target_method: :match
# define_rule :assert, target_method: :include?, preferred_method: :assert_includes
# define_rule :assert, target_method: :instance_of?, inverse: true
#
# @param assertion_method [Symbol] Assertion method like `assert` or `refute`.
# @param target_method [Symbol] Method name offensed by assertion method arguments.
# @param preferred_method [Symbol] An optional param. Custom method name replaced by
# autocorrection. The preferred method name that connects
# `assertion_method` and `target_method` with `_` is
# the default name.
# @param inverse [Boolean, String] An optional param. Order of arguments replaced by autocorrection.
# If string is passed, it becomes a predicate method for the first argument node.
# @api private
#
def define_rule(assertion_method, target_method:, preferred_method: nil, inverse: false)
preferred_method = "#{assertion_method}_#{target_method.to_s.delete('?')}" if preferred_method.nil?
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
include ArgumentRangeHelper
extend AutoCorrector
MSG = 'Prefer using `#{preferred_method}(%<new_arguments>s)`.'
RESTRICT_ON_SEND = %i[#{assertion_method}].freeze
def on_send(node)
return unless node.method?(:#{assertion_method})
return unless (arguments = peel_redundant_parentheses_from(node.arguments))
return unless arguments.first&.call_type?
return if arguments.first.arguments.empty? || !arguments.first.method?(:#{target_method})
add_offense(node, message: offense_message(arguments)) do |corrector|
autocorrect(corrector, node, arguments)
end
end
def autocorrect(corrector, node, arguments)
corrector.replace(node.loc.selector, '#{preferred_method}')
new_arguments = new_arguments(arguments).join(', ')
if enclosed_in_redundant_parentheses?(node)
new_arguments = '(' + new_arguments + ')'
end
corrector.replace(first_argument_range(node), new_arguments)
end
private
def peel_redundant_parentheses_from(arguments)
return arguments unless arguments.first&.begin_type?
peel_redundant_parentheses_from(arguments.first.children)
end
def offense_message(arguments)
message_argument = arguments.last if arguments.first != arguments.last
new_arguments = [
new_arguments(arguments),
message_argument&.source
].flatten.compact.join(', ')
format(
MSG,
new_arguments: new_arguments
)
end
def new_arguments(arguments)
receiver = correct_receiver(arguments.first.receiver)
method_argument = arguments.first.arguments.first
new_arguments = [receiver, method_argument&.source].compact
inverse_condition = if %w[true false].include?('#{inverse}')
#{inverse}
else
method_argument.#{inverse}
end
new_arguments.reverse! if inverse_condition
new_arguments
end
def enclosed_in_redundant_parentheses?(node)
node.arguments.first.begin_type?
end
def correct_receiver(receiver)
receiver ? receiver.source : 'self'
end
RUBY
end
end
end
end