CodeParsingTest.php 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <?php declare(strict_types=1);
  2. namespace PhpParser;
  3. use PhpParser\Node\Expr;
  4. use PhpParser\Node\Stmt;
  5. require_once __DIR__ . '/CodeTestAbstract.php';
  6. class CodeParsingTest extends CodeTestAbstract
  7. {
  8. /**
  9. * @dataProvider provideTestParse
  10. */
  11. public function testParse($name, $code, $expected, $modeLine) {
  12. if (null !== $modeLine) {
  13. $modes = array_fill_keys(explode(',', $modeLine), true);
  14. } else {
  15. $modes = [];
  16. }
  17. list($parser5, $parser7) = $this->createParsers($modes);
  18. list($stmts5, $output5) = $this->getParseOutput($parser5, $code, $modes);
  19. list($stmts7, $output7) = $this->getParseOutput($parser7, $code, $modes);
  20. if (isset($modes['php5'])) {
  21. $this->assertSame($expected, $output5, $name);
  22. $this->assertNotSame($expected, $output7, $name);
  23. } elseif (isset($modes['php7'])) {
  24. $this->assertNotSame($expected, $output5, $name);
  25. $this->assertSame($expected, $output7, $name);
  26. } else {
  27. $this->assertSame($expected, $output5, $name);
  28. $this->assertSame($expected, $output7, $name);
  29. }
  30. $this->checkAttributes($stmts5);
  31. $this->checkAttributes($stmts7);
  32. }
  33. public function createParsers(array $modes) {
  34. $lexer = new Lexer\Emulative(['usedAttributes' => [
  35. 'startLine', 'endLine',
  36. 'startFilePos', 'endFilePos',
  37. 'startTokenPos', 'endTokenPos',
  38. 'comments'
  39. ]]);
  40. return [
  41. new Parser\Php5($lexer),
  42. new Parser\Php7($lexer),
  43. ];
  44. }
  45. // Must be public for updateTests.php
  46. public function getParseOutput(Parser $parser, $code, array $modes) {
  47. $dumpPositions = isset($modes['positions']);
  48. $errors = new ErrorHandler\Collecting;
  49. $stmts = $parser->parse($code, $errors);
  50. $output = '';
  51. foreach ($errors->getErrors() as $error) {
  52. $output .= $this->formatErrorMessage($error, $code) . "\n";
  53. }
  54. if (null !== $stmts) {
  55. $dumper = new NodeDumper(['dumpComments' => true, 'dumpPositions' => $dumpPositions]);
  56. $output .= $dumper->dump($stmts, $code);
  57. }
  58. return [$stmts, canonicalize($output)];
  59. }
  60. public function provideTestParse() {
  61. return $this->getTests(__DIR__ . '/../code/parser', 'test');
  62. }
  63. private function formatErrorMessage(Error $e, $code) {
  64. if ($e->hasColumnInfo()) {
  65. return $e->getMessageWithColumnInfo($code);
  66. } else {
  67. return $e->getMessage();
  68. }
  69. }
  70. private function checkAttributes($stmts) {
  71. if ($stmts === null) {
  72. return;
  73. }
  74. $traverser = new NodeTraverser();
  75. $traverser->addVisitor(new class extends NodeVisitorAbstract {
  76. public function enterNode(Node $node) {
  77. $startLine = $node->getStartLine();
  78. $endLine = $node->getEndLine();
  79. $startFilePos = $node->getStartFilePos();
  80. $endFilePos = $node->getEndFilePos();
  81. $startTokenPos = $node->getStartTokenPos();
  82. $endTokenPos = $node->getEndTokenPos();
  83. if ($startLine < 0 || $endLine < 0 ||
  84. $startFilePos < 0 || $endFilePos < 0 ||
  85. $startTokenPos < 0 || $endTokenPos < 0
  86. ) {
  87. throw new \Exception('Missing location information on ' . $node->getType());
  88. }
  89. if ($endLine < $startLine ||
  90. $endFilePos < $startFilePos ||
  91. $endTokenPos < $startTokenPos
  92. ) {
  93. // Nops and error can have inverted order, if they are empty
  94. if (!$node instanceof Stmt\Nop && !$node instanceof Expr\Error) {
  95. throw new \Exception('End < start on ' . $node->getType());
  96. }
  97. }
  98. }
  99. });
  100. $traverser->traverse($stmts);
  101. }
  102. }