Filename | /Users/timbo/perl5/perlbrew/perls/perl-5.18.2/lib/site_perl/5.18.2/Perl/Critic/Policy/InputOutput/RequireBriefOpen.pm |
Statements | Executed 28 statements in 1.05ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 16µs | 16µs | BEGIN@10 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 8µs | 144µs | BEGIN@16 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 8µs | 56µs | BEGIN@21 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 7µs | 453µs | BEGIN@18 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 7µs | 9µs | default_severity | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 7µs | 28µs | BEGIN@14 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 7µs | 10µs | BEGIN@12 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 6µs | 18µs | BEGIN@11 | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
1 | 1 | 1 | 5µs | 5µs | supported_parameters | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | __ANON__[:102] | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | __ANON__[:141] | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | _find_close_invocations_or_return | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | _get_opened_fh | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | _get_scope | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | _unwrap_block | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | applies_to | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | default_themes | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
0 | 0 | 0 | 0s | 0s | violates | Perl::Critic::Policy::InputOutput::RequireBriefOpen::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | ############################################################################## | ||||
2 | # $URL$ | ||||
3 | # $Date$ | ||||
4 | # $Author$ | ||||
5 | # $Revision$ | ||||
6 | ############################################################################## | ||||
7 | |||||
8 | package Perl::Critic::Policy::InputOutput::RequireBriefOpen; | ||||
9 | |||||
10 | 2 | 38µs | 1 | 16µs | # spent 16µs within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@10 which was called:
# once (16µs+0s) by Module::Pluggable::Object::_require at line 10 # spent 16µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@10 |
11 | 2 | 19µs | 2 | 29µs | # spent 18µs (6+11) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@11 which was called:
# once (6µs+11µs) by Module::Pluggable::Object::_require at line 11 # spent 18µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@11
# spent 11µs making 1 call to strict::import |
12 | 2 | 18µs | 2 | 14µs | # spent 10µs (7+4) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@12 which was called:
# once (7µs+4µs) by Module::Pluggable::Object::_require at line 12 # spent 10µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@12
# spent 4µs making 1 call to warnings::import |
13 | |||||
14 | 2 | 26µs | 2 | 49µs | # spent 28µs (7+21) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@14 which was called:
# once (7µs+21µs) by Module::Pluggable::Object::_require at line 14 # spent 28µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@14
# spent 21µs making 1 call to Exporter::import |
15 | |||||
16 | 2 | 26µs | 2 | 281µs | # spent 144µs (8+137) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@16 which was called:
# once (8µs+137µs) by Module::Pluggable::Object::_require at line 16 # spent 144µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@16
# spent 137µs making 1 call to Exporter::Tiny::import |
17 | |||||
18 | 1 | 200ns | # spent 453µs (7+446) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@18 which was called:
# once (7µs+446µs) by Module::Pluggable::Object::_require at line 20 | ||
19 | hashify parse_arg_list | ||||
20 | 1 | 24µs | 2 | 899µs | }; # spent 453µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@18
# spent 446µs making 1 call to Exporter::import |
21 | 2 | 871µs | 2 | 104µs | # spent 56µs (8+48) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@21 which was called:
# once (8µs+48µs) by Module::Pluggable::Object::_require at line 21 # spent 56µs making 1 call to Perl::Critic::Policy::InputOutput::RequireBriefOpen::BEGIN@21
# spent 48µs making 1 call to base::import |
22 | |||||
23 | 1 | 600ns | our $VERSION = '1.121'; | ||
24 | |||||
25 | #----------------------------------------------------------------------------- | ||||
26 | |||||
27 | 1 | 2µs | 1 | 30µs | Readonly::Scalar my $DESC => q<Close filehandles as soon as possible after opening them..>; # spent 30µs making 1 call to Readonly::Scalar |
28 | 1 | 2µs | 1 | 42µs | Readonly::Scalar my $EXPL => [209]; # spent 42µs making 1 call to Readonly::Scalar |
29 | |||||
30 | 1 | 900ns | 1 | 20µs | Readonly::Scalar my $SCALAR_SIGIL => q<$>; # spent 20µs making 1 call to Readonly::Scalar |
31 | 1 | 1µs | 1 | 20µs | Readonly::Scalar my $GLOB_SIGIL => q<*>; # spent 20µs making 1 call to Readonly::Scalar |
32 | |||||
33 | # Identify the builtins that are equivalent to 'open' and 'close'. Note that | ||||
34 | # 'return' is considered equivalent to 'close'. | ||||
35 | 1 | 2µs | 2 | 27µs | Readonly::Hash my %CLOSE_BUILTIN => hashify( qw{ # spent 24µs making 1 call to Readonly::Hash
# spent 3µs making 1 call to Perl::Critic::Utils::hashify |
36 | close | ||||
37 | CORE::close | ||||
38 | CORE::GLOBAL::close | ||||
39 | return | ||||
40 | } ); | ||||
41 | 1 | 2µs | 2 | 23µs | Readonly::Hash my %OPEN_BUILTIN => hashify( qw{ # spent 21µs making 1 call to Readonly::Hash
# spent 2µs making 1 call to Perl::Critic::Utils::hashify |
42 | open | ||||
43 | CORE::open | ||||
44 | CORE::GLOBAL::open | ||||
45 | } ); | ||||
46 | |||||
47 | # Possible values for $is_lexical | ||||
48 | 1 | 900ns | 1 | 20µs | Readonly::Scalar my $NOT_LEXICAL => 0; # Guaranteed only false value # spent 20µs making 1 call to Readonly::Scalar |
49 | 1 | 800ns | 1 | 19µs | Readonly::Scalar my $LOCAL_LEXICAL => 1; # spent 19µs making 1 call to Readonly::Scalar |
50 | 1 | 900ns | 1 | 19µs | Readonly::Scalar my $NON_LOCAL_LEXICAL => 2; # spent 19µs making 1 call to Readonly::Scalar |
51 | |||||
52 | 1 | 800ns | 1 | 19µs | Readonly::Scalar my $LAST_ELEMENT => -1; # spent 19µs making 1 call to Readonly::Scalar |
53 | |||||
54 | #----------------------------------------------------------------------------- | ||||
55 | |||||
56 | # spent 5µs within Perl::Critic::Policy::InputOutput::RequireBriefOpen::supported_parameters which was called:
# once (5µs+0s) by Perl::Critic::Policy::new at line 88 of Perl/Critic/Policy.pm | ||||
57 | return ( | ||||
58 | { | ||||
59 | 1 | 7µs | name => 'lines', | ||
60 | description => 'The maximum number of lines between an open() and a close().', | ||||
61 | default_string => '9', | ||||
62 | behavior => 'integer', | ||||
63 | integer_minimum => 1, | ||||
64 | }, | ||||
65 | ); | ||||
66 | } | ||||
67 | |||||
68 | 1 | 2µs | # spent 9µs (7+2) within Perl::Critic::Policy::InputOutput::RequireBriefOpen::default_severity which was called:
# once (7µs+2µs) by Perl::Critic::Policy::get_severity at line 331 of Perl/Critic/Policy.pm | ||
69 | sub default_themes { return qw< core pbp maintenance > } | ||||
70 | sub applies_to { return 'PPI::Token::Word' } | ||||
71 | |||||
72 | #----------------------------------------------------------------------------- | ||||
73 | |||||
74 | sub violates { | ||||
75 | my ( $self, $elem, undef ) = @_; | ||||
76 | |||||
77 | # Is it a call to open? | ||||
78 | $OPEN_BUILTIN{$elem->content()} or return; | ||||
79 | return if ! is_function_call($elem); | ||||
80 | my @open_args = parse_arg_list($elem); | ||||
81 | return if 2 > @open_args; # not a valid call to open() | ||||
82 | |||||
83 | my ($is_lexical, $fh) = _get_opened_fh($open_args[0]); | ||||
84 | return if not $fh; | ||||
85 | return if $fh =~ m< \A [*]? STD (?: IN|OUT|ERR ) \z >xms; | ||||
86 | |||||
87 | for my $close_token ( $self->_find_close_invocations_or_return( | ||||
88 | $elem, $is_lexical ) ) { | ||||
89 | # The $close_token might be a close() or a return() | ||||
90 | # It doesn't matter which -- both satisfy this policy | ||||
91 | if (is_function_call($close_token)) { | ||||
92 | my @close_args = parse_arg_list($close_token); | ||||
93 | |||||
94 | my $close_parameter = $close_args[0]; | ||||
95 | if ('ARRAY' eq ref $close_parameter) { | ||||
96 | $close_parameter = ${$close_parameter}[0]; | ||||
97 | } | ||||
98 | if ( $close_parameter ) { | ||||
99 | $close_parameter = "$close_parameter"; | ||||
100 | return if $fh eq $close_parameter; | ||||
101 | |||||
102 | if ( any { m< \A [*] >xms } ($fh, $close_parameter) ) { | ||||
103 | (my $stripped_fh = $fh) =~ s< \A [*] ><>xms; | ||||
104 | (my $stripped_parameter = $close_parameter) =~ | ||||
105 | s< \A [*] ><>xms; | ||||
106 | |||||
107 | return if $stripped_fh eq $stripped_parameter; | ||||
108 | } | ||||
109 | } | ||||
110 | } | ||||
111 | elsif ($is_lexical && is_method_call($close_token)) { | ||||
112 | my $tok = $close_token->sprevious_sibling->sprevious_sibling; | ||||
113 | return if $fh eq $tok; | ||||
114 | } | ||||
115 | } | ||||
116 | |||||
117 | return $self->violation( $DESC, $EXPL, $elem ); | ||||
118 | } | ||||
119 | |||||
120 | sub _find_close_invocations_or_return { | ||||
121 | my ($self, $elem, $is_lexical) = @_; | ||||
122 | |||||
123 | my $parent = $self->_get_scope( $elem, $is_lexical ); | ||||
124 | return if !$parent; # I can't think of a scenario where this would happen | ||||
125 | |||||
126 | my $open_loc = $elem->location; | ||||
127 | # we don't actually allow _lines to be zero or undef, but maybe we will | ||||
128 | my $end_line = $self->{_lines} ? $open_loc->[0] + $self->{_lines} : undef; | ||||
129 | |||||
130 | my $closes = $parent->find(sub { | ||||
131 | ##no critic (ProhibitExplicitReturnUndef) | ||||
132 | my ($parent, $candidate) = @_; ## no critic(Variables::ProhibitReusedNames) | ||||
133 | return undef if $candidate->isa('PPI::Statement::Sub'); | ||||
134 | my $candidate_loc = $candidate->location; | ||||
135 | return undef if !defined $candidate_loc->[0]; | ||||
136 | return 0 if $candidate_loc->[0] < $open_loc->[0]; | ||||
137 | return 0 if $candidate_loc->[0] == $open_loc->[0] && $candidate_loc->[1] <= $open_loc->[1]; | ||||
138 | return undef if defined $end_line && $candidate_loc->[0] > $end_line; | ||||
139 | return 0 if !$candidate->isa('PPI::Token::Word'); | ||||
140 | return $CLOSE_BUILTIN{ $candidate->content() } || 0; | ||||
141 | }); | ||||
142 | return @{$closes || []}; | ||||
143 | } | ||||
144 | |||||
145 | sub _get_scope { | ||||
146 | my ( $self, $elem, $is_lexical ) = @_; | ||||
147 | |||||
148 | my $open_loc = $elem->location; | ||||
149 | my $end_line = ( $self->{_lines} && defined $open_loc->[0] ) ? | ||||
150 | $open_loc->[0] + $self->{_lines} : | ||||
151 | undef; | ||||
152 | |||||
153 | while ( my $dad = $elem->parent) { | ||||
154 | $elem = $dad; | ||||
155 | next if not $elem->scope; | ||||
156 | |||||
157 | # If we are analyzing something like 'open my $fh ...', the | ||||
158 | # most-local scope suffices. RT #64437 | ||||
159 | return $elem if $LOCAL_LEXICAL == $is_lexical; | ||||
160 | next if not defined $end_line; # Presume search everywhere | ||||
161 | |||||
162 | # If we are analyzing something like 'open $fh ...', 'open FH | ||||
163 | # ...', or 'open *FH ...' we need to use a scope that includes | ||||
164 | # the end of the legal range. We just give up and return the | ||||
165 | # current scope if we can not determine any of the locations | ||||
166 | # involved. RT #64437 | ||||
167 | return $elem if not $open_loc; | ||||
168 | my $elem_loc = $elem->location | ||||
169 | or return $elem; | ||||
170 | my $last_kid = $elem->child( $LAST_ELEMENT ) | ||||
171 | or return $elem; # What? no children? | ||||
172 | my $last_kid_loc = $last_kid->location | ||||
173 | or return $elem; | ||||
174 | # At this point, the scope we have, even if it is not the | ||||
175 | # correct scope for the file handle, is big enough that if the | ||||
176 | # corresponding close() is outside it, it must be a violation. | ||||
177 | # RT #64437 | ||||
178 | return $elem if $last_kid_loc->[0] > $end_line; | ||||
179 | } | ||||
180 | return $elem; # Whatever the top-level PPI::Node was. | ||||
181 | } | ||||
182 | |||||
183 | sub _get_opened_fh { | ||||
184 | my ($tokens) = shift; | ||||
185 | |||||
186 | my $is_lexical; | ||||
187 | my $fh; | ||||
188 | |||||
189 | if ( 2 == @{$tokens} ) { | ||||
190 | if ('my' eq $tokens->[0] && | ||||
191 | $tokens->[1]->isa('PPI::Token::Symbol') && | ||||
192 | $SCALAR_SIGIL eq $tokens->[1]->raw_type) { | ||||
193 | |||||
194 | $is_lexical = $LOCAL_LEXICAL; | ||||
195 | $fh = $tokens->[1]; | ||||
196 | } | ||||
197 | } | ||||
198 | elsif (1 == @{$tokens}) { | ||||
199 | my $argument = _unwrap_block( $tokens->[0] ); | ||||
200 | if ( $argument->isa('PPI::Token::Symbol') ) { | ||||
201 | my $sigil = $argument->raw_type(); | ||||
202 | if ($SCALAR_SIGIL eq $sigil) { | ||||
203 | $is_lexical = $NON_LOCAL_LEXICAL; # We need to | ||||
204 | # distinguish between | ||||
205 | # 'open my $fh ...' and | ||||
206 | # 'open $fh ...'. RT #64437 | ||||
207 | $fh = $argument; | ||||
208 | } | ||||
209 | elsif ($GLOB_SIGIL eq $sigil) { | ||||
210 | $is_lexical = $NOT_LEXICAL; | ||||
211 | $fh = $argument; | ||||
212 | } | ||||
213 | } | ||||
214 | elsif ($argument->isa('PPI::Token::Word') && $argument eq uc $argument) { | ||||
215 | $is_lexical = $NOT_LEXICAL; | ||||
216 | $fh = $argument; | ||||
217 | } | ||||
218 | } | ||||
219 | |||||
220 | return ($is_lexical, $fh); | ||||
221 | } | ||||
222 | |||||
223 | sub _unwrap_block { | ||||
224 | my ($element) = @_; | ||||
225 | |||||
226 | return $element if not $element->isa('PPI::Structure::Block'); | ||||
227 | |||||
228 | my @children = $element->schildren(); | ||||
229 | return $element if 1 != @children; | ||||
230 | my $child = $children[0]; | ||||
231 | |||||
232 | return $child if not $child->isa('PPI::Statement'); | ||||
233 | |||||
234 | my @grandchildren = $child->schildren(); | ||||
235 | return $element if 1 != @grandchildren; | ||||
236 | |||||
237 | return $grandchildren[0]; | ||||
238 | } | ||||
239 | |||||
240 | 1 | 7µs | 1; | ||
241 | |||||
242 | __END__ |