Its recently been noted on p5p that perldiag.pod is *way* out of date.
1. Make a *complete* check on perldiag.pod and propose a patch. My first scan shows me that perldiag.pod is hopelessly out of sync example: lt09:/pro/3gl/CPAN 126 > grep -r "t resolve method" perl/ perl/pod/perltoc.pod:use, Cannot resolve method `%s' overloading `% +s' in package `%s', Constant perl/pod/perldiag.pod:=item Can't resolve method `%s' overloading ` +%s' in package `%s' perl/pod/perl5004delta.pod:=item Cannot resolve method `%s' overloa +ding `%s' in package `%s' lt09:/pro/3gl/CPAN 127 > Where is that message issued? The check is to be made both ways. - What messages are issued and not in diag - What messages are in diag but never issued - What messages are in diag and issued, but not in sync
As you might surmise, porters are generally pretty good perl programmers, and not strongly motivated by selfish needs to fix up diagnostics that they very rarely look at.

This illustrates the need, and suggests a nice opportunity, for many monks to join in chant, and to commune together on the virtues of a complete perldiag, maybe even with code-snippets (aka tests) that cause those errors. This is a hard/tedious job, there are about 1000 missing entries in perldiag, so it *really* needs to be a community effort. I think it needs a bit of discussion wrt how to do it too.

1. Ive written a script to find code (Perl_warner, Perl_croak) where errors are issued, and bashed it against the contents of perldiag.pod. Id love to post it here, and see if anyone wants to use and/or improve it.

2. Ive taken its output (perldiag.pod.new), grepped out the =items, prepended each with a '#', and added a 'use Test::More 'no_plan'; to it, and called it 'diag.t'. Now it needs *lots* of examples.

3. I need a good wiki provider, where I could put a few files ( the script, perldiag.pod.new, diag.t) up, and open them up for team editing. being able to attach files is good too, esp for source code, which is easier to work on your own box, and supply patches.

4. the good is the enemy of the perfect.
HM Brand wants perfect (per the quote), but perldiag hasnt even gotten any goodness in 3 years, and Id wager the pumking would tolerate a bit of churn on that file, esp with fossilization as the alternative.

5. If you want to start putting stuff here; snippets or explanations for diags you know are missing, feel free to do so.

6. theres good reason to consider splitting perldiag into several files (peeling platform specific errs to other files is a candidate), but I think thats premature. OTOH, if you want to hack at diagnostics.pm, thats ok too. just be mindful that the hard/tedious work must be more prized.

Replies are listed 'Best First'.
Re: perldiag upgrade
by kidongrok (Acolyte) on Jun 04, 2005 at 13:20 UTC
    ok, here it is.
    pls tryit.

    if you go code spelunking, try the -d option, it will tell you where the errs/warns are issued; file and line.

    =head1 Latent Problems It appears that a the reason (contributing at least) that diags dont match to croaks is because the printf substitutions that are there. The =items are loaded into (my) %diagnostics::transfmt, and values are initialized to a corresponding regex. %transfmt is used in diagnostics::transmo() by splainthis() to match against the actual error and produce the message. By using diagnostics, I think I can splainthis() on the croaks, thus eliminating the ones that are matched by a diagnostics entry.

    Edit by castaway - changed pre tags to code tags

      1st test-snippet to trigger perl error msg, courtesy Nick Clark on p5p.
      ./perl -e '$a = ${*STDOUT{IO}}' Bizarre copy of IO in sassign at -e line 1.
      Should it be possible to get the 'bizzare copy' error at all?
      heres a new, shorter, cleaner, less obfuscated version. It actually produces something reasonable.

      it doesnt presently figure out which perldiag entries are obsolete (ie no code issues them), but, well, its a start.

      #!/usr/local/bin/dperl # -w use strict; use File::Find; use Getopt::Std; use diagnostics; use Data::Dumper; use Text::Balanced qw( extract_delimited extract_bracketed ); $Data::Dumper::Indent = 1; $Data::Dumper::Sortkeys = 1; # sub { shift <=> shift }; my $header; # stores perldiag.pod preamble my %diagpod; # pairsof( =item (.*) => podtext) my %diags; # pairsof( =item (.*) => linenum) by scanning perldiag.p +od my %stmts; # source statement calls to Perl_croak(*) or Perl_warner +(*) my %diagpodidx; # my %warncat; # my (%diagcall, %diagcallidx, %diagcallfull); my %diaginfo; my ($ct_croak, $ct_diag); # controls extraction of info when already doc'd in perldiag our ($opt_p, $opt_s, $opt_i, $opt_u, $opt_d, $opt_c, $opt_U); sub usage { die<<EOH; usage: $0 [options] # scans perl source, finds diagnostics -p : dump perldiags -s : dump source statements which match to diagnostics -u : print unmatched calls -U : print unmatched calls (more info) -d : dump info about the source of the diagnostics -i : insert info into the pod about the source of the diagnostic +s -c : print code extracted EOH } my %formats = qw( IVdf "ld" UVuf "lu" UVof "lo" UVxf "lx" UVXf "lX" NVef "e" NVff "f" NVgf "g" SVf "s" ); my $format_re = join "|", keys %formats; main: { getopts('pseuUdci') or usage; $ct_croak = $ct_diag = 0; parsediagpod(); print "diags from perldiag.pod, numbered: ", Dumper {reverse %diag +s} if $opt_p; find({ wanted => \&lookin, no_chdir => 1}, '.'); print "found $ct_croak different calls in code\n"; my %useddiags; { my (@found); foreach my $diag (keys %diagpod) { push my @new, grep /\Q$diag\E/, keys %stmts; if (@new) { push @found, @new; $useddiags{$diag} = \@new; } } my $exacts = keys %useddiags; print "found $exacts calls matched by diag entry\n"; print "\ndiags-which-matched: ", Dumper(\%useddiags) if $opt_s; } print "calls unmatched by diag: ", Dumper([sort keys %stmts]) if $opt_u; print "calls unmatched by diag: ", Dumper(\%stmts) if $opt_U; print "statements unmatched by diags: ", scalar keys %stmts, "\n"; print "OK now do a SWAG\n"; diagify(%stmts); print "done: ", Dumper(\%diaginfo) if $opt_d; write_diag(); print "done\n"; } sub parsediagpod { open(my $fh, "./pod/perldiag.pod") or die "bad open on perldiag\n"; local $/ = undef; my $buf = <$fh>; # slurp it # pull all existing items and text into hash ($header, %diagpod) = split (/^(?:=item (.*?)\n)/sm, $buf); my @lines = split (/\n/, $buf); my ($lnum,$items); foreach my $line (@lines) { if ($line =~ m/^=item (.*)$/) { $diags{$1} = $lnum; $items++; } $lnum++; } my $ct_diag = keys %diags; print "found $ct_diag diagnostic msgs defined in perldiag.pod\n"; # check for repeats (low value, may trash) if ($ct_diag == $items) { print "theyre all different\n"; } else { print "there'a some overlap\n"; } foreach my $item (keys %diagpod) { my $k = item_sort_key($item); $diagpodidx{$k} = $item; } } sub lookin { # File::Find callback return unless -f $_; return unless /(.*)\.(c|xs)$/; return if $2 eq 'c' and -f "$1.xs"; open(my $fh, $_) or die "bad open on $_\n"; my $file = $_; my ($buf, @warner_lines, $atline); while (<$fh>) { # remember the line number where Perl_warner & Perl_croak are +used. push @warner_lines, $. if /Perl_(warn|croak)/; $buf .= $_; } close $fh; unless ($buf) { warn "file $file is apparently empty\n"; return; } while ($buf =~ m/(Perl_(croak|warn(?:\w*)))/smg) { my $func = $1; $atline = shift @warner_lines; $ct_croak++ if $2 eq 'croak'; my ($argstr, $remainder) = extract_bracketed($buf,'()'); # clean up the match $argstr =~ s/^aTHX_\s*//; $argstr =~ s/\s+/ /smg; if ($opt_c) { print "in $file, at line $atline:\n"; print "\t got $func $argstr\n" if $argstr; } push @{$stmts{"$func$argstr"}}, "$file: $atline"; } return; } sub item_sort_key { # create a key with which to sort diagnostics messages issued by p +erl # see the perldiag.pod intro/header for definition. my($item) = @_; my $old = $item; # remove POD formatting $item =~ s/[A-Z]<(.*?)>/$1/g; # remove printf-style escapes # note: be careful not to remove things like %hash $item =~ s/%([scgp]|lx|\#o)//g; # remove all non-letter characters $item =~ tr/A-Za-z//cd; $item = lc $item; # print "old: $old\nnew: $item\n"; return $item; } sub diagify { # massage a call into item and category my (%stmts) = @_; my $cat; foreach my $call (keys %stmts) { my $new = $call; if ($new =~ s/($format_re)/$formats{$1}/g) { # print "\nold: $call\nnew: $new\n"; } my $warncat; $new =~ s/\)$//; $new =~ s/^Perl_croak\(\s*.THX_ // and $warncat = 'F'; $new =~ s/^Perl_warn(er)?\(\s*.THX_ // and $warncat = 'W'; $new =~ s{packWARN\w*\((.*?)\),\s*}{ print STDERR "whats $new\n" unless $1; $warncat .= ' '.lc($1); $warncat =~ s/warn_//g; #print STDERR "handling: $warncat\t in $new " #if $warncat =~ /,/; $warncat; }e; my ($string,@strings); while ($string = extract_delimited($new, '"','\s*')) { $string =~ s/^\"(.*)\"$/$1/; push @strings, $string; } $new = join('',@strings); $new =~ s/\\\"/\"/g; #print "\nold: $call\nnew: $new\n"; my $sortkey = item_sort_key($new); $diagcall{$new} = delete $stmts{$call}; $diagcallidx{$sortkey} = $new; $diagcallfull{$sortkey} = $call; $warncat{$new} = $warncat; $diaginfo{$sortkey} = { diagmsg => $new, dwarningcat => $warncat, invocation => $call, whence => $diagcall{$new}, }; } } sub write_diag { # output all the data print $header; # # $diagpodidx{$k} = $item; my %idx = (%diagpodidx, %diagcallidx); foreach my $k (sort keys %idx) { my $indiag; if ($diagpodidx{$k}) { print "=item $diagpodidx{$k}\n"; print "$diagpod{$diagpodidx{$k}}"; $indiag = 1; } if (!$indiag and $diagcallidx{$k}) { print "=item $diagcallidx{$k}\n"; print "\n($warncat{$diagcallidx{$k}})\n\n"; print "info: ", Dumper($diaginfo{$k}), "\n" if $opt_i; } elsif ($indiag and $diagcallidx{$k}) { # check for warn-cat match print "correction?: $warncat{$diagcallidx{$k}}\n\n" unless ($diagpod{$diagpodidx{$k}} =~ /\($warncat{$diagcallidx{$k}}\)/); } } print "\n=back\n\n=cut\n"; } __END__ =head1 Latent Problems It appears that a the reason (contributing at least) that diags dont match to croaks is because the printf substitutions that are there. The =items are loaded into (my) %diagnostics::transfmt, and values are initialized to a corresponding regex. %transfmt is used in diagnostics::transmo() by splainthis() to match against the actual error and produce the message. By using diagnostics, I think I can splainthis() on the croaks, thus eliminating the ones that are matched by a diagnostics entry.