2017-11-02 18:56:01 +01:00
|
|
|
<?php declare(strict_types=1);
|
2015-06-20 11:43:16 +02:00
|
|
|
|
|
|
|
namespace PhpParser;
|
|
|
|
|
2017-11-04 18:09:20 +01:00
|
|
|
use PhpParser\Node\Expr;
|
|
|
|
use PhpParser\Node\Stmt;
|
|
|
|
|
2015-06-20 11:43:16 +02:00
|
|
|
class CodeParsingTest extends CodeTestAbstract
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* @dataProvider provideTestParse
|
|
|
|
*/
|
2016-11-23 22:36:48 +01:00
|
|
|
public function testParse($name, $code, $expected, $modeLine) {
|
|
|
|
if (null !== $modeLine) {
|
|
|
|
$modes = array_fill_keys(explode(',', $modeLine), true);
|
|
|
|
} else {
|
|
|
|
$modes = [];
|
|
|
|
}
|
|
|
|
|
2017-01-19 22:23:19 +01:00
|
|
|
list($parser5, $parser7) = $this->createParsers($modes);
|
2017-11-04 18:09:20 +01:00
|
|
|
list($stmts5, $output5) = $this->getParseOutput($parser5, $code, $modes);
|
|
|
|
list($stmts7, $output7) = $this->getParseOutput($parser7, $code, $modes);
|
2015-06-20 11:43:16 +02:00
|
|
|
|
2016-11-23 22:36:48 +01:00
|
|
|
if (isset($modes['php5'])) {
|
2015-06-20 11:43:16 +02:00
|
|
|
$this->assertSame($expected, $output5, $name);
|
|
|
|
$this->assertNotSame($expected, $output7, $name);
|
2018-01-13 16:03:36 +01:00
|
|
|
} elseif (isset($modes['php7'])) {
|
2015-06-20 11:43:16 +02:00
|
|
|
$this->assertNotSame($expected, $output5, $name);
|
|
|
|
$this->assertSame($expected, $output7, $name);
|
|
|
|
} else {
|
|
|
|
$this->assertSame($expected, $output5, $name);
|
|
|
|
$this->assertSame($expected, $output7, $name);
|
|
|
|
}
|
2017-11-04 18:09:20 +01:00
|
|
|
|
|
|
|
$this->checkAttributes($stmts5);
|
|
|
|
$this->checkAttributes($stmts7);
|
2015-06-20 11:43:16 +02:00
|
|
|
}
|
|
|
|
|
2017-01-19 22:23:19 +01:00
|
|
|
public function createParsers(array $modes) {
|
2017-08-13 14:35:03 +02:00
|
|
|
$lexer = new Lexer\Emulative(['usedAttributes' => [
|
2017-11-04 18:09:20 +01:00
|
|
|
'startLine', 'endLine',
|
|
|
|
'startFilePos', 'endFilePos',
|
|
|
|
'startTokenPos', 'endTokenPos',
|
|
|
|
'comments'
|
2017-08-13 14:35:03 +02:00
|
|
|
]]);
|
2017-01-19 22:23:19 +01:00
|
|
|
|
|
|
|
return [
|
2017-04-28 19:09:39 +02:00
|
|
|
new Parser\Php5($lexer),
|
|
|
|
new Parser\Php7($lexer),
|
2017-01-19 22:23:19 +01:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
2018-07-22 15:19:40 +02:00
|
|
|
// Must be public for updateTests.php
|
|
|
|
public function getParseOutput(Parser $parser, $code, array $modes) {
|
2017-01-19 22:23:19 +01:00
|
|
|
$dumpPositions = isset($modes['positions']);
|
|
|
|
|
2016-10-09 13:15:24 +02:00
|
|
|
$errors = new ErrorHandler\Collecting;
|
|
|
|
$stmts = $parser->parse($code, $errors);
|
2015-06-20 11:43:16 +02:00
|
|
|
|
|
|
|
$output = '';
|
2016-10-09 13:15:24 +02:00
|
|
|
foreach ($errors->getErrors() as $error) {
|
2015-06-20 11:43:16 +02:00
|
|
|
$output .= $this->formatErrorMessage($error, $code) . "\n";
|
|
|
|
}
|
|
|
|
|
|
|
|
if (null !== $stmts) {
|
2016-11-23 22:36:48 +01:00
|
|
|
$dumper = new NodeDumper(['dumpComments' => true, 'dumpPositions' => $dumpPositions]);
|
|
|
|
$output .= $dumper->dump($stmts, $code);
|
2015-06-20 11:43:16 +02:00
|
|
|
}
|
|
|
|
|
2017-11-04 18:09:20 +01:00
|
|
|
return [$stmts, canonicalize($output)];
|
2015-06-20 11:43:16 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public function provideTestParse() {
|
|
|
|
return $this->getTests(__DIR__ . '/../code/parser', 'test');
|
|
|
|
}
|
|
|
|
|
|
|
|
private function formatErrorMessage(Error $e, $code) {
|
|
|
|
if ($e->hasColumnInfo()) {
|
2016-09-30 18:24:43 +02:00
|
|
|
return $e->getMessageWithColumnInfo($code);
|
2015-06-20 11:43:16 +02:00
|
|
|
}
|
2019-01-19 11:18:00 +01:00
|
|
|
|
|
|
|
return $e->getMessage();
|
2015-06-20 11:43:16 +02:00
|
|
|
}
|
2017-11-04 18:09:20 +01:00
|
|
|
|
|
|
|
private function checkAttributes($stmts) {
|
|
|
|
if ($stmts === null) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
$traverser = new NodeTraverser();
|
|
|
|
$traverser->addVisitor(new class extends NodeVisitorAbstract {
|
|
|
|
public function enterNode(Node $node) {
|
|
|
|
$startLine = $node->getStartLine();
|
|
|
|
$endLine = $node->getEndLine();
|
|
|
|
$startFilePos = $node->getStartFilePos();
|
|
|
|
$endFilePos = $node->getEndFilePos();
|
|
|
|
$startTokenPos = $node->getStartTokenPos();
|
|
|
|
$endTokenPos = $node->getEndTokenPos();
|
|
|
|
if ($startLine < 0 || $endLine < 0 ||
|
|
|
|
$startFilePos < 0 || $endFilePos < 0 ||
|
|
|
|
$startTokenPos < 0 || $endTokenPos < 0
|
|
|
|
) {
|
|
|
|
throw new \Exception('Missing location information on ' . $node->getType());
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($endLine < $startLine ||
|
|
|
|
$endFilePos < $startFilePos ||
|
|
|
|
$endTokenPos < $startTokenPos
|
|
|
|
) {
|
|
|
|
// Nops and error can have inverted order, if they are empty
|
|
|
|
if (!$node instanceof Stmt\Nop && !$node instanceof Expr\Error) {
|
|
|
|
throw new \Exception('End < start on ' . $node->getType());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
$traverser->traverse($stmts);
|
|
|
|
}
|
2015-06-20 11:43:16 +02:00
|
|
|
}
|