From 23bf4f0c13dc196efbd2027987afbda63c47462a Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Thu, 30 Apr 2015 21:58:45 +0200 Subject: [PATCH] Add some error documentation --- CHANGELOG.md | 22 +++++++++- doc/component/Error.markdown | 78 ++++++++++++++++++++++++++++++++++++ grammar/README.md | 7 ++-- test/PhpParser/ErrorTest.php | 19 +++++++-- 4 files changed, 116 insertions(+), 10 deletions(-) create mode 100644 doc/component/Error.markdown diff --git a/CHANGELOG.md b/CHANGELOG.md index c97ec5a6..32e4e59d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,25 @@ -Version 1.2.3-dev +Version 1.3.0-dev ----------------- -Nothing yet. +### Added + +* Errors can now store the attributes of the node/token where the error occurred. Previously only the start line was + stored. +* If file positions are enabled in the lexer, errors can now provide column information if it is available. + TODO/link +* The parser now provides an experimental error recovery mode, which can be enabled by disabling the `throwOnError` + parser option. In this mode the parser will try to construct a partial AST even if the code is not valid PHP. + TODO/link +* Added support for PHP 7 `yield from` expression. It is represented by `Expr\YieldFrom`. +* Added support for PHP 7 anonymous classes. These are represented by ordinary `Stmt\Class_` nodes with the name set to + `null`. Furthermore this implies that `Expr\New_` can now contain a `Stmt\Class_` in its `class` subnode. + + +### Fixed + +* Fixed registration of PHP 7 aliases, for the case where the old name was used before the new name. +* Fixed handling of precedence when pretty-printing `print` expressions. +* Checks for special class names like `self` are now case-insensitive. Version 1.2.2 (2015-04-03) -------------------------- diff --git a/doc/component/Error.markdown b/doc/component/Error.markdown new file mode 100644 index 00000000..3085272e --- /dev/null +++ b/doc/component/Error.markdown @@ -0,0 +1,78 @@ +Error handling +============== + +Errors during parsing or analysis are represented using the `PhpParser\Error` exception class. In addition to an error +message, an error can also store additional information about the location the error occurred at. + +How much location information is available depends on the origin of the error and how many lexer attributes have been +enabled. At a minimum the start line of the error is usually available. + +Column information +------------------ + +In order to receive information about not only the line, but also the column span an error occurred at, the file +position attributes in the lexer need to be enabled: + +```php +$lexer = new PhpParser\Lexer(array( + 'usedAttributes' => array('comments', 'startLine', 'endLine', 'startFilePos', 'endFilePos'), +)); +$parser = new PhpParser\Parser($lexer); + +try { + $stmts = $parser->parse($code); + // ... +} catch (PhpParser\Error $e) { + // ... +} +``` + +Before using column information it's availability needs to be checked with `$e->hasColumnInfo()`, as the precise +location of an error cannot always be determined. The methods for retrieving column information also have to be passed +the source code of the parsed file. An example for printing an error: + +```php +if ($e->hasColumnInfo()) { + echo $e->getRawMessage() . ' from ' . $e->getStartLine() . ':' . $e->getStartColumn($code) + . ' to ' . $e->getEndLine() . ':' . $e->getEndColumn($code); +} else { + echo $e->getMessage(); +} +``` + +Both line numbers and column numbers are 1-based. EOF errors will be positioned at the position one past the end of the +file. + +Error recovery +-------------- + +> **EXPERIMENTAL** + +By default the parser will stop throw an exception upon encountering the first error during parsing. The parser also +supports an alternative mode, in which the parser will remember the error, but try to continue parsing the rest of the +source code. + +To enable this mode the `throwOnError` parser option needs to be disabled. Any errors that occurred during parsing can +then be retrieved using `$parser->getErrors()`. The `$parser->parse()` method will either return a partial syntax tree +or `null` if recovery fails. + +A usage example: + +```php +$parser = new PhpParser\Parser(new PhpParser\Lexer, array( + 'throwOnError' => false, +)); + +$stmts = $parser->parse($code); +$errors = $parser->getErrors(); + +foreach ($errors as $error) { + // $error is an ordinary PhpParser\Error +} + +if (null !== $stmts) { + // $stmts is a best-effort partial AST +} +``` + +The error recovery implementation is experimental -- it currently won't be able to recover from many types of errors. diff --git a/grammar/README.md b/grammar/README.md index 4bb3e368..909f7c1a 100644 --- a/grammar/README.md +++ b/grammar/README.md @@ -13,8 +13,7 @@ The `.phpy` file is a normal grammer in `kmyacc` (`yacc`) style, with some trans applied to it: * Nodes are created using the syntax `Name[..., ...]`. This is transformed into - `new Node\Name(..., ..., $attributes)` - * `Name::abc` is transformed to `Node\Name::abc` + `new Name(..., ..., attributes())` * Some function-like constructs are resolved (see `rebuildParser.php` for a list) * Associative arrays are written as `[key: value, ...]`, which is transformed to `array('key' => value, ...)` @@ -25,5 +24,5 @@ Building the parser In order to rebuild the parser, you need [moriyoshi's fork of kmyacc](https://github.com/moriyoshi/kmyacc-forked). After you compiled/installed it, run the `rebuildParser.php` script. -By default only the `Parser.php` is built. If you want to additionally build `Parser/Debug.php` and `y.output` run the -script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`. \ No newline at end of file +By default only the `Parser.php` is built. If you want to additionally emit debug symbols and create `y.output`, run the +script with `--debug`. If you want to retain the preprocessed grammar pass `--keep-tmp-grammar`. diff --git a/test/PhpParser/ErrorTest.php b/test/PhpParser/ErrorTest.php index 781eeb3a..9b158921 100644 --- a/test/PhpParser/ErrorTest.php +++ b/test/PhpParser/ErrorTest.php @@ -5,9 +5,14 @@ namespace PhpParser; class ErrorTest extends \PHPUnit_Framework_TestCase { public function testConstruct() { - $error = new Error('Some error', 10); + $error = new Error('Some error', array( + 'startLine' => 10, + 'endLine' => 11, + )); $this->assertSame('Some error', $error->getRawMessage()); + $this->assertSame(10, $error->getStartLine()); + $this->assertSame(11, $error->getEndLine()); $this->assertSame(10, $error->getRawLine()); $this->assertSame('Some error on line 10', $error->getMessage()); @@ -19,16 +24,22 @@ class ErrorTest extends \PHPUnit_Framework_TestCase */ public function testSetMessageAndLine(Error $error) { $error->setRawMessage('Some other error'); - $error->setRawLine(15); - $this->assertSame('Some other error', $error->getRawMessage()); - $this->assertSame(15, $error->getRawLine()); + + $error->setStartLine(15); + $this->assertSame(15, $error->getStartLine()); $this->assertSame('Some other error on line 15', $error->getMessage()); + + $error->setStartLine(17); + $this->assertSame(17, $error->getRawLine()); + $this->assertSame('Some other error on line 17', $error->getMessage()); } public function testUnknownLine() { $error = new Error('Some error'); + $this->assertSame(-1, $error->getStartLine()); + $this->assertSame(-1, $error->getEndLine()); $this->assertSame(-1, $error->getRawLine()); $this->assertSame('Some error on unknown line', $error->getMessage()); }