in perl, 'use strict' disables important warnings - perl

Adding use strict to a perl program disables an important warning message. Is this a bug in perl?
This little program has a problem
#!/usr/bin/perl -w
my $rule;
sub applyRule() {
eval $rule;
}
my $foo_bar;
$foo_bar = "mega-foo-bar-ness";
$rule = 's/#FOO_BAR/$foo_bar/g';
$_ = "Please replace #FOO_BAR with something.";
applyRule();
print STDERR "OUTPUT: $_\n";
and we get a helpful warning message when running that program:
$ ./test1.pl
Use of uninitialized value $foo_bar in substitution (s///) at (eval 1) line 1.
OUTPUT: Please replace with something.
Now add use strict;
#!/usr/bin/perl -w
use strict;
my $rule;
sub applyRule() {
eval $rule;
}
my $foo_bar;
$foo_bar = "mega-foo-bar-ness";
$rule = 's/#FOO_BAR/$foo_bar/g';
$_ = "Please replace #FOO_BAR with something.";
applyRule();
print STDERR "OUTPUT: $_\n";
The substitution just silently fails.
$ ./test1.pl
OUTPUT: Please replace #FOO_BAR with something.
When I have mysterious errors in perl should I always try removing use strict to see if I get some helpful warnings?
edit 1 stackoverflow is asking me to describe why this is not a duplicate of Why use strict and warnings?. This is a case where adding use strict results in fewer warnings, which makes debugging harder -- the opposite of what I expect use strict would do; the opposite of what is suggested in the answers to that question:
The pragmas catch many errors sooner than they would be caught otherwise, which makes it easier to find the root causes of the errors.
edit 2: For anyone else seeing this: another way is:
defined (eval $rule) or warn "eval error: $#\n";
because eval returns undefined if there is an error. The Programming Perl book states in the section about eval
If there is a syntax error or run-time error, eval returns the undefined value and puts the error message in $#. If there is no error, $# is guaranteed to be set to the null string, so you can test it reliably afterward for errors.
Note however that with versions prior to Perl 5.14 there can be problems relying on '$#`.

Instead of a warning, it throws an exception. And you aren't testing to see if eval gave an exception.
Try:
eval "$rule; 1" or warn "exception thrown: $#\n";

Related

Perl: print compilation warnings for do()

I am using do() to execute a perl script. I wanted to make sure it was doing the right error checking, but I noticed that warnings don't get printed when I use do(). I left the following warning on purpose in my script. I see it when I run it on the command line
Useless use of numeric eq (==) in void context at utils/queryEvent/query_event.pl line 47.
When I use do(), this message is suppressed. I saw in perlvar that $# doesn't contain warnings, so I tried to add this before my call to do(), but I still don't see anything.
local $SIG{__WARN__} = sub {
print "Warning #_\n";
};
do(...);
Is there a way I can still print the compilation/parsing warnings?
I'm using Perl v.5.22.1 on Windows 10.
Added edit:
Both the script calling do() and the script being run through do() have use strict; and use warnings;. It is only the parsing warning that isn't showing up. If I call warn in the do script, then I see that (I'm assuming stderr).
Edit:
Adding example script.
A minimal reproducible version is the following
use strict;
use warnings;
sub test
{
return 1;
}
main:
{
(test() == 1);
}
Calling script
use strict;
use warnings;
main:
{
do('test.pl');
}
You are mistaken. do doesn't affect warnings in the least.
There is no warning because there is nothing that should cause a warning. (test() == 1) isn't being evaluated in void context. Instead, its result is returned by do.
But while warnings AREN'T being silenced, exceptions ARE being silenced. Fixing this will fix the other.
Add a trailing 1 to the file loaded by do, and change the do as follows:
do('test.pl')
or die( "Can't execute `./test.pl`: ".( $# // $! ) );
The trailing 1 is used to signal the "caller" whether an error occurred or not. This has the side effect of cause (test() == 1) to be evaluated in void context as expected.
The -w or -W command-line switches will enable warnings throughout your program, including code included through a do statement.

using perl how can you figure out how to turn off a specific warning

I've got some legacy code that I'm dealing with. It's too much to clean up in one shot. It's using \1 inside of the s operator. I looked into perllexwarn and found I can shut it off with 'no warnings qw(syntax)', but I did this with trial and error. Is there an easier way to get right from the warning to the way to shut it off?
It's doing stuff like this:
use strict;
$_ = "abc";
s/abc/\1/;
no warnings qw(syntax);
s/abc/\1/;
the message it generates is :
\1 better written as $1
Execute your script as
perl -Mdiagnostics ./a.pl
or temporarily add use diagnostics; to your script. This will produce something like
\1 better written as $1 at ./a.pl line 4 (#1)
(W syntax) Outside of patterns, backreferences live on as variables.
The use of backslashes is grandfathered on the right-hand side of a
substitution, but stylistically it's better to use the variable form
because other Perl programmers will expect it, and it works better if
there are more than 9 backreferences.
Notice the (W syntax)? The letter is one of the following, and the word is the warning class for which you are looking.
(W) A warning (optional).
(D) A deprecation (enabled by default).
(S) A severe warning (enabled by default).
(F) A fatal error (trappable).
(P) An internal error you should never see (trappable).
(X) A very fatal error (nontrappable).
(A) An alien error message (not generated by Perl).
diagnostics gets its information from perldiag, which you could search manually instead of using use diagnostics;.
Other examples:
$ perl -Mdiagnostics -we'print undef'
Use of uninitialized value in print at -e line 1 (#1)
(W uninitialized) An undefined value was used as if it were already
[...]
$ perl -Mdiagnostics -we'no warnings qw( uninitialized ); print undef'
$ perl -Mdiagnostics -we'sub foo { } sub foo { }'
Subroutine foo redefined at -e line 1 (#1)
(W redefine) You redefined a subroutine. To suppress this warning, say
[...]
$ perl -Mdiagnostics -we'no warnings qw( redefine ); sub foo { } sub foo { }'
$
I'd make a global signal handler, set it in a BEGIN block so it's compiled in early, and skip only the warning you don't want, so you still get any potential unexpected and unrelated warnings (even within the same category, due to not having to disable the whole thing):
use warnings;
use strict;
BEGIN {
$SIG{__WARN__} = sub {
my $warn = shift;
return if $warn =~ /\\\d better written as/;
warn $warn;
};
}
my $x = 'abc';
$x =~ s/(abc)/\1/;
warn "a different warning\n";
Output:
a different warning
You can look the message up in perldoc perldiag. This will tell you the warning category it's in.

perlcritic: eval "require $module";

While digging in some old source code I saw the following:
my $module = $some{module};
eval "require $module";
die "Bad module\n$#" if $#;
while I understand what the code does, it tries "require" a module and die when it is unsuccessful - the perlcritic complains about it
Expression form of "eval" at line 331, column 13. See page 161 of
PBP. (Severity: 5)
Unfortunately I havent the PBP book, so wondering what is the correct method of the above...
Also, in the same source found:
sub test_repo_file {
my($self, $repo, $test) = #_;
my $abspath = repo_abs_path($repo);
return "eval -$test $abspath";
}
Here doesn't understand what solves the "eval", and the perlcritic complains again for the "string eval"...
Can somebody please explain the basic points about the "string eval" and how to write the above correctly?
Running perlcritic --verbose '%d\n' will give you the explanations, too:
The string form of `eval' is recompiled every time it is executed,
whereas the block form is only compiled once. Also, the string form
doesn't give compile-time warnings.
eval "print $foo"; # not ok
eval {print $foo}; # ok
It's applicable to the first case.
The second case doesn't generate any messages for me. Isn't it rather
return eval "-$test $abspath"
You can't use block eval here. You should verify that $test really contains what it should
$test =~ /^[a-z]$/i
and avoid the evaluation of $abspath:
eval "-$test \$abspath"
If you're OK with that, you can than add
## no critic
to the end of the line.
Few users should ever have to use eval EXPR. Most of the time, it's being used as a template system when it shouldn't (e.g. s/.../eval($repl)/e), or it's used to catch exceptions when eval BLOCK should have been used.
If there is reason to use it, it's to execute generated code or user-submitted code. Generating code is tricky and error-prone, and errors have security implications. Executing user-submitted code is a major security concern. As such, every use of eval EXPR should be carefully reviewed.
It's quite appropriate for perlcritic to flag its usage given that virtually every use is an error with major security implications.
In your case, the use of eval EXPR is suboptimal. I'd use
my $path = $module . ".pm";
$path =~ s{::}{/}g;
eval { require $path }
Yes, this is portable.

Will Perl's $# ever be undefined after an eval?

I'm working on the "Error Handling and Reporting" chapter of Mastering Perl. In perlvar's entry for $#, it says:
The Perl syntax error message from the last eval() operator. If $# is the null string, the last eval() parsed and executed correctly (although the operations you invoked may have failed in the normal fashion).
Now I'm wondering when an eval might not execute correctly, leaving $# with an undefined value. Are there any such cases?
Here's one way to do it (but sit down before you read it. ;))
$# = 123;
eval q{
$# = 456;
print ">>>$#<<<\n";
goto SKIP;
};
SKIP:
print ">>>$#<<<\n";
The BACKGROUND section of the Try::Tiny docs contain some info on how $# can be clobbered and why Try::Tiny takes special care to avoid clobberage, and always tests the return value of eval rather than testing $# for truth. All of them are in there because someone ran into them at some point, but I think that the eval-in-DESTROY scenario is the one most likely to trip someone up. Basically, if die causes some object to go out of scope, and that object has a DESTROY that calls eval, then the value that you died with to begin with is irretrievably lost. Assuming the eval in the DESTROY doesn't throw an error, $# will be "" following the outer eval.
Who said anything about setting $# to undef?
"The last eval() parsed and executed correctly" doesn't make any sense: eval isn't parsed at runtime. Surely, it means "the last eval() whose expression was parsed and executed correctly". In other words, "the last eval() whose expression compiled and didn't throw any exceptions when executed".
>perl -MData::Dumper -e"$#=123; eval ''; print(Dumper($#));"
$VAR1 = '';
>perl -MData::Dumper -e"$#=123; eval '~~~'; print(Dumper($#));"
$VAR1 = 'syntax error at (eval 1) line 2, at EOF
';
>perl -MData::Dumper -e"$#=123; eval 'die q{x}'; print(Dumper($#));"
$VAR1 = 'x at (eval 1) line 1.
';

How do you best attribute errors in a Perl method to the caller

Given a method that may fail with warnings and/or errors, I want the error method to show up at the caller. Fir instance this script:
foo(0); # line 1
sub foo {
1 / shift; # line 4
}
Produces the error Illegal division by zero at foo.pl line 4, but I want Illegal division by zero at foo.pl line 1. There should be several ways if I put the method in a module or if I wrap the method body in eval, but I have not found an easy way like this:
sub foo {
attributeErrorsToCaller; # do some magic
1 / shift;
}
Is there such a way?
EDIT: mirod's answer comes close not what I was looking for:
Foo::foo(0); # line 1
package Foo;
use diagnostics -traceonly;
BEGIN { disable diagnostics; }
sub foo {
enable diagnostics;
1 / shift; # line 9
}
Without enable diagnostics the error message is Illegal division by zero at foo.pl line 9.. With enable diagnostics it is still too verbose, but this may also be useful:
Uncaught exception from user code:
Illegal division by zero at foo.pl line 10.
at foo.pl line 10
Foo::foo(0) called at foo.pl line 2
I bet I could hack diagnostics to get exactely the feature I want, but using diagnostics as raw module is probably more recommended.
Carp is very, very close to "do_some_magic" you are asking for. For instance:
#!/usr/bin/perl -w
use strict;
# I fork to be as close to natural die() as possible.
fork() or Foo::foo();
fork() or Foo::bar();
fork() or Foo::baz();
sleep 1;
package Foo;
use Carp;
sub foo { die "Some error (foo)"; };
sub bar { croak "Some error (bar)"; };
sub baz { bar(); };
As you can see, croak() acts almost like die(), but reports error to the caller (even indirectly -- see baz).
However, it won't handle 1/0 for you -- either use eval (or even Try::Tiny), or check input values* and say "division by zero" yourself.
Carp is standard, which means understandable by further maintainers of your code, and also can print neat stack traces via confess or cluck or even print Carp::longmess (see the doc).
*which is good anyway
Would use diagnostics; be enough for you? It will dump the call stack, so the caller is quite easy to find out.
For example in you example:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
foo(0);
sub foo
{
return 1/$_[0];
}
gives this:
`Illegal division by zero at test_die line 12 (#1)
(F) You tried to divide a number by 0. Either something was wrong in
your logic, or you need to put a conditional in to guard against
meaningless input.
Uncaught exception from user code:
Illegal division by zero at test_die line 12.
at test_die line 12
main::foo(0) called at test_die line 8
Not like you describe. You can implement Debug::Trace to pull off a full back trace.
You may also find the perl caller which is quite useful for this type of debugging. But for a live application you will likely need to do more detailed tracing.
http://perldoc.perl.org/functions/caller.html