@@ -50,7 +50,9 @@ CallableParameter
5050 : MaybeDefaultCallableParameter()
5151 ;
5252
53- // Parses "<param>" or "<param> =" expression
53+ // Expects expression:
54+ // - "<param>"
55+ // - "<param> ="
5456MaybeDefaultCallableParameter -> {
5557 if (\count($children) === 1) {
5658 return $children[0];
@@ -63,62 +65,133 @@ MaybeDefaultCallableParameter -> {
6365 $children[0]->optional = true;
6466 return $children[0];
6567}
66- : MaybeNamedCallableParameter() <T_ASSIGN>?
68+ : ( MaybePrefixedVariadicTypedNamedCallableParameter()
69+ | MaybeModifiersNamedCallableParameter() )
70+ <T_ASSIGN>?
6771 ;
6872
69- // Parses "$variable" or "<param> $variable", or "<param>" expression
70- MaybeNamedCallableParameter -> {
71- if ($children instanceof Node\Stmt\Callable\CallableParameterNode) {
73+ // Expects expression:
74+ // - "&...<var_param>"
75+ // - "...&<var_param>"
76+ // - "...<var_param>"
77+ // - "&<var_param>"
78+ // - "<var_param>"
79+ MaybeModifiersNamedCallableParameter -> {
80+ if (!\is_array($children)) {
7281 return $children;
7382 }
7483
75- if (\count($children) === 1) {
76- return $children[0];
84+ $result = \end($children);
85+
86+ foreach ($children as $modifier) {
87+ if ($modifier instanceof \Phplrt\Contracts\Lexer\TokenInterface) {
88+ switch ($modifier->getName()) {
89+ case 'T_AMP':
90+ $result->output = true;
91+ break;
92+ case 'T_ELLIPSIS':
93+ if ($result->variadic) {
94+ throw SemanticException::fromVariadicRedefinition($offset);
95+ }
96+ $result->variadic = true;
97+ break;
98+ }
99+ }
77100 }
78101
79- $children[0]->name = $children[1];
80- return $children[0];
102+ return $result;
81103}
82- : MaybeVariableCallableParameter()
83- | MaybeVariadicCallableParameter() VariableLiteral()?
104+ : <T_AMP> <T_ELLIPSIS> MaybeSuffixedVariadicNamedCallableParameter()
105+ | <T_ELLIPSIS> <T_AMP> MaybeSuffixedVariadicNamedCallableParameter()
106+ | <T_ELLIPSIS> MaybeSuffixedVariadicNamedCallableParameter()
107+ | <T_AMP> MaybeSuffixedVariadicNamedCallableParameter()
108+ | MaybeSuffixedVariadicNamedCallableParameter()
84109 ;
85110
86- // Parses "$variable" expression
87- MaybeVariableCallableParameter -> {
88- return new Node\Stmt\Callable\CallableParameterNode(null, $children[0]);
111+ // Expects expression:
112+ // - "$<var>..."
113+ // - "$<var>"
114+ MaybeSuffixedVariadicNamedCallableParameter -> {
115+ $result = new Node\Stmt\Callable\CallableParameterNode(null, $children[0]);
116+
117+ if (\count($children) !== 1) {
118+ $result->variadic = true;
119+ }
120+
121+ return $result;
89122}
90123 : VariableLiteral()
124+ <T_ELLIPSIS>?
91125 ;
92126
93- // Parses "...<param>" or "<param>...", or "<param>" expression
94- MaybeVariadicCallableParameter -> {
95- if (count($children) === 1) {
127+ MaybePrefixedVariadicTypedNamedCallableParameter -> {
128+ if (\count($children) === 1) {
96129 return $children[0];
97130 }
98131
99- if ($children[0] instanceof Node\Stmt\Callable\ParameterNode) {
100- $children[0]->variadic = true;
101- return $children[0];
132+ if ($children[1]->variadic) {
133+ throw SemanticException::fromVariadicRedefinition($offset);
102134 }
103135
104136 $children[1]->variadic = true;
105137 return $children[1];
106138}
107- : <T_ELLIPSIS> MaybeOutputCallableParameter() // Prefixed variadic argument (Psalm format)
108- | MaybeOutputCallableParameter() <T_ELLIPSIS>? // Suffixed variadic argument (PhpStan + Psalm)
139+ : <T_ELLIPSIS>? MaybeTypedNamedCallableParameter()
109140 ;
110141
111- // Parses "<param>&" or "<param>" expression
112- MaybeOutputCallableParameter -> {
113- $argument = new Node\Stmt\Callable\CallableParameterNode($children[0]);
142+ // Expects expression:
143+ // - "<typed_param> $<var>"
144+ // - "<typed_param>"
145+ MaybeTypedNamedCallableParameter -> {
146+ if (\count($children) === 1) {
147+ return $children[0];
148+ }
114149
115- if (\count($children) !== 1) {
116- $argument->output = true;
150+ $children[0]->name = $children[1];
151+ return $children[0];
152+ }
153+ : MaybeModifiersTypedCallableParameter()
154+ VariableLiteral()?
155+ ;
156+
157+ // Expects expression:
158+ // - "<type>...&"
159+ // - "<type>&..."
160+ // - "<type>&"
161+ // - "<type>..."
162+ // - "<type>"
163+ MaybeModifiersTypedCallableParameter -> {
164+ $result = \reset($children);
165+
166+ foreach ($children as $modifier) {
167+ if ($modifier instanceof \Phplrt\Contracts\Lexer\TokenInterface) {
168+ switch ($modifier->getName()) {
169+ case 'T_AMP':
170+ $result->output = true;
171+ break;
172+ case 'T_ELLIPSIS':
173+ if ($result->variadic) {
174+ throw SemanticException::fromVariadicRedefinition($offset);
175+ }
176+ $result->variadic = true;
177+ break;
178+ }
179+ }
117180 }
118181
119- return $argument;
182+ return $result;
183+ }
184+ : TypedCallableParameter()
185+ ( <T_AMP> <T_ELLIPSIS>?
186+ | <T_ELLIPSIS> <T_AMP>? )?
187+ ;
188+
189+ // Expects expression:
190+ // - "<type>"
191+ TypedCallableParameter -> {
192+ return new Node\Stmt\Callable\CallableParameterNode($children[0]);
120193}
121- : Type() <T_AMP>?
194+ : Type()
122195 ;
123196
124197CallableReturnType
0 commit comments