mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2024-11-26 20:14:46 +01:00
Introduce ErrorHandler
Add ErrorHandler interface, as well as ErrorHandler\Throwing and ErrorHandler\Collecting. The error handler is passed to Parser::parse(). This supersedes the throwOnError option. NameResolver now accepts an ErrorHandler in the ctor.
This commit is contained in:
parent
90834bff8e
commit
f99a96e0a2
13
lib/PhpParser/ErrorHandler.php
Normal file
13
lib/PhpParser/ErrorHandler.php
Normal file
@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser;
|
||||
|
||||
interface ErrorHandler
|
||||
{
|
||||
/**
|
||||
* Handle an error generated during lexing, parsing or some other operation.
|
||||
*
|
||||
* @param Error $error The error that needs to be handled
|
||||
*/
|
||||
public function handleError(Error $error);
|
||||
}
|
46
lib/PhpParser/ErrorHandler/Collecting.php
Normal file
46
lib/PhpParser/ErrorHandler/Collecting.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\ErrorHandler;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Error handler that collects all errors into an array.
|
||||
*
|
||||
* This allows graceful handling of errors.
|
||||
*/
|
||||
class Collecting implements ErrorHandler
|
||||
{
|
||||
/** @var Error[] Collected errors */
|
||||
private $errors = [];
|
||||
|
||||
public function handleError(Error $error) {
|
||||
$this->errors[] = $error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get collected errors.
|
||||
*
|
||||
* @return Error[]
|
||||
*/
|
||||
public function getErrors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether there are any errors.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasErrors() {
|
||||
return !empty($this->errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset/clear collected errors.
|
||||
*/
|
||||
public function clearErrors() {
|
||||
$this->errors = [];
|
||||
}
|
||||
}
|
18
lib/PhpParser/ErrorHandler/Throwing.php
Normal file
18
lib/PhpParser/ErrorHandler/Throwing.php
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\ErrorHandler;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
|
||||
/**
|
||||
* Error handler that handles all errors by throwing them.
|
||||
*
|
||||
* This is the default strategy used by all components.
|
||||
*/
|
||||
class Throwing implements ErrorHandler
|
||||
{
|
||||
public function handleError(Error $error) {
|
||||
throw $error;
|
||||
}
|
||||
}
|
@ -9,7 +9,6 @@ class Lexer
|
||||
{
|
||||
protected $code;
|
||||
protected $tokens;
|
||||
protected $errors;
|
||||
protected $pos;
|
||||
protected $line;
|
||||
protected $filePos;
|
||||
@ -54,13 +53,18 @@ class Lexer
|
||||
* the getErrors() method.
|
||||
*
|
||||
* @param string $code The source code to lex
|
||||
* @param ErrorHandler|null $errorHandler Error handler to use for lexing errors. Defaults to
|
||||
* ErrorHandler\Throwing
|
||||
*/
|
||||
public function startLexing($code) {
|
||||
public function startLexing($code, ErrorHandler $errorHandler = null) {
|
||||
if (null === $errorHandler) {
|
||||
$errorHandler = new ErrorHandler\Throwing();
|
||||
}
|
||||
|
||||
$this->code = $code; // keep the code around for __halt_compiler() handling
|
||||
$this->pos = -1;
|
||||
$this->line = 1;
|
||||
$this->filePos = 0;
|
||||
$this->errors = [];
|
||||
|
||||
// If inline HTML occurs without preceding code, treat it as if it had a leading newline.
|
||||
// This ensures proper composability, because having a newline is the "safe" assumption.
|
||||
@ -70,7 +74,7 @@ class Lexer
|
||||
|
||||
$this->resetErrors();
|
||||
$this->tokens = @token_get_all($code);
|
||||
$this->handleErrors();
|
||||
$this->handleErrors($errorHandler);
|
||||
|
||||
if (false !== $scream) {
|
||||
ini_set('xdebug.scream', $scream);
|
||||
@ -88,7 +92,7 @@ class Lexer
|
||||
}
|
||||
}
|
||||
|
||||
private function handleInvalidCharacterRange($start, $end, $line) {
|
||||
private function handleInvalidCharacterRange($start, $end, $line, ErrorHandler $errorHandler) {
|
||||
for ($i = $start; $i < $end; $i++) {
|
||||
$chr = $this->code[$i];
|
||||
if ($chr === 'b' || $chr === 'B') {
|
||||
@ -105,12 +109,12 @@ class Lexer
|
||||
);
|
||||
}
|
||||
|
||||
$this->errors[] = new Error($errorMsg, [
|
||||
$errorHandler->handleError(new Error($errorMsg, [
|
||||
'startLine' => $line,
|
||||
'endLine' => $line,
|
||||
'startFilePos' => $i,
|
||||
'endFilePos' => $i,
|
||||
]);
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -132,7 +136,7 @@ class Lexer
|
||||
&& false === strpos($error['message'], 'Undefined variable');
|
||||
}
|
||||
|
||||
protected function handleErrors() {
|
||||
protected function handleErrors(ErrorHandler $errorHandler) {
|
||||
if (!$this->errorMayHaveOccurred()) {
|
||||
return;
|
||||
}
|
||||
@ -151,7 +155,8 @@ class Lexer
|
||||
if (substr($this->code, $filePos, $tokenLen) !== $tokenValue) {
|
||||
// Something is missing, must be an invalid character
|
||||
$nextFilePos = strpos($this->code, $tokenValue, $filePos);
|
||||
$this->handleInvalidCharacterRange($filePos, $nextFilePos, $line);
|
||||
$this->handleInvalidCharacterRange(
|
||||
$filePos, $nextFilePos, $line, $errorHandler);
|
||||
$filePos = $nextFilePos;
|
||||
}
|
||||
|
||||
@ -163,19 +168,20 @@ class Lexer
|
||||
if (substr($this->code, $filePos, 2) === '/*') {
|
||||
// Unlike PHP, HHVM will drop unterminated comments entirely
|
||||
$comment = substr($this->code, $filePos);
|
||||
$this->errors[] = new Error('Unterminated comment', [
|
||||
$errorHandler->handleError(new Error('Unterminated comment', [
|
||||
'startLine' => $line,
|
||||
'endLine' => $line + substr_count($comment, "\n"),
|
||||
'startFilePos' => $filePos,
|
||||
'endFilePos' => $filePos + \strlen($comment),
|
||||
]);
|
||||
]));
|
||||
|
||||
// Emulate the PHP behavior
|
||||
$isDocComment = isset($comment[3]) && $comment[3] === '*';
|
||||
$this->tokens[] = [$isDocComment ? T_DOC_COMMENT : T_COMMENT, $comment, $line];
|
||||
} else {
|
||||
// Invalid characters at the end of the input
|
||||
$this->handleInvalidCharacterRange($filePos, \strlen($this->code), $line);
|
||||
$this->handleInvalidCharacterRange(
|
||||
$filePos, \strlen($this->code), $line, $errorHandler);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -183,12 +189,12 @@ class Lexer
|
||||
// Check for unterminated comment
|
||||
$lastToken = $this->tokens[count($this->tokens) - 1];
|
||||
if ($this->isUnterminatedComment($lastToken)) {
|
||||
$this->errors[] = new Error('Unterminated comment', [
|
||||
$errorHandler->handleError(new Error('Unterminated comment', [
|
||||
'startLine' => $line - substr_count($lastToken[1], "\n"),
|
||||
'endLine' => $line,
|
||||
'startFilePos' => $filePos - \strlen($lastToken[1]),
|
||||
'endFilePos' => $filePos,
|
||||
]);
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,15 +308,6 @@ class Lexer
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns errors that occurred during lexing.
|
||||
*
|
||||
* @return Error[] Array of lexer errors
|
||||
*/
|
||||
public function getErrors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles __halt_compiler() by returning the text after it.
|
||||
*
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpParser\Lexer;
|
||||
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Parser\Tokens;
|
||||
|
||||
class Emulative extends \PhpParser\Lexer
|
||||
@ -50,10 +51,10 @@ class Emulative extends \PhpParser\Lexer
|
||||
$this->tokenMap[self::T_POW_EQUAL] = Tokens::T_POW_EQUAL;
|
||||
}
|
||||
|
||||
public function startLexing($code) {
|
||||
public function startLexing($code, ErrorHandler $errorHandler = null) {
|
||||
$this->inObjectAccess = false;
|
||||
|
||||
parent::startLexing($code);
|
||||
parent::startLexing($code, $errorHandler);
|
||||
if ($this->requiresEmulation($code)) {
|
||||
$this->emulateTokens();
|
||||
}
|
||||
|
@ -4,9 +4,7 @@ namespace PhpParser;
|
||||
|
||||
class NodeTraverser implements NodeTraverserInterface
|
||||
{
|
||||
/**
|
||||
* @var NodeVisitor[] Visitors
|
||||
*/
|
||||
/** @var NodeVisitor[] Visitors */
|
||||
protected $visitors;
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpParser\NodeVisitor;
|
||||
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\NodeVisitorAbstract;
|
||||
use PhpParser\Error;
|
||||
use PhpParser\Node;
|
||||
@ -18,6 +19,18 @@ class NameResolver extends NodeVisitorAbstract
|
||||
/** @var array Map of format [aliasType => [aliasName => originalName]] */
|
||||
protected $aliases;
|
||||
|
||||
/** @var ErrorHandler Error handler */
|
||||
protected $errorHandler;
|
||||
|
||||
/**
|
||||
* Constructs a name resolution visitor.
|
||||
*
|
||||
* @param ErrorHandler|null $errorHandler Error handler
|
||||
*/
|
||||
public function __construct(ErrorHandler $errorHandler = null) {
|
||||
$this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
|
||||
}
|
||||
|
||||
public function beforeTraverse(array $nodes) {
|
||||
$this->resetState();
|
||||
}
|
||||
@ -132,13 +145,14 @@ class NameResolver extends NodeVisitorAbstract
|
||||
Stmt\Use_::TYPE_CONSTANT => 'const ',
|
||||
);
|
||||
|
||||
throw new Error(
|
||||
$this->errorHandler->handleError(new Error(
|
||||
sprintf(
|
||||
'Cannot use %s%s as %s because the name is already in use',
|
||||
$typeStringMap[$type], $name, $use->alias
|
||||
),
|
||||
$use->getLine()
|
||||
);
|
||||
$use->getAttributes()
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
$this->aliases[$type][$aliasName] = $name;
|
||||
@ -160,10 +174,10 @@ class NameResolver extends NodeVisitorAbstract
|
||||
// don't resolve special class names
|
||||
if (in_array(strtolower($name->toString()), array('self', 'parent', 'static'))) {
|
||||
if (!$name->isUnqualified()) {
|
||||
throw new Error(
|
||||
$this->errorHandler->handleError(new Error(
|
||||
sprintf("'\\%s' is an invalid class name", $name->toString()),
|
||||
$name->getLine()
|
||||
);
|
||||
$name->getAttributes()
|
||||
));
|
||||
}
|
||||
|
||||
return $name;
|
||||
|
@ -7,18 +7,11 @@ interface Parser {
|
||||
* Parses PHP code into a node tree.
|
||||
*
|
||||
* @param string $code The source code to parse
|
||||
* @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
|
||||
* to ErrorHandler\Throwing.
|
||||
*
|
||||
* @return Node[]|null Array of statements (or null if the 'throwOnError' option is disabled and the parser was
|
||||
* unable to recover from an error).
|
||||
*/
|
||||
public function parse($code);
|
||||
|
||||
/**
|
||||
* Get array of errors that occurred during the last parse.
|
||||
*
|
||||
* This method may only return multiple errors if the 'throwOnError' option is disabled.
|
||||
*
|
||||
* @return Error[]
|
||||
*/
|
||||
public function getErrors();
|
||||
public function parse($code, ErrorHandler $errorHandler = null);
|
||||
}
|
||||
|
@ -3,61 +3,52 @@
|
||||
namespace PhpParser\Parser;
|
||||
|
||||
use PhpParser\Error;
|
||||
use PhpParser\ErrorHandler;
|
||||
use PhpParser\Parser;
|
||||
|
||||
class Multiple implements Parser {
|
||||
/** @var Parser[] List of parsers to try, in order of preference */
|
||||
private $parsers;
|
||||
/** @var Error[] Errors collected during last parse */
|
||||
private $errors;
|
||||
|
||||
/**
|
||||
* Create a parser which will try multiple parsers in an order of preference.
|
||||
*
|
||||
* Parsers will be invoked in the order they're provided to the constructor. If one of the
|
||||
* parsers runs without errors, it's output is returned. Otherwise the errors (and
|
||||
* PhpParser\Error exception) of the first parser are used.
|
||||
* parsers runs without throwing, it's output is returned. Otherwise the exception that the
|
||||
* first parser generated is thrown.
|
||||
*
|
||||
* @param Parser[] $parsers
|
||||
*/
|
||||
public function __construct(array $parsers) {
|
||||
$this->parsers = $parsers;
|
||||
$this->errors = [];
|
||||
}
|
||||
|
||||
public function parse($code) {
|
||||
list($firstStmts, $firstErrors, $firstError) = $this->tryParse($this->parsers[0], $code);
|
||||
if ($firstErrors === []) {
|
||||
$this->errors = [];
|
||||
public function parse($code, ErrorHandler $errorHandler = null) {
|
||||
if (null === $errorHandler) {
|
||||
$errorHandler = new ErrorHandler\Throwing;
|
||||
}
|
||||
|
||||
list($firstStmts, $firstError) = $this->tryParse($this->parsers[0], $errorHandler, $code);
|
||||
if ($firstError === null) {
|
||||
return $firstStmts;
|
||||
}
|
||||
|
||||
for ($i = 1, $c = count($this->parsers); $i < $c; ++$i) {
|
||||
list($stmts, $errors) = $this->tryParse($this->parsers[$i], $code);
|
||||
if ($errors === []) {
|
||||
$this->errors = [];
|
||||
list($stmts, $error) = $this->tryParse($this->parsers[$i], $errorHandler, $code);
|
||||
if ($error === null) {
|
||||
return $stmts;
|
||||
}
|
||||
}
|
||||
|
||||
$this->errors = $firstErrors;
|
||||
if ($firstError) {
|
||||
throw $firstError;
|
||||
}
|
||||
return $firstStmts;
|
||||
throw $firstError;
|
||||
}
|
||||
|
||||
public function getErrors() {
|
||||
return $this->errors;
|
||||
}
|
||||
|
||||
private function tryParse(Parser $parser, $code) {
|
||||
private function tryParse(Parser $parser, ErrorHandler $errorHandler, $code) {
|
||||
$stmts = null;
|
||||
$error = null;
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
$stmts = $parser->parse($code, $errorHandler);
|
||||
} catch (Error $error) {}
|
||||
$errors = $parser->getErrors();
|
||||
return [$stmts, $errors, $error];
|
||||
return [$stmts, $error];
|
||||
}
|
||||
}
|
||||
|
@ -103,8 +103,8 @@ abstract class ParserAbstract implements Parser
|
||||
/** @var array Start attributes of last *read* token */
|
||||
protected $lookaheadStartAttributes;
|
||||
|
||||
/** @var bool Whether to throw on first error */
|
||||
protected $throwOnError;
|
||||
/** @var ErrorHandler Error handler */
|
||||
protected $errorHandler;
|
||||
/** @var Error[] Errors collected during last parse */
|
||||
protected $errors;
|
||||
/** @var int Error state, used to avoid error floods */
|
||||
@ -114,42 +114,36 @@ abstract class ParserAbstract implements Parser
|
||||
* Creates a parser instance.
|
||||
*
|
||||
* @param Lexer $lexer A lexer
|
||||
* @param array $options Options array. The boolean 'throwOnError' option determines whether an exception should be
|
||||
* thrown on first error, or if the parser should try to continue parsing the remaining code
|
||||
* and build a partial AST.
|
||||
* @param array $options Options array. Currently no options are supported.
|
||||
*/
|
||||
public function __construct(Lexer $lexer, array $options = array()) {
|
||||
$this->lexer = $lexer;
|
||||
$this->errors = array();
|
||||
$this->throwOnError = isset($options['throwOnError']) ? $options['throwOnError'] : true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get array of errors that occurred during the last parse.
|
||||
*
|
||||
* This method may only return multiple errors if the 'throwOnError' option is disabled.
|
||||
*
|
||||
* @return Error[]
|
||||
*/
|
||||
public function getErrors() {
|
||||
return $this->errors;
|
||||
if (isset($options['throwOnError'])) {
|
||||
throw new \LogicException(
|
||||
'"throwOnError" is no longer supported, use "errorHandler" instead');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses PHP code into a node tree.
|
||||
*
|
||||
* If a non-throwing error handler is used, the parser will continue parsing after an error
|
||||
* occurred and attempt to build a partial AST.
|
||||
*
|
||||
* @param string $code The source code to parse
|
||||
* @param ErrorHandler|null $errorHandler Error handler to use for lexer/parser errors, defaults
|
||||
* to ErrorHandler\Throwing.
|
||||
*
|
||||
* @return Node[]|null Array of statements (or null if the 'throwOnError' option is disabled and the parser was
|
||||
* unable to recover from an error).
|
||||
*/
|
||||
public function parse($code) {
|
||||
// Initialize the lexer and inherit lexing errors
|
||||
$this->lexer->startLexing($code);
|
||||
$this->errors = $this->lexer->getErrors();
|
||||
if ($this->throwOnError && !empty($this->errors)) {
|
||||
throw $this->errors[0];
|
||||
}
|
||||
public function parse($code, ErrorHandler $errorHandler = null) {
|
||||
$this->errorHandler = $errorHandler ?: new ErrorHandler\Throwing;
|
||||
|
||||
// Initialize the lexer
|
||||
$this->lexer->startLexing($code, $this->errorHandler);
|
||||
|
||||
// We start off with no lookahead-token
|
||||
$symbol = self::SYMBOL_NONE;
|
||||
@ -346,10 +340,7 @@ abstract class ParserAbstract implements Parser
|
||||
}
|
||||
|
||||
protected function emitError(Error $error) {
|
||||
$this->errors[] = $error;
|
||||
if ($this->throwOnError) {
|
||||
throw $error;
|
||||
}
|
||||
$this->errorHandler->handleError($error);
|
||||
}
|
||||
|
||||
protected function getErrorMessage($symbol, $state) {
|
||||
|
@ -15,12 +15,8 @@ class CodeParsingTest extends CodeTestAbstract
|
||||
$lexer = new Lexer\Emulative(array('usedAttributes' => array(
|
||||
'startLine', 'endLine', 'startFilePos', 'endFilePos', 'comments'
|
||||
)));
|
||||
$parser5 = new Parser\Php5($lexer, array(
|
||||
'throwOnError' => false,
|
||||
));
|
||||
$parser7 = new Parser\Php7($lexer, array(
|
||||
'throwOnError' => false,
|
||||
));
|
||||
$parser5 = new Parser\Php5($lexer);
|
||||
$parser7 = new Parser\Php7($lexer);
|
||||
|
||||
$output5 = $this->getParseOutput($parser5, $code);
|
||||
$output7 = $this->getParseOutput($parser7, $code);
|
||||
@ -38,11 +34,11 @@ class CodeParsingTest extends CodeTestAbstract
|
||||
}
|
||||
|
||||
private function getParseOutput(Parser $parser, $code) {
|
||||
$stmts = $parser->parse($code);
|
||||
$errors = $parser->getErrors();
|
||||
$errors = new ErrorHandler\Collecting;
|
||||
$stmts = $parser->parse($code, $errors);
|
||||
|
||||
$output = '';
|
||||
foreach ($errors as $error) {
|
||||
foreach ($errors->getErrors() as $error) {
|
||||
$output .= $this->formatErrorMessage($error, $code) . "\n";
|
||||
}
|
||||
|
||||
|
22
test/PhpParser/ErrorHandler/CollectingTest.php
Normal file
22
test/PhpParser/ErrorHandler/CollectingTest.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\ErrorHandler;
|
||||
|
||||
use PhpParser\Error;
|
||||
|
||||
class CollectingTest extends \PHPUnit_Framework_TestCase {
|
||||
public function testHandleError() {
|
||||
$errorHandler = new Collecting();
|
||||
$this->assertFalse($errorHandler->hasErrors());
|
||||
$this->assertEmpty($errorHandler->getErrors());
|
||||
|
||||
$errorHandler->handleError($e1 = new Error('Test 1'));
|
||||
$errorHandler->handleError($e2 = new Error('Test 2'));
|
||||
$this->assertTrue($errorHandler->hasErrors());
|
||||
$this->assertSame([$e1, $e2], $errorHandler->getErrors());
|
||||
|
||||
$errorHandler->clearErrors();
|
||||
$this->assertFalse($errorHandler->hasErrors());
|
||||
$this->assertEmpty($errorHandler->getErrors());
|
||||
}
|
||||
}
|
16
test/PhpParser/ErrorHandler/ThrowingTest.php
Normal file
16
test/PhpParser/ErrorHandler/ThrowingTest.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace PhpParser\ErrorHandler;
|
||||
|
||||
use PhpParser\Error;
|
||||
|
||||
class ThrowingTest extends \PHPUnit_Framework_TestCase {
|
||||
/**
|
||||
* @expectedException \PhpParser\Error
|
||||
* @expectedExceptionMessage Test
|
||||
*/
|
||||
public function testHandleError() {
|
||||
$errorHandler = new Throwing();
|
||||
$errorHandler->handleError(new Error('Test'));
|
||||
}
|
||||
}
|
@ -19,11 +19,12 @@ class LexerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->markTestSkipped('HHVM does not throw warnings from token_get_all()');
|
||||
}
|
||||
|
||||
$errorHandler = new ErrorHandler\Collecting();
|
||||
$lexer = $this->getLexer(['usedAttributes' => [
|
||||
'comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'
|
||||
]]);
|
||||
$lexer->startLexing($code);
|
||||
$errors = $lexer->getErrors();
|
||||
$lexer->startLexing($code, $errorHandler);
|
||||
$errors = $errorHandler->getErrors();
|
||||
|
||||
$this->assertSame(count($messages), count($errors));
|
||||
for ($i = 0; $i < count($messages); $i++) {
|
||||
|
@ -30,7 +30,6 @@ class MultipleTest extends ParserTest {
|
||||
/** @dataProvider provideTestParse */
|
||||
public function testParse($code, Multiple $parser, $expected) {
|
||||
$this->assertEquals($expected, $parser->parse($code));
|
||||
$this->assertSame([], $parser->getErrors());
|
||||
}
|
||||
|
||||
public function provideTestParse() {
|
||||
@ -92,22 +91,4 @@ class MultipleTest extends ParserTest {
|
||||
$parser = new Multiple([$parserA, $parserB]);
|
||||
$parser->parse('dummy');
|
||||
}
|
||||
|
||||
public function testGetErrors() {
|
||||
$errorsA = [new Error('A1'), new Error('A2')];
|
||||
$parserA = $this->getMockBuilder('PhpParser\Parser')->getMock();
|
||||
$parserA->expects($this->at(0))->method('parse');
|
||||
$parserA->expects($this->at(1))
|
||||
->method('getErrors')->will($this->returnValue($errorsA));
|
||||
|
||||
$errorsB = [new Error('B1'), new Error('B2')];
|
||||
$parserB = $this->getMockBuilder('PhpParser\Parser')->getMock();
|
||||
$parserB->expects($this->at(0))->method('parse');
|
||||
$parserB->expects($this->at(1))
|
||||
->method('getErrors')->will($this->returnValue($errorsB));
|
||||
|
||||
$parser = new Multiple([$parserA, $parserB]);
|
||||
$parser->parse('dummy');
|
||||
$this->assertSame($errorsA, $parser->getErrors());
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user