Skip to content

Commit 1f45a8c

Browse files
committed
Make this reproduce php-ast behavior for 7.2 and 7.0
1 parent 19df945 commit 1f45a8c

File tree

4 files changed

+92
-36
lines changed

4 files changed

+92
-36
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
language: php
22

33
php:
4+
- 7.2
45
- 7.1
6+
- 7.0
57

68
cache:
79
directories:
@@ -12,7 +14,7 @@ before_install:
1214
- composer validate
1315

1416
install:
15-
- composer --prefer-dist install
17+
- composer --ignore-platform-reqs --prefer-dist install
1618

1719
script:
1820
- vendor/bin/phan

src/ASTConverter/ASTConverter.php

Lines changed: 80 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,11 @@ private function startParsing(int $ast_version) {
166166
self::$should_add_placeholders = $this->instance_should_add_placeholders;
167167
}
168168

169-
private static function phpParserStmtlistToAstNode(array $parser_nodes, ?int $lineno) : ast\Node {
169+
/**
170+
* @param array $parser_nodes
171+
* @param ?int $lineno
172+
*/
173+
private static function phpParserStmtlistToAstNode(array $parser_nodes, $lineno) : ast\Node {
170174
$stmts = new ast\Node();
171175
$stmts->kind = ast\AST_STMT_LIST;
172176
$stmts->flags = 0;
@@ -307,15 +311,17 @@ private static function initHandleMap() : array {
307311
'dim' => $n->dim !== null ? self::phpParserNodeToAstNode($n->dim) : null,
308312
], $start_line);
309313
},
310-
'PhpParser\Node\Expr\Assign' => function(PhpParser\Node\Expr\Assign $n, int $start_line) : ?ast\Node {
314+
/** @return ?ast\Node */
315+
'PhpParser\Node\Expr\Assign' => function(PhpParser\Node\Expr\Assign $n, int $start_line) {
311316
return self::astNodeAssign(
312317
self::phpParserNodeToAstNode($n->var),
313318
self::phpParserNodeToAstNode($n->expr),
314319
$start_line,
315320
false
316321
);
317322
},
318-
'PhpParser\Node\Expr\AssignRef' => function(PhpParser\Node\Expr\AssignRef $n, int $start_line) : ?ast\Node {
323+
/** @return ?ast\Node */
324+
'PhpParser\Node\Expr\AssignRef' => function(PhpParser\Node\Expr\AssignRef $n, int $start_line) {
319325
return self::astNodeAssign(
320326
self::phpParserNodeToAstNode($n->var),
321327
self::phpParserNodeToAstNode($n->expr),
@@ -485,7 +491,8 @@ private static function initHandleMap() : array {
485491
);
486492
// FIXME: add a test of ClassConstFetch to php-ast
487493
},
488-
'PhpParser\Node\Expr\ClassConstFetch' => function(PhpParser\Node\Expr\ClassConstFetch $n, int $start_line) : ?ast\Node {
494+
/** @return ?ast\Node */
495+
'PhpParser\Node\Expr\ClassConstFetch' => function(PhpParser\Node\Expr\ClassConstFetch $n, int $start_line) {
489496
return self::phpParserClassconstfetchToAstClassconstfetch($n, $start_line);
490497
},
491498
'PhpParser\Node\Expr\Clone_' => function(PhpParser\Node\Expr\Clone_ $n, int $start_line) : ast\Node {
@@ -593,7 +600,8 @@ private static function initHandleMap() : array {
593600
$start_line
594601
);
595602
},
596-
'PhpParser\Node\Expr\PropertyFetch' => function(PhpParser\Node\Expr\PropertyFetch $n, int $start_line) : ?ast\Node {
603+
/** @return ?ast\Node */
604+
'PhpParser\Node\Expr\PropertyFetch' => function(PhpParser\Node\Expr\PropertyFetch $n, int $start_line) {
597605
return self::phpParserPropertyfetchToAstProp($n, $start_line);
598606
},
599607
'PhpParser\Node\Expr\ShellExec' => function(PhpParser\Node\Expr\ShellExec $n, int $start_line) : ast\Node {
@@ -643,7 +651,8 @@ private static function initHandleMap() : array {
643651
'PhpParser\Node\Expr\UnaryPlus' => function(PhpParser\Node\Expr\UnaryPlus $n, int $start_line) : ast\Node {
644652
return self::astNodeUnaryOp(ast\flags\UNARY_PLUS, self::phpParserNodeToAstNode($n->expr), $start_line);
645653
},
646-
'PhpParser\Node\Expr\Variable' => function(PhpParser\Node\Expr\Variable $n, int $start_line) : ?ast\Node {
654+
/** @return ?ast\Node */
655+
'PhpParser\Node\Expr\Variable' => function(PhpParser\Node\Expr\Variable $n, int $start_line) {
647656
return self::astNodeVariable($n->name, $start_line);
648657
},
649658
'PhpParser\Node\Expr\Yield_' => function(PhpParser\Node\Expr\Yield_ $n, int $start_line) : ast\Node {
@@ -1199,7 +1208,8 @@ private static function astNodeDoWhile($cond, $stmts, int $start_line) : ast\Nod
11991208
);
12001209
}
12011210

1202-
private static function astNodeAssign($var, $expr, int $line, bool $ref) : ?ast\Node {
1211+
/** @return ?ast\Node */
1212+
private static function astNodeAssign($var, $expr, int $line, bool $ref) {
12031213
if ($expr === null) {
12041214
if (self::$should_add_placeholders) {
12051215
$expr = '__INCOMPLETE_EXPR__';
@@ -1348,7 +1358,8 @@ private static function astNodeNullableType(ast\Node $type, int $line) {
13481358
return $node;
13491359
}
13501360

1351-
private static function astNodeVariable($expr, int $line) : ?ast\Node {
1361+
/** @return ?ast\Node */
1362+
private static function astNodeVariable($expr, int $line) {
13521363
// TODO: 2 different ways to handle an Error. 1. Add a placeholder. 2. remove all of the statements in that tree.
13531364
if ($expr instanceof PhpParser\Node) {
13541365
$expr = self::phpParserNodeToAstNode($expr);
@@ -1422,6 +1433,9 @@ private static function phpParserClosureUsesToAstClosureUses(
14221433

14231434
}
14241435

1436+
/**
1437+
* @param ?string $doc_comment
1438+
*/
14251439
private static function astDeclClosure(
14261440
bool $by_ref,
14271441
bool $static,
@@ -1431,7 +1445,7 @@ private static function astDeclClosure(
14311445
$return_type,
14321446
int $start_line,
14331447
int $end_line,
1434-
?string $doc_comment
1448+
$doc_comment
14351449
) : ast\Node {
14361450
$flags = 0;
14371451
if ($by_ref) {
@@ -1473,7 +1487,10 @@ private static function astDeclClosure(
14731487
ast\AST_YIELD_FROM => true,
14741488
];
14751489

1476-
public static function functionBodyIsGenerator(?ast\Node $stmts) : bool {
1490+
/**
1491+
* @param ?ast\Node $stmts
1492+
*/
1493+
public static function functionBodyIsGenerator($stmts) : bool {
14771494
if (!$stmts) {
14781495
return false;
14791496
}
@@ -1492,16 +1509,20 @@ public static function functionBodyIsGenerator(?ast\Node $stmts) : bool {
14921509
return false;
14931510
}
14941511

1512+
/**
1513+
* @param ?array $uses
1514+
* @param ?string $doc_comment
1515+
*/
14951516
private static function astDeclFunction(
14961517
bool $by_ref,
14971518
string $name,
14981519
ast\Node $params,
1499-
?array $uses,
1520+
$uses,
15001521
$return_type,
15011522
$stmts,
15021523
int $line,
15031524
int $end_line,
1504-
?string $doc_comment
1525+
$doc_comment
15051526
) : ast\Node {
15061527
$flags = 0;
15071528
if ($by_ref) {
@@ -1548,16 +1569,17 @@ private static function phpParserClassFlagsToAstClassFlags(int $flags) {
15481569
* @param ?ast\Node $stmts
15491570
* @param int $line
15501571
* @param int $end_line
1572+
* @param ?string $doc_comment
15511573
*/
15521574
private static function astStmtClass(
15531575
int $flags,
1554-
?string $name,
1555-
?ast\Node $extends,
1556-
?array $implements,
1557-
?ast\Node $stmts,
1576+
$name,
1577+
$extends,
1578+
$implements,
1579+
$stmts,
15581580
int $line,
15591581
int $end_line,
1560-
?string $doc_comment
1582+
$doc_comment
15611583
) : ast\Node {
15621584
if ($name === null) {
15631585
$flags |= ast\flags\CLASS_ANONYMOUS;
@@ -1602,9 +1624,10 @@ private static function phpParserArgListToAstArgList(array $args, int $line) : a
16021624
return $node;
16031625
}
16041626

1605-
private static function phpParserUseListToAstUseList(?array $uses) : array {
1627+
/** @param ?array $uses */
1628+
private static function phpParserUseListToAstUseList($uses) : array {
16061629
$ast_uses = [];
1607-
foreach ($uses as $use) {
1630+
foreach ($uses ?? [] as $use) {
16081631
$ast_use = new ast\Node();
16091632
$ast_use->kind = ast\AST_USE_ELEM;
16101633
$ast_use->flags = self::phpParserUseTypeToAstFlags($use->type); // FIXME
@@ -1769,7 +1792,10 @@ private static function phpParserNodesToLeftRightChildren($left, $right) : array
17691792
];
17701793
}
17711794

1772-
private static function phpParserPropelemToAstPropelem(PhpParser\Node\Stmt\PropertyProperty $n, ?string $doc_comment) : ast\Node{
1795+
/**
1796+
* @param ?string $doc_comment
1797+
*/
1798+
private static function phpParserPropelemToAstPropelem(PhpParser\Node\Stmt\PropertyProperty $n, $doc_comment) : ast\Node{
17731799
$children = [
17741800
'name' => $n->name,
17751801
'default' => $n->default ? self::phpParserNodeToAstNode($n->default) : null,
@@ -1780,7 +1806,10 @@ private static function phpParserPropelemToAstPropelem(PhpParser\Node\Stmt\Prope
17801806
return self::newAstNode(ast\AST_PROP_ELEM, 0, $children, $start_line, self::extractPhpdocComment($n->getAttribute('comments') ?? $doc_comment));
17811807
}
17821808

1783-
private static function phpParserConstelemToAstConstelem(PhpParser\Node\Const_ $n, ?string $doc_comment) : ast\Node{
1809+
/**
1810+
* @param ?string $doc_comment
1811+
*/
1812+
private static function phpParserConstelemToAstConstelem(PhpParser\Node\Const_ $n, $doc_comment) : ast\Node{
17841813
$children = [
17851814
'name' => $n->name,
17861815
'value' => self::phpParserNodeToAstNode($n->value),
@@ -1865,19 +1894,24 @@ private static function phpParserDeclareListToAstDeclares(array $declares, int $
18651894
$doc_comment = self::extractPhpdocComment($declare->getAttribute('comments')) ?? $first_doc_comment;
18661895
$first_doc_comment = null;
18671896
if (self::$ast_version >= 50) {
1868-
$children['docComment'] = $doc_comment;
1897+
if (PHP_VERSION_ID >= 70100) {
1898+
$children['docComment'] = $doc_comment;
1899+
}
18691900
}
18701901
$node = new ast\Node(ast\AST_CONST_ELEM, 0, $children, $declare->getAttribute('startLine'));
18711902
if (self::$ast_version < 50 && is_string($doc_comment)) {
1872-
$node->docComment = $doc_comment;
1903+
if (PHP_VERSION_ID >= 70100) {
1904+
$node->docComment = $doc_comment;
1905+
}
18731906
}
18741907
$ast_declare_elements[] = $node;
18751908
}
18761909
return new ast\Node(ast\AST_CONST_DECL, 0, $ast_declare_elements, $start_line);
18771910

18781911
}
18791912

1880-
private static function astStmtDeclare(ast\Node $declares, ?ast\Node $stmts, int $start_line) : ast\Node{
1913+
/** @param ?ast\Node $stmts */
1914+
private static function astStmtDeclare(ast\Node $declares, $stmts, int $start_line) : ast\Node{
18811915
$children = [
18821916
'declares' => $declares,
18831917
'stmts' => $stmts,
@@ -1910,7 +1944,11 @@ private static function astNodeStaticCall($class, $method, ast\Node $args, int $
19101944
return new ast\Node(ast\AST_STATIC_CALL, 0, ['class' => $class, 'method' => $method, 'args' => $args], $start_line);
19111945
}
19121946

1913-
private static function extractPhpdocComment($comments) : ?string {
1947+
/**
1948+
* @param ?string|?array $comments
1949+
* @return ?string
1950+
*/
1951+
private static function extractPhpdocComment($comments) {
19141952
if (\is_string($comments)) {
19151953
return $comments;
19161954
}
@@ -1947,7 +1985,7 @@ private static function phpParserListToAstList(PhpParser\Node\Expr\List_ $n, int
19471985
}
19481986

19491987
// convert list($x,) to list($x), list(,) to list(), etc.
1950-
if (\count($ast_items) > 0 && end($ast_items) === null) {
1988+
if (PHP_VERSION_ID >= 70100 && \count($ast_items) > 0 && end($ast_items) === null) {
19511989
array_pop($ast_items);
19521990
}
19531991
return new ast\Node(ast\AST_ARRAY, ast\flags\ARRAY_SYNTAX_LIST, $ast_items, $start_line);
@@ -1966,11 +2004,16 @@ private static function phpParserArrayToAstArray(PhpParser\Node\Expr\Array_ $n,
19662004
], $item->getAttribute('startLine'));
19672005
}
19682006
}
1969-
$flags = $n->getAttribute('kind') === PhpParser\Node\Expr\Array_::KIND_LONG ? ast\flags\ARRAY_SYNTAX_LONG : ast\flags\ARRAY_SYNTAX_SHORT;
2007+
if (PHP_VERSION_ID < 70100) {
2008+
$flags = 0;
2009+
} else {
2010+
$flags = ($n->getAttribute('kind') === PhpParser\Node\Expr\Array_::KIND_LONG) ? ast\flags\ARRAY_SYNTAX_LONG : ast\flags\ARRAY_SYNTAX_SHORT;
2011+
}
19702012
return new ast\Node(ast\AST_ARRAY, $flags, $ast_items, $start_line);
19712013
}
19722014

1973-
private static function phpParserPropertyfetchToAstProp(PhpParser\Node\Expr\PropertyFetch $n, int $start_line) : ?ast\Node {
2015+
/** @return ?ast\Node */
2016+
private static function phpParserPropertyfetchToAstProp(PhpParser\Node\Expr\PropertyFetch $n, int $start_line) {
19742017
$name = $n->name;
19752018
if (is_object($name)) {
19762019
$name = self::phpParserNodeToAstNode($name);
@@ -1988,7 +2031,8 @@ private static function phpParserPropertyfetchToAstProp(PhpParser\Node\Expr\Prop
19882031
], $start_line);
19892032
}
19902033

1991-
private static function phpParserClassconstfetchToAstClassconstfetch(PhpParser\Node\Expr\ClassConstFetch $n, int $start_line) : ?ast\Node {
2034+
/** @return ?ast\Node */
2035+
private static function phpParserClassconstfetchToAstClassconstfetch(PhpParser\Node\Expr\ClassConstFetch $n, int $start_line) {
19922036
$name = $n->name;
19932037
if (is_object($name)) {
19942038
$name = self::phpParserNodeToAstNode($name);
@@ -2028,7 +2072,9 @@ private static function phpParserNameToString(PhpParser\Node\Name $name) : strin
20282072
private static function newAstNode(int $kind, int $flags, array $children, int $lineno, string $doc_comment = null) : ast\Node {
20292073
if (self::$ast_version >= 45) {
20302074
if (is_string($doc_comment) || array_key_exists($kind, self::_NODES_WITH_NULL_DOC_COMMENT)) {
2031-
$children['docComment'] = $doc_comment;
2075+
if ($kind !== \ast\AST_CONST_ELEM || PHP_VERSION_ID >= 70100) {
2076+
$children['docComment'] = $doc_comment;
2077+
}
20322078
}
20332079
return new ast\Node($kind, $flags, $children, $lineno);
20342080
}
@@ -2079,14 +2125,16 @@ private static function nextDeclId() : int {
20792125

20802126
}
20812127

2082-
function sl($node) : ?int {
2128+
/** @return ?int */
2129+
function sl($node) {
20832130
if ($node instanceof PhpParser\Node) {
20842131
return $node->getAttribute('startLine');
20852132
}
20862133
return null;
20872134
}
20882135

2089-
function el($node) : ?int {
2136+
/** @return ?int */
2137+
function el($node) {
20902138
if ($node instanceof PhpParser\Node) {
20912139
return $node->getAttribute('endLine');
20922140
}

test

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/bin/sh
2-
if php -m | grep '^ast'; then
2+
if php -m | grep '^ast' -q; then
33
vendor/bin/phpunit tests
44
else
55
php -d extension=ast.so vendor/bin/phpunit tests

tests/ASTConverter/ConversionTest.php

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ public function astValidFileExampleProvider() {
4545
$tests = [];
4646
$source_dir = dirname(dirname(realpath(__DIR__))) . '/test_files/src';
4747
$paths = $this->_scanSourceDirForPHP($source_dir);
48+
sort($paths);
4849
$supports40 = self::hasNativeASTSupport(40);
4950
$supports50 = self::hasNativeASTSupport(50);
5051
if (!($supports40 || $supports50)) {
@@ -95,6 +96,10 @@ public static function normalizeLineNumbers(ast\Node $node) : ast\Node {
9596

9697
/** @dataProvider astValidFileExampleProvider */
9798
public function testFallbackFromParser(string $file_name, int $ast_version) {
99+
$test_folder_name = basename(dirname($file_name));
100+
if (PHP_VERSION_ID < 70100 && $test_folder_name === 'php71_or_newer') {
101+
$this->markTestIncomplete('php-ast cannot parse php7.1 syntax when running in php7.0');
102+
}
98103
$contents = file_get_contents($file_name);
99104
if ($contents === false) {
100105
$this->fail("Failed to read $file_name");
@@ -109,7 +114,8 @@ public function testFallbackFromParser(string $file_name, int $ast_version) {
109114
throw new \RuntimeException("Error parsing $file_name with ast version $ast_version", $e->getCode(), $e);
110115
}
111116
$this->assertInstanceOf('\ast\Node', $fallback_ast, 'The fallback must also return a tree of php-ast nodes');
112-
if (stripos($file_name, 'phan_test_files') !== false || stripos($file_name, 'php-src_tests') !== false) {
117+
118+
if ($test_folder_name === 'phan_test_files' || $test_folder_name === 'php-src_tests') {
113119
$fallback_ast = self::normalizeLineNumbers($fallback_ast);
114120
$ast = self::normalizeLineNumbers($ast);
115121
}
@@ -121,7 +127,7 @@ public function testFallbackFromParser(string $file_name, int $ast_version) {
121127
'dumpComments' => true,
122128
'dumpPositions' => true,
123129
]);
124-
$php_parser_node = ASTConverter::phpparserParse($contents);
130+
$php_parser_node = $converter->phpparserParse($contents);
125131
try {
126132
$dump = $node_dumper->dump($php_parser_node);
127133
} catch (\PhpParser\Error $e) {

0 commit comments

Comments
 (0)