File Coverage

blib/lib/Perl/Critic/Policy/References/ProhibitRefChecks.pm
Criterion Covered Total %
statement 93 94 98.9
branch 46 46 100.0
condition 35 42 83.3
subroutine 14 14 100.0
pod 4 8 50.0
total 192 204 94.1


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::References::ProhibitRefChecks;
2              
3 5     5   3903 use 5.010001;
  5         20  
4 5     5   28 use strict;
  5         9  
  5         114  
5 5     5   19 use warnings;
  5         8  
  5         201  
6 5     5   30 use Readonly;
  5         10  
  5         290  
7              
8 5     5   37 use Perl::Critic::Utils qw/:severities :classification precedence_of/;
  5         8  
  5         322  
9 5     5   2065 use base 'Perl::Critic::Policy';
  5         42  
  5         5331  
10              
11             our $VERSION = '0.0.7';
12              
13             Readonly::Scalar my $DESC => q{Do not perform manual ref checks};
14             Readonly::Scalar my $EXPL => undef; # [ ];
15             Readonly::Scalar my $UPREC => precedence_of('-e'); # named unary function precedence
16              
17             ####-----------------------------------------------------------------------------
18              
19             sub supported_parameters {
20             return (
21             {
22 11     11 0 495415 name => 'eq',
23             description => 'Reference types that may be checked via string equality.',
24             default_string => '',
25             behavior => 'string list',
26             },
27             {
28             name => 'ne',
29             description => 'Reference types that may be checked via string inequality.',
30             default_string => '',
31             behavior => 'string list',
32             },
33             {
34             name => 'regexp',
35             description => 'Permit regular expression comparisons.',
36             default_string => '0',
37             behavior => 'boolean',
38             },
39             {
40             name => 'bareref',
41             description => 'Permit a bare if(ref) style check.',
42             default_string => '0',
43             behavior => 'boolean',
44             },
45             );
46             }
47              
48 7818     7818 1 58989257 sub applies_to { return 'PPI::Token::Word' }
49 5768     5768 1 78497 sub default_severity { return $SEVERITY_MEDIUM }
50 1     1 1 3056 sub default_themes { return qw/cosmetic maintenance performance/ }
51              
52             #-----------------------------------------------------------------------------
53              
54             sub invalid {
55 5768     5768 0 17945 my ($self,$elem,$note)=@_;
56 5768   100     18275 $note//='';
57 5768 100       17640 if($note) { $note=" ($note)" }
  5767         13487  
58 5768         33739 return $self->violation(sprintf("%s%s",$DESC,$note),$EXPL,$elem);
59             }
60              
61             sub eqne {
62 9225     9225 0 238168 my ($node)=@_;
63 9225 100       33225 if(!$node) { return }
  6916         25807  
64 2309 100       14246 if(!$node->isa('PPI::Token::Operator')) { return }
  1         5  
65 2308         7172 my $op=$node->content();
66 2308 100 100     22829 if(($op eq 'eq')||($op eq 'ne')) { return 1 }
  257         870  
67 2051         7707 return;
68             }
69              
70             sub decompose {
71 9000     9000 0 1896460 my ($elem)=@_;
72 9000         23787 my %operator=map {$_=>undef} (qw/eq ne !~ =~ cmp/);
  45000         131088  
73 9000         29411 my ($node,$operator,$rhs)=($elem,undef,undef);
74 9000         45406 while($node) {
75 31620 100 100     714065 if($node->isa('PPI::Token::Operator') && exists($operator{$node->content()})) {
    100 100        
76 7964         52234 $operator=$node->content();
77 7964         38531 $rhs=$node->snext_sibling();
78 7964         295249 $node=0;
79             }
80 516         18639 elsif($node->isa('PPI::Token::Operator') && (precedence_of($node)>$UPREC)) { $node=0 }
81 23140         139526 else { $node=$node->snext_sibling() }
82             }
83 9000 100       46325 if(!$rhs) { return ($operator) }
  1036         4579  
84 7964 100       34590 if($rhs->isa('PPI::Token::Quote')) { return ($operator,lc($rhs->string())) }
  6927         31441  
85 1037 100 66     4947 if($rhs->isa('PPI::Token::Word') && ($rhs->content() eq 'ref')) { return ($operator,'ref') }
  260         2372  
86 777         2130 return ($operator,$rhs->content());
87             }
88              
89             sub violates {
90 13576     13576 1 1240413 my ($self,$elem,undef)=@_;
91 13576 100       72290 if(!$elem->isa('PPI::Token::Word')) { return }
  1         10  
92 13575 100       59787 if(!is_perl_builtin($elem)) { return }
  4345         134870  
93 9230 100       327492 if(!is_function_call($elem)) { return }
  2         307  
94 9228 100       2693933 if($elem->content() ne 'ref') { return }
  3         48  
95              
96             # Already handled.
97             # No support for ('quoted' eq ref($x)) at this time.
98 9225 100       66033 if(eqne($elem->sprevious_sibling())){ return }
  257         864  
99              
100 8968   50     33157 $$self{_eq} //={};
101 8968   50     28708 $$self{_ne} //={};
102 8968   50     27984 $$self{_regexp} //=0;
103 8968   50     31605 $$self{_bareref}//=0;
104              
105             # Without options, 'ref' should never be called.
106 8968 100 100     16312 if(!%{$$self{_eq}} && !%{$$self{_ne}} && !$$self{_regexp} && !$$self{_bareref}) { return $self->invalid($elem) }
  8968   100     36114  
  4353   100     23514  
  1         5  
107              
108 8967         26855 my ($operator,$rhs)=decompose($elem);
109              
110 8967 100 100     145149 if(!$operator) {
    100          
    100          
    100          
111 1028 100       3826 if(!$$self{_bareref}) { return $self->invalid($elem,'bare ref check') }
  260         804  
112 768         3364 return;
113             }
114             elsif($operator eq 'eq') {
115 3586   50     15411 $$self{_eqfold}//={map {lc($_)=>undef} keys(%{$$self{_eq}//{}})};
  4   100     27  
  6         36  
116 3586 100       13868 if(!exists($$self{_eqfold}{$rhs})) { return $self->invalid($elem,$rhs) }
  2562         8663  
117 1024         4637 return;
118             }
119             elsif($operator eq 'ne') {
120 3584   50     14606 $$self{_nefold}//={map {lc($_)=>undef} keys(%{$$self{_ne}//{}})};
  2   100     12  
  5         30  
121 3584 100       16609 if(!exists($$self{_nefold}{$rhs})) { return $self->invalid($elem,$rhs) }
  2560         9455  
122 1024         3945 return;
123             }
124             elsif(($operator eq '=~')||($operator eq '!~')) {
125 768 100       2801 if(!$$self{_regexp}) { return $self->invalid($elem,$rhs) }
  384         1239  
126 384         1246 return;
127             }
128             else {
129 1         10 return $self->invalid($elem,$rhs);
130             }
131 0           return;
132             }
133              
134             #-----------------------------------------------------------------------------
135              
136             1;
137              
138             __END__
139              
140             =pod
141              
142             =head1 NAME
143              
144             Perl::Critic::Policy::References::ProhibitRefChecks - Write C<is_arrayref($var)> instead of C<ref($var) eq 'ARRAY'>.
145              
146             =head1 DESCRIPTION
147              
148             Checking references manually is less efficient that using L<Ref::Util> and prone to typos.
149              
150             if(ref($var) eq 'ARRYA') # oops!
151             if(is_arrayref($var)) # ok
152              
153             if(ref($var) ne 'HASH') # no
154             if(!is_hashref($var)) # ok
155              
156             if(ref($var)) # no
157             if(is_ref($var)) # ok
158              
159             =head1 CONFIGURATION
160              
161             Explicit strings may be permitted for checks of the form C<ref(...) eq 'string'>, or C<ref(...) ne 'string'>. Entries are case insensitive and can be the core types or custom modules.
162              
163             [References::ProhibitRefChecks]
164             eq = code
165             ne = code my::module
166              
167             As a special scenario, checks of the form C<ref(...) eq ref(...)> can be permitted with C<eq = ref>. The same works for C<ne = ref>.
168              
169             Regular expression matches are violations by default. To permit checks of the form C<ref(...) =~ /pattern/> or C<!~>:
170              
171             [References::ProhibitRefChecks]
172             regexp = 1
173              
174             Since L<Ref::Util> provides C<is_ref>, in the default configuration the bare C<ref> call is rarely needed. To specifically permit using direct C<ref(...)> calls:
175              
176             [References::ProhibitRefChecks]
177             bareref = 1
178              
179             =head1 NOTES
180              
181             Comparisons to stored values or constants are not supported: C<ref(...) eq $thing> and C<ref(...) eq HASH()> are violations.
182              
183             Lexicographic comparison via C<ref(...) cmp "string"> is a violation.
184              
185             In/equality checks are not bidirectional: C<'HASH' eq ref(...)> will not be considered a violation.
186              
187             =head1 BUGS
188              
189             Named unary functions are not separately considered. A call of C<lc(ref $x) eq "array"> is considered a "bare ref check", whereas C<lc ref($x) eq "array"> is considered an "eq ref check".
190              
191             =cut