Merge branch '3.x' into 4.x

* 3.x:
  Deprecate macro calls without parentheses

# Conflicts:
#	CHANGELOG
#	doc/deprecated.rst
This commit is contained in:
Fabien Potencier
2026-07-28 16:26:41 +02:00
3 changed files with 38 additions and 2 deletions
@@ -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);
@@ -24,6 +24,8 @@ class MacroReferenceExpression extends AbstractExpression implements SupportDefi
{
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
@@ -50,6 +52,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
@@ -62,6 +72,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