On Fri Apr 15 16:44:52 2011, cjay.martin@gmail.com wrote:
Show quoted text> Perl::Critic::Policy::BuiltinFunctions::ProhibitComplexMappings
> and
> Perl::Critic::Policy::ValuesAndExpressions::ProhibitCommaSeparatedStatem
> ents
>
> Perlcritic caught a one-line map with both these errors.
>
>
> Here's the minimal code that flags both errors:
>
> my @in = ( { foo => 1 }, { bar => 2 }, { baz => 3 } );
> my @out = map { { %$_ }, } @in;
>
> ...gave me the warnings:
>
> perlcritic --stern ~/test.pl
> Map blocks should have a single statement at line 5, column 11. See
> page 113 of PBP. (Severity: 3)
> Comma used to separate statements at line 5, column 24. See pages 68,71
> of PBP. (Severity: 4)
>
>
>
>
> Info:
>
> Perl Version: 5.10.1
> OS: Ubuntu 10.4
>
>
> I don't know perlcritic well enough myself to patch this, but if nobody
> touches it when I finally have the time to learn it, I might look into
> it.
This is really two issues.
The ValuesAndExpressions::ProhibitCommaSeparatedStatements notice looks
to me like a true positive. The curly brackets after the map are a
block, and the comma appears at the top level in the block, therefore it
is a statement separator.
The BuiltinFunctions::ProhibitComplexMappings is a false positive, and
yours is not the first ticket in the queue for this. Perl::Critic relies
on PPI to parse Perl. There are a number of cases, including yours,
where PPI calls a set of curly brackets a block, where Perl sees them as
a hash reference constructor. The problem is that Perl uses undocumented
heuristics to disambiguate between blocks and hash constructors, and
nobody (so far) has been brave enough to sort this particular problem
out, for fear of breaking something else.
If you are looking for a way to suppress the warning which is terser
than '## no critic (ProhibitComplexMappings), you can put a unary plus
before the inner left curly bracket. This is Perl's official way to
force curly brackets to be interpreted as a hash reference constructor
(see perlref), like this:
my @out = map { +{ %$ } } @in;
Yes, it looks funny, but that is the way B::Deparse renders your statement:
$ perl -MO=Deparse -e 'my @out = map { { %$_ }, } @in;'
my(@out) = map({+{%$_};} @in);
-e syntax OK