forked from rubocop/rubocop
/
ambiguous_operator.rb
93 lines (80 loc) · 2.8 KB
/
ambiguous_operator.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
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# This cop checks for ambiguous operators in the first argument of a
# method invocation without parentheses.
#
# @example
#
# # bad
#
# # The `*` is interpreted as a splat operator but it could possibly be
# # a `*` method invocation (i.e. `do_something.*(some_array)`).
# do_something *some_array
#
# @example
#
# # good
#
# # With parentheses, there's no ambiguity.
# do_something(*some_array)
class AmbiguousOperator < Cop
include ParserDiagnostic
AMBIGUITIES = {
'+' => { actual: 'positive number', possible: 'addition' },
'-' => { actual: 'negative number', possible: 'subtraction' },
'*' => { actual: 'splat', possible: 'multiplication' },
'&' => { actual: 'block', possible: 'binary AND' },
'**' => { actual: 'keyword splat', possible: 'exponent' }
}.each do |key, hash|
hash[:operator] = key
end
MSG_FORMAT = 'Ambiguous %<actual>s operator. Parenthesize the method ' \
"arguments if it's surely a %<actual>s operator, or add " \
'a whitespace to the right of the `%<operator>s` if it ' \
'should be a %<possible>s.'
def autocorrect(node)
lambda do |corrector|
add_parentheses(node, corrector)
end
end
private
def relevant_diagnostic?(diagnostic)
diagnostic.reason == :ambiguous_prefix
end
def find_offense_node_by(diagnostic)
ast = processed_source.ast
ast.each_node(:splat, :block_pass, :kwsplat) do |node|
next unless offense_position?(node, diagnostic)
offense_node = offense_node(node)
return offense_node if offense_node
end
ast.each_node(:send).find do |send_node|
offense_position?(send_node.first_argument, diagnostic) &&
unary_operator?(send_node.first_argument, diagnostic)
end
end
def alternative_message(diagnostic)
operator = diagnostic.location.source
hash = AMBIGUITIES[operator]
format(MSG_FORMAT, hash)
end
def offense_position?(node, diagnostic)
node.source_range.begin_pos == diagnostic.location.begin_pos
end
def offense_node(node)
case node.type
when :splat, :block_pass
node.parent
when :kwsplat
node.parent.parent
end
end
def unary_operator?(node, diagnostic)
node.source.start_with?(diagnostic.arguments[:prefix])
end
end
end
end
end