At $work, we just fixed a tiny bug by adding a use Try::Tiny and a semicolon.

A colleague asked «how did it even compile, before?»

Well, welcome to another weird corner of
#Perl!

So, when neither
try nor catch are declared, the source:

try { print "in try"; } catch { print "in catch"; } return 42;is parsed as(print "in try")->try((print "in catch")->catch(return 42));because of the Indirect Object Syntax (the thing that makes my $obj = new Class @args equivalent to my $obj = Class->new(@args), and also allows print { $filehandle } @values)

«but wait!», you’ll say, «why am I not getting
Can't locate object method "try" via package "1"?» (after all, print returns 1, and sub 1::try is definitely not there)

and the answer is: the missing semicolon!

see, before looking up a method, perl evaluates all its arguments, in order from left to right. So it first evaluates
print "in try", then print "in catch", then return 42, at which point the control returns to the caller *and neither try nor catch are ever looked up*!

Indeed, if you run:
sub thing { try { print "in try"; } catch { print "in catch"; }; return 42; } thing;(notice the semicolon before return!) you'll get (pardon the newlines):in try in catch Can't locate object method "catch" via package "1"
and the program dies.

Maybe I should mandate
no indirect in all our files…

@Perl

@dakkar @Perl `use v5.36;` or later will disable #Perl indirect object syntax.

And the https://MetaCPAN.org/pod/Perl::Critic::Policy::Objects::ProhibitIndirectSyntax policy will catch it.

You mandate things by adding #PerlCritic to your tests or pre-commit hooks, not by relying on your team’s individual vigilance

Perl::Critic::Policy::Objects::ProhibitIndirectSyntax - Prohibit indirect object call syntax. - metacpan.org

Prohibit indirect object call syntax.