Build all packages removed dependencies of libtest-exception-perl libtest-warn-perl...
[dh-make-perl] / dev / i386 / libperl-critic-perl / libperl-critic-perl-1.088 / lib / Perl / Critic / Policy / ControlStructures / ProhibitMutatingListFunctions.pm
diff --git a/dev/i386/libperl-critic-perl/libperl-critic-perl-1.088/lib/Perl/Critic/Policy/ControlStructures/ProhibitMutatingListFunctions.pm b/dev/i386/libperl-critic-perl/libperl-critic-perl-1.088/lib/Perl/Critic/Policy/ControlStructures/ProhibitMutatingListFunctions.pm
new file mode 100644 (file)
index 0000000..5f054be
--- /dev/null
@@ -0,0 +1,308 @@
+##############################################################################
+#      $URL: http://perlcritic.tigris.org/svn/perlcritic/trunk/Perl-Critic/lib/Perl/Critic/Policy/ControlStructures/ProhibitMutatingListFunctions.pm $
+#     $Date: 2008-07-03 10:19:10 -0500 (Thu, 03 Jul 2008) $
+#   $Author: clonezone $
+# $Revision: 2489 $
+##############################################################################
+
+package Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions;
+
+use 5.006001;
+use strict;
+use warnings;
+use Readonly;
+
+use List::MoreUtils qw( none any );
+
+use Perl::Critic::Utils qw{
+    :booleans :characters :severities :data_conversion :classification :ppi
+};
+
+use base 'Perl::Critic::Policy';
+
+our $VERSION = '1.088';
+
+#-----------------------------------------------------------------------------
+
+Readonly::Array my @BUILTIN_LIST_FUNCS => qw( map grep );
+Readonly::Array my @CPAN_LIST_FUNCS    => _get_cpan_list_funcs();
+
+#-----------------------------------------------------------------------------
+
+sub _get_cpan_list_funcs {
+    return  qw( List::Util::first ),
+        map { 'List::MoreUtils::'.$_ } _get_list_moreutils_funcs();
+}
+
+#-----------------------------------------------------------------------------
+
+sub _get_list_moreutils_funcs {
+    return  qw(any all none notall true false firstidx first_index
+               lastidx last_index insert_after insert_after_string);
+}
+
+#-----------------------------------------------------------------------------
+
+sub _is_topic {
+    my $elem = shift;
+    return defined $elem
+        && $elem->isa('PPI::Token::Magic')
+            && $elem eq q{$_}; ##no critic (InterpolationOfMetachars)
+}
+
+
+#-----------------------------------------------------------------------------
+
+Readonly::Scalar my $DESC => q{Don't modify $_ in list functions};  ##no critic (InterpolationOfMetachars)
+Readonly::Scalar my $EXPL => [ 114 ];
+
+#-----------------------------------------------------------------------------
+
+sub supported_parameters {
+    return (
+        {
+            name            => 'list_funcs',
+            description     => 'The base set of functions to check.',
+            default_string  => join ($SPACE, @BUILTIN_LIST_FUNCS, @CPAN_LIST_FUNCS ),
+            behavior        => 'string list',
+        },
+        {
+            name            => 'add_list_funcs',
+            description     => 'The set of functions to check, in addition to those given in list_funcs.',
+            default_string  => $EMPTY,
+            behavior        => 'string list',
+        },
+    );
+}
+
+sub default_severity { return $SEVERITY_HIGHEST  }
+sub default_themes   { return qw(core bugs pbp)  }
+sub applies_to       { return 'PPI::Token::Word' }
+
+#-----------------------------------------------------------------------------
+
+sub initialize_if_enabled {
+    my ($self, $config) = @_;
+
+    $self->{_all_list_funcs} = {
+        hashify keys %{ $self->{_list_funcs} }, keys %{ $self->{_add_list_funcs} }
+    };
+
+    return $TRUE;
+}
+
+#-----------------------------------------------------------------------------
+
+sub violates {
+    my ($self, $elem, $doc) = @_;
+
+    # Is this element a list function?
+    return if not $self->{_all_list_funcs}->{$elem};
+    return if not is_function_call($elem);
+
+    # Only the block form of list functions can be analyzed.
+    return if not my $first_arg = first_arg( $elem );
+    return if not $first_arg->isa('PPI::Structure::Block');
+    return if not _has_topic_side_effect( $first_arg );
+
+    # Must be a violation
+    return $self->violation( $DESC, $EXPL, $elem );
+}
+
+#-----------------------------------------------------------------------------
+
+sub _has_topic_side_effect {
+    my $node = shift;
+
+    # Search through all significant elements in the block,
+    # testing each element to see if it mutates the topic.
+    my $tokens = $node->find( 'PPI::Token' ) || [];
+    for my $elem ( @{ $tokens } ) {
+        next if not $elem->significant();
+        return 1 if _is_assignment_to_topic( $elem );
+        return 1 if _is_topic_mutating_regex( $elem );
+        return 1 if _is_topic_mutating_func( $elem );
+        return 1 if _is_topic_mutating_substr( $elem );
+    }
+    return;
+}
+
+#-----------------------------------------------------------------------------
+
+sub _is_assignment_to_topic {
+    my $elem = shift;
+    return if not _is_topic( $elem );
+
+    my $sib = $elem->snext_sibling();
+    if ($sib && $sib->isa('PPI::Token::Operator')) {
+        return 1 if _is_assignment_operator( $sib );
+    }
+
+    my $psib = $elem->sprevious_sibling();
+    if ($psib && $psib->isa('PPI::Token::Operator')) {
+        return 1 if _is_increment_operator( $psib );
+    }
+
+    return;
+}
+
+#-----------------------------------------------------------------------------
+
+sub _is_topic_mutating_regex {
+    my $elem = shift;
+    return if ! ( $elem->isa('PPI::Token::Regexp::Substitute')
+                  || $elem->isa('PPI::Token::Regexp::Transliterate') );
+
+    # If the previous sibling does not exist, then
+    # the regex implicitly binds to $_
+    my $prevsib = $elem->sprevious_sibling;
+    return 1 if not $prevsib;
+
+    # If the previous sibling does exist, then it
+    # should be a binding operator.
+    return 1 if not _is_binding_operator( $prevsib );
+
+    # Check if the sibling before the biding operator
+    # is explicitly set to $_
+    my $bound_to = $prevsib->sprevious_sibling;
+    return _is_topic( $bound_to );
+}
+
+#-----------------------------------------------------------------------------
+
+sub _is_topic_mutating_func {
+    my $elem = shift;
+    return if not $elem->isa('PPI::Token::Word');
+    my @mutator_funcs = qw(chop chomp undef);
+    return if not any { $elem eq $_ } @mutator_funcs;
+    return if not is_function_call( $elem );
+
+    # If these functions have no argument,
+    # they default to mutating $_
+    my $first_arg = first_arg( $elem );
+    if (not defined $first_arg) {
+        # undef does not default to $_, unlike the others
+        return if $elem eq 'undef';
+        return 1;
+    }
+    return _is_topic( $first_arg );
+}
+
+#-----------------------------------------------------------------------------
+
+Readonly::Scalar my $MUTATING_SUBSTR_ARG_COUNT => 4;
+
+sub _is_topic_mutating_substr {
+    my $elem = shift;
+    return if $elem ne 'substr';
+    return if not is_function_call( $elem );
+
+    # check and see if the first arg is $_
+    my @args = parse_arg_list( $elem );
+    return @args >= $MUTATING_SUBSTR_ARG_COUNT && _is_topic( $args[0]->[0] );
+}
+
+#-----------------------------------------------------------------------------
+
+{
+    ##no critic(ArgUnpacking)
+
+    my %assignment_ops = hashify qw( = *= /= += -= %= **= x= .= &= |= ^=  &&= ||= ++ -- );
+    sub _is_assignment_operator { return exists $assignment_ops{$_[0]} }
+
+    my %increment_ops = hashify qw( ++ -- );
+    sub _is_increment_operator { return exists $increment_ops{$_[0]} }
+
+    my %binding_ops = hashify qw( =~ !~ );
+    sub _is_binding_operator { return exists $binding_ops{$_[0]} }
+}
+
+1;
+
+#-----------------------------------------------------------------------------
+
+__END__
+
+=pod
+
+=head1 NAME
+
+Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions - Don't modify C<$_> in list functions.
+
+=head1 AFFILIATION
+
+This Policy is part of the core L<Perl::Critic> distribution.
+
+
+=head1 DESCRIPTION
+
+C<map>, C<grep> and other list operators are intended to transform arrays into
+other arrays by applying code to the array elements one by one.  For speed,
+the elements are referenced via a C<$_> alias rather than copying them.  As a
+consequence, if the code block of the C<map> or C<grep> modify C<$_> in any
+way, then it is actually modifying the source array.  This IS technically
+allowed, but those side effects can be quite surprising, especially when the
+array being passed is C<@_> or perhaps C<values(%ENV)>!  Instead authors
+should restrict in-place array modification to C<for(@array) { ... }>
+constructs instead, or use C<List::MoreUtils::apply()>.
+
+=head1 CONFIGURATION
+
+By default, this policy applies to the following list functions:
+
+  map grep
+  List::Util qw(first)
+  List::MoreUtils qw(any all none notall true false firstidx first_index
+                     lastidx last_index insert_after insert_after_string)
+
+This list can be overridden the F<.perlcriticrc> file like this:
+
+ [ControlStructures::ProhibitMutatingListFunctions]
+ list_funcs = map grep List::Util::first
+
+Or, one can just append to the list like so:
+
+ [ControlStructures::ProhibitMutatingListFunctions]
+ add_list_funcs = Foo::Bar::listmunge
+
+=head1 LIMITATIONS
+
+This policy deliberately does not apply to C<for (@array) { ... }> or
+C<List::MoreUtils::apply()>.
+
+Currently, the policy only detects explicit external module usage like this:
+
+  my @out = List::MoreUtils::any {s/^foo//} @in;
+
+and not like this:
+
+  use List::MoreUtils qw(any);
+  my @out = any {s/^foo//} @in;
+
+This policy looks only for modifications of C<$_>.  Other naughtiness could
+include modifying C<$a> and C<$b> in C<sort> and the like.  That's beyond the
+scope of this policy.
+
+=head1 AUTHOR
+
+Chris Dolan <cdolan@cpan.org>
+
+Michael Wolf <MichaelRWolf@att.net>
+
+=head1 COPYRIGHT
+
+Copyright (C) 2006 Chris Dolan.  All rights reserved.
+
+This program is free software; you can redistribute it and/or modify
+it under the same terms as Perl itself.
+
+=cut
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 78
+#   indent-tabs-mode: nil
+# End:
+# ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :
+