Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bug with use if REGEX_COND

Tags:

perl

I would like to conditional load a package if the program name is a test script ending in .t.

However, I'm running into a bug where use if fails when the condition is a regex. I've tested this in Perl 5.10 and 5.16.

The following is my test script ending in .t:

#!/usr/bin/env perl

use v5.10;

BEGIN { say "\$0 is '$0'" }

use if $0 =~ /\.t\z/, 'List::Util', ('pairmap');

say "List::Util is " . ( $INC{"List/Util.pm"} ? '' : 'NOT ' ) . 'included';

Outputs:

$ ./test.t
$0 is './test.t'
List::Util is included

However, the same file with a .pl extension fails:

$ ./test.pl
$0 is './test.pl'
Can't locate pairmap.pm in @INC (@INC contains: /usr/lib64/perl5/5.10.0 /usr/lib64/perl5 /usr/local/share/perl5/x86_64-linux-thread-multi /usr/local/share/perl5 /usr/local/lib64/perl5 /usr/share/perl5 /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 .) at /usr/share/perl5/if.pm line 13.
BEGIN failed--compilation aborted at ./test.pl line 7.

I can coerce the code into working if I double bang the regex or change it to a substr comparison:

use if !!( $0 =~ /\.t\z/ ), 'List::Util', ('pairmap');
use if substr( $0, -2 ) eq '.t', 'List::Util', ('pairmap');

Outputs:

$ ./test.pl
$0 is './test.pl'
List::Util is NOT included

Is this a known bug? If so, in what version was it fixed?

like image 501
Miller Avatar asked Nov 02 '17 22:11

Miller


1 Answers

This is a bug in your code.

The argument list after use MODULE is in, well, list context.

m// in list context returns a list of captured strings if successful (or 1 if the regex contains no capturing groups), or the empty list on failure.

Thus:

use if "test.pl" =~ /\.t\z/, 'List::Util', ('pairmap');

is equivalent to

use if (), 'List::Util', ('pairmap');

(The match failed, so an empty list is returned.)

, in list context is the list concatenation operator, so this gives:

use if 'List::Util', 'pairmap';

'List::Util' is a true value, so this ultimately ends up loading pairmap.pm:

use pairmap;

The fix is to give the match scalar context:

use if scalar($0 =~ /\.t\z/), 'List::Util', 'pairmap';

(! also supplies scalar context to its operand, so !! has the same effect.)

like image 170
melpomene Avatar answered Oct 26 '22 02:10

melpomene