added missing hints in some runtime exceptions

This commit is contained in:
Fabien Potencier
2019-04-11 23:10:44 +02:00
parent 11afd511ce
commit d4db91535d
13 changed files with 95 additions and 53 deletions
+1 -7
View File
@@ -63,7 +63,7 @@ class Error extends \Exception
* @param Source|string|null $source The source context where the error occurred
* @param \Exception $previous The previous exception
*/
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null, $autoGuess = true)
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null)
{
if (null === $source) {
$name = null;
@@ -79,13 +79,7 @@ class Error extends \Exception
$this->lineno = $lineno;
$this->filename = $name;
if ($autoGuess && (-1 === $lineno || null === $name || null === $this->sourcePath)) {
$this->guessTemplateInfo();
}
$this->rawMessage = $message;
$this->updateRepr();
}
-12
View File
@@ -14,22 +14,10 @@ namespace Twig\Error;
/**
* Exception thrown when an error occurs during template loading.
*
* Automatic template information guessing is always turned off as
* if a template cannot be loaded, there is nothing to guess.
* However, when a template is loaded from another one, then, we need
* to find the current context and this is automatically done by
* Twig\Template::displayWithErrorHandling().
*
* This strategy makes Twig\Environment::resolveTemplate() much faster.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class LoaderError extends Error
{
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null)
{
parent::__construct($message, $lineno, $source, $previous, false);
}
}
class_alias('Twig\Error\LoaderError', 'Twig_Error_Loader');
+12 -8
View File
@@ -121,7 +121,7 @@ abstract class CallExpression extends AbstractExpression
$named = true;
$name = $this->normalizeName($name);
} elseif ($named) {
throw new SyntaxError(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName), $this->getTemplateLine(), null, null, false);
throw new SyntaxError(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
}
$parameters[$name] = $node;
@@ -153,14 +153,14 @@ abstract class CallExpression extends AbstractExpression
if (\array_key_exists($name, $parameters)) {
if (\array_key_exists($pos, $parameters)) {
throw new SyntaxError(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), null, null, false);
throw new SyntaxError(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
}
if (\count($missingArguments)) {
throw new SyntaxError(sprintf(
'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
$name, $callType, $callName, implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments)
), $this->getTemplateLine(), null, null, false);
), $this->getTemplateLine(), $this->getSourceContext());
}
$arguments = array_merge($arguments, $optionalArguments);
@@ -182,7 +182,7 @@ abstract class CallExpression extends AbstractExpression
$missingArguments[] = $name;
}
} else {
throw new SyntaxError(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), null, null, false);
throw new SyntaxError(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext());
}
}
@@ -212,10 +212,14 @@ abstract class CallExpression extends AbstractExpression
}
}
throw new SyntaxError(sprintf(
'Unknown argument%s "%s" for %s "%s(%s)".',
\count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
), $unknownParameter ? $unknownParameter->getTemplateLine() : $this->getTemplateLine(), null, null, false);
throw new SyntaxError(
sprintf(
'Unknown argument%s "%s" for %s "%s(%s)".',
\count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
),
$unknownParameter ? $unknownParameter->getTemplateLine() : $this->getTemplateLine(),
$unknownParameter ? $unknownParameter->getSourceContext() : $this->getSourceContext()
);
}
return $arguments;
+1 -1
View File
@@ -47,7 +47,7 @@ class DefinedTest extends TestExpression
} elseif ($node instanceof ConstantExpression || $node instanceof ArrayExpression) {
$node = new ConstantExpression(true, $node->getTemplateLine());
} else {
throw new SyntaxError('The "defined" test only works with simple variables.', $this->getTemplateLine(), null, null, false);
throw new SyntaxError('The "defined" test only works with simple variables.', $lineno);
}
parent::__construct($node, $name, $arguments, $lineno);
+1 -1
View File
@@ -27,7 +27,7 @@ class MacroNode extends Node
{
foreach ($arguments as $argumentName => $argument) {
if (self::VARARGS_NAME === $argumentName) {
throw new SyntaxError(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine(), null, null, false);
throw new SyntaxError(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine(), $argument->getSourceContext());
}
}
+22 -18
View File
@@ -28,15 +28,13 @@ use Twig\Source;
*/
class ModuleNode extends Node
{
private $source;
public function __construct(\Twig_NodeInterface $body, AbstractExpression $parent = null, \Twig_NodeInterface $blocks, \Twig_NodeInterface $macros, \Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '')
{
if (!$name instanceof Source) {
@trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED);
$this->source = new Source($source, $name);
$source = new Source($source, $name);
} else {
$this->source = $name;
$source = $name;
}
$nodes = [
@@ -57,15 +55,16 @@ class ModuleNode extends Node
// embedded templates are set as attributes so that they are only visited once by the visitors
parent::__construct($nodes, [
// source to be remove in 2.0
'source' => $this->source->getCode(),
'source' => $source->getCode(),
// filename to be remove in 2.0 (use getTemplateName() instead)
'filename' => $this->source->getName(),
'filename' => $source->getName(),
'index' => null,
'embedded_templates' => $embeddedTemplates,
], 1);
// populate the template name of all node children
$this->setTemplateName($this->source->getName());
$this->setTemplateName($source->getName());
$this->setSourceContext($source);
}
public function setIndex($index)
@@ -143,7 +142,7 @@ class ModuleNode extends Node
->raw('$this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($this->source->getName())
->repr($this->getSourceContext()->getName())
->raw(', ')
->repr($parent->getTemplateLine())
->raw(')')
@@ -178,8 +177,8 @@ class ModuleNode extends Node
}
$compiler
// if the template name contains */, add a blank to avoid a PHP parse error
->write('/* '.str_replace('*/', '* /', $this->source->getName())." */\n")
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->source->getName(), $this->getAttribute('index')))
->write('/* '.str_replace('*/', '* /', $this->getSourceContext()->getName())." */\n")
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(), $this->getAttribute('index')))
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
->write("{\n")
->indent()
@@ -206,13 +205,16 @@ class ModuleNode extends Node
foreach ($this->getNode('traits') as $i => $trait) {
$this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
$node = $node = $trait->getNode('template');
$compiler
->addDebugInfo($trait->getNode('template'))
->addDebugInfo($node)
->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
->indent()
->write("throw new RuntimeError('Template \"'.")
->subcompile($trait->getNode('template'))
->raw(".'\" cannot be used as a trait.');\n")
->raw(".'\" cannot be used as a trait.', ")
->repr($node->getTemplateLine())
->raw(", \$this->getSourceContext());\n")
->outdent()
->write("}\n")
->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
@@ -228,7 +230,9 @@ class ModuleNode extends Node
->string($key)
->raw(' is not defined in trait ')
->subcompile($trait->getNode('template'))
->raw(".'));\n")
->raw(".'), ")
->repr($node->getTemplateLine())
->raw(", \$this->getSourceContext());\n")
->outdent()
->write("}\n\n")
@@ -327,7 +331,7 @@ class ModuleNode extends Node
->write('$this->parent = $this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($this->source->getName())
->repr($this->getSourceContext()->getName())
->raw(', ')
->repr($parent->getTemplateLine())
->raw(");\n")
@@ -366,7 +370,7 @@ class ModuleNode extends Node
->write("public function getTemplateName()\n", "{\n")
->indent()
->write('return ')
->repr($this->source->getName())
->repr($this->getSourceContext()->getName())
->raw(";\n")
->outdent()
->write("}\n\n")
@@ -456,11 +460,11 @@ class ModuleNode extends Node
->write("public function getSourceContext()\n", "{\n")
->indent()
->write('return new Source(')
->string($compiler->getEnvironment()->isDebug() ? $this->source->getCode() : '')
->string($compiler->getEnvironment()->isDebug() ? $this->getSourceContext()->getCode() : '')
->raw(', ')
->string($this->source->getName())
->string($this->getSourceContext()->getName())
->raw(', ')
->string($this->source->getPath())
->string($this->getSourceContext()->getPath())
->raw(");\n")
->outdent()
->write("}\n")
+17
View File
@@ -13,6 +13,7 @@
namespace Twig\Node;
use Twig\Compiler;
use Twig\Source;
/**
* Represents a node in the AST.
@@ -27,6 +28,7 @@ class Node implements \Twig_NodeInterface
protected $tag;
private $name;
private $sourceContext;
/**
* Constructor.
@@ -235,6 +237,21 @@ class Node implements \Twig_NodeInterface
return $this->name;
}
public function setSourceContext(Source $source)
{
$this->sourceContext = $source;
foreach ($this->nodes as $node) {
if (null !== $node) {
$node->setSourceContext($source);
}
}
}
public function getSourceContext()
{
return $this->sourceContext;
}
/**
* @deprecated since 1.27 (to be removed in 2.0)
*/
+5 -2
View File
@@ -35,14 +35,17 @@ class WithNode extends Node
$compiler->addDebugInfo($this);
if ($this->hasNode('variables')) {
$node = $this->getNode('variables');
$varsName = $compiler->getVarName();
$compiler
->write(sprintf('$%s = ', $varsName))
->subcompile($this->getNode('variables'))
->subcompile($node)
->raw(";\n")
->write(sprintf("if (!twig_test_iterable(\$%s)) {\n", $varsName))
->indent()
->write("throw new RuntimeError('Variables passed to the \"with\" tag must be a hash.');\n")
->write("throw new RuntimeError('Variables passed to the \"with\" tag must be a hash.', ")
->repr($node->getTemplateLine())
->raw(", \$this->getSourceContext());\n")
->outdent()
->write("}\n")
->write(sprintf("\$%s = twig_to_array(\$%s);\n", $varsName, $varsName))
+8 -2
View File
@@ -227,7 +227,10 @@ abstract class Template implements \Twig_TemplateInterface
throw $e;
} catch (\Exception $e) {
throw new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e);
$e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e);
$e->guess();
throw $e;
}
} elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
@@ -438,7 +441,10 @@ abstract class Template implements \Twig_TemplateInterface
throw $e;
} catch (\Exception $e) {
throw new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e);
$e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e);
$e->guess();
throw $e;
}
}
+4 -2
View File
@@ -55,8 +55,9 @@ class Twig_Tests_ExpressionParserTest extends \PHPUnit\Framework\TestCase
public function testArrayExpression($template, $expected)
{
$env = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock(), ['cache' => false, 'autoescape' => false]);
$stream = $env->tokenize(new Source($template, ''));
$stream = $env->tokenize($source = new Source($template, ''));
$parser = new Parser($env);
$expected->setSourceContext($source);
$this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
}
@@ -175,8 +176,9 @@ class Twig_Tests_ExpressionParserTest extends \PHPUnit\Framework\TestCase
public function testStringExpression($template, $expected)
{
$env = new Environment($this->getMockBuilder('\Twig\Loader\LoaderInterface')->getMock(), ['cache' => false, 'autoescape' => false, 'optimizations' => 0]);
$stream = $env->tokenize(new Source($template, ''));
$stream = $env->tokenize($source = new Source($template, ''));
$parser = new Parser($env);
$expected->setSourceContext($source);
$this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
}
@@ -0,0 +1,8 @@
--TEST--
Argument is defined twice in a call
--TEMPLATE--
{{ date(987654, date = 123456) }}
--DATA--
return []
--EXCEPTION--
Twig\Error\SyntaxError: Argument "date" is defined twice for function "date" in "index.twig" at line 2.
@@ -0,0 +1,8 @@
--TEST--
Positional arguments after named arguments in a call
--TEMPLATE--
{{ date(date = 123456, 'Y-m-d') }}
--DATA--
return []
--EXCEPTION--
Twig\Error\SyntaxError: Positional arguments cannot be used after named arguments for function "date" in "index.twig" at line 2.
@@ -0,0 +1,8 @@
--TEST--
"defined" support for "complex" expressions
--TEMPLATE--
{{ (1 + 2) is defined ? 'ok' : 'ko' }}
--DATA--
return []
--EXCEPTION--
Twig\Error\SyntaxError: The "defined" test only works with simple variables in "index.twig" at line 2.