Skip to content

Commit 540a846

Browse files
committed
Add more node types
1 parent 41d9576 commit 540a846

File tree

10 files changed

+176
-21
lines changed

10 files changed

+176
-21
lines changed

src/ASTConverter/ASTConverter.php

Lines changed: 130 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,45 @@ private static function _phpparser_stmtlist_to_ast_node(array $parserNodes, ?int
8787
return $stmts;
8888
}
8989

90+
/**
91+
* @param PHPParser\Node[] $exprs
92+
*/
93+
private static function _phpparser_expr_list_to_expr_list(array $exprs, int $lineno) : \ast\Node {
94+
$children = [];
95+
foreach ($exprs as $expr) {
96+
$childNode = self::_phpparser_node_to_ast_node($expr);
97+
if (is_array($childNode)) {
98+
// Echo_ returns multiple children.
99+
foreach ($childNode as $childNodePart) {
100+
$children[] = $childNodePart;
101+
}
102+
} else if (!is_null($childNode)) {
103+
$children[] = $childNode;
104+
}
105+
}
106+
foreach ($exprs as $parserNode) {
107+
$childNodeLine = sl($parserNode);
108+
if ($childNodeLine > 0) {
109+
$lineno = $childNodeLine;
110+
break;
111+
}
112+
}
113+
return astnode(
114+
\ast\AST_EXPR_LIST,
115+
0,
116+
$children,
117+
$lineno
118+
);
119+
}
120+
90121
/**
91122
* @param PhpParser\Node $n - The node from PHP-Parser
92123
* @return \ast\Node|\ast\Node[]|string|int|float|bool|null - whatever \ast\parse_code would return as the equivalent.
93124
* @suppress PhanUndeclaredProperty
94125
*/
95126
private static final function _phpparser_node_to_ast_node($n) {
96127
if (!($n instanceof PhpParser\Node)) {
128+
debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
97129
throw new \InvalidArgumentException("Invalid type for node: " . (is_object($n) ? get_class($n) : gettype($n)));
98130
}
99131

@@ -137,7 +169,16 @@ private static function _init_handle_map() : array {
137169
return self::_ast_node_assign(
138170
self::_phpparser_node_to_ast_node($n->var),
139171
self::_phpparser_node_to_ast_node($n->expr),
140-
$startLine
172+
$startLine,
173+
false
174+
);
175+
},
176+
'PhpParser\Node\Expr\AssignRef' => function(PhpParser\Node\Expr\AssignRef $n, int $startLine) : ?\ast\Node {
177+
return self::_ast_node_assign(
178+
self::_phpparser_node_to_ast_node($n->var),
179+
self::_phpparser_node_to_ast_node($n->expr),
180+
$startLine,
181+
true
141182
);
142183
},
143184
'PhpParser\Node\Expr\AssignOp\BitwiseAnd' => function(PhpParser\Node\Expr\AssignOp\BitwiseAnd $n, int $startLine) : \ast\Node {
@@ -303,12 +344,18 @@ private static function _init_handle_map() : array {
303344
'PhpParser\Node\Expr\ClassConstFetch' => function(PhpParser\Node\Expr\ClassConstFetch $n, int $startLine) : ?\ast\Node {
304345
return self::_phpparser_classconstfetch_to_ast_classconstfetch($n, $startLine);
305346
},
347+
'PhpParser\Node\Expr\Clone_' => function(PhpParser\Node\Expr\Clone_ $n, int $startLine) : \ast\Node {
348+
return astnode(\ast\AST_CLONE, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine);
349+
},
306350
'PhpParser\Node\Expr\ConstFetch' => function(PhpParser\Node\Expr\ConstFetch $n, int $startLine) : \ast\Node {
307351
return astnode(\ast\AST_CONST, 0, ['name' => self::_phpparser_node_to_ast_node($n->name)], $startLine);
308352
},
309353
'PhpParser\Node\Expr\ErrorSuppress' => function(PhpParser\Node\Expr\ErrorSuppress $n, int $startLine) : \ast\Node {
310354
return self::_ast_node_unary_op(\ast\flags\UNARY_SILENCE, self::_phpparser_node_to_ast_node($n->expr), $startLine);
311355
},
356+
'PhpParser\Node\Expr\Empty_' => function(PhpParser\Node\Expr\Empty_ $n, int $startLine) : \ast\Node {
357+
return astnode(\ast\AST_EMPTY, 0, ['expr' => self::_phpparser_node_to_ast_node($n->expr)], $startLine);
358+
},
312359
'PhpParser\Node\Expr\Eval_' => function(PhpParser\Node\Expr\Eval_ $n, int $startLine) : \ast\Node {
313360
return self::_ast_node_eval(
314361
self::_phpparser_node_to_ast_node($n->expr),
@@ -382,6 +429,18 @@ private static function _init_handle_map() : array {
382429
'args' => self::_phpparser_arg_list_to_ast_arg_list($n->args, $startLine),
383430
], $startLine);
384431
},
432+
'PhpParser\Node\Expr\PreInc' => function(PhpParser\Node\Expr\PreInc $n, int $startLine) : \ast\Node {
433+
return astnode(\ast\AST_PRE_INC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine);
434+
},
435+
'PhpParser\Node\Expr\PreDec' => function(PhpParser\Node\Expr\PreDec $n, int $startLine) : \ast\Node {
436+
return astnode(\ast\AST_PRE_DEC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine);
437+
},
438+
'PhpParser\Node\Expr\PostInc' => function(PhpParser\Node\Expr\PostInc $n, int $startLine) : \ast\Node {
439+
return astnode(\ast\AST_POST_INC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine);
440+
},
441+
'PhpParser\Node\Expr\PostDec' => function(PhpParser\Node\Expr\PostDec $n, int $startLine) : \ast\Node {
442+
return astnode(\ast\AST_POST_DEC, 0, ['var' => self::_phpparser_node_to_ast_node($n->var)], $startLine);
443+
},
385444
'PhpParser\Node\Expr\Print_' => function(PhpParser\Node\Expr\Print_ $n, int $startLine) : \ast\Node {
386445
return astnode(
387446
\ast\AST_PRINT,
@@ -433,6 +492,17 @@ private static function _init_handle_map() : array {
433492
'PhpParser\Node\Expr\Variable' => function(PhpParser\Node\Expr\Variable $n, int $startLine) : ?\ast\Node {
434493
return self::_ast_node_variable($n->name, $startLine);
435494
},
495+
'PhpParser\Node\Expr\Yield_' => function(PhpParser\Node\Expr\Yield_ $n, int $startLine) : ?\ast\Node {
496+
return astnode(
497+
\ast\AST_YIELD,
498+
0,
499+
[
500+
'value' => $n->value !== null ? self::_phpparser_node_to_ast_node($n->value) : null,
501+
'key' => $n->key !== null ? self::_phpparser_node_to_ast_node($n->key) : null,
502+
],
503+
$startLine
504+
);
505+
},
436506
'PhpParser\Node\Name' => function(PhpParser\Node\Name $n, int $startLine) : \ast\Node {
437507
return self::_ast_node_name(
438508
self::_phpparser_name_to_string($n),
@@ -477,6 +547,9 @@ private static function _init_handle_map() : array {
477547
'PhpParser\Node\Scalar\EncapsedStringPart' => function(PhpParser\Node\Scalar\EncapsedStringPart $n, int $startLine) : string {
478548
return $n->value;
479549
},
550+
'PhpParser\Node\Scalar\DNumber' => function(PhpParser\Node\Scalar\DNumber $n, int $startLine) : float {
551+
return (float)$n->value;
552+
},
480553
'PhpParser\Node\Scalar\LNumber' => function(PhpParser\Node\Scalar\LNumber $n, int $startLine) : int {
481554
return (int)$n->value;
482555
},
@@ -563,6 +636,13 @@ private static function _init_handle_map() : array {
563636
$startLine
564637
);
565638
},
639+
'PhpParser\Node\Stmt\Do_' => function(PhpParser\Node\Stmt\Do_ $n, int $startLine) : \ast\Node {
640+
return self::_ast_node_do_while(
641+
self::_phpparser_node_to_ast_node($n->cond),
642+
self::_phpparser_stmtlist_to_ast_node($n->stmts, $startLine),
643+
$startLine
644+
);
645+
},
566646
/**
567647
* @return \ast\Node|\ast\Node[]
568648
*/
@@ -647,18 +727,27 @@ private static function _init_handle_map() : array {
647727
self::_extract_phpdoc_comment($n->getAttribute('comments'))
648728
);
649729
},
650-
/**
651-
* @suppress PhanDeprecatedProperty TODO: figure out alternative
652-
*/
730+
'PhpParser\Node\Stmt\For_' => function(PhpParser\Node\Stmt\For_ $n, int $startLine) : \ast\Node {
731+
return astnode(
732+
\ast\AST_FOR,
733+
0,
734+
[
735+
'init' => \count($n->init) > 0 ? self::_phpparser_expr_list_to_expr_list($n->init, $startLine) : null,
736+
'cond' => \count($n->cond) > 0 ? self::_phpparser_expr_list_to_expr_list($n->cond, $startLine) : null,
737+
'loop' => \count($n->loop) > 0 ? self::_phpparser_expr_list_to_expr_list($n->loop, $startLine) : null,
738+
'stmts' => self::_phpparser_stmtlist_to_ast_node($n->stmts ?? [], $startLine),
739+
],
740+
$startLine
741+
);
742+
},
653743
'PhpParser\Node\Stmt\GroupUse' => function(PhpParser\Node\Stmt\GroupUse $n, int $startLine) : \ast\Node {
654744
return self::_ast_stmt_group_use(
655745
$n->type,
656-
implode('\\', $n->prefix->parts ?? []),
746+
self::_phpparser_name_to_string($n->prefix),
657747
self::_phpparser_use_list_to_ast_use_list($n->uses),
658748
$startLine
659749
);
660750
},
661-
/** @suppress PhanDeprecatedProperty */
662751
'PhpParser\Node\Stmt\Namespace_' => function(PhpParser\Node\Stmt\Namespace_ $n, int $startLine) : \ast\Node {
663752
$nodeDumper = new \PhpParser\NodeDumper([
664753
'dumpComments' => true,
@@ -668,7 +757,7 @@ private static function _init_handle_map() : array {
668757
\ast\AST_NAMESPACE,
669758
0,
670759
[
671-
'name' => implode('\\', $n->name->parts ?? []),
760+
'name' => self::_phpparser_name_to_string($n->name),
672761
'stmts' => isset($n->stmts) ? self::_phpparser_stmtlist_to_ast_node($n->stmts, $startLine) : null,
673762
],
674763
$startLine
@@ -682,7 +771,7 @@ private static function _init_handle_map() : array {
682771
return self::_phpparser_property_to_ast_node($n, $startLine);
683772
},
684773
'PhpParser\Node\Stmt\Return_' => function(PhpParser\Node\Stmt\Return_ $n, int $startLine) : \ast\Node {
685-
return self::_ast_stmt_return(self::_phpparser_node_to_ast_node($n->expr), $startLine);
774+
return self::_ast_stmt_return($n->expr !== null ? self::_phpparser_node_to_ast_node($n->expr) : null, $startLine);
686775
},
687776
/** @return \ast\Node|\ast\Node[] */
688777
'PhpParser\Node\Stmt\Static_' => function(PhpParser\Node\Stmt\Static_ $n, int $startLine) {
@@ -760,6 +849,14 @@ private static function _init_handle_map() : array {
760849
$startLine
761850
);
762851
},
852+
/** @return \ast\Node|\ast\Node[] */
853+
'PhpParser\Node\Stmt\Unset_' => function(PhpParser\Node\Stmt\Unset_ $n, int $startLine) {
854+
$stmts = [];
855+
foreach ($n->vars as $var) {
856+
$stmts[] = astnode(\ast\AST_UNSET, 0, ['var' => self::_phpparser_node_to_ast_node($var)], sl($var) ?: $startLine);
857+
}
858+
return \count($stmts) === 1 ? $stmts[0] : $stmts;
859+
},
763860
'PhpParser\Node\Stmt\Use_' => function(PhpParser\Node\Stmt\Use_ $n, int $startLine) : \ast\Node {
764861
return self::_ast_stmt_use(
765862
$n->type,
@@ -839,18 +936,30 @@ private static function _phpparser_name_list_to_ast_name_list(array $types, int
839936
}
840937

841938
private static function _ast_node_while($cond, $stmts, int $startLine) : \ast\Node {
842-
$node = new \ast\Node();
843-
$node->kind = \ast\AST_WHILE;
844-
$node->lineno = $startLine;
845-
$node->flags = 0;
846-
$node->children = [
847-
'cond' => $cond,
848-
'stmts' => $stmts,
849-
];
850-
return $node;
939+
return astnode(
940+
\ast\AST_WHILE,
941+
0,
942+
[
943+
'cond' => $cond,
944+
'stmts' => $stmts,
945+
],
946+
$startLine
947+
);
948+
}
949+
950+
private static function _ast_node_do_while($cond, $stmts, int $startLine) : \ast\Node {
951+
return astnode(
952+
\ast\AST_DO_WHILE,
953+
0,
954+
[
955+
'stmts' => $stmts,
956+
'cond' => $cond,
957+
],
958+
$startLine
959+
);
851960
}
852961

853-
private static function _ast_node_assign($var, $expr, int $line) : ?\ast\Node {
962+
private static function _ast_node_assign($var, $expr, int $line, bool $ref) : ?\ast\Node {
854963
if ($expr === null) {
855964
if (self::$should_add_placeholders) {
856965
$expr = '__INCOMPLETE_EXPR__';
@@ -859,7 +968,7 @@ private static function _ast_node_assign($var, $expr, int $line) : ?\ast\Node {
859968
}
860969
}
861970
$node = new \ast\Node();
862-
$node->kind = \ast\AST_ASSIGN;
971+
$node->kind = $ref ? \ast\AST_ASSIGN_REF : \ast\AST_ASSIGN;
863972
$node->flags = 0;
864973
$node->children = [
865974
'var' => $var,
@@ -954,10 +1063,10 @@ private static function _phpparser_type_to_ast_node($type, int $line) {
9541063
* @param bool $byRef
9551064
* @param ?\ast\Node $type
9561065
*/
957-
private static function _ast_node_param(bool $byRef, $variadic, $type, $name, $default, int $line) : \ast\Node {
1066+
private static function _ast_node_param(bool $byRef, bool $variadic, $type, $name, $default, int $line) : \ast\Node {
9581067
$node = new \ast\Node;
9591068
$node->kind = \ast\AST_PARAM;
960-
$node->flags = $byRef ? \ast\flags\PARAM_REF : 0;
1069+
$node->flags = ($byRef ? \ast\flags\PARAM_REF : 0) | ($variadic ? \ast\flags\PARAM_VARIADIC : 0);
9611070
$node->lineno = $line;
9621071
$node->children = [
9631072
'type' => $type,

test_files/src/assignref.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
$x =& $y;

test_files/src/dowhile.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
do {foo();} while ($bar);

test_files/src/empty.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
empty($x);
3+
clone($x);
4+
unset($x);

test_files/src/for.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?php
2+
for (;;);
3+
for ($i = 0; $i < 10; $i++) {$a += $i;}
4+
for ($i = 0; $j++, $i++ < 10;) {}

test_files/src/incdecr.php

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?php
2+
$i++;
3+
++$i;
4+
--$i;
5+
$i--;

test_files/src/return.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
function a() {return;}
3+
return;

test_files/src/variadicparam.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
function foo($x = 'default', ...$rest) {}
3+
class A{public static function bar($a, $y = 'default', ...$rest) {}}

test_files/src/yield.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
<?php
2+
function f() { yield; yield 3 => 2; $y = yield; }
3+
function g() {yield;}

tests/ASTConverter/ConversionTest.php

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,33 @@ public function astValidFileExampleProvider() {
3232
return $tests;
3333
}
3434

35+
/** @return void */
36+
private static function normalizeOriginalAST($node) {
37+
if ($node instanceof \ast\Node) {
38+
$kind = $node->kind;
39+
if ($kind === \ast\AST_FUNC_DECL || $kind === \ast\AST_METHOD) {
40+
// https://github.com/nikic/php-ast/issues/64
41+
$node->flags &= ~(0x800000);
42+
}
43+
foreach ($node->children as $c) {
44+
self::normalizeOriginalAST($c);
45+
}
46+
return;
47+
} else if (\is_array($node)) {
48+
foreach ($node as $c) {
49+
self::normalizeOriginalAST($c);
50+
}
51+
}
52+
}
53+
3554
/** @dataProvider astValidFileExampleProvider */
3655
public function testFallbackFromParser(string $fileName) {
3756
$contents = file_get_contents($fileName);
3857
if ($contents === false) {
3958
$this->fail("Failed to read $fileName");
4059
}
4160
$ast = \ast\parse_code($contents, ASTConverter::AST_VERSION);
61+
self::normalizeOriginalAST($ast);
4262
$this->assertInstanceOf('\ast\Node', $ast, 'Examples must be syntactically valid PHP parseable by php-ast');
4363
$fallback_ast = \ASTConverter\ASTConverter::ast_parse_code_fallback($contents, ASTConverter::AST_VERSION);
4464
$this->assertInstanceOf('\ast\Node', $fallback_ast, 'The fallback must also return a tree of php-ast nodes');

0 commit comments

Comments
 (0)