mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2025-01-22 05:11:39 +01:00
Make Error column info attribute based
This commit is contained in:
parent
33a39fae06
commit
4defbc2174
@ -5,24 +5,22 @@ namespace PhpParser;
|
||||
class Error extends \RuntimeException
|
||||
{
|
||||
protected $rawMessage;
|
||||
protected $rawLine;
|
||||
protected $tokens = array();
|
||||
protected $tokenIndex;
|
||||
protected $beginColumnCache;
|
||||
protected $attributes;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param string $message Error message
|
||||
* @param array|int $attributes Attributes of node/token where error occurred
|
||||
* (or start line of error -- deprecated)
|
||||
*/
|
||||
public function __construct($message, $line = -1, array $tokens=array(), $tokenIndex=null) {
|
||||
public function __construct($message, $attributes = array()) {
|
||||
$this->rawMessage = (string) $message;
|
||||
$this->rawLine = (int) $line;
|
||||
$this->tokens = $tokens;
|
||||
$this->tokenIndex = $tokenIndex;
|
||||
if (is_array($attributes)) {
|
||||
$this->attributes = $attributes;
|
||||
} else {
|
||||
$this->attributes = array('startLine' => $attributes);
|
||||
}
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
@ -35,6 +33,34 @@ class Error extends \RuntimeException
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line the error starts in.
|
||||
*
|
||||
* @return int Error start line
|
||||
*/
|
||||
public function getStartLine() {
|
||||
return isset($this->attributes['startLine']) ? $this->attributes['startLine'] : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the line the error ends in.
|
||||
*
|
||||
* @return int Error end line
|
||||
*/
|
||||
public function getEndLine() {
|
||||
return isset($this->attributes['endLine']) ? $this->attributes['endLine'] : -1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the attributes of the node/token the error occurred at.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes() {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line of the PHP file the error occurred in.
|
||||
*
|
||||
@ -46,120 +72,65 @@ class Error extends \RuntimeException
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the error line in the PHP file.
|
||||
* Sets the line the error starts in.
|
||||
*
|
||||
* @return int Error line in the PHP file
|
||||
* @param int $line Error start line
|
||||
*/
|
||||
public function getRawLine() {
|
||||
return $this->rawLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the line of the PHP file the error occurred in.
|
||||
*
|
||||
* @param int $line Error line in the PHP file
|
||||
*/
|
||||
public function setRawLine($line) {
|
||||
$this->rawLine = (int) $line;
|
||||
public function setStartLine($line) {
|
||||
$this->attributes['startLine'] = (int) $line;
|
||||
$this->updateMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if valid token-information is available for this error.
|
||||
* Returns whether the error has start and end column information.
|
||||
*
|
||||
* For column information enable the startFilePos and endFilePos in the lexer options.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasTokenAttributes(){
|
||||
return is_numeric($this->tokenIndex) && isset($this->tokens[(int)$this->tokenIndex]);
|
||||
public function hasColumnInfo() {
|
||||
return isset($this->attributes['startFilePos']) && isset($this->attributes['endFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Gets the start column (0-based) into the line where the error started.
|
||||
*
|
||||
* @param string $code Source code of the file
|
||||
* @return int
|
||||
*/
|
||||
public function getTokenIndex(){
|
||||
return $this->tokenIndex;
|
||||
public function getStartColumn($code) {
|
||||
if (!$this->hasColumnInfo()) {
|
||||
throw new \RuntimeException('Error does not have column information');
|
||||
}
|
||||
|
||||
return $this->toColumn($code, $this->attributes['startFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first column number in which the error happened.
|
||||
* Only works if token-information was provided.
|
||||
* Gets the end column (0-based) into the line where the error ended.
|
||||
*
|
||||
* @param string $code Source code of the file
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getEndColumn($code) {
|
||||
if (!$this->hasColumnInfo()) {
|
||||
throw new \RuntimeException('Error does not have column information');
|
||||
}
|
||||
|
||||
return $beginColumn;
|
||||
return $this->toColumn($code, $this->attributes['endFilePos']);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
private function toColumn($code, $pos) {
|
||||
if ($pos >= strlen($code)) {
|
||||
throw new \RuntimeException('Invalid position information');
|
||||
}
|
||||
|
||||
return $endColumn;
|
||||
}
|
||||
|
||||
private function getTextFromToken($token){
|
||||
|
||||
$tokenText = $token;
|
||||
if(is_array($tokenText)){
|
||||
if(is_int($tokenText[0])){
|
||||
$tokenText = $tokenText[1];
|
||||
|
||||
}else{
|
||||
$tokenText = $tokenText[0];
|
||||
}
|
||||
$lineStartPos = strrpos($code, "\n", $pos - strlen($code));
|
||||
if (false === $lineStartPos) {
|
||||
$lineStartPos = -1;
|
||||
}
|
||||
|
||||
return $tokenText;
|
||||
return $pos - $lineStartPos - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -168,11 +139,20 @@ class Error extends \RuntimeException
|
||||
protected function updateMessage() {
|
||||
$this->message = $this->rawMessage;
|
||||
|
||||
if (-1 === $this->rawLine) {
|
||||
if (-1 === $this->getStartLine()) {
|
||||
$this->message .= ' on unknown line';
|
||||
} else {
|
||||
$this->message .= ' on line ' . $this->rawLine;
|
||||
$this->message .= ' on line ' . $this->getStartLine();
|
||||
}
|
||||
}
|
||||
|
||||
/** @deprecated Use getStartLine() instead */
|
||||
public function getRawLine() {
|
||||
return $this->getStartLine();
|
||||
}
|
||||
|
||||
/** @deprecated Use setStartLine() instead */
|
||||
public function setRawLine($line) {
|
||||
$this->setStartLine($line);
|
||||
}
|
||||
}
|
||||
|
@ -205,8 +205,8 @@ abstract class ParserAbstract
|
||||
+ $endAttributes
|
||||
);
|
||||
} catch (Error $e) {
|
||||
if (-1 === $e->getRawLine() && isset($startAttributes['startLine'])) {
|
||||
$e->setRawLine($startAttributes['startLine']);
|
||||
if (-1 === $e->getStartLine() && isset($startAttributes['startLine'])) {
|
||||
$e->setStartLine($startAttributes['startLine']);
|
||||
}
|
||||
|
||||
throw $e;
|
||||
@ -239,14 +239,9 @@ abstract class ParserAbstract
|
||||
$expectedString = '';
|
||||
}
|
||||
|
||||
$tokens = $this->lexer->getTokens();
|
||||
$errorPosition = $this->lexer->getPosition();
|
||||
|
||||
throw new Error(
|
||||
'Syntax error, unexpected ' . $this->symbolToName[$symbol] . $expectedString,
|
||||
$startAttributes['startLine'],
|
||||
$tokens,
|
||||
$errorPosition
|
||||
$startAttributes + $endAttributes
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -33,32 +33,55 @@ class ErrorTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertSame('Some error on unknown line', $error->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testConstruct
|
||||
*/
|
||||
public function testColumnNumbers() {
|
||||
/** @dataProvider provideTestColumnInfo */
|
||||
public function testColumnInfo($code, $startPos, $endPos, $startColumn, $endColumn) {
|
||||
$error = new Error('Some error', array(
|
||||
'startFilePos' => $startPos,
|
||||
'endFilePos' => $endPos,
|
||||
));
|
||||
|
||||
$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());
|
||||
$this->assertSame(true, $error->hasColumnInfo());
|
||||
$this->assertSame($startColumn, $error->getStartColumn($code));
|
||||
$this->assertSame($endColumn, $error->getEndColumn($code));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testConstruct
|
||||
*/
|
||||
public function testTokenInformationMissing(){
|
||||
public function provideTestColumnInfo() {
|
||||
return array(
|
||||
// Error at "bar"
|
||||
array("<?php foo bar baz", 10, 12, 10, 12),
|
||||
array("<?php\nfoo bar baz", 10, 12, 4, 6),
|
||||
array("<?php foo\nbar baz", 10, 12, 0, 2),
|
||||
array("<?php foo bar\nbaz", 10, 12, 10, 12),
|
||||
array("<?php\r\nfoo bar baz", 11, 13, 4, 6),
|
||||
// Error at "baz"
|
||||
array("<?php foo bar baz", 14, 16, 14, 16),
|
||||
array("<?php foo bar\nbaz", 14, 16, 0, 2),
|
||||
// Error at string literal
|
||||
array("<?php foo 'bar\nbaz' xyz", 10, 18, 10, 3),
|
||||
array("<?php\nfoo 'bar\nbaz' xyz", 10, 18, 4, 3),
|
||||
array("<?php foo\n'\nbarbaz\n'\nxyz", 10, 19, 0, 0),
|
||||
// Error over full string
|
||||
array("<?php", 0, 4, 0, 4),
|
||||
array("<?\nphp", 0, 5, 0, 2),
|
||||
);
|
||||
}
|
||||
|
||||
public function testNoColumnInfo(){
|
||||
$error = new Error('Some error', 3);
|
||||
|
||||
$this->assertSame(false, $error->hasTokenAttributes());
|
||||
$this->assertSame(null, $error->getBeginColumn());
|
||||
$this->assertSame(null, $error->getEndColumn());
|
||||
$this->assertSame(false, $error->hasColumnInfo());
|
||||
try {
|
||||
$error->getStartColumn('');
|
||||
$this->fail('Expected RuntimeException');
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->assertEquals('Error does not have column information', $e->getMessage());
|
||||
}
|
||||
try {
|
||||
$error->getEndColumn('');
|
||||
$this->fail('Expected RuntimeException');
|
||||
} catch (\RuntimeException $e) {
|
||||
$this->assertEquals('Error does not have column information', $e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user