diff --git a/pod/perldelta.pod b/pod/perldelta.pod index d53738408312..ed1fe8a71f2c 100644 --- a/pod/perldelta.pod +++ b/pod/perldelta.pod @@ -260,6 +260,14 @@ and L =item * +L + +(F) The subroutine indicated hasn't been defined, or if it was, it has +since been undefined. This could also indicate a mistyped package +separator, when a single colon was typed instead of two colons. + +=item * + XXX L =back diff --git a/pod/perldiag.pod b/pod/perldiag.pod index 7f981e8008ff..14b3021e1ccf 100644 --- a/pod/perldiag.pod +++ b/pod/perldiag.pod @@ -6820,6 +6820,12 @@ Perhaps it's in a different package? See L. (F) The subroutine indicated hasn't been defined, or if it was, it has since been undefined. +=item Undefined subroutine &%s called, close to label '%s' + +(F) The subroutine indicated hasn't been defined, or if it was, it has +since been undefined. This could also indicate a mistyped package +separator, when a single colon was typed instead of two colons. + =item Undefined subroutine called (F) The anonymous subroutine you're trying to call hasn't been defined, diff --git a/pp_hot.c b/pp_hot.c index 641bedc42569..f79c10af5e92 100644 --- a/pp_hot.c +++ b/pp_hot.c @@ -6212,6 +6212,33 @@ Perl_clear_defarray(pTHX_ AV* av, bool abandon) } } +/* S_croak_undefined_subroutine is a helper function for pp_entersub. + * It takes assorted DIE() logic out of that hot function. + */ +static void +S_croak_undefined_subroutine(pTHX_ CV const *cv, GV const *gv) +{ + if (cv) { + if (CvLEXICAL(cv) && CvHASGV(cv)) + croak("Undefined subroutine &%" SVf " called", + SVfARG(cv_name((CV*)cv, NULL, 0))); + else /* pp_entersub triggers when (CvANON(cv) || !CvHASGV(cv)) */ + croak("Undefined subroutine called"); + } else { /* pp_entersub triggers when (!cv) after `try_autoload` */ + SV *sub_name = newSV_type_mortal(SVt_PV); + gv_efullname3(sub_name, gv, NULL); + + /* Heuristic to spot BOOP:boop() typo, when the intention was + * to call BOOP::boop(). */ + const char * label = CopLABEL(PL_curcop); + if (label) { + croak("Undefined subroutine &%" SVf " called, close to label '%s'", + SVfARG(sub_name), label); + } + croak("Undefined subroutine &%" SVf " called", SVfARG(sub_name)); + } + NOT_REACHED; /* NOTREACHED */ +} PP(pp_entersub) { @@ -6306,14 +6333,12 @@ PP(pp_entersub) assert((void*)&CvROOT(cv) == (void*)&CvXSUB(cv)); while (UNLIKELY(!CvROOT(cv))) { GV* autogv; - SV* sub_name; /* anonymous or undef'd function leaves us no recourse */ if (CvLEXICAL(cv) && CvHASGV(cv)) - DIE(aTHX_ "Undefined subroutine &%" SVf " called", - SVfARG(cv_name(cv, NULL, 0))); + S_croak_undefined_subroutine(aTHX_ cv, NULL); if (CvANON(cv) || !CvHASGV(cv)) { - DIE(aTHX_ "Undefined subroutine called"); + S_croak_undefined_subroutine(aTHX_ cv, NULL); } /* autoloaded stub? */ @@ -6330,11 +6355,8 @@ PP(pp_entersub) : 0)); cv = autogv ? GvCV(autogv) : NULL; } - if (!cv) { - sub_name = sv_newmortal(); - gv_efullname3(sub_name, gv, NULL); - DIE(aTHX_ "Undefined subroutine &%" SVf " called", SVfARG(sub_name)); - } + if (!cv) + S_croak_undefined_subroutine(aTHX_ NULL, gv); } /* unrolled "CvCLONE(cv) && ! CvCLONED(cv)" */ diff --git a/t/lib/croak/pp_hot b/t/lib/croak/pp_hot index bc00a484c6df..325091c28e1c 100644 --- a/t/lib/croak/pp_hot +++ b/t/lib/croak/pp_hot @@ -46,6 +46,14 @@ Undefined subroutine &main::foo called at - line 3. EXPECT Undefined subroutine &main::foo called at - line 2. ######## +# NAME package separator typo, creating a label by accident + package BEEP; + sub boop; + package main; + BEEP:boop(); +EXPECT +Undefined subroutine &main::boop called, close to label 'BEEP' at - line 4. +######## # NAME calling undef scalar &{+undef}; EXPECT