pjcj / Devel--Cover

Code coverage metrics for Perl
http://www.pjcj.net/perl.html
93 stars 87 forks source link

First use of "binmode STDOUT, ':encoding(UTF-8)'" shows as a use statement in structure #311

Open c-folsom opened 1 year ago

c-folsom commented 1 year ago

When using "binmode" with ":encoding()" if the line of code is run then this line will show up as if it was a "use" statement and once as a normal statement line in the structure file. If it is not run then it does not show up as if it was a "use" statement. It does not matter what the file handle is or the encoding type. If "encoding" is not used then this does not happen, i.e. "binmode STDOUT, ':utf8';".

The main issue is that this will cause merge conflicts if one test does not run this line of code and the second test does. Then when merging the structures do not match. This can be seen with the code below:

sub foo {
   print "foo\n";
   binmode STDOUT, ':encoding(UTF-8)';
}
foo();

The structure then acts as if the "binmode" line was both a "use" statement and a normal line so the statement structure is [5, 3, 3, 3, 2, 3]. If we remove the call to foo() then the "binmode" line show up as a single statement line [2, 3].

I also noticed if "binmode" with "encoding" is used twice then only the first call will show up as a "use" statement plus a normal statement line. The second will then only show up as a normal statement line. For example if the file contained:

binmode STDIN, ':encoding(utf8)';
binmode STDOUT, ':encoding(UTF-8)';

This file would give the following structure: {"subroutine":[[1,"BEGIN"]],"start":{"1":{"BEGIN":[{"time":null,"statement":2,"branch":null,"pod":null,"subroutine":null,"condition":null}]},"-1":{"__COVER__":[{"subroutine":1,"condition":null,"time":null,"statement":5,"branch":null,"pod":null}]}},"file":"f2.pl","statement":[1,2,1,1,1],"digest":"8374f104aa1d19561eeea886e144a687"}

The structure would also be the same if both "binmode" lines were identical:

binmode STDIN, ':encoding(utf8)';
binmode STDIN, ':encoding(utf8)';

I did noticed that if running this using the perl debugger the first call to "binmode" is run four times:

main::(f2.pl:1):        binmode STDIN, ':encoding(utf8)';
main::CODE(0x28c6560)(f2.pl:1): binmode STDIN, ':encoding(utf8)';
PerlIO::CODE(0x28c6560)(/path/to/PerlIO.pm:3):
3:      our $VERSION = '1.11';
PerlIO::CODE(0x28c6560)(/path/to/PerlIO.pm:6):
6:      our %alias;
PerlIO::CODE(0x28c6560)(/path/to/PerlIO.pm:29):
29:     1;
30:     __END__
main::CODE(0x28c6560)(f2.pl:1): binmode STDIN, ':encoding(utf8)';
main::CODE(0x28c6560)(f2.pl:1): binmode STDIN, ':encoding(utf8)';
PerlIO::import(/path/to/PerlIO.pm:10):
10:      my $class = shift;
c-folsom commented 1 year ago

Found a workaround for this issue. If including a use statement for "encoding()" then when "binmode" using "encoding()" is called for the first time it does not show up like a use statement:

use open ':encoding(UTF-8)';

binmode STDIN, ':encoding(utf8)';
binmode STDIN, ':encoding(utf8)';

This does not show a use statement for the first call of "binmode", statement structure [2,3,1,1,1].

I think what is happening here is when "binmode" or "open" are used with "encoding()" for the first time there is a use statement being used to include "encoding()", Devel::Cover then only sees the use statement and writes it to the structure file if the line that uses "encoding()" is called.