The docs state that modifier-rescue has higher precedence than assignments which have higher precedence than modifier-if. This is true for v = expr rescue $! if condition but not for v = expr if condition rescue $! which is treated as (v = expr if condition) rescue $! rather than v = expr if (condition rescue $!)
This goes similarly for defined? expr rescue $! not expr rescue $! expr1 and expr2 rescue $! expr1 or expr2 rescue $!
So maybe the documentation should state that modifier-rescue has equal precedence to modifier-if & others, with an exception made for assignments? I'm not entirely sure how to describe that exception though.
The current doc about precedence is correct. The behavior you showed is not caused by precedence, but by the grammer itself.
The point is, that <stmt> rescue <stmt> is a statement, not an expression. The right side of modifier-if must be an expression, so <stmt> rescue <stmt> cannot be a right side of modifier-if. So, <stmt> if <stmt> rescue <stmt> can parse only as (<stmt> if <stmt>) rescue <stmt>.
defined? also requires an expression as its argument. So defined? expr rescue $! can parse only as defined? (expr rescue $!).
You can see the precedence by the following code: stmt if v = condition rescue $!. It can parse as both (stmt if v = condition) rescue $! and stmt if v = (condition rescue $!) but the second one is chosen because modifier-rescue has higher precedence than modifier-if.
So a if b rescue c is parsed as ‹a if b› rescue c because a if ‹b rescue c› would be a SyntaxError.
In any case, even if the issue is not precedence itself, I think the documentation should be updated somehow, because it leads one to think that a if b rescue c is equivalent to a if (b rescue c)
Now that I understand the nature of the issue I've tried writing a documentation patch.
Document the difference between expressions and statements [ci skip]
In the grammar, all expressions are statements, but not all
statements are expressions. Some parts of the grammar accept
expressions and not other types of statements, which causes
similar looking code to parse differently due to operator
precedence.