This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
Problems with SKIP in makemaker
[perl5.git] / lib / diagnostics.pm
... / ...
CommitLineData
1package diagnostics;
2
3=head1 NAME
4
5diagnostics - Perl compiler pragma to force verbose warning diagnostics
6
7splain - standalone program to do the same thing
8
9=head1 SYNOPSIS
10
11As a pragma:
12
13 use diagnostics;
14 use diagnostics -verbose;
15
16 enable diagnostics;
17 disable diagnostics;
18
19Aa a program:
20
21 perl program 2>diag.out
22 splain [-v] [-p] diag.out
23
24
25=head1 DESCRIPTION
26
27=head2 The C<diagnostics> Pragma
28
29This module extends the terse diagnostics normally emitted by both the
30perl compiler and the perl interpeter, augmenting them with the more
31explicative and endearing descriptions found in L<perldiag>. Like the
32other pragmata, it affects the compilation phase of your program rather
33than merely the execution phase.
34
35To use in your program as a pragma, merely invoke
36
37 use diagnostics;
38
39at the start (or near the start) of your program. (Note
40that this I<does> enable perl's B<-w> flag.) Your whole
41compilation will then be subject(ed :-) to the enhanced diagnostics.
42These still go out B<STDERR>.
43
44Due to the interaction between runtime and compiletime issues,
45and because it's probably not a very good idea anyway,
46you may not use C<no diagnostics> to turn them off at compiletime.
47However, you may control there behaviour at runtime using the
48disable() and enable() methods to turn them off and on respectively.
49
50The B<-verbose> flag first prints out the L<perldiag> introduction before
51any other diagnostics. The $diagnostics::PRETTY variable can generate nicer
52escape sequences for pagers.
53
54=head2 The I<splain> Program
55
56While apparently a whole nuther program, I<splain> is actually nothing
57more than a link to the (executable) F<diagnostics.pm> module, as well as
58a link to the F<diagnostics.pod> documentation. The B<-v> flag is like
59the C<use diagnostics -verbose> directive.
60The B<-p> flag is like the
61$diagnostics::PRETTY variable. Since you're post-processing with
62I<splain>, there's no sense in being able to enable() or disable() processing.
63
64Output from I<splain> is directed to B<STDOUT>, unlike the pragma.
65
66=head1 EXAMPLES
67
68The following file is certain to trigger a few errors at both
69runtime and compiletime:
70
71 use diagnostics;
72 print NOWHERE "nothing\n";
73 print STDERR "\n\tThis message should be unadorned.\n";
74 warn "\tThis is a user warning";
75 print "\nDIAGNOSTIC TESTER: Please enter a <CR> here: ";
76 my $a, $b = scalar <STDIN>;
77 print "\n";
78 print $x/$y;
79
80If you prefer to run your program first and look at its problem
81afterwards, do this:
82
83 perl -w test.pl 2>test.out
84 ./splain < test.out
85
86Note that this is not in general possible in shells of more dubious heritage,
87as the theoretical
88
89 (perl -w test.pl >/dev/tty) >& test.out
90 ./splain < test.out
91
92Because you just moved the existing B<stdout> to somewhere else.
93
94If you don't want to modify your source code, but still have on-the-fly
95warnings, do this:
96
97 exec 3>&1; perl -w test.pl 2>&1 1>&3 3>&- | splain 1>&2 3>&-
98
99Nifty, eh?
100
101If you want to control warnings on the fly, do something like this.
102Make sure you do the C<use> first, or you won't be able to get
103at the enable() or disable() methods.
104
105 use diagnostics; # checks entire compilation phase
106 print "\ntime for 1st bogus diags: SQUAWKINGS\n";
107 print BOGUS1 'nada';
108 print "done with 1st bogus\n";
109
110 disable diagnostics; # only turns off runtime warnings
111 print "\ntime for 2nd bogus: (squelched)\n";
112 print BOGUS2 'nada';
113 print "done with 2nd bogus\n";
114
115 enable diagnostics; # turns back on runtime warnings
116 print "\ntime for 3rd bogus: SQUAWKINGS\n";
117 print BOGUS3 'nada';
118 print "done with 3rd bogus\n";
119
120 disable diagnostics;
121 print "\ntime for 4th bogus: (squelched)\n";
122 print BOGUS4 'nada';
123 print "done with 4th bogus\n";
124
125=head1 INTERNALS
126
127Diagnostic messages derive from the F<perldiag.pod> file when available at
128runtime. Otherwise, they may be embedded in the file itself when the
129splain package is built. See the F<Makefile> for details.
130
131If an extant $SIG{__WARN__} handler is discovered, it will continue
132to be honored, but only after the diagnostics::splainthis() function
133(the module's $SIG{__WARN__} interceptor) has had its way with your
134warnings.
135
136There is a $diagnostics::DEBUG variable you may set if you're desperately
137curious what sorts of things are being intercepted.
138
139 BEGIN { $diagnostics::DEBUG = 1 }
140
141
142=head1 BUGS
143
144Not being able to say "no diagnostics" is annoying, but may not be
145insurmountable.
146
147The C<-pretty> directive is called too late to affect matters.
148You have to to this instead, and I<before> you load the module.
149
150 BEGIN { $diagnostics::PRETTY = 1 }
151
152I could start up faster by delaying compilation until it should be
153needed, but this gets a "panic: top_level" when using the pragma form
154in Perl 5.001e.
155
156While it's true that this documentation is somewhat subserious, if you use
157a program named I<splain>, you should expect a bit of whimsy.
158
159=head1 AUTHOR
160
161Tom Christiansen <F<tchrist@mox.perl.com>>, 25 June 1995.
162
163=cut
164
165require 5.001;
166use Carp;
167
168use Config;
169if ($^O eq 'VMS') {
170 $PODFILE = VMS::Filespec::unixify($Config{privlibexp}).'/pod/perldiag.pod';
171}
172else {
173 $PODFILE = $Config{privlibexp} . "/pod/perldiag.pod";
174}
175
176$DEBUG ||= 0;
177my $WHOAMI = ref bless []; # nobody's business, prolly not even mine
178
179$| = 1;
180
181local $_;
182
183CONFIG: {
184 $opt_p = $opt_d = $opt_v = $opt_f = '';
185 %HTML_2_Troff = %HTML_2_Latin_1 = %HTML_2_ASCII_7 = ();
186 %exact_duplicate = ();
187
188 unless (caller) {
189 $standalone++;
190 require Getopt::Std;
191 Getopt::Std::getopts('pdvf:') || die "Usage: $0 [-v] [-p] [-f splainpod]";
192 $PODFILE = $opt_f if $opt_f;
193 $DEBUG = 2 if $opt_d;
194 $VERBOSE = $opt_v;
195 $PRETTY = $opt_p;
196 }
197
198 if (open(POD_DIAG, $PODFILE)) {
199 warn "Happy happy podfile from real $PODFILE\n" if $DEBUG;
200 last CONFIG;
201 }
202
203 if (caller) {
204 INCPATH: {
205 for $file ( (map { "$_/$WHOAMI.pm" } @INC), $0) {
206 warn "Checking $file\n" if $DEBUG;
207 if (open(POD_DIAG, $file)) {
208 while (<POD_DIAG>) {
209 next unless /^__END__\s*# wish diag dbase were more accessible/;
210 print STDERR "podfile is $file\n" if $DEBUG;
211 last INCPATH;
212 }
213 }
214 }
215 }
216 } else {
217 print STDERR "podfile is <DATA>\n" if $DEBUG;
218 *POD_DIAG = *main::DATA;
219 }
220}
221if (eof(POD_DIAG)) {
222 die "couldn't find diagnostic data in $PODFILE @INC $0";
223}
224
225
226%HTML_2_Troff = (
227 'amp' => '&', # ampersand
228 'lt' => '<', # left chevron, less-than
229 'gt' => '>', # right chevron, greater-than
230 'quot' => '"', # double quote
231
232 "Aacute" => "A\\*'", # capital A, acute accent
233 # etc
234
235);
236
237%HTML_2_Latin_1 = (
238 'amp' => '&', # ampersand
239 'lt' => '<', # left chevron, less-than
240 'gt' => '>', # right chevron, greater-than
241 'quot' => '"', # double quote
242
243 "Aacute" => "\xC1" # capital A, acute accent
244
245 # etc
246);
247
248%HTML_2_ASCII_7 = (
249 'amp' => '&', # ampersand
250 'lt' => '<', # left chevron, less-than
251 'gt' => '>', # right chevron, greater-than
252 'quot' => '"', # double quote
253
254 "Aacute" => "A" # capital A, acute accent
255 # etc
256);
257
258*HTML_Escapes = do {
259 if ($standalone) {
260 $PRETTY ? \%HTML_2_Latin_1 : \%HTML_2_ASCII_7;
261 } else {
262 \%HTML_2_Latin_1;
263 }
264};
265
266*THITHER = $standalone ? *STDOUT : *STDERR;
267
268$transmo = <<EOFUNC;
269sub transmo {
270 local \$^W = 0; # recursive warnings we do NOT need!
271 study;
272EOFUNC
273
274### sub finish_compilation { # 5.001e panic: top_level for embedded version
275 print STDERR "FINISHING COMPILATION for $_\n" if $DEBUG;
276 ### local
277 $RS = '';
278 local $_;
279 while (<POD_DIAG>) {
280 #s/(.*)\n//;
281 #$header = $1;
282
283 unescape();
284 if ($PRETTY) {
285 sub noop { return $_[0] } # spensive for a noop
286 sub bold { my $str =$_[0]; $str =~ s/(.)/$1\b$1/g; return $str; }
287 sub italic { my $str = $_[0]; $str =~ s/(.)/_\b$1/g; return $str; }
288 s/[BC]<(.*?)>/bold($1)/ges;
289 s/[LIF]<(.*?)>/italic($1)/ges;
290 } else {
291 s/[BC]<(.*?)>/$1/gs;
292 s/[LIF]<(.*?)>/$1/gs;
293 }
294 unless (/^=/) {
295 if (defined $header) {
296 if ( $header eq 'DESCRIPTION' &&
297 ( /Optional warnings are enabled/
298 || /Some of these messages are generic./
299 ) )
300 {
301 next;
302 }
303 s/^/ /gm;
304 $msg{$header} .= $_;
305 }
306 next;
307 }
308 unless ( s/=item (.*)\s*\Z//) {
309
310 if ( s/=head1\sDESCRIPTION//) {
311 $msg{$header = 'DESCRIPTION'} = '';
312 }
313 next;
314 }
315
316 # strip formatting directives in =item line
317 ($header = $1) =~ s/[A-Z]<(.*?)>/$1/g;
318
319 if ($header =~ /%[sd]/) {
320 $rhs = $lhs = $header;
321 #if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E\\d+\Q$2\E\$/g) {
322 if ($lhs =~ s/(.*?)%d(?!%d)(.*)/\Q$1\E\\d+\Q$2\E/g) {
323 $lhs =~ s/\\%s/.*?/g;
324 } else {
325 # if i had lookbehind negations, i wouldn't have to do this \377 noise
326 $lhs =~ s/(.*?)%s/\Q$1\E.*?\377/g;
327 #$lhs =~ s/\377([^\377]*)$/\Q$1\E\$/;
328 $lhs =~ s/\377([^\377]*)$/\Q$1\E/;
329 $lhs =~ s/\377//g;
330 $lhs =~ s/\.\*\?$/.*/; # Allow %s at the end to eat it all
331 }
332 $transmo .= " s{^$lhs}\n {\Q$rhs\E}s\n\t&& return 1;\n";
333 } else {
334 $transmo .= " m{^\Q$header\E} && return 1;\n";
335 }
336
337 print STDERR "$WHOAMI: Duplicate entry: \"$header\"\n"
338 if $msg{$header};
339
340 $msg{$header} = '';
341 }
342
343
344 close POD_DIAG unless *main::DATA eq *POD_DIAG;
345
346 die "No diagnostics?" unless %msg;
347
348 $transmo .= " return 0;\n}\n";
349 print STDERR $transmo if $DEBUG;
350 eval $transmo;
351 die $@ if $@;
352 $RS = "\n";
353### }
354
355if ($standalone) {
356 if (!@ARGV and -t STDIN) { print STDERR "$0: Reading from STDIN\n" }
357 while (defined ($error = <>)) {
358 splainthis($error) || print THITHER $error;
359 }
360 exit;
361} else {
362 $old_w = 0; $oldwarn = ''; $olddie = '';
363}
364
365sub import {
366 shift;
367 $old_w = $^W;
368 $^W = 1; # yup, clobbered the global variable; tough, if you
369 # want diags, you want diags.
370 return if $SIG{__WARN__} eq \&warn_trap;
371
372 for (@_) {
373
374 /^-d(ebug)?$/ && do {
375 $DEBUG++;
376 next;
377 };
378
379 /^-v(erbose)?$/ && do {
380 $VERBOSE++;
381 next;
382 };
383
384 /^-p(retty)?$/ && do {
385 print STDERR "$0: I'm afraid it's too late for prettiness.\n";
386 $PRETTY++;
387 next;
388 };
389
390 warn "Unknown flag: $_";
391 }
392
393 $oldwarn = $SIG{__WARN__};
394 $olddie = $SIG{__DIE__};
395 $SIG{__WARN__} = \&warn_trap;
396 $SIG{__DIE__} = \&death_trap;
397}
398
399sub enable { &import }
400
401sub disable {
402 shift;
403 $^W = $old_w;
404 return unless $SIG{__WARN__} eq \&warn_trap;
405 $SIG{__WARN__} = $oldwarn;
406 $SIG{__DIE__} = $olddie;
407}
408
409sub warn_trap {
410 my $warning = $_[0];
411 if (caller eq $WHOAMI or !splainthis($warning)) {
412 print STDERR $warning;
413 }
414 &$oldwarn if defined $oldwarn and $oldwarn and $oldwarn ne \&warn_trap;
415};
416
417sub death_trap {
418 my $exception = $_[0];
419
420 # See if we are coming from anywhere within an eval. If so we don't
421 # want to explain the exception because it's going to get caught.
422 my $in_eval = 0;
423 my $i = 0;
424 while (1) {
425 my $caller = (caller($i++))[3] or last;
426 if ($caller eq '(eval)') {
427 $in_eval = 1;
428 last;
429 }
430 }
431
432 splainthis($exception) unless $in_eval;
433 if (caller eq $WHOAMI) { print STDERR "INTERNAL EXCEPTION: $exception"; }
434 &$olddie if defined $olddie and $olddie and $olddie ne \&death_trap;
435
436 # We don't want to unset these if we're coming from an eval because
437 # then we've turned off diagnostics. (Actually what does this next
438 # line do? -PSeibel)
439 $SIG{__DIE__} = $SIG{__WARN__} = '' unless $in_eval;
440 local($Carp::CarpLevel) = 1;
441 confess "Uncaught exception from user code:\n\t$exception";
442 # up we go; where we stop, nobody knows, but i think we die now
443 # but i'm deeply afraid of the &$olddie guy reraising and us getting
444 # into an indirect recursion loop
445};
446
447sub splainthis {
448 local $_ = shift;
449 ### &finish_compilation unless %msg;
450 s/\.?\n+$//;
451 my $orig = $_;
452 # return unless defined;
453 if ($exact_duplicate{$_}++) {
454 return 1;
455 }
456 s/, <.*?> (?:line|chunk).*$//;
457 $real = s/(.*?) at .*? (?:line|chunk) \d+.*/$1/;
458 s/^\((.*)\)$/$1/;
459 return 0 unless &transmo;
460 $orig = shorten($orig);
461 if ($old_diag{$_}) {
462 autodescribe();
463 print THITHER "$orig (#$old_diag{$_})\n";
464 $wantspace = 1;
465 } else {
466 autodescribe();
467 $old_diag{$_} = ++$count;
468 print THITHER "\n" if $wantspace;
469 $wantspace = 0;
470 print THITHER "$orig (#$old_diag{$_})\n";
471 if ($msg{$_}) {
472 print THITHER $msg{$_};
473 } else {
474 if (0 and $standalone) {
475 print THITHER " **** Error #$old_diag{$_} ",
476 ($real ? "is" : "appears to be"),
477 " an unknown diagnostic message.\n\n";
478 }
479 return 0;
480 }
481 }
482 return 1;
483}
484
485sub autodescribe {
486 if ($VERBOSE and not $count) {
487 print THITHER &{$PRETTY ? \&bold : \&noop}("DESCRIPTION OF DIAGNOSTICS"),
488 "\n$msg{DESCRIPTION}\n";
489 }
490}
491
492sub unescape {
493 s {
494 E<
495 ( [A-Za-z]+ )
496 >
497 } {
498 do {
499 exists $HTML_Escapes{$1}
500 ? do { $HTML_Escapes{$1} }
501 : do {
502 warn "Unknown escape: E<$1> in $_";
503 "E<$1>";
504 }
505 }
506 }egx;
507}
508
509sub shorten {
510 my $line = $_[0];
511 if (length($line) > 79 and index($line, "\n") == -1) {
512 my $space_place = rindex($line, ' ', 79);
513 if ($space_place != -1) {
514 substr($line, $space_place, 1) = "\n\t";
515 }
516 }
517 return $line;
518}
519
520
521# have to do this: RS isn't set until run time, but we're executing at compile time
522$RS = "\n";
523
5241 unless $standalone; # or it'll complain about itself
525__END__ # wish diag dbase were more accessible