Take a look at this block of code and see if you can guess what it does.

END: {
print "Exiting...n";
print "s = $sn";
$s = 'Hello from the BEGIN phase';

You’d expect that the BEGIN block sets $s at compile time, so then that gets printed in the print statement, and then the END block executes at the end of the program. Yes, it would, except that those aren’t BEGIN & END blocks. They’re normal blocks that just happen to be named BEGIN and END.

Credit Randy Lauen for bringing this to my attention. Today at my day job he found a horrifying code construct, one that I’d been using (incorrectly) for quite some time now. You see, these two blocks are not the same:

# Here's some code that gets executed at compile time
# This code gets executed at run time, because the colon
# after BEGIN means it's just a plain old label

Randy had been tracking down a bug for over an hour and couldn’t figure out why his variables weren’t getting set at compile time. Turns out the BEGIN block he was modifying was actually a not-special-at-all BEGIN: block. And he found 40+ instances of the spurious colon in the codebase. Of course, INIT, CHECK, UNITCHECK and END are also rendered non-special by a colon as well.

Most of the time, this is invisible. People tend to put their BEGIN blocks at the top of the program, so the difference between a real BEGIN block and a block that happens to be named BEGIN are not noticed. Still, it’s a a bug waiting to happen.
Sounds like an ideal check for Perl::Critic policy, no? Mike O’Regan jumped to it, and submitted a patch to Perl::Critic, so those of you using Perl::Critic (and if you’re not, you should be) can get warned of using colons on those specially-named blocks once a new version is released, shortly I hope.

I’m not the only one to have been doing this. Google Code Search turns up
over 100 instances, including some in the core Perl tests (heck, maybe from me!), as well as mailman and POE.

Check your codebases. If you’re using ack, it’s just:

$ ack --perl '^(BEGIN|END|INIT|CHECK|UNITCHECK):s*{'

Have you fallen prey to this? Let me know.