NAME
Perl::Critic::Policy::ControlStructures::ProhibitBareBlockLoopControls - Prohibit unlabeled loop controls in non-loop blocks
VERSION
version 0.02
DESCRIPTION
Using next, last, or redo inside blocks that are not real loops or where control flow is confusing (e.g. bare {} blocks, do {} blocks, anonymous subroutines, eval {}, map/grep blocks) leads to confusing or buggy behaviour:
Bare
{}blocks are loops that execute once, sonextandlastboth exit the block;nextadditionally runs any attachedcontinueblock.do {}blocks are not loops — control keywords target the nearest enclosing real loop instead of thedoblock itself.Anonymous subroutines
sub {}andeval {}are not loops either.map {}/grep {}blocks are expression blocks — loop controls do not behave as expected.
EXAMPLES
Bare blocks ({ })
{
next; # not ok
}
{
last; # not ok
}
{
last LOOP; # ok
}
{
redo; # not ok
}
{
redo LOOP; # 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 fornextandredo)require_label— flag unless the keyword has an explicit label (default forlast)allow— do not check this keyword at all
Block-type overrides (do_block, bare_block) can modify the behaviour for specific block types:
bare_block:forbid,require_label(default), orfollowper-keyword settingsdo_block:forbid(default),require_label, orfollowper-keyword settings
Bare blocks are real loops (they execute once), so labeled controls work correctly — require_label is the default. 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
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