From e727bea0dabf76ada523b041ad4b0c73d6798139 Mon Sep 17 00:00:00 2001 From: "Paul \"LeoNerd\" Evans" Date: Fri, 31 Oct 2025 18:12:16 +0000 Subject: [PATCH] Collect names of multiple erroring parameters Rather than stop immediately at the first failure, we can be a bit more helpful by collecting up potentially multiple names at once. We limit the list to 5 elements, just to limit the potential output in the case of accidentally passing a list of hundreds or thousands of elements into such a function. --- pod/perldiag.pod | 19 ++++++++++------ pp.c | 50 +++++++++++++++++++++++++++++++++++------- t/lib/croak/signatures | 21 ++++++++++++++++++ 3 files changed, 75 insertions(+), 15 deletions(-) diff --git a/pod/perldiag.pod b/pod/perldiag.pod index 0ab63025a162..cdb7b1eb0882 100644 --- a/pod/perldiag.pod +++ b/pod/perldiag.pod @@ -4163,9 +4163,12 @@ which is forbidden. See L and L. =item Missing required named parameter '%s' to subroutine '%s' -(F) A subroutine was invoked that uses a signature that declares a -non-optional named parameter, but the caller did not provide a value associated -with that name. The caller of the subroutine is presumably at fault. +=item Missing required named parameters '%s' to subroutine '%s' + +(F) A subroutine was invoked that uses a signature that declares at least +one non-optional named parameter, but the caller did not provide a value +associated with that name. The caller of the subroutine is presumably at +fault. The message attempts to include the name of the called subroutine. If the subroutine has been aliased, the subroutine's original name will be @@ -7530,10 +7533,12 @@ perl does not recognise the name of the requested attribute. =item Unrecognized named parameter '%s' to subroutine '%s' -(F) A subroutine was invoked that uses a signature that declares named parameters but has no -slurpy parameter, and the caller provided a name that did not -match any declared named parameter. The caller of the subroutine is -presumably at fault. +=item Unrecognized named parameters '%s' to subroutine '%s' + +(F) A subroutine was invoked that uses a signature that declares named +parameters but has no slurpy parameter, and the caller provided at least one +name that did not match any declared named parameter. The caller of the +subroutine is presumably at fault. The message attempts to include the name of the called subroutine. If the subroutine has been aliased, the subroutine's original name will be diff --git a/pp.c b/pp.c index bcfa2744e44b..2b4299c21847 100644 --- a/pp.c +++ b/pp.c @@ -7890,6 +7890,27 @@ PP(pp_argcheck) return NORMAL; } +/* Helper function for collecting up hash key names for a human-readable + * error message string + */ +#define accumulate_error_names(n_errorsp, error_namesp, namepv, namelen) S_accumulate_error_names(aTHX_ n_errorsp, error_namesp, namepv, namelen) +STATIC void +S_accumulate_error_names(pTHX_ size_t *n_errorsp, SV **error_namesp, const char *namepv, STRLEN namelen) +{ + size_t n_errors = ++(*n_errorsp); + + SV *error_names = (*error_namesp); + if(!error_names) + error_names = (*error_namesp) = newSVpvs_flags("", SVs_TEMP); + + if(n_errors <= 5) { + /* Only bother collecting up the first 5 */ + if(n_errors > 1) + sv_catpvs(error_names, ", "); + sv_catpvf(error_names, "'%" UTF8f "'", UTF8fARG(true, namelen, namepv)); + } +} + PP(pp_multiparam) { struct op_multiparam_aux *aux = (struct op_multiparam_aux *)cUNOP_AUX->op_aux; @@ -7983,6 +8004,9 @@ PP(pp_multiparam) SvPADSTALE_on(PAD_SVl(named->padix)); } + size_t n_errors = 0; + SV *error_names = NULL; + while(argc) { SV **svp; @@ -8060,22 +8084,32 @@ PP(pp_multiparam) hv_store_ent(hv, name, newSVsv(val), 0); } else { - // TODO: Consider collecting up all the names of unrecognised - // in one string - croak_caller("Unrecognized named parameter '%" UTF8f "' to subroutine '%" SVf "'", - UTF8fARG(true, namelen, namepv), S_find_runcv_name()); + accumulate_error_names(&n_errors, &error_names, namepv, namelen); } } + if(n_errors) { + if(n_errors > 5) + sv_catpvs(error_names, ", ..."); + /* diag_listed_as: Unrecognized named parameter '%s' to subroutine '%s' */ + croak_caller("Unrecognized named parameter%s %" SVf " to subroutine '%" SVf "'", + n_errors > 1 ? "s" : "", SVfARG(error_names), SVfARG(S_find_runcv_name())); + } + for(size_t namedix = 0; namedix < n_named; namedix++) { struct op_multiparam_named_aux *named = aux->named + namedix; if(!named->is_required || !SvPADSTALE(PAD_SVl(named->padix))) continue; - // TODO: Consider collecting up all the names of missing - // parameters in one string - croak_caller("Missing required named parameter '%" UTF8f "' to subroutine '%" SVf "'", - UTF8fARG(true, named->namelen, named->namepv), S_find_runcv_name()); + accumulate_error_names(&n_errors, &error_names, named->namepv, named->namelen); + } + + if(n_errors) { + if(n_errors > 5) + sv_catpvs(error_names, ", ..."); + /* diag_listed_as: Missing required named parameter '%s' to subroutine '%s' */ + croak_caller("Missing required named parameter%s %" SVf " to subroutine '%" SVf "'", + n_errors > 1 ? "s" : "", SVfARG(error_names), SVfARG(S_find_runcv_name())); } } diff --git a/t/lib/croak/signatures b/t/lib/croak/signatures index 49fb4e855917..ae77e8604074 100644 --- a/t/lib/croak/signatures +++ b/t/lib/croak/signatures @@ -285,9 +285,30 @@ f(alpha => 123); ## line 4 EXPECT Missing required named parameter 'beta' to subroutine 'main::f' at - line 4. ######## +# NAME multiple missing required named +no warnings 'experimental::signature_named_parameters'; +sub f (:$alpha, :$beta) { } ## line 3 +f(); ## line 4 +EXPECT +Missing required named parameters 'alpha', 'beta' to subroutine 'main::f' at - line 4. +######## # NAME unrecognised named no warnings 'experimental::signature_named_parameters'; sub f (:$alpha) { } ## line 3 f(alpha => 123, gamma => 456); ## line 4 EXPECT Unrecognized named parameter 'gamma' to subroutine 'main::f' at - line 4. +######## +# NAME multiple unrecognised named +no warnings 'experimental::signature_named_parameters'; +sub f (:$alpha) { } ## line 3 +f(alpha => 123, gamma => 456, delta => 789); ## line 4 +EXPECT +Unrecognized named parameters 'gamma', 'delta' to subroutine 'main::f' at - line 4. +######## +# NAME multiple unrecognised named limiting +no warnings 'experimental::signature_named_parameters'; +sub f (:$alpha) { } ## line 3 +f(alpha => 123, 'a' .. 'z'); ## line 4 +EXPECT +Unrecognized named parameters 'a', 'c', 'e', 'g', 'i', ... to subroutine 'main::f' at - line 4.