Subject: | Accidental semicolon (;) in the middle of try/catch/finally statement is silently ignored |
I've discovered the nice Try::Tiny as a means of fighting the loss of
time when debugging issues concerning scope and values of $@.
But as soon as I started using it I discovered that if you accidently
add a semicolon in the middle of different try/catch/finally clauses,
Perl silently ignores subsequent catch/finally. I.e. exception is not
caught and it is not processed. :-o
I suggest to add some warning (probably, controlled via "use warnings"
and implemented using "warnings::warnif(..)") to help to diagnose this case.
In a patch that I attach with this issue, I've implemented this using
DESTROY methods in Try::Tiny::Catch and Try::Tiny::Finally packages.
When you call "catch(..);" or "finally(..);" not as an argument to
"try(..)", the object returned from them destroys before the next
statement and a warning is written. But when you chain them to "try {}
..", then when "sub try" code processes arguments they are reblessed to
Try::Tiny::CatchProcessed and Try::Tiny::FinallyProcessed and the
DESTROY destructor is not called after the entire statement.
Feel free to discuss this issue (do we need to use warnif (and thus call
warnings::register() on a module level) to allow FATAL warnings? warning
message format? etc.). :)
Andrey
Subject: | try_tiny.patch |
--- a/Try/Tiny.pm
+++ b/Try/Tiny.pm
@@ -40,8 +40,10 @@ sub try (&;@) {
if ( $ref eq 'Try::Tiny::Catch' ) {
$catch = ${$code_ref};
+ bless $code_ref, 'Try::Tiny::CatchProcessed'; # To not trigger a warning/error from no-standalone catch
} elsif ( $ref eq 'Try::Tiny::Finally' ) {
push @finally, ${$code_ref};
+ bless $code_ref, 'Try::Tiny::FinallyProcessed'; # To not trigger a warning/error from no-standalone finally
} else {
use Carp;
confess("Unknown code ref type given '${ref}'. Check your usage & try again");
@@ -120,6 +122,16 @@ sub catch (&;@) {
);
}
+{
+ package # hide from PAUSE
+ Try::Tiny::Catch;
+ sub DESTROY
+ {
+ my @caller = caller();
+ warn "catch without try, check semicolons in try/catch/finally statement BEFORE the line at ".$caller[1]." line ".$caller[2]."\n";
+ }
+}
+
sub finally (&;@) {
my ( $block, @rest ) = @_;
@@ -130,6 +142,16 @@ sub finally (&;@) {
}
{
+ package # hide from PAUSE
+ Try::Tiny::Finally;
+ sub DESTROY
+ {
+ my @caller = caller();
+ warn "finally without try, check semicolons in try/catch/finally statement BEFORE the line at ".$caller[1]." line ".$caller[2]."\n";
+ }
+}
+
+{
package # hide from PAUSE
Try::Tiny::ScopeGuard;