Chris@13: createParsers($modes); Chris@13: list($stmts5, $output5) = $this->getParseOutput($parser5, $code, $modes); Chris@13: list($stmts7, $output7) = $this->getParseOutput($parser7, $code, $modes); Chris@0: Chris@0: if (isset($modes['php5'])) { Chris@0: $this->assertSame($expected, $output5, $name); Chris@0: $this->assertNotSame($expected, $output7, $name); Chris@13: } elseif (isset($modes['php7'])) { Chris@0: $this->assertNotSame($expected, $output5, $name); Chris@0: $this->assertSame($expected, $output7, $name); Chris@0: } else { Chris@0: $this->assertSame($expected, $output5, $name); Chris@0: $this->assertSame($expected, $output7, $name); Chris@0: } Chris@13: Chris@13: $this->checkAttributes($stmts5); Chris@13: $this->checkAttributes($stmts7); Chris@0: } Chris@0: Chris@13: public function createParsers(array $modes) { Chris@13: $lexer = new Lexer\Emulative(['usedAttributes' => [ Chris@13: 'startLine', 'endLine', Chris@13: 'startFilePos', 'endFilePos', Chris@13: 'startTokenPos', 'endTokenPos', Chris@13: 'comments' Chris@13: ]]); Chris@13: Chris@13: return [ Chris@13: new Parser\Php5($lexer), Chris@13: new Parser\Php7($lexer), Chris@13: ]; Chris@13: } Chris@13: Chris@17: // Must be public for updateTests.php Chris@17: public function getParseOutput(Parser $parser, $code, array $modes) { Chris@13: $dumpPositions = isset($modes['positions']); Chris@13: Chris@0: $errors = new ErrorHandler\Collecting; Chris@0: $stmts = $parser->parse($code, $errors); Chris@0: Chris@0: $output = ''; Chris@0: foreach ($errors->getErrors() as $error) { Chris@0: $output .= $this->formatErrorMessage($error, $code) . "\n"; Chris@0: } Chris@0: Chris@0: if (null !== $stmts) { Chris@0: $dumper = new NodeDumper(['dumpComments' => true, 'dumpPositions' => $dumpPositions]); Chris@0: $output .= $dumper->dump($stmts, $code); Chris@0: } Chris@0: Chris@13: return [$stmts, canonicalize($output)]; Chris@0: } Chris@0: Chris@0: public function provideTestParse() { Chris@0: return $this->getTests(__DIR__ . '/../code/parser', 'test'); Chris@0: } Chris@0: Chris@0: private function formatErrorMessage(Error $e, $code) { Chris@0: if ($e->hasColumnInfo()) { Chris@0: return $e->getMessageWithColumnInfo($code); Chris@0: } Chris@17: Chris@17: return $e->getMessage(); Chris@0: } Chris@13: Chris@13: private function checkAttributes($stmts) { Chris@13: if ($stmts === null) { Chris@13: return; Chris@13: } Chris@13: Chris@13: $traverser = new NodeTraverser(); Chris@13: $traverser->addVisitor(new class extends NodeVisitorAbstract { Chris@13: public function enterNode(Node $node) { Chris@13: $startLine = $node->getStartLine(); Chris@13: $endLine = $node->getEndLine(); Chris@13: $startFilePos = $node->getStartFilePos(); Chris@13: $endFilePos = $node->getEndFilePos(); Chris@13: $startTokenPos = $node->getStartTokenPos(); Chris@13: $endTokenPos = $node->getEndTokenPos(); Chris@13: if ($startLine < 0 || $endLine < 0 || Chris@13: $startFilePos < 0 || $endFilePos < 0 || Chris@13: $startTokenPos < 0 || $endTokenPos < 0 Chris@13: ) { Chris@13: throw new \Exception('Missing location information on ' . $node->getType()); Chris@13: } Chris@13: Chris@13: if ($endLine < $startLine || Chris@13: $endFilePos < $startFilePos || Chris@13: $endTokenPos < $startTokenPos Chris@13: ) { Chris@13: // Nops and error can have inverted order, if they are empty Chris@13: if (!$node instanceof Stmt\Nop && !$node instanceof Expr\Error) { Chris@13: throw new \Exception('End < start on ' . $node->getType()); Chris@13: } Chris@13: } Chris@13: } Chris@13: }); Chris@13: $traverser->traverse($stmts); Chris@13: } Chris@0: }