forked from rubocop/rubocop-rails
-
Notifications
You must be signed in to change notification settings - Fork 0
/
delegate.rb
122 lines (105 loc) · 3.4 KB
/
delegate.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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# frozen_string_literal: true
module RuboCop
module Cop
module Rails
# Looks for delegations that could have been created
# automatically with the `delegate` method.
#
# Safe navigation `&.` is ignored because Rails' `allow_nil`
# option checks not just for nil but also delegates if nil
# responds to the delegated method.
#
# The `EnforceForPrefixed` option (defaulted to `true`) means that
# using the target object as a prefix of the method name
# without using the `delegate` method will be a violation.
# When set to `false`, this case is legal.
#
# @example
# # bad
# def bar
# foo.bar
# end
#
# # good
# delegate :bar, to: :foo
#
# # good
# def bar
# foo&.bar
# end
#
# # good
# private
# def bar
# foo.bar
# end
#
# @example EnforceForPrefixed: true (default)
# # bad
# def foo_bar
# foo.bar
# end
#
# # good
# delegate :bar, to: :foo, prefix: true
#
# @example EnforceForPrefixed: false
# # good
# def foo_bar
# foo.bar
# end
#
# # good
# delegate :bar, to: :foo, prefix: true
class Delegate < Base
extend AutoCorrector
include VisibilityHelp
MSG = 'Use `delegate` to define delegations.'
def_node_matcher :delegate?, <<~PATTERN
(def _method_name _args
(send (send nil? _) _ ...))
PATTERN
def on_def(node)
return unless trivial_delegate?(node)
return if private_or_protected_delegation(node)
register_offense(node)
end
private
def register_offense(node)
add_offense(node.loc.keyword) do |corrector|
delegation = ["delegate :#{node.body.method_name}", "to: :#{node.body.receiver.method_name}"]
delegation << ['prefix: true'] if node.method?(prefixed_method_name(node.body))
corrector.replace(node.source_range, delegation.join(', '))
end
end
def trivial_delegate?(def_node)
delegate?(def_node) &&
method_name_matches?(def_node.method_name, def_node.body) &&
arguments_match?(def_node.arguments, def_node.body)
end
def arguments_match?(arg_array, body)
argument_array = body.arguments
return false if arg_array.size != argument_array.size
arg_array.zip(argument_array).all? do |arg, argument|
arg.arg_type? && argument.lvar_type? && arg.children == argument.children
end
end
def method_name_matches?(method_name, body)
method_name == body.method_name || (include_prefix_case? && method_name == prefixed_method_name(body))
end
def include_prefix_case?
cop_config['EnforceForPrefixed']
end
def prefixed_method_name(body)
[body.receiver.method_name, body.method_name].join('_').to_sym
end
def private_or_protected_delegation(node)
private_or_protected_inline(node) || node_visibility(node) != :public
end
def private_or_protected_inline(node)
processed_source[node.first_line - 1].strip.match?(/\A(private )|(protected )/)
end
end
end
end
end