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   95507 use strict;
  134         336  
  134         4924  
3 134     134   793 use warnings;
  134         221  
  134         4052  
4 134     134   645 use List::Util qw/any/;
  134         233  
  134         9463  
5 134     134   1092 use Perl::Lint::Constants::Type;
  134         234  
  134         82735  
6 134     134   1300 use Perl::Lint::Constants::Kind;
  134         239  
  134         8289  
7 134     134   1200 use B::Keywords;
  134         1114  
  134         5336  
8 134     134   670 use parent "Perl::Lint::Policy";
  134         241  
  134         786  
9              
10             use constant {
11 134         195499 DESC => 'Return value of flagged function ignored',
12             EXPL => [208, 278],
13 134     134   9665 };
  134         241  
14              
15             sub evaluate {
16 42     42 0 89 my ($class, $file, $tokens, $src, $args) = @_;
17              
18 42         60 my $is_target_all = 0;
19 42         103 my @target_functions = qw/open close say/;
20 42         55 my $allowed_functions;
21 42 100       126 if (my $required_checked_syscalls_arg = $args->{require_checked_syscalls}) {
22 7 50       40 if (my $functions = $required_checked_syscalls_arg->{functions}) {
23 7 100       38 if ($functions eq ':builtins') {
    100          
24 3         123 @target_functions = @B::Keywords::Functions;
25             }
26             elsif ($functions eq ':all') {
27 3         11 $is_target_all = 1;
28             }
29             else {
30 1         3 @target_functions = ($functions);
31             }
32             }
33              
34 7 100       33 if ($allowed_functions = $required_checked_syscalls_arg->{exclude_functions}) {
35 3         12 @target_functions = grep {$_ ne $allowed_functions} @target_functions;
  485         764  
36             }
37             }
38              
39 42         64 my $is_in_assign_context = 0;
40 42         62 my $is_in_statement_context = 0;
41 42         43 my $is_called_syscalls_in_void = 0;
42 42         55 my $is_enabled_autodie = 0;
43 42         53 my @violations;
44 42         133 for (my $i = 0; my $token = $tokens->[$i]; $i++) {
45 665         601 my $token_type = $token->{type};
46 665         517 my $token_kind = $token->{kind};
47 665         612 my $token_data = $token->{data};
48              
49 665 100       873 if ($token_type == ASSIGN) {
50 9         10 $is_in_assign_context = 1;
51 9         14 next;
52             }
53              
54 656 100       841 if ($token_type == USED_NAME) {
55 20 100       50 if ($token_data eq 'Fatal') {
    100          
56 8         15 my $next_token = $tokens->[$i+1];
57 8         12 my $next_token_type = $next_token->{type};
58 8         10 my $next_token_data = $next_token->{data};
59 8 100 33     47 if ($next_token_type == REG_LIST) {
    100 33        
    50          
60 4         14 for ($i += 3; my $token = $tokens->[$i]; $i++) {
61 8         12 my $token_type = $token->{type};
62 8         10 my $token_data = $token->{data};
63 8 100       22 if ($token_type == REG_EXP) {
    50          
64 4         7 @target_functions = grep {$_ ne $token_data} @target_functions
  12         26  
65             }
66             elsif ($token_type == REG_DELIM) {
67 4         8 last;
68             }
69             }
70             }
71 3     3   10 elsif ($next_token_type == LEFT_PAREN) {
72 2         5 my $left_paren_num = 1;
73 2         8 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 3   27 if ($token_type == LEFT_PAREN) {
  3 50 33     10  
77 0         0 $left_paren_num++;
78             }
79             elsif (($token_type == STRING || $token_type == RAW_STRING) && any {$_ eq $token_data} @target_functions) {
80 2         15 return [];
81             }
82             else {
83 0 0       0 last if --$left_paren_num <= 0;
84             }
85             }
86             }
87             elsif (($next_token_type == STRING || $next_token_type == RAW_STRING) && any {$_ eq $next_token_data} @target_functions) {
88 2         5 last;
89             }
90             }
91             elsif ($token_data eq 'autodie') {
92 4 100       10 if ($tokens->[$i+1]->{type} == REG_LIST) {
93 2         7 for ($i += 3; my $token = $tokens->[$i]; $i++) {
94 4         4 my $token_type = $token->{type};
95 4 100 100     23 if ($token_type == REG_EXP && $token->{data} =~ /\A\s*:io\s*\Z/) {
    100          
96 1         4 $is_enabled_autodie = 1;
97             }
98             elsif ($token_type == REG_DELIM) {
99 2         5 last;
100             }
101             }
102             }
103             else {
104 2         5 $is_enabled_autodie = 1;
105             }
106             }
107              
108 16         28 next;
109             }
110              
111 636 100 66     967 if ($token_type == NAMESPACE && $token_data eq 'Fatal') {
112 2         6 my $skipped_token = $tokens->[$i+2];
113 2 50 33     19 if ($skipped_token && $skipped_token->{type} == NAMESPACE && $skipped_token->{data} eq 'Exception') {
      33        
114 2         8 for ($i += 3; my $token = $tokens->[$i]; $i++) {
115 8         7 my $token_type = $token->{type};
116 8         11 my $token_data = $token->{data};
117 8 100 66     60 if ($token_type == REG_LIST) {
    50 66        
    50          
    100          
118 2         10 for ($i += 2; my $token = $tokens->[$i]; $i++) {
119 4         5 my $token_type = $token->{type};
120 4         6 my $token_data = $token->{data};
121 4 100       26 if ($token_type == REG_EXP) {
    50          
122 2         4 @target_functions = grep {$_ ne $token_data} @target_functions
  6         15  
123             }
124             elsif ($token_type == REG_DELIM) {
125 2         10 last;
126             }
127             }
128             }
129 6     6   22 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   0 if ($token_type == LEFT_PAREN) {
  0 0 0     0  
135 0         0 $left_paren_num++;
136             }
137             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             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         3 last;
150             }
151             }
152             }
153              
154 2         7 next;
155             }
156              
157 634 100       759 if ($token_kind == KIND_STMT) {
158 27         27 $is_in_statement_context = 1;
159              
160 27 100       52 if ($tokens->[$i+1]->{type} == LEFT_PAREN) {
161 3         5 $i++;
162 3         4 my $left_paren_num = 1;
163 3         12 for ($i++; my $token = $tokens->[$i]; $i++) {
164 3         7 my $token_type = $token->{type};
165 3 50       10 if ($token_type == LEFT_PAREN) {
166 0         0 $left_paren_num++;
167             }
168             else {
169 3 50       15 last if --$left_paren_num <= 0;
170             }
171             }
172             }
173 27         47 next;
174             }
175              
176 607 100 100     1581 if ($token_type == BUILTIN_FUNC || ($is_target_all && $token_type == RETURN)) {
      66        
177 97 100 100 430   543 if ($is_target_all || any {$_ eq $token_data} @target_functions) {
  430 100       558  
178 69 100 66     196 if (!$is_in_assign_context && !$is_in_statement_context) {
179 35         43 $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     9 if ($next_token->{type} == KEY && $next_token->{data} eq 'autodie') {
185 1         3 $is_enabled_autodie = 0;
186             }
187             }
188 97         321 next;
189             }
190              
191 510 100 66     742 if (
      66        
192             $is_target_all &&
193             ($token_type == CALL || ($token_type == KEY && $tokens->[++$i]->{type} == LEFT_PAREN))
194             ) {
195 3 100 66     64 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         5 $is_called_syscalls_in_void = 1;
200             }
201 2         9 next;
202             }
203              
204 507 100       637 if ($token_kind == KIND_OP) {
205 16         18 $is_in_statement_context = 1;
206 16         14 $is_called_syscalls_in_void = 0;
207 16         30 next;
208             }
209              
210 491 100       1061 if ($token_kind == KIND_STMT_END) {
211 100 100       160 next if $is_enabled_autodie;
212              
213 95 100       144 if ($is_called_syscalls_in_void) {
214 21         117 push @violations, {
215             filename => $file,
216             line => $token->{line},
217             description => DESC,
218             explanation => EXPL,
219             policy => __PACKAGE__,
220             };
221             }
222              
223 95         88 $is_in_assign_context = 0;
224 95         68 $is_in_statement_context = 0;
225 95         73 $is_called_syscalls_in_void = 0;
226 95         189 next;
227             }
228              
229             }
230              
231 40         236 return \@violations;
232             }
233              
234             1;
235