# Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014 Kevin Ryde

# This file is part of Perl-Critic-Pulp.

# Perl-Critic-Pulp is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Perl-Critic-Pulp is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Perl-Critic-Pulp.  If not, see <http://www.gnu.org/licenses/>.


package Perl::Critic::Policy::ValuesAndExpressions::ProhibitEmptyCommas;
use 5.006;
use strict;
use warnings;
use base 'Perl::Critic::Policy';
use Perl::Critic::Utils;
use Perl::Critic::Pulp::Utils;

# uncomment this to run the ### lines
# use Smart::Comments;

our $VERSION = 84;


use constant supported_parameters => ();
use constant default_severity => $Perl::Critic::Utils::SEVERITY_LOW;
use constant default_themes   => qw(pulp cosmetic);
use constant applies_to       => ('PPI::Token::Operator');

sub violates {
  my ($self, $elem, $document) = @_;

  $Perl::Critic::Pulp::Utils::COMMA{$elem} or return;

  my $prev = $elem->sprevious_sibling;
  if ($prev && ! ($prev->isa('PPI::Token::Operator')
                  && $Perl::Critic::Pulp::Utils::COMMA{$prev})) {
    # have a previous element and it's not a comma operator
    return;
  }

  # A statement like
  #
  #     return bless({@_}, $class)
  #
  # is parsed by PPI as
  #
  #     PPI::Structure::List        ( ... )
  #       PPI::Statement::Compound
  #         PPI::Structure::Block   { ... }
  #           PPI::Statement
  #             PPI::Token::Magic   '@_'
  #       PPI::Statement::Expression
  #         PPI::Token::Operator    ','
  #         PPI::Token::Symbol      '$class'
  #
  # so the "{@_}" bit is not an immediate predecessor of the "," operator.
  # If our $elem has no $prev then also look outwards to see if it's at the
  # start of an expression which is in a list and there's something
  # preceding in the list.
  #
  if (! $prev) {
    my $parent = $elem->parent;
    if ($parent->isa('PPI::Statement::Expression')
        && $parent->parent->isa('PPI::Structure::List')
        && $parent->sprevious_sibling) {
      return;
    }
  }

  # An expression like
  #
  #     [{%a},{}]
  #
  # is parsed by PPI 1.215 as
  #
  #     PPI::Statement
  #       PPI::Structure::Constructor  	[ ... ]
  #         PPI::Statement::Compound
  #           PPI::Structure::Block  	{ ... }
  #             PPI::Statement
  #              PPI::Token::Symbol  	'%a'
  #         PPI::Statement
  #          PPI::Token::Operator  	','
  #           PPI::Structure::Constructor  	{ ... }
  #
  # so the "{%a}" bit is not an immediate predecessor of the "," operator.
  # If our $elem has no $prev then also look upwards to see if it's at the
  # start of an statement which is in a constructor and there's something
  # preceding in that constructor.
  #
  if (! $prev) {
    my $parent = $elem->parent;
    ### parent: ref $parent
    if ($parent->isa('PPI::Statement')
        && $parent->parent->isa('PPI::Structure::Constructor')
        && $parent->sprevious_sibling) {
      return;
    }
  }

  # $prev is either nothing or a comma operator
  return $self->violation ('Empty comma operator',
                           '',
                           $elem);
}

1;
__END__

=for stopwords Ryde

=head1 NAME

Perl::Critic::Policy::ValuesAndExpressions::ProhibitEmptyCommas - disallow empty consecutive commas

=head1 DESCRIPTION

This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
add-on.  It prohibits empty comma operators C<,> or C<=E<gt>> meaning either
consecutive commas or a comma at the start of a list or expression.

    print 'foo',,'bar';      # bad
    @a = (,1,2);             # bad
    foo (x, => 123);         # bad
    a =>=> 456;              # bad
    for (; $i++<10; $i++,,)  # bad
    func (,)                 # bad

Extra commas like this are harmless and simply collapse out when the program
runs (see L<perldata/List value constructors>), so this policy is only under
the "cosmetic" theme (see L<Perl::Critic/POLICY THEMES>).  Usually this sort
of thing is just a stray, or leftover from cut and paste, or perhaps some
over-enthusiastic column layout.  Occasionally it can be something more
dubious,

    # did you mean 1..6 range operator?
    @a = (1,,6);        # bad

    # this is two args, did you want three?
    foo (1, , 2);       # bad

    # this is three args, probably you forgot a value
    bar (abc => ,       # bad
         def => 20);

A comma at the end of a list or call is allowed.  That's quite usual and can
be a good thing when cutting and pasting lines (see C<RequireTrailingCommas>
to mandate them!).

    @b = ("foo",
          "bar",  # ok
         );

If you use multiple commas in some systematic way for code layout you can
always disable C<ProhibitEmptyCommas> from your F<.perlcriticrc> file in the
usual way (see L<Perl::Critic/CONFIGURATION>),

    [-ValuesAndExpressions::ProhibitEmptyCommas]

=head1 SEE ALSO

L<Perl::Critic::Pulp>,
L<Perl::Critic>,
L<Perl::Critic::Policy::CodeLayout::RequireTrailingCommas>,
L<Perl::Critic::Policy::ValuesAndExpressions::ProhibitCommaSeparatedStatements>,
L<Perl::Critic::Policy::Tics::ProhibitManyArrows>

=head1 HOME PAGE

http://user42.tuxfamily.org/perl-critic-pulp/index.html

=head1 COPYRIGHT

Copyright 2008, 2009, 2010, 2011, 2012, 2013, 2014 Kevin Ryde

Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

Perl-Critic-Pulp is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
Perl-Critic-Pulp.  If not, see <http://www.gnu.org/licenses/>.

=cut
