http://qs321.pair.com?node_id=11107089

rir has asked for the wisdom of the Perl Monks concerning the following question:

As a long time user of perlcritic, I am surprised that my policies do not report use of goto. Searching Meta::CPAN on ControlStructures finds nothing.

Is there such a policy?

Be well,
rir

Replies are listed 'Best First'.
Re: Go to perl::critic (updated)
by haukex (Archbishop) on Oct 06, 2019 at 17:26 UTC
    Is there such a policy?

    Here's one :-)

    package Perl::Critic::Policy::ControlStructures::ProhibitGotoLabel; use warnings; use strict; use base 'Perl::Critic::Policy'; use Perl::Critic::Utils qw/:severities/; # to locate the path where to place this file, do e.g.: # $ perl -MFile::Basename=dirname -MPerl::Critic::Policy::ControlStruc +tures::ProhibitUnreachableCode -le 'print dirname $INC{"Perl/Critic/P +olicy/ControlStructures/ProhibitUnreachableCode.pm"}' # or # $ dirname `perldoc -l Perl::Critic::Policy::ControlStructures::Prohi +bitUnreachableCode` our $VERSION = '0.001'; sub supported_parameters { return } sub default_severity { return $SEVERITY_MEDIUM } sub default_themes { return qw/core bugs/ } sub applies_to { return 'PPI::Token::Word' } =head1 DESCRIPTION A C<goto LABEL> is considered a bad practice by a number of people, as it can lead to hard-to-understand and brittle spaghetti code. Instead of a C<goto>, use different control structures, or place a label on a block (such as a loop's block) and use C<next LABEL>, C<last LABEL>, or C<redo LABEL> instead. C<goto &NAME> is very different from C<goto LABEL> and is not covered by this policy. C<goto EXPR> is ambiguous and this policy will report such cases when C<EXPR> does not begin with C<&>. Whether this is a false positive or not cannot be determined by a static parse. =cut my $DESC = q{goto LABEL used}; my $EXPL = q{Considered bad practice}; sub violates { my ($self, $elem) = @_; return if $elem->content() ne 'goto'; my $target = $elem->snext_sibling(); return $self->violation('Nothing following "goto"?', 'It seems there is a lone "goto" in your code?', $elem) if !$target; return if $target->isa('PPI::Token::Symbol') && $target->raw_type +eq '&' || $target->isa('PPI::Token::Cast') && $target->content +eq '&' || $target->isa('PPI::Token::Word') && $target->content +eq '__SUB__'; return $self->violation($DESC,$EXPL,$elem); } 1;
      A C<goto LABEL> is considered a bad practice by a number of people, as it can lead to hard-to-understand and brittle spaghetti code.

      Who cares what management thinks? Perl's goto LABEL is an awesome tool when one wants loop-like flow control outside of loops. It's the easy way to implement interactive shell scripts:

      INPUT: print 'number> '; chomp(my $input = <STDIN>); if ($input and $input =~ /[0-9]+/) { print $input, "\n" } else { goto INPUT }
        Perl's goto LABEL is an awesome tool when one wants loop-like flow control outside of loops.

        But unnecessary in many cases:

        my $input; INPUT: { print 'number> '; chomp($input = <STDIN>); redo INPUT unless $input && $input=~/\A[0-9]+\z/; } print $input, "\n";

        (Or just use a proper prompting module, such as IO::Prompter.)

        TIMTOWTDI - I'm not saying that goto isn't sometimes an acceptable solution. But IMHO those cases are much more rare than an actual need for goto, especially in a language like Perl that has many other nice options for flow control. And of course if you want to use goto, all Perl::Critic policies are optional; this one just happens to be what the OP wanted. I'm not going to start a debate about whether goto is good or not. Note my wording: "A goto LABEL is considered a bad practice by a number of people, as it can lead to hard-to-understand and brittle spaghetti code." There was a good line by a comedian, paraphrasing: "I love the phrase 'a number of' because it can mean anything. 'A number of supermodels want to sleep with me.' That number is zero."

        Update: Fixed the regex.

        Who cares what management thinks?

        Employees? Duh :)

      Hi

      Does that leave a way out for with comment "## nocritic" or some such (whatever the standard)?

      Or is that something you gotta add

        Does that leave a way out for with comment "## nocritic" or some such

        Yes, it's a normal Perl::Critic policy, so the line "goto FOO; ## no critic (ProhibitGotoLabel)" won't warn.

Re: Go to perl::critic
by rjt (Curate) on Oct 06, 2019 at 07:45 UTC

    No such policy. Well, just an orthogonal one that marks code as reachable if it has a label.

    My counter-question to you is, what's so bad about goto? Food scientists trying to come up with gluten-free spaghetti have no idea computer science had it covered decades ago. Plus it's great for extra artistic renditions of Queen songs that remind me of how much my programming style has changed since Freddie was filling stadiums:

    pts/2 ryan@pi:~/src/shorts $> cat goto.pl #!/usr/bin/env perl use 5.010; use strict; say 'Thunderbolt and lightning, very, very frightening me'; # say 'Galileo' for 1..5 has nothing on this: my $galileos = 5; galileo: say "Galileo"; goto galileo if --$galileos; figaro: say 'Figaro magnifico'; pts/2 ryan@pi:~/src/shorts $> perlcritic goto.pl goto.pl source OK

      Other than that I also get no warning, you should pass -1 to perlcritic to get all levels of warnings. The default is level 5, which does suppress most warnings.


      Enjoy, Have FUN! H.Merijn

        You're right of course. However, after making sure goto wasn't flagged at any level, I took some artistic license to maximize comedic value and minimize snippet length. ;-)

      Okay, I'll play the straight man.

      What is wrong with goto is that it can be obfuscating and it can be slow--slow motion slow.

      rir

Re: Go to perl::critic
by Anonymous Monk on Oct 06, 2019 at 09:35 UTC

      Presumably the line number and gist of Dijkstra’s 51 year old letter/paper; named Perl::Critic::Policy::ConsideredHarmful::Goto or some other Java apology.

        haukex was awake :) anything to add?
        #!/usr/bin/perl -- use strict; use warnings; use diagnostics; sub GOOD { LINE: while(<STDIN>){ next LINE; last LINE; } } sub GOOOD { goto &GOOD; } sub GOOOOD { my $GOOD = 'GOOD'; goto &$GOOD; } sub REALLY { goto &GOOD; } sub GOODREALLY { goto sub { goto &REALLY; }; } sub GOODREHEALLY { my $GOO = sub { goto &REALLY; }; goto $GOO; } sub GOODREHEHEALLY { goto do { sub { goto &REALLY; }; }; } sub BAD { goto LINE; } sub BAAD { my $LINE = 'LINE'; goto $LINE; } sub BAAAD { goto ("LINE", "LINE", "LINE")[@_]; } BAAAAD: { goto &REALLy; # Can't goto subroutine outside a subroutine } goto END; ## bad :END exit( 0 );