NAME

Perl::Critic::Policy::ControlStructures::ProhibitBareBlockLoopControls - Prohibit unlabeled loop controls in non-loop blocks

VERSION

version 0.04

DESCRIPTION

This policy flags next, last, and redo when used inside blocks that are not real loops or where control flow is confusing (bare {} blocks, do {} blocks, anonymous subroutines, eval {}, map/grep).

The default policy depends on both the keyword and the enclosing block type:

Block type        last            next            redo
─────────────────────────────────────────────────────────
bare C<{}>        require_label   require_label   require_label
C<do {}>          forbid          forbid          forbid
anonymous C<sub>  require_label   forbid          forbid
C<eval {}>        require_label   forbid          forbid
C<map>/C<grep>    require_label   forbid          forbid

Why these blocks are problematic:

  • Bare {} blocks are loops that execute once, so next and last both exit the block; next additionally runs any attached continue block. Labels work here, so require_label is the default for all three keywords.

  • do {} blocks are not loops — control keywords target the nearest enclosing real loop instead of the do block itself.

  • Anonymous subroutines sub {} and eval {} are not loops either.

  • map {} / grep {} blocks are expression blocks — loop controls do not behave as expected.

EXAMPLES

Bare blocks ({ })

{
    next;           # not ok
}

FOO: {
    next FOO;       # ok
}

{
    last;           # not ok
}

BAR: {
    last BAR;       # ok
}

{
    redo;           # not ok
}

BAZ: {
    redo BAZ;       # ok
}

Bare blocks inside real loops

for my $x (@items) {
    {
        next;           # not ok
    }
}

for my $x (@items) {
    FOR: {
        next FOR;       # ok
    }
}

FOR:
for my $x (@items) {
    {
        next FOR;       # ok
    }
}

do { } blocks

do {
    next;           # not ok
};

do {
    last LOOP;      # not ok (label does not help — do is not a loop)
};

Anonymous subroutines (sub { })

sub {
    next;           # not ok
};

sub {
    last LABEL;     # ok (if called from within a matching loop)
};

eval { } blocks

eval {
    last;           # not ok
};

map and grep blocks

map  { next; } @list;    # not ok
grep { redo; } @list;    # not ok

Real loops (always safe)

while (1) {
    next;                # ok
}

for my $x (@items) {
    last if $x eq 'foo'; # ok
}

foreach my $k (keys %h) {
    next;                # ok
}

CONFIGURATION

Each keyword next, last, redo can be set to one of:

forbid — always flag this keyword in non-loop blocks (default for next and redo)
require_label — flag unless the keyword has an explicit label (default for last)
allow — do not check this keyword at all in non-loop blocks

These per-keyword defaults apply when no block-type override is in effect (or the override is set to follow; see below).

Block-type overrides (do_block, bare_block) can modify the behaviour for specific block types:

bare_block: forbid, require_label (default), or follow per-keyword settings
do_block: forbid (default), require_label, or follow per-keyword settings

Bare blocks are real loops (they execute once), so labeled controls work correctly — require_label is the default. This overrides the per-keyword forbid for next and redo, making all three keywords require labels in bare blocks. do blocks are not loops at all, so controls are forbidden entirely by default; require_label allows them when an outer loop is the intended target.

Example .perlcriticrc:

[ControlStructures::ProhibitBareBlockLoopControls]
next        = forbid
last        = require_label
redo        = forbid
do_block    = forbid
bare_block  = require_label

Note: This is the default configuration.

SEE ALSO

"last" in perlfunc, "next" in perlfunc, "redo" in perlfunc, Perl::Critic::Policy::ControlStructures::ProhibitReturnInDoBlock

AUTHOR

Dean Hamstead <dean@fragfest.com.au>

COPYRIGHT AND LICENSE

This software is Copyright (c) 2026 by Dean Hamstead.

This is free software, licensed under:

The MIT (X11) License