Skip to content

[Php56] Add missing parentheses for lower-precedence operands in PowToExpRector#8161

Merged
TomasVotruba merged 2 commits into
mainfrom
fix-pow-to-exp-precedence-parentheses
Jul 8, 2026
Merged

[Php56] Add missing parentheses for lower-precedence operands in PowToExpRector#8161
TomasVotruba merged 2 commits into
mainfrom
fix-pow-to-exp-precedence-parentheses

Conversation

@TomasVotruba

Copy link
Copy Markdown
Member

Fixes rectorphp/rector#9804

PowToExpRector converts pow($a, $b) to $a ** $b, but ** binds tighter than most operators. When an operand binds looser, dropping the parentheses silently changes the result.

Before

pow(~3, 4);          // becomes ~3 ** 4        => ~(3 ** 4)   = -82  (want 256)
pow(!3, 4);          // becomes !3 ** 4        => !(3 ** 4)   = false (want 0)
pow((int) "5x", 2);  // becomes (int) "5x" ** 2 => (int)("5x" ** 2)
pow($a += 4, 3);     // becomes $a += 4 ** 3   => $a += (4 ** 3)
pow($a ? 4 : 3, 2);  // becomes $a ? 4 : 3 ** 2 => $a ? 4 : (3 ** 2)
pow(2, $a ? 3 : 4);  // becomes 2 ** $a ? 3 : 4 => (2 ** $a) ? 3 : 4

After

-pow(~3, 4);          +(~3) ** 4;
-pow(!3, 4);          +(!3) ** 4;
-pow((int) "5x", 2);  +((int) "5x") ** 2;
-pow($a += 4, 3);     +($a += 4) ** 3;
-pow($a ? 4 : 3, 2);  +($a ? 4 : 3) ** 2;
-pow(2, $a ? 3 : 4);  +2 ** ($a ? 3 : 4);

Operands already legal bare on the right-hand side of ** stay clean:

 pow(2, -3);   =>  2 ** -3;
 pow(2, ~3);   =>  2 ** ~3;

Handled operand types: unary ~ ! + -, casts, instanceof, ternary, compound assignments (+= etc.), print, yield, yield from. Left and right operands are treated separately since unary/cast operands only need wrapping on the left.

Verified by evaluating original vs. transformed code in real PHP: identical output for every case.

…oExpRector

pow() converted to ** dropped required parentheses around operands that bind
looser than the ** operator, changing semantics. Wrap unary operators, casts,
ternary, compound assignments, print and yield operands when needed.

Fixes rectorphp/rector#9804
Precedence checks for ** operands are reusable by any rule that builds a Pow
node from arbitrary operands, e.g. compound **= handling.
@TomasVotruba TomasVotruba merged commit d1a6359 into main Jul 8, 2026
65 checks passed
@TomasVotruba TomasVotruba deleted the fix-pow-to-exp-precedence-parentheses branch July 8, 2026 21:03
@TomasVotruba

Copy link
Copy Markdown
Member Author

Let's ship this 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

1 participant