ruby-on-railsrubocop

Why does rubocop's Lint/AmbiguousOperatorPrecedence cop complain about a content_tag in a Rails app?


The following lines lead to a complain from Rubocop's Lint/AmbiguousOperatorPrecedence cop. I don't get what the problem is?

  def rating_stars(rating)
    content_tag(:span, class: 'color-accent') do
      content_tag(
        :svg,
        xlmns: 'http://www.w3.org/2000/svg',
        class: 'smallicon',
        viewBox: '0 0 24 24',
        fill: 'currentColor',
        stroke: 'currentColor',
        # rubocop:disable Layout/LineLength
        'stroke-width': '1') do
          tag.path(d: 'M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z')
        # rubocop:enable Layout/LineLength
      end * rating <<
      content_tag(
        :svg,
        xmlns: 'http://www.w3.org/2000/svg',
        class: 'smallicon',
        fill: 'none',
        viewBox: '0 0 24 24',
        stroke: 'currentColor',
        'stroke-width': '1') do
          # rubocop:disable Layout/LineLength
          tag.path('stroke-linecap': 'round',
            'stroke-linejoin': 'round',
            d: 'M11.049 2.927c.3-.921 1.603-.921 1.902 0l1.519 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.888a1 1 0 00-.363 1.118l1.518 4.674c.3.922-.755 1.688-1.538 1.118l-3.976-2.888a1 1 0 00-1.176 0l-3.976 2.888c-.783.57-1.838-.197-1.538-1.118l1.518-4.674a1 1 0 00-.363-1.118l-3.976-2.888c-.784-.57-.38-1.81.588-1.81h4.914a1 1 0 00.951-.69l1.519-4.674z')
            # rubocop:enable Layout/LineLength
        end * (5- rating)
    end
  end

The complaint is as follows (referring to lines 40 and 52, in above example these are lines 3 and 15):

app/helpers/books_helper.rb:40:7: W: [Correctable] Lint/AmbiguousOperatorPrecedence: Wrap expressions with varying precedence with parentheses to avoid ambiguity.
      content_tag( ...
      ^^^^^^^^^^^^
app/helpers/books_helper.rb:52:7: W: [Correctable] Lint/AmbiguousOperatorPrecedence: Wrap expressions with varying precedence with parentheses to avoid ambiguity.
      content_tag( ...
      ^^^^^^^^^^^^

Solution

  • The Lint/AmbiguousOperatorPrecedence docs say:

    Looks for expressions containing multiple binary operators where precedence is ambiguous due to lack of parentheses. For example, in 1 + 2 * 3, the multiplication will happen before the addition, but lexically it appears that the addition will happen first.

    The "good" examples in the documentation show that this cop is trying to suggest adding parentheses so that the order of operations is explicit.

    Here's an abbreviated version of your code:

    content_tag(...) do
    end <<
    content_tag(...) do
    end * (5 - rating)
    

    That has this pattern:

    x << y * z
    

    It isn't obvious if << or * will be executed first.

    Here's a smaller example of this pattern. Say we had code like:

    [] << 1 * 2
    

    RuboCop is suggesting that we make the operations explicit with:

    [] << (1 * 2)
     => [2]
    

    This way it's clear that the code isn't going to do something like this instead:

    ([] << 1) * 2
     => [1, 1]