forked from rubocop/rubocop
/
lambda.rb
132 lines (112 loc) · 3.55 KB
/
lambda.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
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true
module RuboCop
module Cop
module Style
# This cop (by default) checks for uses of the lambda literal syntax for
# single line lambdas, and the method call syntax for multiline lambdas.
# It is configurable to enforce one of the styles for both single line
# and multiline lambdas as well.
#
# @example EnforcedStyle: line_count_dependent (default)
# # bad
# f = lambda { |x| x }
# f = ->(x) do
# x
# end
#
# # good
# f = ->(x) { x }
# f = lambda do |x|
# x
# end
#
# @example EnforcedStyle: lambda
# # bad
# f = ->(x) { x }
# f = ->(x) do
# x
# end
#
# # good
# f = lambda { |x| x }
# f = lambda do |x|
# x
# end
#
# @example EnforcedStyle: literal
# # bad
# f = lambda { |x| x }
# f = lambda do |x|
# x
# end
#
# # good
# f = ->(x) { x }
# f = ->(x) do
# x
# end
class Lambda < Cop
include ConfigurableEnforcedStyle
LITERAL_MESSAGE = 'Use the `-> { ... }` lambda literal syntax for ' \
'%<modifier>s lambdas.'
METHOD_MESSAGE = 'Use the `lambda` method for %<modifier>s ' \
'lambdas.'
OFFENDING_SELECTORS = {
style: {
lambda: { single_line: '->', multiline: '->' },
literal: { single_line: 'lambda', multiline: 'lambda' },
line_count_dependent: { single_line: 'lambda', multiline: '->' }
}
}.freeze
def on_block(node)
return unless node.lambda?
selector = node.send_node.source
return unless offending_selector?(node, selector)
add_offense(node,
location: node.send_node.source_range,
message: message(node, selector))
end
alias on_numblock on_block
def autocorrect(node)
if node.send_node.source == 'lambda'
lambda do |corrector|
autocorrect_method_to_literal(corrector, node)
end
else
LambdaLiteralToMethodCorrector.new(node)
end
end
private
def offending_selector?(node, selector)
lines = node.multiline? ? :multiline : :single_line
selector == OFFENDING_SELECTORS[:style][style][lines]
end
def message(node, selector)
message = selector == '->' ? METHOD_MESSAGE : LITERAL_MESSAGE
format(message, modifier: message_line_modifier(node))
end
def message_line_modifier(node)
case style
when :line_count_dependent
node.multiline? ? 'multiline' : 'single line'
else
'all'
end
end
def autocorrect_method_to_literal(corrector, node)
corrector.replace(node.send_node.source_range, '->')
return unless node.arguments?
arg_str = "(#{lambda_arg_string(node.arguments)})"
corrector.insert_after(node.send_node.source_range, arg_str)
corrector.remove(arguments_with_whitespace(node))
end
def arguments_with_whitespace(node)
node.loc.begin.end.join(node.arguments.loc.end)
end
def lambda_arg_string(args)
args.children.map(&:source).join(', ')
end
end
end
end
end