diff options
author | David Mitchell <davem@iabyn.com> | 2017-04-16 09:50:04 +0100 |
---|---|---|
committer | David Mitchell <davem@iabyn.com> | 2017-04-18 12:58:32 +0100 |
commit | d31614f579da61846a22a2eb69b1d0412c86d54f (patch) | |
tree | 2ffbe3601110e86146c1eef3c59cbf127767c2a4 /pp_ctl.c | |
parent | 4b62894a4418bf61f306acb452472eb9fe79974e (diff) | |
download | perl-d31614f579da61846a22a2eb69b1d0412c86d54f.tar.gz |
emit require module name err hint only when valid
RT #131098
The helpful "you may need to install" hint which 'require' sometimes
includes in its error message these days (split across multiple lines for
clarity):
$ perl -e'require Foo::Bar'
Can't locate Foo/Bar.pm in @INC
(you may need to install the Foo::Bar module)
(@INC contains: ... ) at ...
is a bit over-enthusiastic when the pathname hasn't actually been derived
from a module name:
$ perl -e'require "Foo.+/%#Bar.pm"'
Can't locate Foo.+%#Bar.pm in @INC
(you may need to install the Foo.+::%#Bar module)
(@INC contains: ... ) at ...
This commit changes things so that the hint message is only emitted if the
reverse-mapped module name is legal as a bareword:
$ perl -e'require "Foo.+/%#Bar.pm"'
Can't locate Foo.+%#Bar.pm in @INC
(@INC contains: ... ) at ...
Diffstat (limited to 'pp_ctl.c')
-rw-r--r-- | pp_ctl.c | 54 |
1 files changed, 42 insertions, 12 deletions
@@ -4101,22 +4101,52 @@ S_require_file(pTHX_ SV *sv) SSize_t i; SV *const msg = newSVpvs_flags("", SVs_TEMP); SV *const inc = newSVpvs_flags("", SVs_TEMP); + const char *e = name + len - 3; /* possible .pm */ for (i = 0; i <= AvFILL(ar); i++) { sv_catpvs(inc, " "); sv_catsv(inc, *av_fetch(ar, i, TRUE)); } - if (len >= 4 && memEQ(name + len - 3, ".pm", 4)) { - const char *c, *e = name + len - 3; - sv_catpv(msg, " (you may need to install the "); - for (c = name; c < e; c++) { - if (*c == '/') { - sv_catpvs(msg, "::"); - } - else { - sv_catpvn(msg, c, 1); - } - } - sv_catpv(msg, " module)"); + if (e > name && _memEQs(e, ".pm")) { + const char *c; + bool utf8 = cBOOL(SvUTF8(sv)); + + /* if the filename, when converted from "Foo/Bar.pm" + * form back to Foo::Bar form, makes a valid + * package name (i.e. parseable by C<require + * Foo::Bar>), then emit a hint. + * + * this loop is modelled after the one in + S_parse_ident */ + c = name; + while (c < e) { + if (utf8 && isIDFIRST_utf8_safe(c, e)) { + c += UTF8SKIP(c); + while (c < e && isIDCONT_utf8_safe( + (const U8*) c, (const U8*) e)) + c += UTF8SKIP(c); + } + else if (isWORDCHAR_A(*c)) { + while (c < e && isWORDCHAR_A(*c)) + c++; + } + else if (*c == '/') + c++; + else + break; + } + + if (c == e && isIDFIRST_lazy_if_safe(name, e, utf8)) { + sv_catpv(msg, " (you may need to install the "); + for (c = name; c < e; c++) { + if (*c == '/') { + sv_catpvs(msg, "::"); + } + else { + sv_catpvn(msg, c, 1); + } + } + sv_catpv(msg, " module)"); + } } else if (len >= 2 && memEQ(name + len - 2, ".h", 3)) { sv_catpv(msg, " (change .h to .ph maybe?) (did you run h2ph?)"); |