From 612852805fdfe94a12a28e1339a5bfd02a3eda9e Mon Sep 17 00:00:00 2001 From: Jeroen Versteeg Date: Sat, 16 Mar 2024 16:44:23 +0100 Subject: [PATCH] Mark implicit macro argument default values as such with an attribute This change causes no difference to compiled templates or to macro argument semantics. Consider the following macro: ```twig {% macro marco(po, lo = null) %}{% endmacro %} ``` With this change, the `ConstantExpression` for argument `po` will have an attribute `isImplicit`, whose value will be `true`. (Note that `lo` will not have that attribute.) This allows node visitors to distinguish between arguments that do and those that do not have explicit default values even if the value is `null`. This is useful for [static code analysis](https://github.com/twigphp/Twig/issues/4003). For example, a static analysis tool might consider arguments with no explicit default value as non-optional. --- src/ExpressionParser.php | 3 ++- tests/ParserTest.php | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/ExpressionParser.php b/src/ExpressionParser.php index 6839bc93204..f33bd113521 100644 --- a/src/ExpressionParser.php +++ b/src/ExpressionParser.php @@ -591,7 +591,7 @@ public function parseFilterExpressionRaw($node, $tag = null) * Parses arguments. * * @param bool $namedArguments Whether to allow named arguments or not - * @param bool $definition Whether we are parsing arguments for a function definition + * @param bool $definition Whether we are parsing arguments for a function (or macro) definition * * @return Node * @@ -642,6 +642,7 @@ public function parseArguments($namedArguments = false, $definition = false, $al if (null === $name) { $name = $value->getAttribute('name'); $value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine()); + $value->setAttribute('isImplicit', true); } $args[$name] = $value; } else { diff --git a/tests/ParserTest.php b/tests/ParserTest.php index cdd8e875743..4774bf5ee1e 100644 --- a/tests/ParserTest.php +++ b/tests/ParserTest.php @@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Twig\Environment; use Twig\Error\SyntaxError; +use Twig\Lexer; use Twig\Loader\LoaderInterface; use Twig\Node\Node; use Twig\Node\SetNode; @@ -175,6 +176,37 @@ public function testGetVarName() $this->addToAssertionCount(1); } + public function testImplicitMacroArgumentDefaultValues() + { + $template = '{% macro marco (po, lo = null) %}{% endmacro %}'; + $lexer = new Lexer(new Environment($this->createMock(LoaderInterface::class))); + $stream = $lexer->tokenize(new Source($template, 'index')); + + $argumentNodes = $this->getParser() + ->parse($stream) + ->getNode('macros') + ->getNode('marco') + ->getNode('arguments') + ; + + self::assertTrue( + $argumentNodes->getNode('po')->hasAttribute('isImplicit') + ); + self::assertTrue( + $argumentNodes->getNode('po')->getAttribute('isImplicit') + ); + self::assertNull( + $argumentNodes->getNode('po')->getAttribute('value') + ); + + self::assertFalse( + $argumentNodes->getNode('lo')->hasAttribute('isImplicit') + ); + self::assertNull( + $argumentNodes->getNode('lo')->getAttribute('value') + ); + } + protected function getParser() { $parser = new Parser(new Environment($this->createMock(LoaderInterface::class)));