1
0
mirror of https://github.com/danog/PHP-Parser.git synced 2024-11-30 04:19:30 +01:00

added column-numbers to syntax errors

This commit is contained in:
Gerrit Addiks 2015-04-14 20:31:06 +02:00 committed by Nikita Popov
parent e4eab9ec0c
commit 33a39fae06
6 changed files with 181 additions and 15 deletions

View File

@ -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) {
@ -86,6 +95,7 @@ Operations is a list of the following options (--dump by default):
--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);
}

View File

@ -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 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;
}
}
}

View File

@ -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.
*

View File

@ -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
);
}
}

View File

@ -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());
}
}