line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions; |
2
|
|
|
|
|
|
|
|
3
|
40
|
|
|
40
|
|
28590
|
use 5.010001; |
|
40
|
|
|
|
|
181
|
|
4
|
40
|
|
|
40
|
|
281
|
use strict; |
|
40
|
|
|
|
|
123
|
|
|
40
|
|
|
|
|
845
|
|
5
|
40
|
|
|
40
|
|
228
|
use warnings; |
|
40
|
|
|
|
|
118
|
|
|
40
|
|
|
|
|
955
|
|
6
|
40
|
|
|
40
|
|
227
|
use Readonly; |
|
40
|
|
|
|
|
146
|
|
|
40
|
|
|
|
|
2260
|
|
7
|
|
|
|
|
|
|
|
8
|
40
|
|
|
|
|
2097
|
use Perl::Critic::Utils qw{ |
9
|
|
|
|
|
|
|
:booleans :characters :severities :data_conversion :classification :ppi |
10
|
40
|
|
|
40
|
|
301
|
}; |
|
40
|
|
|
|
|
144
|
|
11
|
|
|
|
|
|
|
|
12
|
40
|
|
|
40
|
|
24891
|
use parent 'Perl::Critic::Policy'; |
|
40
|
|
|
|
|
127
|
|
|
40
|
|
|
|
|
317
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '1.150'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
Readonly::Array my @BUILTIN_LIST_FUNCS => qw( map grep ); |
19
|
|
|
|
|
|
|
Readonly::Array my @CPAN_LIST_FUNCS => _get_cpan_list_funcs(); |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub _get_cpan_list_funcs { |
24
|
|
|
|
|
|
|
return qw( List::Util::first ), |
25
|
40
|
|
|
40
|
|
147
|
map { ('List::MoreUtils::'.$_, 'List::SomeUtils::'.$_) } _get_list_moreutils_funcs(); |
|
480
|
|
|
|
|
1424
|
|
26
|
|
|
|
|
|
|
} |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
sub _get_list_moreutils_funcs { |
31
|
40
|
|
|
40
|
|
174
|
return qw(any all none notall true false firstidx first_index |
32
|
|
|
|
|
|
|
lastidx last_index insert_after insert_after_string); |
33
|
|
|
|
|
|
|
} |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
sub _is_topic { |
38
|
0
|
|
|
0
|
|
0
|
my $elem = shift; |
39
|
0
|
|
0
|
|
|
0
|
return defined $elem |
40
|
|
|
|
|
|
|
&& $elem->isa('PPI::Token::Magic') |
41
|
|
|
|
|
|
|
&& $elem->content() eq q{$_}; ##no critic (InterpolationOfMetachars) |
42
|
|
|
|
|
|
|
} |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
Readonly::Scalar my $DESC => q{Don't modify $_ in list functions}; ##no critic (InterpolationOfMetachars) |
48
|
|
|
|
|
|
|
Readonly::Scalar my $EXPL => [ 114 ]; |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
sub supported_parameters { |
53
|
|
|
|
|
|
|
return ( |
54
|
|
|
|
|
|
|
{ |
55
|
91
|
|
|
91
|
0
|
2110
|
name => 'list_funcs', |
56
|
|
|
|
|
|
|
description => 'The base set of functions to check.', |
57
|
|
|
|
|
|
|
default_string => join ($SPACE, @BUILTIN_LIST_FUNCS, @CPAN_LIST_FUNCS ), |
58
|
|
|
|
|
|
|
behavior => 'string list', |
59
|
|
|
|
|
|
|
}, |
60
|
|
|
|
|
|
|
{ |
61
|
|
|
|
|
|
|
name => 'add_list_funcs', |
62
|
|
|
|
|
|
|
description => 'The set of functions to check, in addition to those given in list_funcs.', |
63
|
|
|
|
|
|
|
default_string => $EMPTY, |
64
|
|
|
|
|
|
|
behavior => 'string list', |
65
|
|
|
|
|
|
|
}, |
66
|
|
|
|
|
|
|
); |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
74
|
|
|
74
|
1
|
292
|
sub default_severity { return $SEVERITY_HIGHEST } |
70
|
92
|
|
|
92
|
1
|
455
|
sub default_themes { return qw(core bugs pbp certrule ) } |
71
|
36
|
|
|
36
|
1
|
114
|
sub applies_to { return 'PPI::Token::Word' } |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub initialize_if_enabled { |
76
|
71
|
|
|
71
|
1
|
261
|
my ($self, $config) = @_; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
$self->{_all_list_funcs} = { |
79
|
71
|
|
|
|
|
214
|
hashify keys %{ $self->{_list_funcs} }, keys %{ $self->{_add_list_funcs} } |
|
71
|
|
|
|
|
997
|
|
|
71
|
|
|
|
|
394
|
|
80
|
|
|
|
|
|
|
}; |
81
|
|
|
|
|
|
|
|
82
|
71
|
|
|
|
|
463
|
return $TRUE; |
83
|
|
|
|
|
|
|
} |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
sub violates { |
88
|
358
|
|
|
358
|
1
|
586
|
my ($self, $elem, $doc) = @_; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
# Is this element a list function? |
91
|
358
|
100
|
|
|
|
629
|
return if not $self->{_all_list_funcs}->{$elem}; |
92
|
3
|
50
|
|
|
|
20
|
return if not is_function_call($elem); |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# Only the block form of list functions can be analyzed. |
95
|
0
|
0
|
|
|
|
|
return if not my $first_arg = first_arg( $elem ); |
96
|
0
|
0
|
|
|
|
|
return if not $first_arg->isa('PPI::Structure::Block'); |
97
|
0
|
0
|
|
|
|
|
return if not _has_topic_side_effect( $first_arg, $doc ); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Must be a violation |
100
|
0
|
|
|
|
|
|
return $self->violation( $DESC, $EXPL, $elem ); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
sub _has_topic_side_effect { |
106
|
0
|
|
|
0
|
|
|
my ( $node, $doc ) = @_; |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# Search through all significant elements in the block, |
109
|
|
|
|
|
|
|
# testing each element to see if it mutates the topic. |
110
|
0
|
|
0
|
|
|
|
my $tokens = $node->find( 'PPI::Token' ) || []; |
111
|
0
|
|
|
|
|
|
for my $elem ( @{ $tokens } ) { |
|
0
|
|
|
|
|
|
|
112
|
0
|
0
|
|
|
|
|
next if not $elem->significant(); |
113
|
0
|
0
|
|
|
|
|
return 1 if _is_assignment_to_topic( $elem ); |
114
|
0
|
0
|
|
|
|
|
return 1 if _is_topic_mutating_regex( $elem, $doc ); |
115
|
0
|
0
|
|
|
|
|
return 1 if _is_topic_mutating_func( $elem ); |
116
|
0
|
0
|
|
|
|
|
return 1 if _is_topic_mutating_substr( $elem ); |
117
|
|
|
|
|
|
|
} |
118
|
0
|
|
|
|
|
|
return; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
sub _is_assignment_to_topic { |
124
|
0
|
|
|
0
|
|
|
my $elem = shift; |
125
|
0
|
0
|
|
|
|
|
return if not _is_topic( $elem ); |
126
|
|
|
|
|
|
|
|
127
|
0
|
|
|
|
|
|
my $sib = $elem->snext_sibling(); |
128
|
0
|
0
|
0
|
|
|
|
if ($sib && $sib->isa('PPI::Token::Operator')) { |
129
|
0
|
0
|
|
|
|
|
return 1 if _is_assignment_operator( $sib ); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
my $psib = $elem->sprevious_sibling(); |
133
|
0
|
0
|
0
|
|
|
|
if ($psib && $psib->isa('PPI::Token::Operator')) { |
134
|
0
|
0
|
|
|
|
|
return 1 if _is_increment_operator( $psib ); |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
|
137
|
0
|
|
|
|
|
|
return; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub _is_topic_mutating_regex { |
143
|
0
|
|
|
0
|
|
|
my ( $elem, $doc ) = @_; |
144
|
0
|
0
|
0
|
|
|
|
return if ! ( $elem->isa('PPI::Token::Regexp::Substitute') |
145
|
|
|
|
|
|
|
|| $elem->isa('PPI::Token::Regexp::Transliterate') ); |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
# Exempt PPI::Token::Regexp::Transliterate objects IF the replacement |
148
|
|
|
|
|
|
|
# string is empty AND neither the /d or /s flags are specified, OR the |
149
|
|
|
|
|
|
|
# replacement string equals the match string AND neither the /c or /s |
150
|
|
|
|
|
|
|
# flags are specified. RT 44515. |
151
|
|
|
|
|
|
|
# |
152
|
|
|
|
|
|
|
# NOTE that, at least as of 5.14.2, tr/// does _not_ participate in the |
153
|
|
|
|
|
|
|
# 'use re /modifiers' mechanism. And a good thing, too, since the |
154
|
|
|
|
|
|
|
# modifiers that _are_ common (/s and /d) mean something completely |
155
|
|
|
|
|
|
|
# different in tr///. |
156
|
0
|
0
|
|
|
|
|
if ( $elem->isa( 'PPI::Token::Regexp::Transliterate') ) { |
157
|
0
|
|
|
|
|
|
my $subs = $elem->get_substitute_string(); |
158
|
0
|
|
|
|
|
|
my %mods = $elem->get_modifiers(); |
159
|
0
|
0
|
|
|
|
|
$mods{r} and return; # Introduced in Perl 5.13.7 |
160
|
0
|
0
|
|
|
|
|
if ( $EMPTY eq $subs ) { |
|
|
0
|
|
|
|
|
|
161
|
0
|
0
|
0
|
|
|
|
$mods{d} or $mods{s} or return; |
162
|
|
|
|
|
|
|
} elsif ( $elem->get_match_string() eq $subs ) { |
163
|
0
|
0
|
0
|
|
|
|
$mods{c} or $mods{s} or return; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# As of 5.13.2, the substitute built-in supports the /r modifier, which |
168
|
|
|
|
|
|
|
# causes the operation to return the modified string and leave the |
169
|
|
|
|
|
|
|
# original unmodified. This does not parse under earlier Perls, so there |
170
|
|
|
|
|
|
|
# is no version check. |
171
|
|
|
|
|
|
|
|
172
|
0
|
0
|
|
|
|
|
if ( $elem->isa( 'PPI::Token::Regexp::Substitute' ) ) { |
173
|
0
|
0
|
|
|
|
|
my $re = $doc->ppix_regexp_from_element( $elem ) |
174
|
|
|
|
|
|
|
or return; |
175
|
0
|
0
|
|
|
|
|
$re->modifier_asserted( 'r' ) |
176
|
|
|
|
|
|
|
and return; |
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# If the previous sibling does not exist, then |
180
|
|
|
|
|
|
|
# the regex implicitly binds to $_ |
181
|
0
|
|
|
|
|
|
my $prevsib = $elem->sprevious_sibling; |
182
|
0
|
0
|
|
|
|
|
return 1 if not $prevsib; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# If the previous sibling does exist, then it |
185
|
|
|
|
|
|
|
# should be a binding operator. |
186
|
0
|
0
|
|
|
|
|
return 1 if not _is_binding_operator( $prevsib ); |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Check if the sibling before the binding operator |
189
|
|
|
|
|
|
|
# is explicitly set to $_ |
190
|
0
|
|
|
|
|
|
my $bound_to = $prevsib->sprevious_sibling; |
191
|
0
|
|
|
|
|
|
return _is_topic( $bound_to ); |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
sub _is_topic_mutating_func { |
197
|
0
|
|
|
0
|
|
|
my $elem = shift; |
198
|
0
|
0
|
|
|
|
|
return if not $elem->isa('PPI::Token::Word'); |
199
|
|
|
|
|
|
|
|
200
|
0
|
|
|
|
|
|
state $mutator_funcs = { hashify qw( chop chomp undef ) }; |
201
|
0
|
0
|
|
|
|
|
return if !$mutator_funcs->{$elem->content()}; |
202
|
|
|
|
|
|
|
|
203
|
0
|
0
|
|
|
|
|
return if not is_function_call( $elem ); |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# If these functions have no argument, |
206
|
|
|
|
|
|
|
# they default to mutating $_ |
207
|
0
|
|
|
|
|
|
my $first_arg = first_arg( $elem ); |
208
|
0
|
0
|
|
|
|
|
if (not defined $first_arg) { |
209
|
|
|
|
|
|
|
# undef does not default to $_, unlike the others |
210
|
0
|
|
|
|
|
|
return $elem->content() ne 'undef'; |
211
|
|
|
|
|
|
|
} |
212
|
0
|
|
|
|
|
|
return _is_topic( $first_arg ); |
213
|
|
|
|
|
|
|
} |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Readonly::Scalar my $MUTATING_SUBSTR_ARG_COUNT => 4; |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
sub _is_topic_mutating_substr { |
220
|
0
|
|
|
0
|
|
|
my $elem = shift; |
221
|
0
|
0
|
|
|
|
|
return if $elem->content() ne 'substr'; |
222
|
0
|
0
|
|
|
|
|
return if not is_function_call( $elem ); |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
# check and see if the first arg is $_ |
225
|
0
|
|
|
|
|
|
my @args = parse_arg_list( $elem ); |
226
|
0
|
|
0
|
|
|
|
return @args >= $MUTATING_SUBSTR_ARG_COUNT && _is_topic( $args[0]->[0] ); |
227
|
|
|
|
|
|
|
} |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub _is_assignment_operator { ## no critic (RequireArgUnpacking) |
232
|
0
|
|
|
0
|
|
|
state $assignment_ops = { hashify qw( = *= /= += -= %= **= x= .= &= |= ^= &&= ||= <<= >>= //= ++ --) }; |
233
|
0
|
|
|
|
|
|
return exists $assignment_ops->{$_[0]}; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
sub _is_increment_operator { ## no critic (RequireArgUnpacking) |
237
|
0
|
|
|
0
|
|
|
state $increment_ops = { hashify qw( ++ -- ) }; |
238
|
0
|
|
|
|
|
|
return exists $increment_ops->{$_[0]}; |
239
|
|
|
|
|
|
|
} |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
sub _is_binding_operator { ## no critic (RequireArgUnpacking) |
242
|
0
|
|
|
0
|
|
|
state $binding_ops = { hashify qw( =~ !~ ) }; |
243
|
0
|
|
|
|
|
|
return exists $binding_ops->{$_[0]}; |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
1; |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
#----------------------------------------------------------------------------- |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
__END__ |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=pod |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
=head1 NAME |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
Perl::Critic::Policy::ControlStructures::ProhibitMutatingListFunctions - Don't modify C<$_> in list functions. |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=head1 AFFILIATION |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
This Policy is part of the core L<Perl::Critic|Perl::Critic> |
262
|
|
|
|
|
|
|
distribution. |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
=head1 DESCRIPTION |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
C<map>, C<grep> and other list operators are intended to transform |
268
|
|
|
|
|
|
|
arrays into other arrays by applying code to the array elements one by |
269
|
|
|
|
|
|
|
one. For speed, the elements are referenced via a C<$_> alias rather |
270
|
|
|
|
|
|
|
than copying them. As a consequence, if the code block of the C<map> |
271
|
|
|
|
|
|
|
or C<grep> modify C<$_> in any way, then it is actually modifying the |
272
|
|
|
|
|
|
|
source array. This IS technically allowed, but those side effects can |
273
|
|
|
|
|
|
|
be quite surprising, especially when the array being passed is C<@_> |
274
|
|
|
|
|
|
|
or perhaps C<values(%ENV)>! Instead authors should restrict in-place |
275
|
|
|
|
|
|
|
array modification to C<for(@array) { ... }> constructs instead, or |
276
|
|
|
|
|
|
|
use C<List::SomeUtils:apply()> or C<List::MoreUtils::apply()>. |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=head1 CONFIGURATION |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
By default, this policy applies to the following list functions: |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
map grep |
283
|
|
|
|
|
|
|
List::Util qw(first) |
284
|
|
|
|
|
|
|
List::MoreUtils qw(any all none notall true false firstidx |
285
|
|
|
|
|
|
|
first_index lastidx last_index insert_after |
286
|
|
|
|
|
|
|
insert_after_string) |
287
|
|
|
|
|
|
|
List::SomeUtils qw(any all none notall true false firstidx |
288
|
|
|
|
|
|
|
first_index lastidx last_index insert_after |
289
|
|
|
|
|
|
|
insert_after_string) |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
This list can be overridden the F<.perlcriticrc> file like this: |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
[ControlStructures::ProhibitMutatingListFunctions] |
294
|
|
|
|
|
|
|
list_funcs = map grep List::Util::first |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
Or, one can just append to the list like so: |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
[ControlStructures::ProhibitMutatingListFunctions] |
299
|
|
|
|
|
|
|
add_list_funcs = Foo::Bar::listmunge |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
=head1 LIMITATIONS |
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
This policy deliberately does not apply to C<for (@array) { ... }> or |
304
|
|
|
|
|
|
|
C<List::MoreUtils::apply()> C<List::SomeUtils::apply()>. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
Currently, the policy only detects explicit external module usage like |
307
|
|
|
|
|
|
|
this: |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
my @out = List::MoreUtils::any {s/^foo//} @in; |
310
|
|
|
|
|
|
|
my @out = List::SomeUtils::any {s/^foo//} @in; |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
and not like this: |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
use List::MoreUtils qw(any); |
315
|
|
|
|
|
|
|
my @out = any {s/^foo//} @in; |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
use List::SomeUtils qw(any); |
318
|
|
|
|
|
|
|
my @out = any {s/^foo//} @in; |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
This policy looks only for modifications of C<$_>. Other naughtiness |
321
|
|
|
|
|
|
|
could include modifying C<$a> and C<$b> in C<sort> and the like. |
322
|
|
|
|
|
|
|
That's beyond the scope of this policy. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
=head1 SEE ALSO |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
There is discussion of this policy at |
328
|
|
|
|
|
|
|
L<http://perlmonks.org/index.pl?node_id=743445>. |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=head1 AUTHOR |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Chris Dolan <cdolan@cpan.org> |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Michael Wolf <MichaelRWolf@att.net> |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head1 COPYRIGHT |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Copyright (c) 2006-2021 Chris Dolan. |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify |
343
|
|
|
|
|
|
|
it under the same terms as Perl itself. |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
=cut |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
# Local Variables: |
348
|
|
|
|
|
|
|
# mode: cperl |
349
|
|
|
|
|
|
|
# cperl-indent-level: 4 |
350
|
|
|
|
|
|
|
# fill-column: 78 |
351
|
|
|
|
|
|
|
# indent-tabs-mode: nil |
352
|
|
|
|
|
|
|
# End: |
353
|
|
|
|
|
|
|
# ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround : |
354
|
|
|
|
|
|
|
|