The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#
# Copyright (C) 2015 Tomasz Konojacki
#
# This library is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself, either Perl version 5.20.1 or,
# at your option, any later version of Perl 5 you may have available.
#

package Perl::Critic::Policy::References::ProhibitComplexDoubleSigils;

use strict;
use warnings;

use Readonly;
use List::Util;

use Perl::Critic::Utils qw{ :severities };
use base 'Perl::Critic::Policy';

our $VERSION = '0.2';

#-----------------------------------------------------------------------------

Readonly::Scalar my $DESC => q{Complex double-sigil dereferences};
Readonly::Scalar my $EXPL => q{Found complex double-sigil dereference without curly braces.};

#-----------------------------------------------------------------------------

sub supported_parameters { return ()                 }
sub default_severity     { return $SEVERITY_LOW      }
sub default_themes       { return qw(cosmetic)       }
sub applies_to           { return 'PPI::Token::Cast' }

#-----------------------------------------------------------------------------

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

    return if $elem eq q{\\};

    my $sib = $elem->snext_sibling;

    return if !$sib;
    return if $sib->isa('PPI::Structure::Block');

    if ($sib->isa('PPI::Token::Symbol')) {
        my $next_sib = $sib->snext_sibling;
        return if !$next_sib;

        # e.g $$fobar[0]->{a} or $$foo{key}->[6]
        if ($next_sib->isa('PPI::Structure::Subscript')) {
            return $self->violation( $DESC, $EXPL, $elem );
        }
        # e.g. &$foobar(1, 2, 3)
        elsif ($next_sib->isa('PPI::Structure::List')) {
            return $self->violation( $DESC, $EXPL, $elem );
        }
        # e.g. @$foobar->{foo}->[0]
        elsif ($next_sib->isa('PPI::Token::Operator') && $next_sib eq q/->/) {
            return $self->violation( $DESC, $EXPL, $elem );
        }
    }

    return; #ok!
}

'Bei Mir Bistu Shein';

__END__

=head1 NAME

Perl::Critic::Policy::References::ProhibitComplexDoubleSigils - allow C<$$foo>
but not C<$$foo[1]-E<gt>{dadsdas}-E<gt>[7]>.

=head1 DESCRIPTION

This L<Perl::Critic> policy is very similar to
L<Perl::Critic::Policy::References::ProhibitDoubleSigils> but it allows
non-complex double sigil dereferences, for details see EXAMPLES and RATIONALE
sections.

=head1 EXAMPLES

    # These are allowed:
    my @array = @$arrayref;
    my %hash = %$hashref;
    my $scalar = $$scalarref;

    for (@$arrayref) {
        ...
    }

    # These are not:
    my $scalar = $$arrayref[0]->{foobar}; # use these instead:
                                          # $arrayref->[0]->{foobar}
                                          # ${$arrayref}[0]->{foobar}

    my $scalar = $$hashref{bar}->[1]; # use these instead:
                                      # $hashref->{bar}->[1]
                                      # ${$hashref}{bar}->[1]

    &$coderef()->{1234}->[1]; # use these instead:
                              # $coderef->()->{1234}->[1]
                              # &{$coderef}()->{1234}->[1]

    ...

=head1 RATIONALE

There are some cases when using braces in dereferences makes sense, I don't
deny it, but in my opinion it reduces code readability in most common cases.
L<Perl::Critic::Policy::References::ProhibitDoubleSigils> is simply too
strict. Consider following examples:

    # exhibit A:
    for (@{$foo}) {
        ...
    }

    # exhibit B:
    for (@$foo) {
        ...
    }

If you think that C<B> is more legible, this critic policy is for you.

=head1 CAVEATS

Enabling both L<Perl::Critic::Policy::References::ProhibitDoubleSigils> and
this policy is not a very wise choice.

=head1 EXPORT

None by default.

=head1 FOSSIL REPOSTIORY

Perl::Critic::policy::References::ProhibitComplexDoubleSigils Fossil repository
is hosted at xenu.tk:

    http://code.xenu.tk/repos.cgi/prohibit-complex-double-sigils

=head1 SEE ALSO

=over 4

=item *
L<Perl::Critic>

=item *
L<Perl::Critic::Policy::References::ProhibitDoubleSigils>

=back

=head1 AUTHOR

Tomasz Konojacki <me@xenu.tk>, http://xenu.tk

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2015 Tomasz Konojacki

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.20.1 or,
at your option, any later version of Perl 5 you may have available.

=cut