1 <?php declare(strict_types=1);
5 use PhpParser\Node\Expr;
6 use PhpParser\Node\Stmt;
8 require_once __DIR__ . '/CodeTestAbstract.php';
10 class CodeParsingTest extends CodeTestAbstract
13 * @dataProvider provideTestParse
15 public function testParse($name, $code, $expected, $modeLine) {
16 if (null !== $modeLine) {
17 $modes = array_fill_keys(explode(',', $modeLine), true);
22 list($parser5, $parser7) = $this->createParsers($modes);
23 list($stmts5, $output5) = $this->getParseOutput($parser5, $code, $modes);
24 list($stmts7, $output7) = $this->getParseOutput($parser7, $code, $modes);
26 if (isset($modes['php5'])) {
27 $this->assertSame($expected, $output5, $name);
28 $this->assertNotSame($expected, $output7, $name);
29 } elseif (isset($modes['php7'])) {
30 $this->assertNotSame($expected, $output5, $name);
31 $this->assertSame($expected, $output7, $name);
33 $this->assertSame($expected, $output5, $name);
34 $this->assertSame($expected, $output7, $name);
37 $this->checkAttributes($stmts5);
38 $this->checkAttributes($stmts7);
41 public function createParsers(array $modes) {
42 $lexer = new Lexer\Emulative(['usedAttributes' => [
43 'startLine', 'endLine',
44 'startFilePos', 'endFilePos',
45 'startTokenPos', 'endTokenPos',
50 new Parser\Php5($lexer),
51 new Parser\Php7($lexer),
55 private function getParseOutput(Parser $parser, $code, array $modes) {
56 $dumpPositions = isset($modes['positions']);
58 $errors = new ErrorHandler\Collecting;
59 $stmts = $parser->parse($code, $errors);
62 foreach ($errors->getErrors() as $error) {
63 $output .= $this->formatErrorMessage($error, $code) . "\n";
66 if (null !== $stmts) {
67 $dumper = new NodeDumper(['dumpComments' => true, 'dumpPositions' => $dumpPositions]);
68 $output .= $dumper->dump($stmts, $code);
71 return [$stmts, canonicalize($output)];
74 public function provideTestParse() {
75 return $this->getTests(__DIR__ . '/../code/parser', 'test');
78 private function formatErrorMessage(Error $e, $code) {
79 if ($e->hasColumnInfo()) {
80 return $e->getMessageWithColumnInfo($code);
82 return $e->getMessage();
86 private function checkAttributes($stmts) {
87 if ($stmts === null) {
91 $traverser = new NodeTraverser();
92 $traverser->addVisitor(new class extends NodeVisitorAbstract {
93 public function enterNode(Node $node) {
94 $startLine = $node->getStartLine();
95 $endLine = $node->getEndLine();
96 $startFilePos = $node->getStartFilePos();
97 $endFilePos = $node->getEndFilePos();
98 $startTokenPos = $node->getStartTokenPos();
99 $endTokenPos = $node->getEndTokenPos();
100 if ($startLine < 0 || $endLine < 0 ||
101 $startFilePos < 0 || $endFilePos < 0 ||
102 $startTokenPos < 0 || $endTokenPos < 0
104 throw new \Exception('Missing location information on ' . $node->getType());
107 if ($endLine < $startLine ||
108 $endFilePos < $startFilePos ||
109 $endTokenPos < $startTokenPos
111 // Nops and error can have inverted order, if they are empty
112 if (!$node instanceof Stmt\Nop && !$node instanceof Expr\Error) {
113 throw new \Exception('End < start on ' . $node->getType());
118 $traverser->traverse($stmts);