Finish listReference

This commit is contained in:
Daniil Gentili 2020-10-05 11:03:24 +02:00
parent 2cb2f73898
commit 37e0cfac27
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
2 changed files with 28 additions and 13 deletions

View File

@ -17,7 +17,6 @@ use PhpParser\Node\Expr\Cast\Bool_;
use PhpParser\Node\Expr\Closure; use PhpParser\Node\Expr\Closure;
use PhpParser\Node\Expr\FuncCall; use PhpParser\Node\Expr\FuncCall;
use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\New_;
use PhpParser\Node\Expr\StaticCall; use PhpParser\Node\Expr\StaticCall;
use PhpParser\Node\Expr\Ternary; use PhpParser\Node\Expr\Ternary;
use PhpParser\Node\Expr\Variable; use PhpParser\Node\Expr\Variable;
@ -181,13 +180,13 @@ class Context
$parent->setAttribute('currentNodeIndex', $nodeKeyIndex - 1); $parent->setAttribute('currentNodeIndex', $nodeKeyIndex - 1);
return; // Done, inserted! return; // Done, inserted!
} }
// Cannot insert, parent is not a statement // Cannot insert, parent is not a statement
$node = &$parent->{$nodeKey}; $node = &$parent->{$nodeKey};
// If we insert before a conditional branch of a conditional expression, // If we insert before a conditional branch of a conditional expression,
// make sure the conditional branch has no side effects; // make sure the conditional branch has no side effects;
// if it does, turn the entire conditional expression into an if, and bubble it up // if it does, turn the entire conditional expression into an if, and bubble it up
// //
// Unless we want to go crazy, do not consider side effect evaluation order for stuff like function call arguments, maths and so on. // Unless we want to go crazy, do not consider side effect evaluation order for stuff like function call arguments, maths and so on.
// //
if ($node instanceof BooleanOr && $nodeKey === 'right' && Tools::hasSideEffects($node->right)) { if ($node instanceof BooleanOr && $nodeKey === 'right' && Tools::hasSideEffects($node->right)) {

View File

@ -6,6 +6,7 @@ use Phabel\Context;
use Phabel\Plugin; use Phabel\Plugin;
use PhpParser\Node\Expr\Array_; use PhpParser\Node\Expr\Array_;
use PhpParser\Node\Expr\ArrayDimFetch; use PhpParser\Node\Expr\ArrayDimFetch;
use PhpParser\Node\Expr\ArrayItem;
use PhpParser\Node\Expr\Assign; use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\AssignRef; use PhpParser\Node\Expr\AssignRef;
use PhpParser\Node\Expr\List_; use PhpParser\Node\Expr\List_;
@ -26,13 +27,13 @@ class ListReference extends Plugin
*/ */
public function enterForeach(Foreach_ $node, Context $ctx): void public function enterForeach(Foreach_ $node, Context $ctx): void
{ {
if (!($node->valueVar instanceof List_ || $node->valueVar instanceof Array_) || !$this->shouldSplit($node->valueVar)) { if (!($node->valueVar instanceof List_ || $node->valueVar instanceof Array_) || !$this->shouldSplit($node->valueVar, true)) {
return; return;
} }
$list = $node->valueVar; $list = $node->valueVar;
$var = $node->valueVar = $ctx->getVariable(); $var = $node->valueVar = $ctx->getVariable();
$assignments = self::splitList($list, $var); $assignments = self::splitList($list, $var);
$node->stmts $node->stmts = \array_merge($assignments, $node->stmts);
} }
/** /**
* Parse list assignment with custom keys. * Parse list assignment with custom keys.
@ -46,8 +47,19 @@ class ListReference extends Plugin
if (!($node->var instanceof List_ || $node->var instanceof Array_) || !$this->shouldSplit($node->var)) { if (!($node->var instanceof List_ || $node->var instanceof Array_) || !$this->shouldSplit($node->var)) {
return; return;
} }
[$node->var, $array] = $this->splitList($node->var, $ctx); $isStmt = $ctx->parents[0]->getAttribute('currentNode') === 'stmts';
$node->expr = self::callPoly('destructure', $array, $node->expr); $list = $node->var;
$var = $ctx->getVariable();
$assignments = self::splitList($list, $var);
if ($isStmt) {
$last = \array_pop($assignments);
$ctx->insertBefore($node, new Assign($node->var, $node->expr), ...$assignments);
return $last;
}
// On newer versions of php, list assignment returns the original array
$ctx->insertBefore($node, new Assign($var, $node->expr), ...$assignments);
return $var;
} }
/** /**
* Split referenced list into multiple assignments. * Split referenced list into multiple assignments.
@ -66,9 +78,7 @@ class ListReference extends Plugin
continue; continue;
} }
$curKey = $item->key ?? $key++; $curKey = $item->key ?? $key++;
if ($item->value instanceof List_ || $item->value instanceof Array_) { if ($item->byRef) {
// Do nothing, will re-split later if needed
} else if ($item->byRef) {
$assignments []= new AssignRef($item->value, new ArrayDimFetch($var, $curKey)); $assignments []= new AssignRef($item->value, new ArrayDimFetch($var, $curKey));
} else { } else {
$assignments []= new Assign($item->value, new ArrayDimFetch($var, $curKey)); $assignments []= new Assign($item->value, new ArrayDimFetch($var, $curKey));
@ -79,15 +89,21 @@ class ListReference extends Plugin
/** /**
* Whether this is a referenced list. * Whether this is a referenced list.
* *
* @param List_|Array_ $list List * @param List_|Array_ $list List
* @param bool $recurse Whether to recurse while checking
* *
* @return boolean * @return boolean
*/ */
private function shouldSplit($list): bool private function shouldSplit($list, bool $recurse = false): bool
{ {
/** @var ArrayItem $item */
foreach ($list->items ?? [] as $item) { foreach ($list->items ?? [] as $item) {
if ($item->byRef) { if ($item->byRef) {
return true; return true;
} elseif ($recurse && ($item->value instanceof List_ || $item->value instanceof Array_)) {
if ($this->shouldSplit($item->value, $recurse)) {
return true;
}
} }
} }
return false; return false;