mirror of
https://github.com/danog/PHP-Parser.git
synced 2024-11-26 20:04:48 +01:00
added column-numbers to syntax errors
This commit is contained in:
parent
e4eab9ec0c
commit
33a39fae06
@ -10,7 +10,7 @@ ini_set('xdebug.var_display_max_children', -1);
|
||||
ini_set('xdebug.var_display_max_data', -1);
|
||||
ini_set('xdebug.var_display_max_depth', -1);
|
||||
|
||||
list($operations, $files) = parseArgs($argv);
|
||||
list($operations, $files, $attributes) = parseArgs($argv);
|
||||
|
||||
/* Dump nodes by default */
|
||||
if (empty($operations)) {
|
||||
@ -45,7 +45,16 @@ foreach ($files as $file) {
|
||||
try {
|
||||
$stmts = $parser->parse($code);
|
||||
} catch (PhpParser\Error $e) {
|
||||
die("==> Parse Error: {$e->getMessage()}\n");
|
||||
|
||||
$message = $e->getMessage();
|
||||
|
||||
if($attributes['with-column-info'] && $e->hasTokenAttributes()){
|
||||
$beginColumn = $e->getBeginColumn();
|
||||
$endColumn = $e->getEndColumn();
|
||||
$message .= ", column {$beginColumn} to {$endColumn}";
|
||||
}
|
||||
|
||||
die($message . "\n");
|
||||
}
|
||||
|
||||
foreach ($operations as $operation) {
|
||||
@ -81,11 +90,12 @@ The file arguments can also be replaced with a code string:
|
||||
|
||||
Operations is a list of the following options (--dump by default):
|
||||
|
||||
--dump -d Dump nodes using NodeDumper
|
||||
--pretty-print -p Pretty print file using PrettyPrinter\Standard
|
||||
--serialize-xml Serialize nodes using Serializer\XML
|
||||
--var-dump var_dump() nodes (for exact structure)
|
||||
--resolve-names -N Resolve names using NodeVisitor\NameResolver
|
||||
--dump -d Dump nodes using NodeDumper
|
||||
--pretty-print -p Pretty print file using PrettyPrinter\Standard
|
||||
--serialize-xml Serialize nodes using Serializer\XML
|
||||
--var-dump var_dump() nodes (for exact structure)
|
||||
--resolve-names -N Resolve names using NodeVisitor\NameResolver
|
||||
--with-column-info -c Show column-numbers for occoured errors. (if any)
|
||||
|
||||
Example:
|
||||
|
||||
@ -101,6 +111,9 @@ OUTPUT
|
||||
function parseArgs($args) {
|
||||
$operations = array();
|
||||
$files = array();
|
||||
$attributes = array(
|
||||
'with-column-info' => false,
|
||||
);
|
||||
|
||||
array_shift($args);
|
||||
$parseOptions = true;
|
||||
@ -129,6 +142,10 @@ function parseArgs($args) {
|
||||
case '-N';
|
||||
$operations[] = 'resolve-names';
|
||||
break;
|
||||
case '--with-column-info':
|
||||
case '-c';
|
||||
$attributes['with-column-info'] = true;
|
||||
break;
|
||||
case '--':
|
||||
$parseOptions = false;
|
||||
break;
|
||||
@ -141,5 +158,5 @@ function parseArgs($args) {
|
||||
}
|
||||
}
|
||||
|
||||
return array($operations, $files);
|
||||
return array($operations, $files, $attributes);
|
||||
}
|
||||
|
@ -6,16 +6,23 @@ class Error extends \RuntimeException
|
||||
{
|
||||
protected $rawMessage;
|
||||
protected $rawLine;
|
||||
protected $tokens = array();
|
||||
protected $tokenIndex;
|
||||
protected $beginColumnCache;
|
||||
|
||||
/**
|
||||
* Creates an Exception signifying a parse error.
|
||||
*
|
||||
* @param string $message Error message
|
||||
* @param int $line Error line in PHP file
|
||||
* @param string $message Error message
|
||||
* @param int $line Error line in PHP file
|
||||
* @param array $tokens Array of all tokens in the file that caused the error.
|
||||
* @param int $tokenIndex Index in $tokens of token where error happened.
|
||||
*/
|
||||
public function __construct($message, $line = -1) {
|
||||
public function __construct($message, $line = -1, array $tokens=array(), $tokenIndex=null) {
|
||||
$this->rawMessage = (string) $message;
|
||||
$this->rawLine = (int) $line;
|
||||
$this->tokens = $tokens;
|
||||
$this->tokenIndex = $tokenIndex;
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
@ -57,6 +64,104 @@ class Error extends \RuntimeException
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if valid token-information is available for this error.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTokenAttributes(){
|
||||
return is_numeric($this->tokenIndex) && isset($this->tokens[(int)$this->tokenIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tokens for the php-file in which this error happened.
|
||||
* Only works if token-information was provided.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getTokens(){
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sthe index in tokens in which this error happened.
|
||||
* Only works if token-information was provided.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getTokenIndex(){
|
||||
return $this->tokenIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first column number in which the error happened.
|
||||
* Only works if token-information was provided.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getBeginColumn() {
|
||||
$beginColumn = null;
|
||||
|
||||
if($this->hasTokenAttributes()) {
|
||||
|
||||
if(!is_null($this->beginColumnCache)) {
|
||||
$beginColumn = $this->beginColumnCache;
|
||||
|
||||
} else {
|
||||
$beginColumn = 0;
|
||||
$tokenIndex = $this->tokenIndex;
|
||||
for($i=$tokenIndex-1;$i>=0;$i--) {
|
||||
$tokenText = $this->getTextFromToken($this->tokens[$i]);
|
||||
|
||||
$beginColumn += strlen($tokenText);
|
||||
|
||||
$newlinePosition = strrpos($tokenText, "\n");
|
||||
if($newlinePosition !== false){
|
||||
$beginColumn -= $newlinePosition;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $beginColumn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last column number in which the error happened.
|
||||
* Only works if token-information was provided.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getEndColumn(){
|
||||
$endColumn = null;
|
||||
|
||||
if($this->hasTokenAttributes()){
|
||||
$beginColumn = $this->getBeginColumn();
|
||||
$token = $this->tokens[(int)$this->tokenIndex];
|
||||
$tokenText = $this->getTextFromToken($token);
|
||||
$endColumn = $beginColumn + strlen($tokenText);
|
||||
}
|
||||
|
||||
return $endColumn;
|
||||
}
|
||||
|
||||
private function getTextFromToken($token){
|
||||
|
||||
$tokenText = $token;
|
||||
if(is_array($tokenText)){
|
||||
if(is_int($tokenText[0])){
|
||||
$tokenText = $tokenText[1];
|
||||
|
||||
}else{
|
||||
$tokenText = $tokenText[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $tokenText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the exception message after a change to rawMessage or rawLine.
|
||||
*/
|
||||
@ -68,5 +173,6 @@ class Error extends \RuntimeException
|
||||
} else {
|
||||
$this->message .= ' on line ' . $this->rawLine;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,6 +212,15 @@ class Lexer
|
||||
return $this->tokens;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the current token-index (position in tokens).
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getPosition() {
|
||||
return $this->pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles __halt_compiler() by returning the text after it.
|
||||
*
|
||||
|
@ -239,9 +239,14 @@ abstract class ParserAbstract
|
||||
$expectedString = '';
|
||||
}
|
||||
|
||||
$tokens = $this->lexer->getTokens();
|
||||
$errorPosition = $this->lexer->getPosition();
|
||||
|
||||
throw new Error(
|
||||
'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString,
|
||||
$startAttributes['startLine']
|
||||
$startAttributes['startLine'],
|
||||
$tokens,
|
||||
$errorPosition
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +32,33 @@ class ErrorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame(-1, $error->getRawLine());
|
||||
$this->assertSame('Some error on unknown line', $error->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testConstruct
|
||||
*/
|
||||
public function testColumnNumbers() {
|
||||
|
||||
$faultyCode = "<?php \$foo = bar baz; ?>";
|
||||
|
||||
$tokens = token_get_all($faultyCode);
|
||||
|
||||
$error = new Error('Some error', 1, $tokens, 5);
|
||||
|
||||
$this->assertSame(true, $error->hasTokenAttributes());
|
||||
$this->assertSame(13, $error->getBeginColumn());
|
||||
$this->assertSame(16, $error->getEndColumn());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testConstruct
|
||||
*/
|
||||
public function testTokenInformationMissing(){
|
||||
|
||||
$error = new Error('Some error', 3);
|
||||
|
||||
$this->assertSame(false, $error->hasTokenAttributes());
|
||||
$this->assertSame(null, $error->getBeginColumn());
|
||||
$this->assertSame(null, $error->getEndColumn());
|
||||
}
|
||||
}
|
||||
|
@ -3,4 +3,4 @@ New without a class
|
||||
<?php
|
||||
new;
|
||||
-----
|
||||
Syntax error, unexpected ';' on line 2
|
||||
Syntax error, unexpected ';' on line 2
|
||||
|
Loading…
Reference in New Issue
Block a user