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   72472 use strict;
  134         196  
  134         3080  
3 134     134   421 use warnings;
  134         168  
  134         2862  
4 134     134   444 use List::Util qw/any/;
  134         183  
  134         7306  
5 134     134   894 use Perl::Lint::Constants::Type;
  134         177  
  134         59497  
6 134     134   961 use Perl::Lint::Constants::Kind;
  134         185  
  134         5921  
7 134     134   925 use B::Keywords;
  134         997  
  134         3665  
8 134     134   434 use parent "Perl::Lint::Policy";
  134         173  
  134         563  
9              
10             use constant {
11 134         121180 DESC => 'Return value of flagged function ignored',
12             EXPL => [208, 278],
13 134     134   7044 };
  134         176  
14              
15             sub evaluate {
16 42     42 0 67 my ($class, $file, $tokens, $src, $args) = @_;
17              
18 42         40 my $is_target_all = 0;
19 42         79 my @target_functions = qw/open close say/;
20 42         39 my $allowed_functions;
21 42 100       89 if (my $required_checked_syscalls_arg = $args->{require_checked_syscalls}) {
22 7 50       23 if (my $functions = $required_checked_syscalls_arg->{functions}) {
23 7 100       25 if ($functions eq ':builtins') {
    100          
24 3         62 @target_functions = @B::Keywords::Functions;
25             }
26             elsif ($functions eq ':all') {
27 3         6 $is_target_all = 1;
28             }
29             else {
30 1         2 @target_functions = ($functions);
31             }
32             }
33              
34 7 100       22 if ($allowed_functions = $required_checked_syscalls_arg->{exclude_functions}) {
35 3         7 @target_functions = grep {$_ ne $allowed_functions} @target_functions;
  485         395  
36             }
37             }
38              
39 42         50 my $is_in_assign_context = 0;
40 42         36 my $is_in_statement_context = 0;
41 42         31 my $is_called_syscalls_in_void = 0;
42 42         29 my $is_enabled_autodie = 0;
43 42         33 my @violations;
44 42         105 for (my $i = 0; my $token = $tokens->[$i]; $i++) {
45 665         460 my $token_type = $token->{type};
46 665         405 my $token_kind = $token->{kind};
47 665         476 my $token_data = $token->{data};
48              
49 665 100       756 if ($token_type == ASSIGN) {
50 9         6 $is_in_assign_context = 1;
51 9         14 next;
52             }
53              
54 656 100       678 if ($token_type == USED_NAME) {
55 20 100       49 if ($token_data eq 'Fatal') {
    100          
56 8         9 my $next_token = $tokens->[$i+1];
57 8         9 my $next_token_type = $next_token->{type};
58 8         7 my $next_token_data = $next_token->{data};
59 8 100 33     41 if ($next_token_type == REG_LIST) {
    100 33        
    50          
60 4         8 for ($i += 3; my $token = $tokens->[$i]; $i++) {
61 8         9 my $token_type = $token->{type};
62 8         7 my $token_data = $token->{data};
63 8 100       13 if ($token_type == REG_EXP) {
    50          
64 4         7 @target_functions = grep {$_ ne $token_data} @target_functions
  12         22  
65             }
66             elsif ($token_type == REG_DELIM) {
67 4         7 last;
68             }
69             }
70             }
71             elsif ($next_token_type == LEFT_PAREN) {
72 2         4 my $left_paren_num = 1;
73 2         5 for ($i += 2; my $token = $tokens->[$i]; $i++) {
74 2         5 my $token_type = $token->{type};
75 2         3 my $token_data = $token->{data};
76 2 50 33     22 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   9 elsif (($next_token_type == STRING || $next_token_type == RAW_STRING) && any {$_ eq $next_token_data} @target_functions) {
88 2         3 last;
89             }
90             }
91             elsif ($token_data eq 'autodie') {
92 4 100       11 if ($tokens->[$i+1]->{type} == REG_LIST) {
93 2         5 for ($i += 3; my $token = $tokens->[$i]; $i++) {
94 4         5 my $token_type = $token->{type};
95 4 100 100     19 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         3 $is_enabled_autodie = 1;
105             }
106             }
107              
108 16         27 next;
109             }
110              
111 636 100 66     844 if ($token_type == NAMESPACE && $token_data eq 'Fatal') {
112 2         4 my $skipped_token = $tokens->[$i+2];
113 2 50 33     16 if ($skipped_token && $skipped_token->{type} == NAMESPACE && $skipped_token->{data} eq 'Exception') {
      33        
114 2         7 for ($i += 3; my $token = $tokens->[$i]; $i++) {
115 8         8 my $token_type = $token->{type};
116 8         8 my $token_data = $token->{data};
117 8 100 66     42 if ($token_type == REG_LIST) {
    50 66        
    50          
    100          
118 2         5 for ($i += 2; my $token = $tokens->[$i]; $i++) {
119 4         4 my $token_type = $token->{type};
120 4         4 my $token_data = $token->{data};
121 4 100       18 if ($token_type == REG_EXP) {
    50          
122 2         2 @target_functions = grep {$_ ne $token_data} @target_functions
  6         13  
123             }
124             elsif ($token_type == REG_DELIM) {
125 2         6 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   18 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         4 last;
150             }
151             }
152             }
153              
154 2         4 next;
155             }
156              
157 634 100       664 if ($token_kind == KIND_STMT) {
158 27         18 $is_in_statement_context = 1;
159              
160 27 100       49 if ($tokens->[$i+1]->{type} == LEFT_PAREN) {
161 3         5 $i++;
162 3         5 my $left_paren_num = 1;
163 3         6 for ($i++; my $token = $tokens->[$i]; $i++) {
164 3         6 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       9 last if --$left_paren_num <= 0;
170             }
171             }
172             }
173 27         35 next;
174             }
175              
176 607 100 100     1175 if ($token_type == BUILTIN_FUNC || ($is_target_all && $token_type == RETURN)) {
      66        
177 97 100 100 430   424 if ($is_target_all || any {$_ eq $token_data} @target_functions) {
  430 100       433  
178 69 100 66     177 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         3 my $next_token = $tokens->[++$i];
184 1 50 33     7 if ($next_token->{type} == KEY && $next_token->{data} eq 'autodie') {
185 1         2 $is_enabled_autodie = 0;
186             }
187             }
188 97         260 next;
189             }
190              
191 510 100 66     594 if (
      66        
192             $is_target_all &&
193             ($token_type == CALL || ($token_type == KEY && $tokens->[++$i]->{type} == LEFT_PAREN))
194             ) {
195 3 100 66     44 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         5 next;
202             }
203              
204 507 100       527 if ($token_kind == KIND_OP) {
205 16         16 $is_in_statement_context = 1;
206 16         12 $is_called_syscalls_in_void = 0;
207 16         27 next;
208             }
209              
210 491 100       850 if ($token_kind == KIND_STMT_END) {
211 100 100       120 next if $is_enabled_autodie;
212              
213 95 100       114 if ($is_called_syscalls_in_void) {
214             push @violations, {
215             filename => $file,
216             line => $token->{line},
217 21         81 description => DESC,
218             explanation => EXPL,
219             policy => __PACKAGE__,
220             };
221             }
222              
223 95         73 $is_in_assign_context = 0;
224 95         59 $is_in_statement_context = 0;
225 95         62 $is_called_syscalls_in_void = 0;
226 95         154 next;
227             }
228              
229             }
230              
231 40         170 return \@violations;
232             }
233              
234             1;
235