Support parsing PHP 7.4's arrow functions

This requires PHP 7.4-dev from May 2nd or later.

Add various test cases of this working,
as well as tests of error tolerance.

Fixes #291
This commit is contained in:
Tyson Andre 2019-05-11 21:01:13 -04:00
Родитель c74eb57ee5
Коммит 2836a80f87
15 изменённых файлов: 2567 добавлений и 4 удалений

Просмотреть файл

@ -0,0 +1,48 @@
<?php
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
namespace Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\FunctionLike;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\Expression;
use Microsoft\PhpParser\Node\FunctionHeader;
use Microsoft\PhpParser\Node\FunctionReturnType;
use Microsoft\PhpParser\Token;
class ArrowFunctionCreationExpression extends Expression implements FunctionLike {
/** @var Token|null */
public $staticModifier;
use FunctionHeader, FunctionReturnType;
/** @var Token `=>` */
public $arrowToken;
/** @var Node|Token */
public $resultExpression;
const CHILD_NAMES = [
'staticModifier',
// FunctionHeader
'functionKeyword',
'byRefToken',
'name',
'openParen',
'parameters',
'closeParen',
// FunctionReturnType
'colonToken',
'questionToken',
'returnType',
// body
'arrowToken',
'resultExpression',
];
}

Просмотреть файл

@ -10,6 +10,7 @@ use Microsoft\PhpParser\NamespacedNameInterface;
use Microsoft\PhpParser\NamespacedNameTrait;
use Microsoft\PhpParser\Node;
use Microsoft\PhpParser\Node\Expression\AnonymousFunctionCreationExpression;
use Microsoft\PhpParser\Node\Expression\ArrowFunctionCreationExpression;
use Microsoft\PhpParser\Node\Expression\CallExpression;
use Microsoft\PhpParser\Node\Expression\ObjectCreationExpression;
use Microsoft\PhpParser\ResolvedName;
@ -180,6 +181,7 @@ class QualifiedName extends Node implements NamespacedNameInterface {
$this->parent instanceof Node\Expression\MemberAccessExpression || $this->parent instanceof CallExpression ||
$this->parent instanceof ObjectCreationExpression ||
$this->parent instanceof Node\Expression\ScopedPropertyAccessExpression || $this->parent instanceof AnonymousFunctionCreationExpression ||
$this->parent instanceof ArrowFunctionCreationExpression ||
($this->parent instanceof Node\Expression\BinaryExpression && $this->parent->operator->kind === TokenKind::InstanceOfKeyword)
);
}

Просмотреть файл

@ -19,6 +19,7 @@ use Microsoft\PhpParser\Node\Expression\{
AnonymousFunctionCreationExpression,
ArgumentExpression,
ArrayCreationExpression,
ArrowFunctionCreationExpression,
AssignmentExpression,
BinaryExpression,
BracedExpression,
@ -563,7 +564,7 @@ class Parser {
// function-static-declaration
case TokenKind::StaticKeyword:
// Check that this is not an anonymous-function-creation-expression
if (!$this->lookahead([TokenKind::FunctionKeyword, TokenKind::OpenParenToken, TokenKind::ColonColonToken])) {
if (!$this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword, TokenKind::OpenParenToken, TokenKind::ColonColonToken])) {
return $this->parseFunctionStaticDeclaration($parentNode);
}
break;
@ -899,6 +900,7 @@ class Parser {
// anonymous-function-creation-expression
case TokenKind::StaticKeyword:
case TokenKind::FunctionKeyword:
case TokenKind::FnKeyword:
return true;
}
return \in_array($token->kind, $this->reservedWordTokens, true);
@ -978,12 +980,13 @@ class Parser {
case TokenKind::StaticKeyword:
// handle `static::`, `static(`, `new static;`, `instanceof static`
if (($this->lookahead([TokenKind::ColonColonToken, TokenKind::OpenParenToken])) ||
(!$this->lookahead(TokenKind::FunctionKeyword))
(!$this->lookahead([TokenKind::FunctionKeyword, TokenKind::FnKeyword]))
) {
return $this->parseQualifiedName($parentNode);
}
// Could be `static function` anonymous function creation expression, so flow through
case TokenKind::FunctionKeyword:
case TokenKind::FnKeyword:
return $this->parseAnonymousFunctionCreationExpression($parentNode);
case TokenKind::TrueReservedWord:
@ -3326,15 +3329,56 @@ class Parser {
}
private function parseAnonymousFunctionCreationExpression($parentNode) {
$staticModifier = $this->eatOptional1(TokenKind::StaticKeyword);
if ($this->getCurrentToken()->kind === TokenKind::FnKeyword) {
return $this->parseArrowFunctionCreationExpression($parentNode, $staticModifier);
}
$anonymousFunctionCreationExpression = new AnonymousFunctionCreationExpression();
$anonymousFunctionCreationExpression->parent = $parentNode;
$anonymousFunctionCreationExpression->staticModifier = $this->eatOptional1(TokenKind::StaticKeyword);
$anonymousFunctionCreationExpression->staticModifier = $staticModifier;
$this->parseFunctionType($anonymousFunctionCreationExpression, false, true);
return $anonymousFunctionCreationExpression;
}
private function parseArrowFunctionCreationExpression($parentNode, $staticModifier) : ArrowFunctionCreationExpression {
$arrowFunction = new ArrowFunctionCreationExpression();
$arrowFunction->parent = $parentNode;
$arrowFunction->staticModifier = $staticModifier;
$arrowFunction->functionKeyword = $this->eat1(TokenKind::FnKeyword);
$arrowFunction->byRefToken = $this->eatOptional1(TokenKind::AmpersandToken);
$arrowFunction->name = $this->eatOptional($this->nameOrKeywordOrReservedWordTokens);
if (isset($arrowFunction->name)) {
// Anonymous functions should not have names.
// This is based on the code for AnonymousFunctionCreationExpression.
$arrowFunction->name->kind = TokenKind::Name;
$arrowFunction->name = new SkippedToken($arrowFunction->name); // TODO instead handle this during post-walk
}
$arrowFunction->openParen = $this->eat1(TokenKind::OpenParenToken);
$arrowFunction->parameters = $this->parseDelimitedList(
DelimitedList\ParameterDeclarationList::class,
TokenKind::CommaToken,
$this->isParameterStartFn(),
$this->parseParameterFn(),
$arrowFunction);
$arrowFunction->closeParen = $this->eat1(TokenKind::CloseParenToken);
if ($this->checkToken(TokenKind::ColonToken)) {
$arrowFunction->colonToken = $this->eat1(TokenKind::ColonToken);
$arrowFunction->questionToken = $this->eatOptional1(TokenKind::QuestionToken);
$arrowFunction->returnType = $this->parseReturnTypeDeclaration($arrowFunction);
}
$arrowFunction->arrowToken = $this->eat1(TokenKind::DoubleArrowToken);
$arrowFunction->resultExpression = $this->parseExpression($arrowFunction);
return $arrowFunction;
}
private function parseAnonymousFunctionUseClause($parentNode) {
$anonymousFunctionUseClause = new AnonymousFunctionUseClause();
$anonymousFunctionUseClause->parent = $parentNode;

Просмотреть файл

@ -6,9 +6,10 @@
namespace Microsoft\PhpParser;
// If this predates PHP 7.4, T_COALESCE_EQUAL is unavailable.
// If this predates PHP 7.4, T_COALESCE_EQUAL and T_FN are unavailable.
// The replacement value is arbitrary - it just has to be different from other values of token constants.
define(__NAMESPACE__ . '\T_COALESCE_EQUAL', defined('T_COALESCE_EQUAL') ? constant('T_COALESCE_EQUAL') : 'T_COALESCE_EQUAL');
define(__NAMESPACE__ . '\T_FN', defined('T_FN') ? constant('T_FN') : 'T_FN');
/**
* Tokenizes content using PHP's built-in `token_get_all`, and converts to "lightweight" Token representation.
@ -192,6 +193,7 @@ class PhpTokenizer implements TokenStreamProviderInterface {
T_FINALLY => TokenKind::FinallyKeyword,
T_FOR => TokenKind::ForKeyword,
T_FOREACH => TokenKind::ForeachKeyword,
T_FN => TokenKind::FnKeyword,
T_FUNCTION => TokenKind::FunctionKeyword,
T_GLOBAL => TokenKind::GlobalKeyword,
T_GOTO => TokenKind::GotoKeyword,

Просмотреть файл

@ -85,6 +85,7 @@ class TokenKind {
const XorKeyword = 165;
const YieldKeyword = 166;
const YieldFromKeyword = 167;
const FnKeyword = 168;
const OpenBracketToken = 201;
const CloseBracketToken = 202;

Просмотреть файл

@ -43,6 +43,7 @@ class TokenStringMaps {
"finally" => TokenKind::FinallyKeyword,
"for" => TokenKind::ForKeyword,
"foreach" => TokenKind::ForeachKeyword,
"fn" => TokenKind::FnKeyword,
"function" => TokenKind::FunctionKeyword,
"global" => TokenKind::GlobalKeyword,
"goto" => TokenKind::GotoKeyword,

Просмотреть файл

@ -0,0 +1,9 @@
<?php
$f = fn(array $x) => $x;
fn(): int => $x;
fn($x = 42) => yield $x;
fn(&$x) => $x;
fn&($x) => $x;
fn($x, ...$rest) => $rest;
static fn() => 1;
$f = static fn() => 2;

Просмотреть файл

@ -0,0 +1 @@
[]

Просмотреть файл

@ -0,0 +1,568 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": {
"kind": "ArrayKeyword",
"textLength": 5
},
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": {
"kind": "ColonToken",
"textLength": 1
},
"questionToken": null,
"returnType": {
"kind": "IntReservedWord",
"textLength": 3
},
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": {
"kind": "EqualsToken",
"textLength": 1
},
"default": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 2
}
}
}
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"YieldExpression": {
"yieldOrYieldFromKeyword": {
"kind": "YieldKeyword",
"textLength": 5
},
"arrayElement": {
"ArrayElement": {
"elementKey": null,
"arrowToken": null,
"byRef": null,
"elementValue": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": {
"kind": "AmpersandToken",
"textLength": 1
},
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": {
"kind": "AmpersandToken",
"textLength": 1
},
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
},
{
"kind": "CommaToken",
"textLength": 1
},
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": null,
"dotDotDotToken": {
"kind": "DotDotDotToken",
"textLength": 3
},
"variableName": {
"kind": "VariableName",
"textLength": 5
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 5
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"ArrowFunctionCreationExpression": {
"staticModifier": {
"kind": "StaticKeyword",
"textLength": 6
},
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": {
"kind": "StaticKeyword",
"textLength": 6
},
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 1
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,9 @@
<?php
$fn1 = fn(\stdClass $x) : => $x;
$fn2 = fn(): ? => ($x);
$fn3 = fn($x = 42 => yield $x;
$fn4 = fn(&$x) $x;
$fn5 = fn&Ns\MyClass $x) : ?Ns\MyClass => $x;
$fn = fn$str) => $str;
$cb = fn( => $undef;
$fn = fn() => ;

Просмотреть файл

@ -0,0 +1,56 @@
[
{
"kind": 0,
"message": "'ReturnType' expected.",
"start": 31,
"length": 0
},
{
"kind": 0,
"message": "'ReturnType' expected.",
"start": 53,
"length": 0
},
{
"kind": 0,
"message": "')' expected.",
"start": 80,
"length": 0
},
{
"kind": 0,
"message": "'=>' expected.",
"start": 108,
"length": 0
},
{
"kind": 0,
"message": "Unexpected 'Name'",
"start": 123,
"length": 2
},
{
"kind": 0,
"message": "'(' expected.",
"start": 125,
"length": 0
},
{
"kind": 0,
"message": "'(' expected.",
"start": 167,
"length": 0
},
{
"kind": 0,
"message": "')' expected.",
"start": 191,
"length": 0
},
{
"kind": 0,
"message": "'Expression' expected.",
"start": 216,
"length": 0
}
]

Просмотреть файл

@ -0,0 +1,729 @@
{
"SourceFileNode": {
"statementList": [
{
"InlineHtml": {
"scriptSectionEndTag": null,
"text": null,
"scriptSectionStartTag": {
"kind": "ScriptSectionStartTag",
"textLength": 6
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": {
"QualifiedName": {
"globalSpecifier": {
"kind": "BackslashToken",
"textLength": 1
},
"relativeSpecifier": null,
"nameParts": [
{
"kind": "Name",
"textLength": 8
}
]
}
},
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": {
"kind": "ColonToken",
"textLength": 1
},
"questionToken": null,
"returnType": {
"error": "MissingToken",
"kind": "ReturnType",
"textLength": 0
},
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": {
"kind": "ColonToken",
"textLength": 1
},
"questionToken": {
"kind": "QuestionToken",
"textLength": 1
},
"returnType": {
"error": "MissingToken",
"kind": "ReturnType",
"textLength": 0
},
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"ParenthesizedExpression": {
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"expression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": {
"kind": "EqualsToken",
"textLength": 1
},
"default": {
"NumericLiteral": {
"children": {
"kind": "IntegerLiteralToken",
"textLength": 2
}
}
}
}
}
]
}
},
"closeParen": {
"error": "MissingToken",
"kind": "CloseParenToken",
"textLength": 0
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"YieldExpression": {
"yieldOrYieldFromKeyword": {
"kind": "YieldKeyword",
"textLength": 5
},
"arrayElement": {
"ArrayElement": {
"elementKey": null,
"arrowToken": null,
"byRef": null,
"elementValue": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": {
"kind": "AmpersandToken",
"textLength": 1
},
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"error": "MissingToken",
"kind": "DoubleArrowToken",
"textLength": 0
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": {
"kind": "AmpersandToken",
"textLength": 1
},
"name": {
"error": "SkippedToken",
"kind": "Name",
"textLength": 2
},
"openParen": {
"error": "MissingToken",
"kind": "OpenParenToken",
"textLength": 0
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": {
"QualifiedName": {
"globalSpecifier": {
"kind": "BackslashToken",
"textLength": 1
},
"relativeSpecifier": null,
"nameParts": [
{
"kind": "Name",
"textLength": 7
}
]
}
},
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 2
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": {
"kind": "ColonToken",
"textLength": 1
},
"questionToken": {
"kind": "QuestionToken",
"textLength": 1
},
"returnType": {
"QualifiedName": {
"globalSpecifier": null,
"relativeSpecifier": null,
"nameParts": [
{
"kind": "Name",
"textLength": 2
},
{
"kind": "BackslashToken",
"textLength": 1
},
{
"kind": "Name",
"textLength": 7
}
]
}
},
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 2
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 3
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"error": "MissingToken",
"kind": "OpenParenToken",
"textLength": 0
},
"parameters": {
"ParameterDeclarationList": {
"children": [
{
"Parameter": {
"questionToken": null,
"typeDeclaration": null,
"byRefToken": null,
"dotDotDotToken": null,
"variableName": {
"kind": "VariableName",
"textLength": 4
},
"equalsToken": null,
"default": null
}
}
]
}
},
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 4
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 3
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": null,
"closeParen": {
"error": "MissingToken",
"kind": "CloseParenToken",
"textLength": 0
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 6
}
}
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
},
{
"ExpressionStatement": {
"expression": {
"AssignmentExpression": {
"leftOperand": {
"Variable": {
"dollar": null,
"name": {
"kind": "VariableName",
"textLength": 3
}
}
},
"operator": {
"kind": "EqualsToken",
"textLength": 1
},
"byRef": null,
"rightOperand": {
"ArrowFunctionCreationExpression": {
"staticModifier": null,
"functionKeyword": {
"kind": "FnKeyword",
"textLength": 2
},
"byRefToken": null,
"name": null,
"openParen": {
"kind": "OpenParenToken",
"textLength": 1
},
"parameters": null,
"closeParen": {
"kind": "CloseParenToken",
"textLength": 1
},
"colonToken": null,
"questionToken": null,
"returnType": null,
"arrowToken": {
"kind": "DoubleArrowToken",
"textLength": 2
},
"resultExpression": {
"error": "MissingToken",
"kind": "Expression",
"textLength": 0
}
}
}
}
},
"semicolon": {
"kind": "SemicolonToken",
"textLength": 1
}
}
}
],
"endOfFileToken": {
"kind": "EndOfFileToken",
"textLength": 0
}
}
}

Просмотреть файл

@ -0,0 +1,12 @@
<?php
$fn1 = fn(\stdClass $x) : stdClass => $x;
$fn2 = fn(): int => ($x);
$fn3 = fn($x = 42) => yield $x;
$fn4 = fn(&$x) => $x;
$fn5 = fn&(Ns\MyClass $x) : Ns\MyClass => $x;
$fn = fn($str) => preg_match($regex, $str, $matches) && ($matches[1] % 7 == 0);
$cb = fn() => fn() => $undef;
$fn = fn() => call_user_func(function () use ($arg) {
var_export($arg);
return $arg;
});

Просмотреть файл

@ -0,0 +1 @@
[]

Разница между файлами не показана из-за своего большого размера Загрузить разницу