NAME

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

VERSION

version 0.06

DESCRIPTION

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

The hazards fall into two distinct categories.

Bare {} blocks (real loops with surprising semantics)

Bare {} blocks are real loops -- next, last, and redo work correctly within them. The danger is that readers often do not recognise a bare block as a looping construct:

  • A bare block executes exactly once, so next exits the block (it does not restart it). last also exits; redo re-executes the single pass and skips any code that would follow.

  • Inside a real loop, an unlabeled next / last / redo targets the bare block, not the outer loop. This is almost always unintentional and can mask subtle bugs when the block is added during refactoring.

  • A continue block attached to a bare block runs after next, which further surprises readers who think of the block as a one-shot scope.

Non-loop constructs (controls leak or fail at runtime)

do {}, sub {}, eval {}, map {}, and grep {} are not loops. Loop controls inside them either leak to an enclosing real loop or cause a runtime error:

  • do {}, sub {}, eval {} -- the control escapes to the nearest enclosing real loop (Perl emits "Exiting %s via %s" at the warnings level). If no enclosing loop exists, Perl dies with Can't "%s" outside a loop block.

  • map {} / grep {} -- these are iteration constructs (the block runs once per element, just like for my $x (@list)), but they are not loop-control targets. next does not skip to the next map element; it escapes to the nearest enclosing real loop (or is a runtime error). Use an explicit for loop when you need loop control.

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

Keyword           bare C<{}>      C<do {}>
------------------------------------------------
last              require_label   forbid
next              forbid          forbid
redo              forbid          forbid

EXAMPLES

The following examples illustrate the default configuration.

Bare blocks ({ })

{
    next;           # not ok
}

FOO: {
    next FOO;       # not ok
}

{
    last;           # not ok
}

BAR: {
    last BAR;       # ok
}

{
    redo;           # not ok
}

BAZ: {
    redo BAZ;       # not ok
}

Bare blocks inside real loops

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

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

BAR:
for my $x (@items) {
    {
        next BAR;       # ok  (targets the outer real loop)
    }
}

do { } blocks

do {
    next;           # not ok - fatal error at runtime (no enclosing loop)
};

while (1) {
    do {
        next;       # not ok - exits the while, not the do (action-at-a-distance)
    };
}

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 - fatal error at runtime (no enclosing loop)

while (<$fh>) {
    my @words = map { last; } split;  # not ok - exits the while, not the map
}

grep { redo; } @list;    # not ok

Use a for loop when you need loop control:

for my $elem (@list) {
    next if cond($elem);  # 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, or follow (default) 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. The default is follow, which defers to the per-keyword setting for each keyword: last requires a label, next and redo are always flagged. 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  = follow

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