File Coverage

blib/lib/Perl/Lint/Policy/InputOutput/RequireCheckedSyscalls.pm
Criterion Covered Total %
statement 133 146 91.1
branch 73 94 77.6
condition 33 63 52.3
subroutine 13 14 92.8
pod 0 1 0.0
total 252 318 79.2


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::InputOutput::RequireCheckedSyscalls;
2 134     134   72695 use strict;
  134         205  
  134         3247  
3 134     134   420 use warnings;
  134         190  
  134         3007  
4 134     134   470 use List::Util qw/any/;
  134         182  
  134         7155  
5 134     134   863 use Perl::Lint::Constants::Type;
  134         523  
  134         61458  
6 134     134   915 use Perl::Lint::Constants::Kind;
  134         182  
  134         6169  
7 134     134   953 use B::Keywords;
  134         1041  
  134         4049  
8 134     134   450 use parent "Perl::Lint::Policy";
  134         168  
  134         630  
9              
10             use constant {
11 134         125002 DESC => 'Return value of flagged function ignored',
12             EXPL => [208, 278],
13 134     134   7241 };
  134         203  
14              
15             sub evaluate {
16 42     42 0 53 my ($class, $file, $tokens, $src, $args) = @_;
17              
18 42         34 my $is_target_all = 0;
19 42         65 my @target_functions = qw/open close say/;
20 42         29 my $allowed_functions;
21 42 100       84 if (my $required_checked_syscalls_arg = $args->{require_checked_syscalls}) {
22 7 50       17 if (my $functions = $required_checked_syscalls_arg->{functions}) {
23 7 100       15 if ($functions eq ':builtins') {
    100          
24 3         45 @target_functions = @B::Keywords::Functions;
25             }
26             elsif ($functions eq ':all') {
27 3         4 $is_target_all = 1;
28             }
29             else {
30 1         2 @target_functions = ($functions);
31             }
32             }
33              
34 7 100       15 if ($allowed_functions = $required_checked_syscalls_arg->{exclude_functions}) {
35 3         5 @target_functions = grep {$_ ne $allowed_functions} @target_functions;
  485         380  
36             }
37             }
38              
39 42         40 my $is_in_assign_context = 0;
40 42         28 my $is_in_statement_context = 0;
41 42         31 my $is_called_syscalls_in_void = 0;
42 42         28 my $is_enabled_autodie = 0;
43 42         25 my @violations;
44 42         93 for (my $i = 0; my $token = $tokens->[$i]; $i++) {
45 665         466 my $token_type = $token->{type};
46 665         399 my $token_kind = $token->{kind};
47 665         452 my $token_data = $token->{data};
48              
49 665 100       742 if ($token_type == ASSIGN) {
50 9         9 $is_in_assign_context = 1;
51 9         12 next;
52             }
53              
54 656 100       708 if ($token_type == USED_NAME) {
55 20 100       42 if ($token_data eq 'Fatal') {
    100          
56 8         11 my $next_token = $tokens->[$i+1];
57 8         6 my $next_token_type = $next_token->{type};
58 8         11 my $next_token_data = $next_token->{data};
59 8 100 33     29 if ($next_token_type == REG_LIST) {
    100 33        
    50          
60 4         8 for ($i += 3; my $token = $tokens->[$i]; $i++) {
61 8         8 my $token_type = $token->{type};
62 8         5 my $token_data = $token->{data};
63 8 100       21 if ($token_type == REG_EXP) {
    50          
64 4         5 @target_functions = grep {$_ ne $token_data} @target_functions
  12         23  
65             }
66             elsif ($token_type == REG_DELIM) {
67 4         7 last;
68             }
69             }
70             }
71             elsif ($next_token_type == LEFT_PAREN) {
72 2         3 my $left_paren_num = 1;
73 2         7 for ($i += 2; my $token = $tokens->[$i]; $i++) {
74 2         3 my $token_type = $token->{type};
75 2         3 my $token_data = $token->{data};
76 2 50 33     19 if ($token_type == LEFT_PAREN) {
    50 33        
77 0         0 $left_paren_num++;
78             }
79 3     3   7 elsif (($token_type == STRING || $token_type == RAW_STRING) && any {$_ eq $token_data} @target_functions) {
80 2         12 return [];
81             }
82             else {
83 0 0       0 last if --$left_paren_num <= 0;
84             }
85             }
86             }
87 3     3   8 elsif (($next_token_type == STRING || $next_token_type == RAW_STRING) && any {$_ eq $next_token_data} @target_functions) {
88 2         4 last;
89             }
90             }
91             elsif ($token_data eq 'autodie') {
92 4 100       11 if ($tokens->[$i+1]->{type} == REG_LIST) {
93 2         6 for ($i += 3; my $token = $tokens->[$i]; $i++) {
94 4         3 my $token_type = $token->{type};
95 4 100 100     20 if ($token_type == REG_EXP && $token->{data} =~ /\A\s*:io\s*\Z/) {
    100          
96 1         3 $is_enabled_autodie = 1;
97             }
98             elsif ($token_type == REG_DELIM) {
99 2         3 last;
100             }
101             }
102             }
103             else {
104 2         2 $is_enabled_autodie = 1;
105             }
106             }
107              
108 16         26 next;
109             }
110              
111 636 100 66     778 if ($token_type == NAMESPACE && $token_data eq 'Fatal') {
112 2         3 my $skipped_token = $tokens->[$i+2];
113 2 50 33     14 if ($skipped_token && $skipped_token->{type} == NAMESPACE && $skipped_token->{data} eq 'Exception') {
      33        
114 2         5 for ($i += 3; my $token = $tokens->[$i]; $i++) {
115 8         9 my $token_type = $token->{type};
116 8         9 my $token_data = $token->{data};
117 8 100 66     38 if ($token_type == REG_LIST) {
    50 66        
    50          
    100          
118 2         4 for ($i += 2; my $token = $tokens->[$i]; $i++) {
119 4         4 my $token_type = $token->{type};
120 4         5 my $token_data = $token->{data};
121 4 100       15 if ($token_type == REG_EXP) {
    50          
122 2         3 @target_functions = grep {$_ ne $token_data} @target_functions
  6         12  
123             }
124             elsif ($token_type == REG_DELIM) {
125 2         5 last;
126             }
127             }
128             }
129             elsif ($token_type == LEFT_PAREN) {
130 0         0 my $left_paren_num = 1;
131 0         0 for ($i++; my $token = $tokens->[$i]; $i++) {
132 0         0 my $token_type = $token->{type};
133 0         0 my $token_data = $token->{data};
134 0 0 0     0 if ($token_type == LEFT_PAREN) {
    0 0        
135 0         0 $left_paren_num++;
136             }
137 0     0   0 elsif (($token_type == STRING || $token_type == RAW_STRING) && any {$_ eq $token_data} @target_functions) {
138 0         0 return [];
139             }
140             else {
141 0 0       0 last if --$left_paren_num <= 0;
142             }
143             }
144             }
145 6     6   17 elsif (($token_type == STRING || $token_type == RAW_STRING) && any {$_ eq $token_data} @target_functions) {
146 0         0 last;
147             }
148             elsif ($token->{kind} == KIND_STMT_END) {
149 2         2 last;
150             }
151             }
152             }
153              
154 2         4 next;
155             }
156              
157 634 100       643 if ($token_kind == KIND_STMT) {
158 27         19 $is_in_statement_context = 1;
159              
160 27 100       46 if ($tokens->[$i+1]->{type} == LEFT_PAREN) {
161 3         4 $i++;
162 3         4 my $left_paren_num = 1;
163 3         8 for ($i++; my $token = $tokens->[$i]; $i++) {
164 3         5 my $token_type = $token->{type};
165 3 50       6 if ($token_type == LEFT_PAREN) {
166 0         0 $left_paren_num++;
167             }
168             else {
169 3 50       8 last if --$left_paren_num <= 0;
170             }
171             }
172             }
173 27         40 next;
174             }
175              
176 607 100 100     1139 if ($token_type == BUILTIN_FUNC || ($is_target_all && $token_type == RETURN)) {
      66        
177 97 100 100 430   405 if ($is_target_all || any {$_ eq $token_data} @target_functions) {
  430 100       421  
178 69 100 66     126 if (!$is_in_assign_context && !$is_in_statement_context) {
179 35         34 $is_called_syscalls_in_void = 1;
180             }
181             }
182             elsif ($token_data eq 'no') {
183 1         2 my $next_token = $tokens->[++$i];
184 1 50 33     6 if ($next_token->{type} == KEY && $next_token->{data} eq 'autodie') {
185 1         1 $is_enabled_autodie = 0;
186             }
187             }
188 97         243 next;
189             }
190              
191 510 100 66     558 if (
      66        
192             $is_target_all &&
193             ($token_type == CALL || ($token_type == KEY && $tokens->[++$i]->{type} == LEFT_PAREN))
194             ) {
195 3 100 66     33 if ($allowed_functions && $token_data =~ /\A$allowed_functions\s*\(?/) {
    50 33        
196 1         4 next;
197             }
198             elsif (!$is_in_assign_context && !$is_in_statement_context) {
199 2         2 $is_called_syscalls_in_void = 1;
200             }
201 2         4 next;
202             }
203              
204 507 100       528 if ($token_kind == KIND_OP) {
205 16         8 $is_in_statement_context = 1;
206 16         13 $is_called_syscalls_in_void = 0;
207 16         24 next;
208             }
209              
210 491 100       867 if ($token_kind == KIND_STMT_END) {
211 100 100       134 next if $is_enabled_autodie;
212              
213 95 100       127 if ($is_called_syscalls_in_void) {
214             push @violations, {
215             filename => $file,
216             line => $token->{line},
217 21         70 description => DESC,
218             explanation => EXPL,
219             policy => __PACKAGE__,
220             };
221             }
222              
223 95         61 $is_in_assign_context = 0;
224 95         60 $is_in_statement_context = 0;
225 95         59 $is_called_syscalls_in_void = 0;
226 95         154 next;
227             }
228              
229             }
230              
231 40         150 return \@violations;
232             }
233              
234             1;
235