Michael G Schwern wrote:
"my $foo = $p || $q" is not boolean. I'm not even sure you can call it
"pseudo-boolean" without understanding the surrounding code. How do you
know that $q can never be false?
The other examples in the ticket play out the same way:
bless {}, ref $class || $class;
$class being false would be quite the error. Devel::Cover can't know that
in most cases you want to ignore the 0 || 0 case because you assume $class
to always be some true value and don't think its worth testing that it
might be false.
I think this is a coverage vs correctness distinction. The idea that I
was trying to convey is that while these expressions use a boolean
operator for a shortcut, they aren't really about truth vs. falsity of
the overall expression, *except* when they are being used as part of a
conditional statement. From a coverage perspective, what should matter
in "my $foo = $p || $q" is that $foo takes on the values of both $p and
$q at some point during the test suite, not whether or not $foo takes on
both true and false values -- coverage of that condition should be
checked when $foo is used in another expression.
In the "bless" case -- you're right that the case of $class being false
may be of interest, but that's not what this common idiom actually
does. The code will blithely pass a false value to bless (with
potentially unexpected results depending on whether $class is 0, "", or
undef). That failure is an example of where correctness can't be
validated by coverage -- where the error lies between the ears of the
programmer. :-) If $class were explictly tested, then Devel::Cover
would pick it up properly, such as in "bless {}, ref $class || $class ||
die".
Want.pm seems to imply that this might be possible, but I don't know the
guts of Perl well enough. The concept I had was that *EXCEPT* in true
boolean context, the "$p || $q" idiom is (I think) pretty much logically
equivalent to a trinary operation "$p ? $p : $q" (ignoring potential
side effects) and thus the truth table in this situation only needs to
include the first operand, thus avoiding the false-alarm.
That assumption only works in void context.
# Don't care about the return value of die.
open FILE, $foo || die $!;
# DO care about what $q is
foo( $p || $q );
Following my logic above, here I'd argue that we *don't* care what $q is
from a coverage perspective, only that both $p and $q are passed to foo
at some point. However, if for foo() we have:
sub foo { my $val = shift; print "Hi!" if $val }
We now care about what $val is from a coverage standpoint, so we get the
same coverage testing that way and we pick up the case we want where $q
is false in the original call. Unfortunately, this doesn't work with
Devel::Cover when foo() is a built-in or subroutine imported from
another module. In such a case, one option could be to use "foo( $p ||
$q || '' )" or, more precisely, "foo( $p || $q || $q )" -- which are
awkward, but are actually forcing the program to evaluate $q as true or
false.
I guess that begs the question for coverage testers which case is more
common -- caring only that "$p || $q" takes on the value of $p and $q,
or caring that both $p and $q take on both true and false values.
Regards,
David Golden