Deprecate macro calls without parentheses

This commit is contained in:
Fabien Potencier
2026-07-28 14:43:19 +02:00
parent a2a4c1b4f2
commit ad305b414e
5 changed files with 45 additions and 2 deletions
+1
View File
@@ -7,6 +7,7 @@
* Fix duplicated macro argument names triggering a PHP fatal error instead of a `SyntaxError`
* Deprecate defining a macro more than once in the same template
* Deprecate `TemplateVariable` and `AssignTemplateVariable`; use `MacroVariable` and `AssignMacroVariable` instead
* Deprecate omitting parentheses when calling a macro; it will throw a `SyntaxError` in 4.0
# 3.28.0 (2026-07-03)
+6
View File
@@ -318,6 +318,12 @@ Macros
3.29 and will throw a ``SyntaxError`` in Twig 4.0. Give each macro a unique
name.
* Omitting parentheses when calling a macro (e.g. ``macros.input`` or
``macros.(name)``) is deprecated as of Twig 3.29 and will throw a
``SyntaxError`` in Twig 4.0. Add parentheses after the macro name (e.g.
``macros.input()`` or ``macros.(name)()``). Parentheses remain optional when
testing whether a macro is defined.
Filters
-------
@@ -77,11 +77,17 @@ final class DotExpressionParser extends AbstractExpressionParser implements Infi
&& \is_string($name = $attribute->getAttribute('value'))
&& preg_match('#^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$#D', $name)
) {
return new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$name, $arguments, $expr->getTemplateLine());
$node = new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$name, $arguments, $expr->getTemplateLine());
$node->setHasParentheses(Template::METHOD_CALL === $type);
return $node;
}
if ($isMacroTarget && !$attribute instanceof ConstantExpression) {
return new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), $attribute, $arguments, $expr->getTemplateLine());
$node = new MacroReferenceExpression(new MacroVariable($expr->getAttribute('name'), $expr->getTemplateLine()), $attribute, $arguments, $expr->getTemplateLine());
$node->setHasParentheses(Template::METHOD_CALL === $type);
return $node;
}
return new GetAttrExpression($expr, $attribute, $arguments, $type, $lineno, $nullSafe);
@@ -25,6 +25,8 @@ class MacroReferenceExpression extends AbstractExpression implements SupportDefi
use SupportDefinedTestDeprecationTrait;
use SupportDefinedTestTrait;
private bool $hasParentheses = true;
/**
* @param string|AbstractExpression $name A static macro method name (e.g. "macro_foo") or, for a dynamic
* call, an expression resolving to the macro name (without the
@@ -51,6 +53,14 @@ class MacroReferenceExpression extends AbstractExpression implements SupportDefi
parent::__construct($nodes, $attributes, $lineno);
}
/**
* @internal
*/
public function setHasParentheses(bool $hasParentheses): void
{
$this->hasParentheses = $hasParentheses;
}
public function __clone()
{
// The template node must not be deep-cloned because its name is
@@ -63,6 +73,10 @@ class MacroReferenceExpression extends AbstractExpression implements SupportDefi
public function compile(Compiler $compiler): void
{
if (!$this->hasParentheses && !$this->definedTest) {
trigger_deprecation('twig/twig', '3.29', 'Omitting parentheses when calling a macro is deprecated and will throw a SyntaxError in Twig 4.0; add parentheses after the macro name in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine());
}
if ($this->hasNode('name')) {
$this->compileDynamic($compiler);
@@ -0,0 +1,16 @@
--TEST--
Omitting parentheses when calling macros is deprecated
--DEPRECATION--
Since twig/twig 3.29: Omitting parentheses when calling a macro is deprecated and will throw a SyntaxError in Twig 4.0; add parentheses after the macro name in "index.twig" at line 4.
Since twig/twig 3.29: Omitting parentheses when calling a macro is deprecated and will throw a SyntaxError in Twig 4.0; add parentheses after the macro name in "index.twig" at line 5.
--TEMPLATE--
{% import _self as macros %}
{% set name = 'hello' %}
{{ macros.hello }}
{{ _self.(name) }}
{% macro hello() %}Hello{% endmacro %}
--DATA--
return []
--EXPECT--
Hello
Hello