mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2025-01-22 13:21:12 +01:00
Support insertion of nullable nodes
Still incomplete in some places and the formatting is not always ideal.
This commit is contained in:
parent
b9b6aeeed9
commit
5e565e8046
@ -110,6 +110,7 @@ abstract class PrettyPrinterAbstract
|
||||
* this node.
|
||||
*/
|
||||
protected $removalMap;
|
||||
protected $insertionMap;
|
||||
|
||||
/**
|
||||
* Creates a pretty printer instance using the given options.
|
||||
@ -363,6 +364,7 @@ abstract class PrettyPrinterAbstract
|
||||
$this->initializeLabelCharMap();
|
||||
$this->initializeFixupMap();
|
||||
$this->initializeRemovalMap();
|
||||
$this->initializeInsertionMap();
|
||||
|
||||
$this->origTokens = $origTokens;
|
||||
$this->indentLevel = 0;
|
||||
@ -436,7 +438,9 @@ abstract class PrettyPrinterAbstract
|
||||
$subNode = $node->$subNodeName;
|
||||
$origSubNode = $origNode->$subNodeName;
|
||||
|
||||
if (!$origSubNode instanceof Node || (!$subNode instanceof Node && $subNode !== null)) {
|
||||
if ((!$subNode instanceof Node && $subNode !== null)
|
||||
|| (!$origSubNode instanceof Node && $origSubNode !== null)
|
||||
) {
|
||||
if ($subNode === $origSubNode) {
|
||||
// Unchanged, can reuse old code
|
||||
continue;
|
||||
@ -462,12 +466,35 @@ abstract class PrettyPrinterAbstract
|
||||
return $this->pFallback($node);
|
||||
}
|
||||
|
||||
$extraLeft = '';
|
||||
$extraRight = '';
|
||||
if ($origSubNode !== null) {
|
||||
$subStartPos = $origSubNode->getAttribute('startTokenPos', -1);
|
||||
$subEndPos = $origSubNode->getAttribute('endTokenPos', -1);
|
||||
if ($subStartPos < 0 || $subEndPos < 0) {
|
||||
// Shouldn't happen
|
||||
return $this->pFallback($node);
|
||||
}
|
||||
} else {
|
||||
if ($subNode === null) {
|
||||
// Both null, nothing to do
|
||||
continue;
|
||||
}
|
||||
|
||||
// A node has been inserted, check if we have insertion information for it
|
||||
$key = $type . '->' . $subNodeName;
|
||||
if (!isset($this->insertionMap[$key])) {
|
||||
return $this->pFallback($node);
|
||||
}
|
||||
|
||||
list($findToken, $extraLeft, $extraRight) = $this->insertionMap[$key];
|
||||
if (null !== $findToken) {
|
||||
$subStartPos = $this->findRight($pos, $findToken) + 1;
|
||||
} else {
|
||||
$subStartPos = $pos;
|
||||
}
|
||||
$subEndPos = $subStartPos - 1;
|
||||
}
|
||||
|
||||
if (null === $subNode) {
|
||||
// A node has been removed, check if we have removal information for it
|
||||
@ -489,6 +516,8 @@ abstract class PrettyPrinterAbstract
|
||||
$result .= $this->getTokenCode($pos, $subStartPos, $indentAdjustment);
|
||||
|
||||
if (null !== $subNode) {
|
||||
$result .= $extraLeft;
|
||||
|
||||
$origIndentLevel = $this->indentLevel;
|
||||
$this->indentLevel = $this->getIndentationBefore($subStartPos) + $indentAdjustment;
|
||||
|
||||
@ -506,6 +535,8 @@ abstract class PrettyPrinterAbstract
|
||||
|
||||
$this->safeAppend($result, $res);
|
||||
$this->indentLevel = $origIndentLevel;
|
||||
|
||||
$result .= $extraRight;
|
||||
}
|
||||
|
||||
$pos = $subEndPos + 1;
|
||||
@ -848,6 +879,17 @@ abstract class PrettyPrinterAbstract
|
||||
return $pos;
|
||||
}
|
||||
|
||||
protected function findRight($pos, $findTokenType) {
|
||||
$tokens = $this->origTokens;
|
||||
for ($count = \count($tokens); $pos < $count; $pos++) {
|
||||
$type = $tokens[$pos][0];
|
||||
if ($type === $findTokenType) {
|
||||
return $pos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the LHS of a call must be wrapped in parenthesis.
|
||||
*
|
||||
@ -988,6 +1030,12 @@ abstract class PrettyPrinterAbstract
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Lazily initializes the removal map.
|
||||
*
|
||||
* The removal map is used to determine which additional tokens should be returned when a
|
||||
* certain node is replaced by null.
|
||||
*/
|
||||
protected function initializeRemovalMap() {
|
||||
if ($this->removalMap) return;
|
||||
|
||||
@ -1026,4 +1074,39 @@ abstract class PrettyPrinterAbstract
|
||||
// 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a plain node
|
||||
];
|
||||
}
|
||||
|
||||
protected function initializeInsertionMap() {
|
||||
if ($this->insertionMap) return;
|
||||
|
||||
// TODO: "yield" where both key and value are inserted doesn't work
|
||||
$this->insertionMap = [
|
||||
'Expr_ArrayDimFetch->dim' => ['[', null, null],
|
||||
'Expr_ArrayItem->key' => [null, null, ' => '],
|
||||
'Expr_Closure->returnType' => [')', ' : ', null],
|
||||
'Expr_Ternary->if' => ['?', ' ', ' '],
|
||||
'Expr_Yield->key' => [T_YIELD, ' ', ' => '],
|
||||
'Expr_Yield->value' => [T_YIELD, ' ', null],
|
||||
'Param->type' => [null, null, ' '],
|
||||
'Param->default' => [null, ' = ', null],
|
||||
'Stmt_Break->num' => [T_BREAK, ' ', null],
|
||||
'Stmt_ClassMethod->returnType' => [')', ' : ', null],
|
||||
'Stmt_Class->extends' => [null, ' extends ', null],
|
||||
'Stmt_Continue->num' => [T_CONTINUE, ' ', null],
|
||||
'Stmt_Foreach->keyVar' => [T_AS, ' ', ' => '],
|
||||
'Stmt_Function->returnType' => [')', ' : ', null],
|
||||
//'Stmt_If->else' => [null, ' ', null], // TODO
|
||||
'Stmt_Namespace->name' => [T_NAMESPACE, ' ', null],
|
||||
'Stmt_PropertyProperty->default' => [null, ' = ', null],
|
||||
'Stmt_Return->expr' => [T_RETURN, ' ', null],
|
||||
'Stmt_StaticVar->default' => [null, ' = ', null],
|
||||
//'Stmt_TraitUseAdaptation_Alias->newName' => [T_AS, ' ', null], // TODO
|
||||
'Stmt_TryCatch->finally' => [null, ' ', null],
|
||||
|
||||
// 'Expr_Exit->expr': Complicated due to optional ()
|
||||
// 'Stmt_Case->cond': Conversion from default to case
|
||||
// 'Stmt_Class->name': Unclear
|
||||
// 'Stmt_Declare->stmts': Not a proper node
|
||||
// 'Stmt_TraitUseAdaptation_Alias->newModifier': Not a proper node
|
||||
];
|
||||
}
|
||||
}
|
||||
|
166
test/code/formatPreservation/insertionOfNullable.test
Normal file
166
test/code/formatPreservation/insertionOfNullable.test
Normal file
@ -0,0 +1,166 @@
|
||||
Insertion of a nullable node
|
||||
-----
|
||||
<?php
|
||||
|
||||
// TODO: The result spacing isn't always optimal. We may want to skip whitespace in some cases.
|
||||
|
||||
function
|
||||
foo(
|
||||
$x,
|
||||
&$y
|
||||
)
|
||||
{}
|
||||
|
||||
$foo
|
||||
[
|
||||
];
|
||||
|
||||
[
|
||||
$value
|
||||
];
|
||||
|
||||
function
|
||||
()
|
||||
{};
|
||||
|
||||
$x
|
||||
?
|
||||
:
|
||||
$y;
|
||||
|
||||
yield
|
||||
$v ;
|
||||
yield ;
|
||||
|
||||
break
|
||||
;
|
||||
continue
|
||||
;
|
||||
return
|
||||
;
|
||||
|
||||
class
|
||||
X
|
||||
{
|
||||
public
|
||||
function y()
|
||||
{}
|
||||
|
||||
private
|
||||
$x
|
||||
;
|
||||
}
|
||||
|
||||
foreach (
|
||||
$x
|
||||
as
|
||||
$y
|
||||
) {}
|
||||
|
||||
static
|
||||
$var
|
||||
;
|
||||
|
||||
try {
|
||||
} catch (X
|
||||
$y) {
|
||||
}
|
||||
-----
|
||||
$stmts[0]->returnType = new Node\Name('Foo');
|
||||
$stmts[0]->params[0]->type = new Node\Identifier('int');
|
||||
$stmts[0]->params[1]->type = new Node\Identifier('array');
|
||||
$stmts[0]->params[1]->default = new Expr\ConstFetch(new Node\Name('null'));
|
||||
$stmts[1]->expr->dim = new Expr\Variable('a');
|
||||
$stmts[2]->expr->items[0]->key = new Scalar\String_('X');
|
||||
$stmts[3]->expr->returnType = new Node\Name('Bar');
|
||||
$stmts[4]->expr->if = new Expr\Variable('z');
|
||||
$stmts[5]->expr->key = new Expr\Variable('k');
|
||||
$stmts[6]->expr->value = new Expr\Variable('v');
|
||||
$stmts[7]->num = new Scalar\LNumber(2);
|
||||
$stmts[8]->num = new Scalar\LNumber(2);
|
||||
$stmts[9]->expr = new Expr\Variable('x');
|
||||
$stmts[10]->extends = new Node\Name\FullyQualified('Bar');
|
||||
$stmts[10]->stmts[0]->returnType = new Node\Name('Y');
|
||||
$stmts[10]->stmts[1]->props[0]->default = new Scalar\DNumber(42.0);
|
||||
$stmts[11]->keyVar = new Expr\Variable('z');
|
||||
$stmts[12]->vars[0]->default = new Scalar\String_('abc');
|
||||
$stmts[13]->finally = new Stmt\Finally_([]);
|
||||
-----
|
||||
<?php
|
||||
|
||||
// TODO: The result spacing isn't always optimal. We may want to skip whitespace in some cases.
|
||||
|
||||
function
|
||||
foo(
|
||||
int $x,
|
||||
array &$y = null
|
||||
) : Foo
|
||||
{}
|
||||
|
||||
$foo
|
||||
[$a
|
||||
];
|
||||
|
||||
[
|
||||
'X' => $value
|
||||
];
|
||||
|
||||
function
|
||||
() : Bar
|
||||
{};
|
||||
|
||||
$x
|
||||
? $z
|
||||
:
|
||||
$y;
|
||||
|
||||
yield $k =>
|
||||
$v ;
|
||||
yield $v ;
|
||||
|
||||
break 2
|
||||
;
|
||||
continue 2
|
||||
;
|
||||
return $x
|
||||
;
|
||||
|
||||
class
|
||||
X extends \Bar
|
||||
{
|
||||
public
|
||||
function y() : Y
|
||||
{}
|
||||
|
||||
private
|
||||
$x = 42.0
|
||||
;
|
||||
}
|
||||
|
||||
foreach (
|
||||
$x
|
||||
as $z =>
|
||||
$y
|
||||
) {}
|
||||
|
||||
static
|
||||
$var = 'abc'
|
||||
;
|
||||
|
||||
try {
|
||||
} catch (X
|
||||
$y) {
|
||||
} finally {
|
||||
}
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace
|
||||
{ echo 42; }
|
||||
-----
|
||||
$stmts[0]->name = new Node\Name('Foo');
|
||||
-----
|
||||
<?php
|
||||
|
||||
namespace Foo
|
||||
{ echo 42; }
|
Loading…
x
Reference in New Issue
Block a user