Skip to content

Commit 0faf367

Browse files
committed
Support proposed first-class callable syntax
Supports https://wiki.php.net/rfc/first_class_callable_syntax It is possible that partial function application may allow mixing `...` with other arguments in the future, if that passes. See https://wiki.php.net/rfc/first_class_callable_syntax#syntax_choice So modify the argument instead of replacing the entire argument list with the token `...`
1 parent e0814a0 commit 0faf367

8 files changed

+226
-3
lines changed

src/Node/Expression/ArgumentExpression.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class ArgumentExpression extends Expression {
1919
/** @var Token|null */
2020
public $dotDotDotToken;
2121

22-
/** @var Expression */
22+
/** @var Expression|null null for first-class callable syntax */
2323
public $expression;
2424

2525
const CHILD_NAMES = [

src/Parser.php

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,7 @@ private function parseParameterFn() {
835835
$parameter->typeDeclarationList = $this->tryParseParameterTypeDeclarationList($parameter);
836836
if ($parameter->typeDeclarationList) {
837837
$children = $parameter->typeDeclarationList->children;
838-
if (end($children) instanceof MissingToken && ($children[count($children) - 2]->kind ?? null) === TokenKind::AmpersandToken) {
838+
if (end($children) instanceof MissingToken && ($children[\count($children) - 2]->kind ?? null) === TokenKind::AmpersandToken) {
839839
array_pop($parameter->typeDeclarationList->children);
840840
$parameter->byRefToken = array_pop($parameter->typeDeclarationList->children);
841841
if (!$parameter->typeDeclarationList->children) {
@@ -3140,14 +3140,27 @@ private function parseObjectCreationExpression($parentNode) {
31403140
return $objectCreationExpression;
31413141
}
31423142

3143+
/**
3144+
* @return DelimitedList\ArgumentExpressionList|null
3145+
*/
31433146
private function parseArgumentExpressionList($parentNode) {
3144-
return $this->parseDelimitedList(
3147+
$list = $this->parseDelimitedList(
31453148
DelimitedList\ArgumentExpressionList::class,
31463149
TokenKind::CommaToken,
31473150
$this->isArgumentExpressionStartFn(),
31483151
$this->parseArgumentExpressionFn(),
31493152
$parentNode
31503153
);
3154+
$children = $list->children ?? null;
3155+
if (is_array($children) && \count($children) === 1) {
3156+
$arg = $children[0];
3157+
if ($arg instanceof ArgumentExpression) {
3158+
if ($arg->dotDotDotToken && $arg->expression instanceof MissingToken && !$arg->colonToken && !$arg->name) {
3159+
$arg->expression = null;
3160+
}
3161+
}
3162+
}
3163+
return $list;
31513164
}
31523165

31533166
/**
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
$sl = strlen(...);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[]
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
{
2+
"SourceFileNode": {
3+
"statementList": [
4+
{
5+
"InlineHtml": {
6+
"scriptSectionEndTag": null,
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 6
11+
}
12+
}
13+
},
14+
{
15+
"ExpressionStatement": {
16+
"expression": {
17+
"AssignmentExpression": {
18+
"leftOperand": {
19+
"Variable": {
20+
"dollar": null,
21+
"name": {
22+
"kind": "VariableName",
23+
"textLength": 3
24+
}
25+
}
26+
},
27+
"operator": {
28+
"kind": "EqualsToken",
29+
"textLength": 1
30+
},
31+
"byRef": null,
32+
"rightOperand": {
33+
"CallExpression": {
34+
"callableExpression": {
35+
"QualifiedName": {
36+
"globalSpecifier": null,
37+
"relativeSpecifier": null,
38+
"nameParts": [
39+
{
40+
"kind": "Name",
41+
"textLength": 6
42+
}
43+
]
44+
}
45+
},
46+
"openParen": {
47+
"kind": "OpenParenToken",
48+
"textLength": 1
49+
},
50+
"argumentExpressionList": {
51+
"ArgumentExpressionList": {
52+
"children": [
53+
{
54+
"ArgumentExpression": {
55+
"name": null,
56+
"colonToken": null,
57+
"dotDotDotToken": {
58+
"kind": "DotDotDotToken",
59+
"textLength": 3
60+
},
61+
"expression": null
62+
}
63+
}
64+
]
65+
}
66+
},
67+
"closeParen": {
68+
"kind": "CloseParenToken",
69+
"textLength": 1
70+
}
71+
}
72+
}
73+
}
74+
},
75+
"semicolon": {
76+
"kind": "SemicolonToken",
77+
"textLength": 1
78+
}
79+
}
80+
}
81+
],
82+
"endOfFileToken": {
83+
"kind": "EndOfFileToken",
84+
"textLength": 0
85+
}
86+
}
87+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?php
2+
$a = intdiv(..., 3);
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[
2+
{
3+
"kind": 0,
4+
"message": "'Expression' expected.",
5+
"start": 21,
6+
"length": 0
7+
}
8+
]
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"SourceFileNode": {
3+
"statementList": [
4+
{
5+
"InlineHtml": {
6+
"scriptSectionEndTag": null,
7+
"text": null,
8+
"scriptSectionStartTag": {
9+
"kind": "ScriptSectionStartTag",
10+
"textLength": 6
11+
}
12+
}
13+
},
14+
{
15+
"ExpressionStatement": {
16+
"expression": {
17+
"AssignmentExpression": {
18+
"leftOperand": {
19+
"Variable": {
20+
"dollar": null,
21+
"name": {
22+
"kind": "VariableName",
23+
"textLength": 2
24+
}
25+
}
26+
},
27+
"operator": {
28+
"kind": "EqualsToken",
29+
"textLength": 1
30+
},
31+
"byRef": null,
32+
"rightOperand": {
33+
"CallExpression": {
34+
"callableExpression": {
35+
"QualifiedName": {
36+
"globalSpecifier": null,
37+
"relativeSpecifier": null,
38+
"nameParts": [
39+
{
40+
"kind": "Name",
41+
"textLength": 6
42+
}
43+
]
44+
}
45+
},
46+
"openParen": {
47+
"kind": "OpenParenToken",
48+
"textLength": 1
49+
},
50+
"argumentExpressionList": {
51+
"ArgumentExpressionList": {
52+
"children": [
53+
{
54+
"ArgumentExpression": {
55+
"name": null,
56+
"colonToken": null,
57+
"dotDotDotToken": {
58+
"kind": "DotDotDotToken",
59+
"textLength": 3
60+
},
61+
"expression": {
62+
"error": "MissingToken",
63+
"kind": "Expression",
64+
"textLength": 0
65+
}
66+
}
67+
},
68+
{
69+
"kind": "CommaToken",
70+
"textLength": 1
71+
},
72+
{
73+
"ArgumentExpression": {
74+
"name": null,
75+
"colonToken": null,
76+
"dotDotDotToken": null,
77+
"expression": {
78+
"NumericLiteral": {
79+
"children": {
80+
"kind": "IntegerLiteralToken",
81+
"textLength": 1
82+
}
83+
}
84+
}
85+
}
86+
}
87+
]
88+
}
89+
},
90+
"closeParen": {
91+
"kind": "CloseParenToken",
92+
"textLength": 1
93+
}
94+
}
95+
}
96+
}
97+
},
98+
"semicolon": {
99+
"kind": "SemicolonToken",
100+
"textLength": 1
101+
}
102+
}
103+
}
104+
],
105+
"endOfFileToken": {
106+
"kind": "EndOfFileToken",
107+
"textLength": 0
108+
}
109+
}
110+
}

0 commit comments

Comments
 (0)