Fix inheritance logic to be able to use instanceof ContextVariable instead of NameExpression

This commit is contained in:
Fabien Potencier
2025-01-23 18:39:15 +01:00
parent 72c91f6260
commit b6d9dd62ee
15 changed files with 33 additions and 22 deletions
+4 -4
View File
@@ -655,7 +655,7 @@ class ExpressionParser
$name = null;
if ($namedArguments && (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || (!$definition && $token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':')))) {
if (!$value instanceof NameExpression) {
if (!$value instanceof ContextVariable) {
throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());
}
$name = $value->getAttribute('name');
@@ -743,7 +743,7 @@ class ExpressionParser
$arguments = new Nodes([0 => $this->getPrimary()]);
}
if ('defined' === $test->getName() && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) {
if ('defined' === $test->getName() && $node instanceof ContextVariable && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) {
$node = new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine());
}
@@ -898,7 +898,7 @@ class ExpressionParser
$name = null;
if (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) {
if (!$value instanceof NameExpression) {
if (!$value instanceof ContextVariable) {
throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());
}
$name = $value->getAttribute('name');
@@ -946,7 +946,7 @@ class ExpressionParser
}
if (
$node instanceof NameExpression
$node instanceof ContextVariable
&& (
null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))
|| '_self' === $node->getAttribute('name') && $attribute instanceof ConstantExpression
+2 -1
View File
@@ -13,6 +13,7 @@ namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\Node\Expression\Unary\StringCastUnary;
use Twig\Node\Expression\Variable\ContextVariable;
class ArrayExpression extends AbstractExpression
{
@@ -99,7 +100,7 @@ class ArrayExpression extends AbstractExpression
++$nextIndex;
} else {
$key = null;
if ($pair['key'] instanceof NameExpression) {
if ($pair['key'] instanceof ContextVariable) {
$pair['key'] = new StringCastUnary($pair['key'], $pair['key']->getTemplateLine());
}
if ($pair['key'] instanceof TempNameExpression) {
+2 -1
View File
@@ -15,8 +15,9 @@ namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\Error\SyntaxError;
use Twig\Node\Expression\Variable\AssignContextVariable;
use Twig\Node\Expression\Variable\ContextVariable;
class AssignNameExpression extends NameExpression
class AssignNameExpression extends ContextVariable
{
public function __construct(string $name, int $lineno)
{
@@ -15,11 +15,11 @@ use Twig\Compiler;
use Twig\Node\EmptyNode;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\BlockReferenceExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\OperatorEscapeInterface;
use Twig\Node\Expression\Test\DefinedTest;
use Twig\Node\Expression\Test\NullTest;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\TwigTest;
final class NullCoalesceBinary extends AbstractBinary implements OperatorEscapeInterface
@@ -28,7 +28,7 @@ final class NullCoalesceBinary extends AbstractBinary implements OperatorEscapeI
{
parent::__construct($left, $right, $lineno);
if (!$left instanceof NameExpression) {
if (!$left instanceof ContextVariable) {
$test = new DefinedTest(clone $left, new TwigTest('defined'), new EmptyNode(), $left->getTemplateLine());
// for "block()", we don't need the null test as the return value is always a string
if (!$left instanceof BlockReferenceExpression) {
+2 -2
View File
@@ -19,9 +19,9 @@ use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\FilterExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\Ternary\ConditionalTernary;
use Twig\Node\Expression\Test\DefinedTest;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\Node;
use Twig\TwigFilter;
use Twig\TwigTest;
@@ -53,7 +53,7 @@ class DefaultFilter extends FilterExpression
$default = new FilterExpression($node, new TwigFilter('default', [CoreExtension::class, 'default']), $arguments, $node->getTemplateLine());
}
if ('default' === $name && ($node instanceof NameExpression || $node instanceof GetAttrExpression)) {
if ('default' === $name && ($node instanceof ContextVariable || $node instanceof GetAttrExpression)) {
$test = new DefinedTest(clone $node, new TwigTest('defined'), new EmptyNode(), $node->getTemplateLine());
$false = \count($arguments) ? $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine());
+2 -1
View File
@@ -14,6 +14,7 @@ namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\Extension\SandboxExtension;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Template;
class GetAttrExpression extends AbstractExpression
@@ -28,7 +29,7 @@ class GetAttrExpression extends AbstractExpression
$nodes['arguments'] = $arguments;
}
if ($arguments && !$arguments instanceof ArrayExpression && !$arguments instanceof NameExpression) {
if ($arguments && !$arguments instanceof ArrayExpression && !$arguments instanceof ContextVariable) {
trigger_deprecation('twig/twig', '3.15', \sprintf('Not passing a "%s" instance as the "arguments" argument of the "%s" constructor is deprecated ("%s" given).', ArrayExpression::class, static::class, $arguments::class));
}
+2 -1
View File
@@ -12,6 +12,7 @@
namespace Twig\Node\Expression;
use Twig\Compiler;
use Twig\Node\Expression\Variable\ContextVariable;
class MethodCallExpression extends AbstractExpression
{
@@ -21,7 +22,7 @@ class MethodCallExpression extends AbstractExpression
parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno);
if ($node instanceof NameExpression) {
if ($node instanceof ContextVariable) {
$node->setAttribute('always_defined', true);
}
}
@@ -18,6 +18,7 @@ use Twig\Node\Expression\Binary\NullCoalesceBinary;
use Twig\Node\Expression\Test\DefinedTest;
use Twig\Node\Expression\Test\NullTest;
use Twig\Node\Expression\Unary\NotUnary;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\Node;
use Twig\TwigTest;
@@ -60,7 +61,7 @@ class NullCoalesceExpression extends ConditionalExpression
* cases might be implemented as an optimizer node visitor, but has not been done
* as benefits are probably not worth the added complexity.
*/
if ($this->getNode('expr2') instanceof NameExpression) {
if ($this->getNode('expr2') instanceof ContextVariable) {
$this->getNode('expr2')->setAttribute('always_defined', true);
$compiler
->raw('((')
+2 -1
View File
@@ -24,6 +24,7 @@ use Twig\Node\Expression\MacroReferenceExpression;
use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\TestExpression;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\Node;
use Twig\TwigTest;
@@ -49,7 +50,7 @@ class DefinedTest extends TestExpression
trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, \get_class($node));
}
if ($node instanceof NameExpression) {
if ($node instanceof ContextVariable) {
$node->setAttribute('is_defined_test', true);
} elseif ($node instanceof GetAttrExpression) {
$node->setAttribute('is_defined_test', true);
@@ -13,6 +13,6 @@ namespace Twig\Node\Expression\Variable;
use Twig\Node\Expression\NameExpression;
final class ContextVariable extends NameExpression
class ContextVariable extends NameExpression
{
}
+2 -1
View File
@@ -16,6 +16,7 @@ use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\Variable\AssignTemplateVariable;
use Twig\Node\Expression\Variable\ContextVariable;
/**
* Represents an import node.
@@ -44,7 +45,7 @@ class ImportNode extends Node
{
$compiler->subcompile($this->getNode('var'));
if ($this->getNode('expr') instanceof NameExpression && '_self' === $this->getNode('expr')->getAttribute('name')) {
if ($this->getNode('expr') instanceof ContextVariable && '_self' === $this->getNode('expr')->getAttribute('name')) {
$compiler->raw('$this');
} else {
$compiler
+4 -3
View File
@@ -19,6 +19,7 @@ use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\ForNode;
use Twig\Node\IncludeNode;
use Twig\Node\Node;
@@ -137,13 +138,13 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
// when do we need to add the loop variable back?
// the loop variable is referenced for the current loop
elseif ($node instanceof NameExpression && 'loop' === $node->getAttribute('name')) {
elseif ($node instanceof ContextVariable && 'loop' === $node->getAttribute('name')) {
$node->setAttribute('always_defined', true);
$this->addLoopToCurrent();
}
// optimize access to loop targets
elseif ($node instanceof NameExpression && \in_array($node->getAttribute('name'), $this->loopsTargets)) {
elseif ($node instanceof ContextVariable && \in_array($node->getAttribute('name'), $this->loopsTargets)) {
$node->setAttribute('always_defined', true);
}
@@ -173,7 +174,7 @@ final class OptimizerNodeVisitor implements NodeVisitorInterface
|| 'parent' === $node->getNode('attribute')->getAttribute('value')
)
&& (true === $this->loops[0]->getAttribute('with_loop')
|| ($node->getNode('node') instanceof NameExpression
|| ($node->getNode('node') instanceof ContextVariable
&& 'loop' === $node->getNode('node')->getAttribute('name')
)
)
+2 -1
View File
@@ -22,6 +22,7 @@ use Twig\Node\Expression\MethodCallExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\OperatorEscapeInterface;
use Twig\Node\Expression\ParentExpression;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\Node;
/**
@@ -146,7 +147,7 @@ final class SafeAnalysisNodeVisitor implements NodeVisitorInterface
} elseif ($node instanceof MethodCallExpression || $node instanceof MacroReferenceExpression) {
// all macro calls are safe
$this->setSafe($node, ['all']);
} elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression) {
} elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof ContextVariable) {
$name = $node->getNode('node')->getAttribute('name');
if (\in_array($name, $this->safeVars)) {
$this->setSafe($node, ['all']);
+2 -1
View File
@@ -23,6 +23,7 @@ use Twig\Node\Expression\FunctionExpression;
use Twig\Node\Expression\GetAttrExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\Unary\SpreadUnary;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\ModuleNode;
use Twig\Node\Node;
use Twig\Node\Nodes;
@@ -122,7 +123,7 @@ final class SandboxNodeVisitor implements NodeVisitorInterface
private function wrapNode(Node $node, string $name): void
{
$expr = $node->getNode($name);
if (($expr instanceof NameExpression || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) {
if (($expr instanceof ContextVariable || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) {
// Simplify in 4.0 as the spread attribute has been removed there
$new = new CheckToStringNode($expr);
if ($expr->hasAttribute('spread')) {
+2 -1
View File
@@ -14,6 +14,7 @@ namespace Twig\TokenParser;
use Twig\Node\EmbedNode;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\NameExpression;
use Twig\Node\Expression\Variable\ContextVariable;
use Twig\Node\Node;
use Twig\Token;
@@ -35,7 +36,7 @@ final class EmbedTokenParser extends IncludeTokenParser
$parentToken = $fakeParentToken = new Token(Token::STRING_TYPE, '__parent__', $token->getLine());
if ($parent instanceof ConstantExpression) {
$parentToken = new Token(Token::STRING_TYPE, $parent->getAttribute('value'), $token->getLine());
} elseif ($parent instanceof NameExpression) {
} elseif ($parent instanceof ContextVariable) {
$parentToken = new Token(Token::NAME_TYPE, $parent->getAttribute('name'), $token->getLine());
}