File Coverage

blib/lib/Perl/Critic/Policy/BuiltinFunctions/ProhibitUselessTopic.pm
Criterion Covered Total %
statement 44 46 95.6
branch 16 20 80.0
condition 5 9 55.5
subroutine 10 10 100.0
pod 4 5 80.0
total 79 90 87.7


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::BuiltinFunctions::ProhibitUselessTopic;
2              
3 40     40   27107 use strict;
  40         171  
  40         1251  
4 40     40   299 use warnings;
  40         125  
  40         966  
5 40     40   229 use Readonly;
  40         135  
  40         2005  
6              
7 40     40   288 use Perl::Critic::Utils qw{ :severities :classification :ppi hashify };
  40         135  
  40         2235  
8 40     40   15715 use parent 'Perl::Critic::Policy';
  40         155  
  40         301  
9              
10             our $VERSION = '1.146';
11              
12             ## no critic ( ValuesAndExpressions::RequireInterpolationOfMetachars )
13             ## The numerous $_ variables make false positives.
14             Readonly::Scalar my $DESC => q{Useless use of $_};
15             Readonly::Scalar my $EXPL_FILETEST => q{$_ should be omitted when using a filetest operator};
16             Readonly::Scalar my $EXPL_FUNCTION => q{$_ should be omitted when calling "%s"};
17             Readonly::Scalar my $EXPL_FUNCTION_SPLIT => q{$_ should be omitted when calling "split" with two arguments};
18              
19 95     95 0 1605 sub supported_parameters { return () }
20 89     89 1 442 sub default_severity { return $SEVERITY_LOW }
21 74     74 1 262 sub default_themes { return qw( core ) }
22 36     36 1 153 sub applies_to { return 'PPI::Token::Operator', 'PPI::Token::Word' }
23              
24             Readonly::Array my @FILETEST_OPERATORS => qw( -r -w -x -o -R -W -X -O -e -z -s -f -d -l -p -S -b -c -u -g -k -T -B -M -A -C );
25             Readonly::Hash my %FILETEST_OPERATORS => hashify( @FILETEST_OPERATORS );
26              
27             Readonly::Array my @TOPICAL_FUNCS => qw(
28             abs alarm
29             chomp chop chr chroot cos
30             defined
31             eval exp
32             fc
33             glob
34             hex
35             int
36             lc lcfirst length log lstat
37             mkdir
38             oct ord
39             pos print
40             quotemeta
41             readlink readpipe ref require reverse rmdir
42             say sin split sqrt stat study
43             uc ucfirst unlink unpack
44             );
45             Readonly::Hash my %TOPICAL_FUNCS => hashify( @TOPICAL_FUNCS );
46              
47             Readonly::Hash my %APPLIES_TO => ( %TOPICAL_FUNCS, %FILETEST_OPERATORS );
48              
49             sub violates {
50 530     530 1 1051 my ( $self, $elem, undef ) = @_;
51              
52 530 100       1090 return if not exists $APPLIES_TO{ $elem->content };
53              
54 31         410 my $content = $elem->content;
55              
56             # Are we looking at a filetest?
57 31 100       218 if ( $FILETEST_OPERATORS{ $content } ) {
58             # Is there a $_ following it?
59 2         20 my $op_node = $elem->snext_sibling;
60 2 50 33     69 if ( $op_node && $op_node->isa('PPI::Token::Magic') ) {
61 2         8 my $op = $op_node->content;
62 2 50       12 if ( $op eq '$_' ) {
63 2         10 return $self->violation( $DESC, $EXPL_FILETEST, $elem );
64             }
65             }
66 0         0 return;
67             }
68              
69 29 50 33     293 if ( $TOPICAL_FUNCS{ $content } && is_perl_builtin( $elem ) ) {
70 29         396 my $is_split = $content eq 'split';
71              
72 29         105 my @args = parse_arg_list( $elem );
73              
74 29         65 my $nth_arg_for_topic;
75 29 100       79 if ( $is_split ) {
76 4 100       19 return if @args != 2; # Ignore split( /\t/ ) or split( /\t/, $_, 3 )
77 2         5 $nth_arg_for_topic = 2;
78             }
79             else {
80 25         65 $nth_arg_for_topic = 1;
81             }
82              
83 27 50       97 if ( @args == $nth_arg_for_topic ) {
84 27         63 my $topic_arg = $args[ $nth_arg_for_topic - 1 ];
85 27         55 my @tokens = @{$topic_arg};
  27         62  
86 27 100 100     124 if ( (@tokens == 1) && ($tokens[0]->content eq '$_') ) {
87 13 100       112 my $msg = $is_split ? $EXPL_FUNCTION_SPLIT : (sprintf $EXPL_FUNCTION, $content);
88 13         49 return $self->violation( $DESC, $msg, $elem );
89             }
90             }
91 14         134 return;
92             }
93              
94 0           return;
95             }
96              
97              
98             1;
99              
100             __END__
101              
102             =pod
103              
104             =for stopwords filetest
105              
106             =head1 NAME
107              
108             Perl::Critic::Policy::BuiltinFunctions::ProhibitUselessTopic - Don't pass $_ to built-in functions that assume it, or to most filetest operators.
109              
110             =head1 AFFILIATION
111              
112             This Policy is part of the L<Perl::Critic|Perl::Critic> distribution.
113              
114             =head1 DESCRIPTION
115              
116             There are a number of places where C<$_>, or "the topic" variable,
117             is unnecessary.
118              
119             =head2 Topic unnecessary for certain Perl built-in functions
120              
121             Many Perl built-in functions will operate on C<$_> if no argument
122             is passed. For example, the C<length> function will operate on
123             C<$_> by default. This snippet:
124              
125             for ( @list ) {
126             if ( length( $_ ) == 4 ) { ...
127              
128             is more idiomatically written as:
129              
130             for ( @list ) {
131             if ( length == 4 ) { ...
132              
133             In the case of the C<split> function, the second argument is the
134             one that defaults to C<$_>. This snippet:
135              
136             for ( @list ) {
137             my @args = split /\t/, $_;
138              
139             is better written as:
140              
141             for ( @list ) {
142             my @args = split /\t/;
143              
144             There is one built-in that this policy does B<not> check for:
145             C<reverse> called with C<$_>.
146              
147             The C<reverse> function only operates on C<$_> if called in scalar
148             context. Therefore:
149              
150             for ( @list ) {
151             my $backwards = reverse $_;
152              
153             is better written as:
154              
155             for ( @list ) {
156             my $backwards = reverse;
157              
158             However, the distinction for scalar vs. list context on C<reverse>
159             is not yet working. See L<KNOWN BUGS|KNOWN BUGS> below.
160              
161             =head2 Topic unnecessary for most filetest operators
162              
163             Another place that C<$_> is unnecessary is with a filetest operator.
164              
165             # These are identical.
166             my $size = -s $_;
167             my $size = -s;
168              
169             # These are identical.
170             if ( -r $_ ) { ...
171             if ( -r ) { ...
172              
173             The exception is after the C<-t> filetest operator, which instead of
174             defaulting to C<$_> defaults to C<STDIN>.
175              
176             # These are NOT identical.
177             if ( -t $_ ) { ...
178             if ( -t ) { ... # Checks STDIN, not $_
179              
180             =head1 KNOWN BUGS
181              
182             This policy flags a false positive on C<reverse> called in list
183             context, since C<reverse> in list context does I<not> assume C<$_>.
184              
185             my $s = reverse( $_ ); # $_ is useless.
186             my @a = reverse( $_ ); # $_ is not useless here.
187              
188             =head1 CONFIGURATION
189              
190             This Policy is not configurable except for the standard options.
191              
192             =head1 AUTHOR
193              
194             Andy Lester <andy@petdance.com>
195              
196             =head1 COPYRIGHT
197              
198             Copyright (c) 2013-2022 Andy Lester <andy@petdance.com>
199              
200             This library is free software; you can redistribute it and/or modify it
201             under the terms of the Artistic License 2.0.
202              
203             =cut
204              
205             # Local Variables:
206             # mode: cperl
207             # cperl-indent-level: 4
208             # fill-column: 78
209             # indent-tabs-mode: nil
210             # c-indentation-style: bsd
211             # End:
212             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :