@@ -259,7 +259,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
259259%type <ast> unprefixed_use_declarations const_decl inner_statement
260260%type <ast> expr optional_expr while_statement for_statement foreach_variable
261261%type <ast> foreach_statement declare_statement finally_statement unset_variable variable
262- %type <ast> extends_from parameter optional_type_without_static argument global_var
262+ %type <ast> extends_from parameter optional_type_without_static argument argument_no_expr global_var
263263%type <ast> static_var class_statement trait_adaptation trait_precedence trait_alias
264264%type <ast> absolute_trait_method_reference trait_method_reference property echo_expr
265265%type <ast> new_dereferenceable new_non_dereferenceable anonymous_class class_name class_name_reference simple_variable
@@ -287,7 +287,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
287287%type <ast> enum_declaration_statement enum_backing_type enum_case enum_case_expr
288288%type <ast> function_name non_empty_member_modifiers
289289%type <ast> property_hook property_hook_list optional_property_hook_list hooked_property property_hook_body
290- %type <ast> optional_parameter_list
290+ %type <ast> optional_parameter_list clone_argument_list non_empty_clone_argument_list
291291
292292%type <num> returns_ref function fn is_reference is_variadic property_modifiers property_hook_modifiers
293293%type <num> method_modifiers class_const_modifiers member_modifier optional_cpp_modifiers
@@ -914,13 +914,42 @@ non_empty_argument_list:
914914 { $$ = zend_ast_list_add($1 , $3 ); }
915915;
916916
917- argument :
918- expr { $$ = $1 ; }
919- | identifier ' :' expr
917+ /* `clone_argument_list` is necessary to resolve a parser ambiguity (shift-reduce conflict)
918+ * of `clone($expr)`, which could either be parsed as a function call with `$expr` as the first
919+ * argument or as a use of the `clone` language construct with an expression with useless
920+ * parenthesis. Both would be valid and result in the same AST / the same semantics.
921+ * `clone_argument_list` is defined in a way that an `expr` in the first position needs to
922+ * be followed by a `,` which is not valid syntax for a parenthesized `expr`, ensuring
923+ * that calling `clone()` with a single unnamed parameter is handled by the language construct
924+ * syntax.
925+ */
926+ clone_argument_list :
927+ ' (' ' )' { $$ = zend_ast_create_list(0 , ZEND_AST_ARG_LIST); }
928+ | ' (' non_empty_clone_argument_list possible_comma ' )' { $$ = $2 ; }
929+ | ' (' expr ' ,' ' )' { $$ = zend_ast_create_list(1 , ZEND_AST_ARG_LIST, $2 ); }
930+ | ' (' T_ELLIPSIS ' )' { $$ = zend_ast_create_fcc(); }
931+ ;
932+
933+ non_empty_clone_argument_list :
934+ expr ' ,' argument
935+ { $$ = zend_ast_create_list(2 , ZEND_AST_ARG_LIST, $1 , $3 ); }
936+ | argument_no_expr
937+ { $$ = zend_ast_create_list(1 , ZEND_AST_ARG_LIST, $1 ); }
938+ | non_empty_clone_argument_list ' ,' argument
939+ { $$ = zend_ast_list_add($1 , $3 ); }
940+ ;
941+
942+ argument_no_expr :
943+ identifier ' :' expr
920944 { $$ = zend_ast_create(ZEND_AST_NAMED_ARG, $1 , $3 ); }
921945 | T_ELLIPSIS expr { $$ = zend_ast_create(ZEND_AST_UNPACK, $2 ); }
922946;
923947
948+ argument :
949+ expr { $$ = $1 ; }
950+ | argument_no_expr { $$ = $1 ; }
951+ ;
952+
924953global_var_list :
925954 global_var_list ' ,' global_var { $$ = zend_ast_list_add($1 , $3 ); }
926955 | global_var { $$ = zend_ast_create_list(1 , ZEND_AST_STMT_LIST, $1 ); }
@@ -1228,10 +1257,10 @@ expr:
12281257 { $$ = zend_ast_create(ZEND_AST_ASSIGN, $1 , $3 ); }
12291258 | variable ' =' ampersand variable
12301259 { $$ = zend_ast_create(ZEND_AST_ASSIGN_REF, $1 , $4 ); }
1231- | T_CLONE ' ( ' T_ELLIPSIS ' ) ' {
1260+ | T_CLONE clone_argument_list {
12321261 zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE));
12331262 name->attr = ZEND_NAME_FQ;
1234- $$ = zend_ast_create(ZEND_AST_CALL, name, zend_ast_create_fcc() );
1263+ $$ = zend_ast_create(ZEND_AST_CALL, name, $2 );
12351264 }
12361265 | T_CLONE expr {
12371266 zend_ast *name = zend_ast_create_zval_from_str(ZSTR_KNOWN(ZEND_STR_CLONE));
0 commit comments