Skip to content

Commit 303e066

Browse files
author
Jeroen Versteeg
committed
Add inspection for positional macro arguments after named ones
1 parent edc222d commit 303e066

File tree

5 files changed

+90
-1
lines changed

5 files changed

+90
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ no explicit default value as required.
152152
* ✅ Call with *too many* arguments (except if `varargs` is used)
153153
* ✅ Call with *too few* arguments
154154
* ✅ Required argument declared after optional
155+
* ✅ Positional argument after named in call expression
155156
* ⌛ Type mismatch in macro call
156157

157158
# Acknowledgments

composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"name": "Jeroen Versteeg"
99
}
1010
],
11-
"version": "1.0.6",
11+
"version": "1.0.7",
1212
"autoload": {
1313
"psr-4": {
1414
"AlisQI\\TwigQI\\": "src/"

src/Extension.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use AlisQI\TwigQI\Inspection\BadArgumentCountInMacroCall;
99
use AlisQI\TwigQI\Inspection\InvalidConstant;
1010
use AlisQI\TwigQI\Inspection\InvalidDotOperation;
11+
use AlisQI\TwigQI\Inspection\PositionalMacroArgumentAfterNamed;
1112
use AlisQI\TwigQI\Inspection\RequiredMacroArgumentAfterOptional;
1213
use AlisQI\TwigQI\Inspection\UndeclaredVariableInMacro;
1314
use AlisQI\TwigQI\Inspection\InvalidTypes;
@@ -25,6 +26,7 @@ public function getNodeVisitors(): array
2526
new InvalidConstant(),
2627

2728
new BadArgumentCountInMacroCall(),
29+
new PositionalMacroArgumentAfterNamed(),
2830
new RequiredMacroArgumentAfterOptional(),
2931
new UndeclaredVariableInMacro(),
3032
];
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AlisQI\TwigQI\Inspection;
6+
7+
use AlisQI\TwigQI\Helper\NodeLocation;
8+
use Twig\Environment;
9+
use Twig\Node\Expression\MacroReferenceExpression;
10+
use Twig\Node\Node;
11+
use Twig\NodeVisitor\NodeVisitorInterface;
12+
13+
class PositionalMacroArgumentAfterNamed implements NodeVisitorInterface
14+
{
15+
public function enterNode(Node $node, Environment $env): Node
16+
{
17+
if ($node instanceof MacroReferenceExpression) {
18+
if (!$this->checkCall($node)) {
19+
trigger_error(
20+
sprintf("Positional macro argument after named (at %s)", new NodeLocation($node)),
21+
E_USER_ERROR
22+
);
23+
}
24+
}
25+
26+
return $node;
27+
}
28+
29+
private function checkCall(MacroReferenceExpression $node): bool
30+
{
31+
$namedArgumentEncountered = false;
32+
foreach ($node->getNode('arguments')->getKeyValuePairs() as ['key' => $key]) {
33+
$name = $key->getAttribute('name');
34+
35+
if (is_int($name)) {
36+
if ($namedArgumentEncountered) {
37+
return false;
38+
}
39+
} else {
40+
$namedArgumentEncountered = true;
41+
}
42+
}
43+
44+
return true;
45+
}
46+
47+
public function leaveNode(Node $node, Environment $env): ?Node
48+
{
49+
return $node;
50+
}
51+
52+
public function getPriority(): int
53+
{
54+
return 0;
55+
}
56+
57+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace AlisQI\TwigQI\Tests;
6+
7+
class PositionalMacroArgumentAfterNamedTest extends AbstractTestCase
8+
{
9+
public function test_itSupportsNamedArguments(): void
10+
{
11+
$this->env->createTemplate(<<<EOF
12+
{% macro marco(po, lo) %}{% endmacro %}
13+
{{ _self.polo(po=13, lo: 37) }}
14+
EOF);
15+
16+
self::assertEmpty($this->errors, implode(', ', $this->errors));
17+
}
18+
19+
public function test_itErrorsForPositionalArgumentAfterNamed(): void
20+
{
21+
$this->env->createTemplate(<<<EOF
22+
{% macro marco(po, lo) %}{% endmacro %}
23+
{{ _self.polo(po: 13, 37) }}
24+
EOF);
25+
26+
self::assertNotEmpty($this->errors);
27+
}
28+
29+
}

0 commit comments

Comments
 (0)