Twig icon indicating copy to clipboard operation
Twig copied to clipboard

Cannot use three dots spread operator on v.3.21.x

Open fabioflx opened this issue 7 months ago • 10 comments

Since v3.21.x, clearing the cache of a Symfony project fails if twig files use the "three dots" (aka spread operator) format to add circumstantial elements to an array. The following code works on v3.20.x instead:

{% set something = [
    ...true ? ['a'] : ['b']
  ]
%}

fabioflx avatar May 09 '25 06:05 fabioflx

In 3.21.x, using parenthesis seems to provide the behaviour you expect.


{% set something = [
    ...(true ? ['a'] : ['b'])
  ]
%}

But indeed the same expression without procudes an error, where it did not in 3.19

smnandre avatar May 10 '25 16:05 smnandre

What error do you get ?

stof avatar May 13 '25 12:05 stof

I suppose that this is a precedence problem.

fabpot avatar May 13 '25 16:05 fabpot

What error do you get ?

The error says: CRITICAL [php] Uncaught Error: syntax error, unexpected token "..." ["exception" => ParseError { …}]

The line of Twig cache file related to the error created with composer update is:

(isset($context["dataController"]) || array_key_exists("dataController", $context) ? $context["dataController"] : (function () { throw new RuntimeError('Variable "dataController" does not exist.', 130, $this->source); })())]]]]]), (((( ...        // line 136

Every Twig file using three dots notation ends with "... // line nnn" in the cache files and all of them gives a critical error. This only affects Twig version from v3.21.x as this works flawlessly in v3.20.x instead.

fabioflx avatar May 14 '25 07:05 fabioflx

I suppose that this is a precedence problem.

@fabpot It seems indeed.

If I hard-update CoreExtension with the following:

// Line 336   change precedence from 512 to 10
            new UnaryOperatorExpressionParser(SpreadUnary::class, '...', 10, description: 'Spread operator'),

and if I set a precedence of 10 in the ConditionalTernaryExpressionParser


    public function getPrecedence(): int
    {
        return 10;
    }

... it works. 🤷‍♂

But this feels very very random :)

Quite surprisingly, the tests are all green.... except one that does not feel to be immediately related


There was 1 failure:

1) Twig\Tests\IntegrationTest::testLegacyIntegration with data set "tags/sandbox/array.legacy.test" ('tags/sandbox/array.legacy.test', 'sandbox tag', '', array('\n{%- sandbox %}\n    {%- inc...x %}\n', '\n{{ [a][0] }}\n{{ dump([a][0]) }}'), false, array(array('--DATA--\nreturn ['a' => 'b']...1) "b"', '\nreturn ['a' => 'b']\n', '\nreturn ['autoescape' => fal...rue]\n', '\nb\nstring(1) "b"')), 'Since twig/twig 3.15: The "sa...ine 2.')
sandbox tag (in tags/sandbox/array.legacy.test)
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
 'b\n
+/[...]/Twig/src/Extension/DebugExtension.php:57:\n
 string(1) "b"'

smnandre avatar May 16 '25 03:05 smnandre

changing the Twig precedence is not the solution to me (making the precedence order match the PHP order might make the compiled template valid, but it changes the meaning of the Twig template).

What we need to do is to make sure we generate valid PHP code instead.

stof avatar May 16 '25 07:05 stof

Maybe some adjustments could be done (where the recent Operator/parseExpression changes seems to be -at least partially- related) .. but not sure to be the most efficient here

smnandre avatar May 16 '25 16:05 smnandre

This issue also affects the adoption of Symfony 7.3 which now requires at least Twig version 3.21 which has this bug.

fabioflx avatar Jun 13 '25 10:06 fabioflx

This also affects Twig release 3.x. I think this bug is critical because it prevents the adoption of Symfony 7.3.x (which is a Stable Release since May 2025) as well as newer versions.

fabioflx avatar Jul 24 '25 13:07 fabioflx

@fabpot I suggest that the compilation of unary operators always wrap the operand in parenthesis, instead of assuming that the PHP precedence for that expression will be equivalent to the Twig one.

stof avatar Oct 24 '25 13:10 stof