Perl Part I · Module 14 · Errors, Warnings, and Debugging
Turning Specific Warnings Into Errors
A warning tells you something is wrong and then carries on anyway. Usually that is the right trade. Sometimes it is not: when a missing value becomes zero and that zero flows into a timing report, a message on standard error is not enough, because the script finishes successfully with a wrong answer — Chapter 14.1's third and most dangerous kind of failure. Perl lets you nominate one category of warning, inside one block of code, and have it stop the script instead. This chapter measures the mechanism, shows that it really is limited to the block and the category you named, and is honest about the one place you should not use it.
Advanced11 min readPerlPart IDebugging
Part I · Module 14 · Chapter 14.6 · Turning Specific Warnings Into Errors
1. The Problem: A Missing Value Becomes Zero
use strict;
use warnings;
my %slack = (A => 0.44, B => undef, C => 0.12);
# the warning goes to stderr; the ARITHMETIC still happens
{
no warnings 'uninitialized'; # quietened so the result is visible
my $total = 0;
$total += $slack{$_} for sort keys %slack;
printf "sum of A, B, C = %s\n", $total;
printf "undef + 1 = %s\n", $slack{B} + 1;
printf "average over 3 pins = %.3f\n", $total / 3;
}
print "\nnothing failed. the report is simply wrong.\n";sum of A, B, C = 0.56
undef + 1 = 1
average over 3 pins = 0.187
nothing failed. the report is simply wrong.undef was treated as 0. Every line of arithmetic succeeded. The average is computed over three pins when only two had data, so it is understated — and the script exits 0.
Perl does warn about this. But a warning on standard error is easy to lose: Chapter 14.2 measured that script.pl > report.txt does not capture it at all, and a batch job that discards stderr discards the only notice you were given.
2. Making That One Warning Fatal
use strict;
use warnings;
my %slack = (A => 0.44, B => undef);
print "--- plain block: warns, and carries on\n";
{
my $ok = eval { no warnings 'uninitialized'; my $t = $slack{B} + 1; 1 };
print " survived? ", ($ok ? "yes" : "NO"), "\n";
}
print "--- FATAL block: the same line dies\n";
{
use warnings FATAL => 'uninitialized';
my $ok = eval { my $t = $slack{B} + 1; 1 };
my $e = $@;
$e =~ s/ at .* line \d+\.?\s*\z//;
$e =~ s/\s+\z//;
print " survived? ", ($ok ? "yes" : "NO"), "\n";
print " \$\@ = '$e'\n";
}
print "--- after the block: not fatal any more\n";
{
my $ok = eval { no warnings 'uninitialized'; my $t = $slack{B} + 1; 1 };
print " survived? ", ($ok ? "yes" : "NO"), "\n";
}--- plain block: warns, and carries on
survived? yes
--- FATAL block: the same line dies
survived? NO
$@ = 'Use of uninitialized value $slack{"B"} in addition (+)'
--- after the block: not fatal any more
survived? yesThree measured results from one piece of arithmetic:
In a plain block it survived. A warning, nothing more.
Under use warnings FATAL => 'uninitialized' it died, and the message is better than you might expect: Use of uninitialized value $slack{"B"} in addition (+). Perl names the exact hash key and the exact operation. That is a far better starting point than a wrong number in a report.
After the block it survived again. This is the part worth dwelling on, because it is what makes the feature usable at all.
use warnings is a lexical pragma. It applies from where it appears to the end of the enclosing block, file or eval, and not one statement further. It is not a global setting and it is not affected by which functions you call — a subroutine defined elsewhere keeps whatever warning rules were in force where it was written, not where it was called from. Chapter 12.8 drew the contrast with local, which is the opposite: dynamic, following the call chain.
So you can turn this on for the twenty lines that compute a report and leave the rest of a large script alone.
3. One Category, Not All Of Them
FATAL => 'all' exists, and it is a much bigger hammer than it looks. Measured both ways:
use strict;
use warnings;
{
use warnings FATAL => 'uninitialized';
my $u;
my $numeric = eval { my $x = "abc" + 1; 1 }; # the "numeric" category
my $uninit = eval { my $x = $u + 1; 1 }; # the "uninitialized" category
printf "FATAL=>'uninitialized' numeric warning: %s\n", $numeric ? "survived" : "DIED";
printf "FATAL=>'uninitialized' uninit warning: %s\n", $uninit ? "survived" : "DIED";
}
{
use warnings FATAL => 'all';
my $numeric = eval { my $x = "abc" + 1; 1 };
printf "FATAL=>'all' numeric warning: %s\n", $numeric ? "survived" : "DIED";
}FATAL=>'uninitialized' numeric warning: survived
FATAL=>'uninitialized' uninit warning: DIED
FATAL=>'all' numeric warning: DIEDThe selectivity is real. Under FATAL => 'uninitialized', the non-numeric string warning survived while the undefined-value warning died. Under FATAL => 'all', the same numeric warning died too.
The categories you are most likely to want are:
uninitialized— a value that was never set. The one this chapter is built around.numeric— a string used as a number. Chapter 14.7 measures why this one matters for timing reports specifically.once,recursion,closure,io— narrower, andperldoc perllexwarnlists the full tree.
4. Where To Use It, And Where Not To
Use it around a calculation whose result is reported as fact. Summing slack across pins, computing a utilisation percentage, deriving a pass or fail verdict. In all of these, a silently-zero input produces a plausible wrong number, and plausible wrong numbers are what get acted on.
sub report_average_slack {
my (%slack) = @_;
use warnings FATAL => 'uninitialized'; # only inside this sub
my $total = 0;
$total += $slack{$_} for keys %slack;
return $total / scalar keys %slack;
}That subroutine now refuses to produce a number from incomplete data, and the caller — wrapping it in an eval or a try from the last two chapters — can report which pin was missing instead of printing a confident average.
Use it in your test scripts generally. A test run is exactly where you want to be told loudly, and nobody downstream depends on the script surviving.
Do not use it in a module other people call. This is the important exception. A fatal warning deep inside a library turns a condition the caller could have tolerated into a death they did not ask for and cannot easily switch off, because your pragma is in your file and not theirs. If a library wants to object, it should croak — which is deliberate, documented, catchable, and points at the caller rather than at your internals. That is Chapter 14.9.
Do not use it to find bugs you could find by reading. The pragma is for conditions that must not pass silently in production. For a one-off hunt, $SIG{__WARN__} with a stack trace tells you where a warning came from without changing what the script does — Chapter 14.11 shows the form.
5. What To Remember
- A warning does not stop the arithmetic. Measured:
undefsummed as0and the average came out understated, with exit status 0. - Standard error is easy to lose —
> report.txtdoes not capture it, so the only notice can disappear. use warnings FATAL => 'category';turns that one warning into a realdie.- The message names the variable — measured,
Use of uninitialized value $slack{"B"} in addition (+). - It is lexical. Measured: fatal inside the block, not fatal in the next one. It stops at the end of the enclosing block, file or
eval. - A subroutine keeps the rules in force where it was written, not where it was called — the opposite of
local. - The category is honoured. Measured: under
FATAL => 'uninitialized'a numeric warning survived; underFATAL => 'all'it died. - Do not ship
FATAL => 'all'— a future Perl adding a warning to a category turns working code into a failure at upgrade time. - Good places: a calculation whose output is reported as fact, and test scripts.
- Bad place: a module other people call. Use
croakinstead, so the objection is deliberate and points at the caller. no warnings 'category';is the matching tool, in the smallest block that needs it, never bare and never broad.
Chapter 14.7 goes through the warnings themselves. Four of them account for most of what Perl reports, each is a precise statement about your data, and one of them silently turns a timing value with a unit attached into a plain number.
Where this fits
Part of the Perl curriculum.
