mirror of
https://github.com/phabelio/PHP-Parser.git
synced 2024-11-26 20:14:46 +01:00
Add support for union types
We definitely need to introduce a general "Type" abstraction in the next major version.
This commit is contained in:
parent
5b1cd2e4f2
commit
664c10121e
@ -461,6 +461,7 @@ parameter:
|
|||||||
type_expr:
|
type_expr:
|
||||||
type { $$ = $1; }
|
type { $$ = $1; }
|
||||||
| '?' type { $$ = Node\NullableType[$2]; }
|
| '?' type { $$ = Node\NullableType[$2]; }
|
||||||
|
| union_type { $$ = Node\UnionType[$1]; }
|
||||||
;
|
;
|
||||||
|
|
||||||
type:
|
type:
|
||||||
@ -469,6 +470,11 @@ type:
|
|||||||
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
| T_CALLABLE { $$ = Node\Identifier['callable']; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
union_type:
|
||||||
|
type '|' type { init($1, $3); }
|
||||||
|
| union_type '|' type { push($1, $3); }
|
||||||
|
;
|
||||||
|
|
||||||
optional_type:
|
optional_type:
|
||||||
/* empty */ { $$ = null; }
|
/* empty */ { $$ = null; }
|
||||||
| type_expr { $$ = $1; }
|
| type_expr { $$ = $1; }
|
||||||
|
@ -17,7 +17,7 @@ class ArrowFunction extends Expr implements FunctionLike
|
|||||||
/** @var Node\Param[] */
|
/** @var Node\Param[] */
|
||||||
public $params = [];
|
public $params = [];
|
||||||
|
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType */
|
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
|
|
||||||
/** @var Expr */
|
/** @var Expr */
|
||||||
|
@ -16,7 +16,7 @@ class Closure extends Expr implements FunctionLike
|
|||||||
public $params;
|
public $params;
|
||||||
/** @var ClosureUse[] use()s */
|
/** @var ClosureUse[] use()s */
|
||||||
public $uses;
|
public $uses;
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
|
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
/** @var Node\Stmt[] Statements */
|
/** @var Node\Stmt[] Statements */
|
||||||
public $stmts;
|
public $stmts;
|
||||||
|
@ -23,7 +23,7 @@ interface FunctionLike extends Node
|
|||||||
/**
|
/**
|
||||||
* Get the declared return type or null
|
* Get the declared return type or null
|
||||||
*
|
*
|
||||||
* @return null|Identifier|Node\Name|Node\NullableType
|
* @return null|Identifier|Node\Name|Node\NullableType|Node\UnionType
|
||||||
*/
|
*/
|
||||||
public function getReturnType();
|
public function getReturnType();
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use PhpParser\NodeAbstract;
|
|||||||
|
|
||||||
class Param extends NodeAbstract
|
class Param extends NodeAbstract
|
||||||
{
|
{
|
||||||
/** @var null|Identifier|Name|NullableType Type declaration */
|
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
|
||||||
public $type;
|
public $type;
|
||||||
/** @var bool Whether parameter is passed by reference */
|
/** @var bool Whether parameter is passed by reference */
|
||||||
public $byRef;
|
public $byRef;
|
||||||
@ -20,12 +20,12 @@ class Param extends NodeAbstract
|
|||||||
/**
|
/**
|
||||||
* Constructs a parameter node.
|
* Constructs a parameter node.
|
||||||
*
|
*
|
||||||
* @param Expr\Variable|Expr\Error $var Parameter variable
|
* @param Expr\Variable|Expr\Error $var Parameter variable
|
||||||
* @param null|Expr $default Default value
|
* @param null|Expr $default Default value
|
||||||
* @param null|string|Identifier|Name|NullableType $type Type declaration
|
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
|
||||||
* @param bool $byRef Whether is passed by reference
|
* @param bool $byRef Whether is passed by reference
|
||||||
* @param bool $variadic Whether this is a variadic argument
|
* @param bool $variadic Whether this is a variadic argument
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$var, Expr $default = null, $type = null,
|
$var, Expr $default = null, $type = null,
|
||||||
|
@ -15,7 +15,7 @@ class ClassMethod extends Node\Stmt implements FunctionLike
|
|||||||
public $name;
|
public $name;
|
||||||
/** @var Node\Param[] Parameters */
|
/** @var Node\Param[] Parameters */
|
||||||
public $params;
|
public $params;
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
|
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
/** @var Node\Stmt[]|null Statements */
|
/** @var Node\Stmt[]|null Statements */
|
||||||
public $stmts;
|
public $stmts;
|
||||||
|
@ -16,7 +16,7 @@ class Function_ extends Node\Stmt implements FunctionLike
|
|||||||
public $name;
|
public $name;
|
||||||
/** @var Node\Param[] Parameters */
|
/** @var Node\Param[] Parameters */
|
||||||
public $params;
|
public $params;
|
||||||
/** @var null|Node\Identifier|Node\Name|Node\NullableType Return type */
|
/** @var null|Node\Identifier|Node\Name|Node\NullableType|Node\UnionType Return type */
|
||||||
public $returnType;
|
public $returnType;
|
||||||
/** @var Node\Stmt[] Statements */
|
/** @var Node\Stmt[] Statements */
|
||||||
public $stmts;
|
public $stmts;
|
||||||
|
@ -6,6 +6,7 @@ use PhpParser\Node;
|
|||||||
use PhpParser\Node\Identifier;
|
use PhpParser\Node\Identifier;
|
||||||
use PhpParser\Node\Name;
|
use PhpParser\Node\Name;
|
||||||
use PhpParser\Node\NullableType;
|
use PhpParser\Node\NullableType;
|
||||||
|
use PhpParser\Node\UnionType;
|
||||||
|
|
||||||
class Property extends Node\Stmt
|
class Property extends Node\Stmt
|
||||||
{
|
{
|
||||||
@ -13,16 +14,16 @@ class Property extends Node\Stmt
|
|||||||
public $flags;
|
public $flags;
|
||||||
/** @var PropertyProperty[] Properties */
|
/** @var PropertyProperty[] Properties */
|
||||||
public $props;
|
public $props;
|
||||||
/** @var null|Identifier|Name|NullableType Type declaration */
|
/** @var null|Identifier|Name|NullableType|UnionType Type declaration */
|
||||||
public $type;
|
public $type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a class property list node.
|
* Constructs a class property list node.
|
||||||
*
|
*
|
||||||
* @param int $flags Modifiers
|
* @param int $flags Modifiers
|
||||||
* @param PropertyProperty[] $props Properties
|
* @param PropertyProperty[] $props Properties
|
||||||
* @param array $attributes Additional attributes
|
* @param array $attributes Additional attributes
|
||||||
* @param null|string|Identifier|Name|NullableType $type Type declaration
|
* @param null|string|Identifier|Name|NullableType|UnionType $type Type declaration
|
||||||
*/
|
*/
|
||||||
public function __construct(int $flags, array $props, array $attributes = [], $type = null) {
|
public function __construct(int $flags, array $props, array $attributes = [], $type = null) {
|
||||||
$this->attributes = $attributes;
|
$this->attributes = $attributes;
|
||||||
|
30
lib/PhpParser/Node/UnionType.php
Normal file
30
lib/PhpParser/Node/UnionType.php
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace PhpParser\Node;
|
||||||
|
|
||||||
|
use PhpParser\NodeAbstract;
|
||||||
|
|
||||||
|
class UnionType extends NodeAbstract
|
||||||
|
{
|
||||||
|
/** @var (Identifier|Name)[] Types */
|
||||||
|
public $types;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a union type.
|
||||||
|
*
|
||||||
|
* @param (Identifier|Name)[] $types Types
|
||||||
|
* @param array $attributes Additional attributes
|
||||||
|
*/
|
||||||
|
public function __construct(array $types, array $attributes = []) {
|
||||||
|
$this->attributes = $attributes;
|
||||||
|
$this->types = $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSubNodeNames() : array {
|
||||||
|
return ['types'];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getType() : string {
|
||||||
|
return 'UnionType';
|
||||||
|
}
|
||||||
|
}
|
@ -162,12 +162,18 @@ class NameResolver extends NodeVisitorAbstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
private function resolveType($node) {
|
private function resolveType($node) {
|
||||||
|
if ($node instanceof Name) {
|
||||||
|
return $this->resolveClassName($node);
|
||||||
|
}
|
||||||
if ($node instanceof Node\NullableType) {
|
if ($node instanceof Node\NullableType) {
|
||||||
$node->type = $this->resolveType($node->type);
|
$node->type = $this->resolveType($node->type);
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
if ($node instanceof Name) {
|
if ($node instanceof Node\UnionType) {
|
||||||
return $this->resolveClassName($node);
|
foreach ($node->types as &$type) {
|
||||||
|
$type = $this->resolveType($type);
|
||||||
|
}
|
||||||
|
return $node;
|
||||||
}
|
}
|
||||||
return $node;
|
return $node;
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -656,6 +656,8 @@ abstract class ParserAbstract implements Parser
|
|||||||
'iterable' => true,
|
'iterable' => true,
|
||||||
'void' => true,
|
'void' => true,
|
||||||
'object' => true,
|
'object' => true,
|
||||||
|
'null' => true,
|
||||||
|
'false' => true,
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!$name->isUnqualified()) {
|
if (!$name->isUnqualified()) {
|
||||||
|
@ -37,6 +37,10 @@ class Standard extends PrettyPrinterAbstract
|
|||||||
return '?' . $this->p($node->type);
|
return '?' . $this->p($node->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function pUnionType(Node\UnionType $node) {
|
||||||
|
return $this->pImplode($node->types, '|');
|
||||||
|
}
|
||||||
|
|
||||||
protected function pIdentifier(Node\Identifier $node) {
|
protected function pIdentifier(Node\Identifier $node) {
|
||||||
return $node->name;
|
return $node->name;
|
||||||
}
|
}
|
||||||
|
@ -1295,6 +1295,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
//'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
|
//'Expr_ShellExec->parts' => '', // TODO These need to be treated more carefully
|
||||||
//'Scalar_Encapsed->parts' => '',
|
//'Scalar_Encapsed->parts' => '',
|
||||||
'Stmt_Catch->types' => '|',
|
'Stmt_Catch->types' => '|',
|
||||||
|
'UnionType->types' => '|',
|
||||||
'Stmt_If->elseifs' => ' ',
|
'Stmt_If->elseifs' => ' ',
|
||||||
'Stmt_TryCatch->catches' => ' ',
|
'Stmt_TryCatch->catches' => ' ',
|
||||||
|
|
||||||
@ -1396,6 +1397,7 @@ abstract class PrettyPrinterAbstract
|
|||||||
* Stmt_TraitUseAdaptation_Precedence->insteadof
|
* Stmt_TraitUseAdaptation_Precedence->insteadof
|
||||||
* Stmt_Unset->vars
|
* Stmt_Unset->vars
|
||||||
* Stmt_Use->uses
|
* Stmt_Use->uses
|
||||||
|
* UnionType->types
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* TODO
|
/* TODO
|
||||||
|
@ -94,13 +94,6 @@ namespace Baz {
|
|||||||
C;
|
C;
|
||||||
E;
|
E;
|
||||||
K;
|
K;
|
||||||
|
|
||||||
class ClassWithTypeProperties
|
|
||||||
{
|
|
||||||
public float $php = 7.4;
|
|
||||||
public ?Foo $person;
|
|
||||||
protected static ?bool $probability;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EOC;
|
EOC;
|
||||||
$expectedCode = <<<'EOC'
|
$expectedCode = <<<'EOC'
|
||||||
@ -169,12 +162,6 @@ namespace Baz {
|
|||||||
\Y\T\B\C;
|
\Y\T\B\C;
|
||||||
\Y\T\D\E;
|
\Y\T\D\E;
|
||||||
\Z\T\K;
|
\Z\T\K;
|
||||||
class ClassWithTypeProperties
|
|
||||||
{
|
|
||||||
public float $php = 7.4;
|
|
||||||
public ?\Baz\Foo $person;
|
|
||||||
protected static ?bool $probability;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
EOC;
|
EOC;
|
||||||
|
|
||||||
@ -210,6 +197,14 @@ class A extends B implements C, D {
|
|||||||
|
|
||||||
interface A extends C, D {
|
interface A extends C, D {
|
||||||
public function a(A $a) : A;
|
public function a(A $a) : A;
|
||||||
|
public function b(A|B|int $a): A|B|int;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClassWithTypeProperties {
|
||||||
|
public float $php = 7.4;
|
||||||
|
public ?Foo $person;
|
||||||
|
protected static ?bool $probability;
|
||||||
|
public A|B|int $prop;
|
||||||
}
|
}
|
||||||
|
|
||||||
function f(A $a) : A {}
|
function f(A $a) : A {}
|
||||||
@ -252,6 +247,14 @@ class A extends \NS\B implements \NS\C, \NS\D
|
|||||||
interface A extends \NS\C, \NS\D
|
interface A extends \NS\C, \NS\D
|
||||||
{
|
{
|
||||||
public function a(\NS\A $a) : \NS\A;
|
public function a(\NS\A $a) : \NS\A;
|
||||||
|
public function b(\NS\A|\NS\B|int $a) : \NS\A|\NS\B|int;
|
||||||
|
}
|
||||||
|
class ClassWithTypeProperties
|
||||||
|
{
|
||||||
|
public float $php = 7.4;
|
||||||
|
public ?\NS\Foo $person;
|
||||||
|
protected static ?bool $probability;
|
||||||
|
public \NS\A|\NS\B|int $prop;
|
||||||
}
|
}
|
||||||
function f(\NS\A $a) : \NS\A
|
function f(\NS\A $a) : \NS\A
|
||||||
{
|
{
|
||||||
|
@ -306,4 +306,14 @@ $stmts[0]->expr->expr->items[] = new Expr\ArrayItem(new Scalar\LNumber(24));
|
|||||||
$array = [
|
$array = [
|
||||||
1, 2,
|
1, 2,
|
||||||
3, 24,
|
3, 24,
|
||||||
];
|
];
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
function test(): A
|
||||||
|
|B {}
|
||||||
|
-----
|
||||||
|
$stmts[0]->returnType->types[] = new Node\Name('C');
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
function test(): A
|
||||||
|
|B|C {}
|
@ -38,4 +38,15 @@ function foo(
|
|||||||
$b,
|
$b,
|
||||||
$x,
|
$x,
|
||||||
$y
|
$y
|
||||||
) {}
|
) {}
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
function test(): A
|
||||||
|
|B
|
||||||
|
|C {}
|
||||||
|
-----
|
||||||
|
array_pop($stmts[0]->returnType->types);
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
function test(): A
|
||||||
|
|B {}
|
92
test/code/parser/stmt/function/unionTypes.test
Normal file
92
test/code/parser/stmt/function/unionTypes.test
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
Union types
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public A|iterable|null $prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test(A|B $a): int|false {}
|
||||||
|
-----
|
||||||
|
!!php7
|
||||||
|
array(
|
||||||
|
0: Stmt_Class(
|
||||||
|
flags: 0
|
||||||
|
name: Identifier(
|
||||||
|
name: Test
|
||||||
|
)
|
||||||
|
extends: null
|
||||||
|
implements: array(
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
0: Stmt_Property(
|
||||||
|
flags: MODIFIER_PUBLIC (1)
|
||||||
|
type: UnionType(
|
||||||
|
types: array(
|
||||||
|
0: Name(
|
||||||
|
parts: array(
|
||||||
|
0: A
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Identifier(
|
||||||
|
name: iterable
|
||||||
|
)
|
||||||
|
2: Identifier(
|
||||||
|
name: null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
props: array(
|
||||||
|
0: Stmt_PropertyProperty(
|
||||||
|
name: VarLikeIdentifier(
|
||||||
|
name: prop
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Stmt_Function(
|
||||||
|
byRef: false
|
||||||
|
name: Identifier(
|
||||||
|
name: test
|
||||||
|
)
|
||||||
|
params: array(
|
||||||
|
0: Param(
|
||||||
|
type: UnionType(
|
||||||
|
types: array(
|
||||||
|
0: Name(
|
||||||
|
parts: array(
|
||||||
|
0: A
|
||||||
|
)
|
||||||
|
)
|
||||||
|
1: Name(
|
||||||
|
parts: array(
|
||||||
|
0: B
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
byRef: false
|
||||||
|
variadic: false
|
||||||
|
var: Expr_Variable(
|
||||||
|
name: a
|
||||||
|
)
|
||||||
|
default: null
|
||||||
|
)
|
||||||
|
)
|
||||||
|
returnType: UnionType(
|
||||||
|
types: array(
|
||||||
|
0: Identifier(
|
||||||
|
name: int
|
||||||
|
)
|
||||||
|
1: Identifier(
|
||||||
|
name: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
stmts: array(
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
18
test/code/prettyPrinter/stmt/union_types.test
Normal file
18
test/code/prettyPrinter/stmt/union_types.test
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
Union types
|
||||||
|
-----
|
||||||
|
<?php
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
public A|iterable|null $prop;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test(A|B $a): int|false {}
|
||||||
|
-----
|
||||||
|
!!php7
|
||||||
|
class Test
|
||||||
|
{
|
||||||
|
public A|iterable|null $prop;
|
||||||
|
}
|
||||||
|
function test(A|B $a) : int|false
|
||||||
|
{
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user