Skip to content

Commit 565a1c4

Browse files
committed
minor symfony#24007 [ExpressionLanguage] SyntaxError : make a proposal when no function or variable found (fmata)
This PR was merged into the 3.4 branch. Discussion ---------- [ExpressionLanguage] SyntaxError : make a proposal when no function or variable found | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | n/a | License | MIT | Doc PR | needed ? Now when an Expression fails to be parsed, a SyntaxError is raised with an explicit message but it's impossible to extract the function, variable or the pair token type/token value to parse. When a functional user encounters a SyntaxError, by default there is no mechanism to understand why what he typed is incorrect. This PR exposes when it's possible the subject of the violation and in case of function or variable undefined in the Expression, a proposal is made. ```php <?php require 'vendor/autoload.php'; use Symfony\Component\ExpressionLanguage\Lexer; use Symfony\Component\ExpressionLanguage\Parser; use Symfony\Component\ExpressionLanguage\SyntaxError; $lexer = new Lexer(); $parser = new Parser(array()); try { $parser->parse($lexer->tokenize('user.city.departement in [departement, departement2]'), array('user', 'departement1', 'departement2')); } catch (SyntaxError $e) { echo $e->getMessage(); } ``` Outputs : > Variable "departement" is not valid around position 27 for expression `user.city.departement in [departement, departement2]`. > > Did you mean "departement1"? Commits ------- f19cf8a [ExpressionLanguage] make a proposal in SyntaxError message
2 parents a0bdd5a + f19cf8a commit 565a1c4

File tree

3 files changed

+30
-3
lines changed

3 files changed

+30
-3
lines changed

src/Symfony/Component/ExpressionLanguage/Parser.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,13 +195,13 @@ public function parsePrimaryExpression()
195195
default:
196196
if ('(' === $this->stream->current->value) {
197197
if (false === isset($this->functions[$token->value])) {
198-
throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression());
198+
throw new SyntaxError(sprintf('The function "%s" does not exist', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, array_keys($this->functions));
199199
}
200200

201201
$node = new Node\FunctionNode($token->value, $this->parseArguments());
202202
} else {
203203
if (!in_array($token->value, $this->names, true)) {
204-
throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression());
204+
throw new SyntaxError(sprintf('Variable "%s" is not valid', $token->value), $token->cursor, $this->stream->getExpression(), $token->value, $this->names);
205205
}
206206

207207
// is the name used in the compiled code different

src/Symfony/Component/ExpressionLanguage/SyntaxError.php

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,29 @@
1313

1414
class SyntaxError extends \LogicException
1515
{
16-
public function __construct($message, $cursor = 0, $expression = '')
16+
public function __construct($message, $cursor = 0, $expression = '', $subject = null, array $proposals = null)
1717
{
1818
$message = sprintf('%s around position %d', $message, $cursor);
1919
if ($expression) {
2020
$message = sprintf('%s for expression `%s`', $message, $expression);
2121
}
2222
$message .= '.';
2323

24+
if (null !== $subject && null !== $proposals) {
25+
$minScore = INF;
26+
foreach ($proposals as $proposal) {
27+
$distance = levenshtein($subject, $proposal);
28+
if ($distance < $minScore) {
29+
$guess = $proposal;
30+
$minScore = $distance;
31+
}
32+
}
33+
34+
if (isset($guess) && $minScore < 3) {
35+
$message .= sprintf(' Did you mean "%s"?', $guess);
36+
}
37+
}
38+
2439
parent::__construct($message);
2540
}
2641
}

src/Symfony/Component/ExpressionLanguage/Tests/ParserTest.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,16 @@ public function getInvalidPostfixData()
195195
),
196196
);
197197
}
198+
199+
/**
200+
* @expectedException \Symfony\Component\ExpressionLanguage\SyntaxError
201+
* @expectedExceptionMessage Did you mean "baz"?
202+
*/
203+
public function testNameProposal()
204+
{
205+
$lexer = new Lexer();
206+
$parser = new Parser(array());
207+
208+
$parser->parse($lexer->tokenize('foo > bar'), array('foo', 'baz'));
209+
}
198210
}

0 commit comments

Comments
 (0)