class RuboCop::Cop::Lint::AmbiguousOperatorPrecedence
a * b / c % d
a + b + c
# good (same precedence)
(a ** b) + c
a || (b && c)
a + (b * c)
# good (different precedence)
a ** b + c
a || b && c
a + b * c
# bad
@example
NOTE: Ranges are handled by ‘Lint/AmbiguousRange`.
operators (ie. `a =~ b`) because those are not ambiguous.
The cop does not consider unary operators (ie. `!a` or `-b`) or comparison
lexically it appears that the addition will happen first.
in `1 + 2 * 3`, the multiplication will happen before the addition, but
where precedence is ambiguous due to lack of parentheses. For example,
Looks for expressions containing multiple binary operators
def autocorrect(corrector, node)
def autocorrect(corrector, node) corrector.wrap(node, '(', ')') end
def greater_precedence?(node1, node2)
def greater_precedence?(node1, node2) node1_precedence = precedence(node1) node2_precedence = precedence(node2) return false unless node1_precedence && node2_precedence node2_precedence > node1_precedence end
def on_and(node)
def on_and(node) return unless (parent = node.parent) return if parent.begin_type? # if the `and` is in a `begin`, it's parenthesized already return unless parent.or_type? add_offense(node) do |corrector| autocorrect(corrector, node) end end
def on_new_investigation
def on_new_investigation # Cache the precedence of each node being investigated # so that we only need to calculate it once @node_precedences = {} super end
def on_send(node)
def on_send(node) return if node.parenthesized? return unless (parent = node.parent) return unless operator?(parent) return unless greater_precedence?(node, parent) add_offense(node) do |corrector| autocorrect(corrector, node) end end
def operator?(node)
def operator?(node) (node.send_type? && RESTRICT_ON_SEND.include?(node.method_name)) || node.operator_keyword? end
def operator_name(node)
def operator_name(node) if node.send_type? node.method_name else node.operator.to_sym end end
def precedence(node)
def precedence(node) @node_precedences.fetch(node) do PRECEDENCE.index { |operators| operators.include?(operator_name(node)) } end end