File Coverage

blib/lib/Perl/Critic/Policy/ErrorHandling/RequireCheckingReturnValueOfEval.pm
Criterion Covered Total %
statement 26 125 20.8
branch 1 82 1.2
condition 0 36 0.0
subroutine 12 21 57.1
pod 4 5 80.0
total 43 269 15.9


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::ErrorHandling::RequireCheckingReturnValueOfEval;
2              
3 40     40   28362 use 5.010001;
  40         206  
4 40     40   297 use strict;
  40         123  
  40         1450  
5 40     40   233 use warnings;
  40         124  
  40         1014  
6              
7 40     40   250 use Readonly;
  40         121  
  40         2239  
8              
9 40     40   341 use Scalar::Util qw< refaddr >;
  40         117  
  40         2653  
10              
11 40         2259 use Perl::Critic::Utils qw< :booleans :characters :severities hashify
12 40     40   310 precedence_of >;
  40         149  
13 40     40   14057 use parent 'Perl::Critic::Policy';
  40         118  
  40         330  
14              
15             our $VERSION = '1.150';
16              
17             #-----------------------------------------------------------------------------
18              
19             Readonly::Scalar my $DESC => 'Return value of eval not tested.';
20             ## no critic (RequireInterpolationOfMetachars)
21             Readonly::Scalar my $EXPL =>
22             q<You can't depend upon the value of $@/$EVAL_ERROR to tell whether an eval failed.>;
23             ## use critic
24              
25             Readonly::Hash my %BOOLEAN_OPERATORS => hashify qw< || && // or and >;
26             Readonly::Hash my %POSTFIX_OPERATORS =>
27             hashify qw< for foreach if unless while until >;
28              
29             Readonly::Scalar my $PRECEDENCE_OF_EQUALS => precedence_of( q{=} );
30              
31             #-----------------------------------------------------------------------------
32              
33 89     89 0 1613 sub supported_parameters { return () }
34 74     74 1 326 sub default_severity { return $SEVERITY_MEDIUM }
35 74     74 1 303 sub default_themes { return qw( core bugs ) }
36 1     1 1 4 sub applies_to { return 'PPI::Token::Word' }
37              
38             #-----------------------------------------------------------------------------
39              
40             sub violates {
41 9     9 1 18 my ( $self, $elem, undef ) = @_;
42              
43 9 50       20 return if $elem->content() ne 'eval';
44              
45 0 0         my $evaluated = $elem->snext_sibling() or return; # Nothing to eval!
46 0           my $following = $evaluated->snext_sibling();
47              
48 0 0         return if _is_returned( $elem ); # GitHub #324
49 0 0         return if _is_in_right_hand_side_of_assignment($elem);
50 0 0         return if _is_in_postfix_expression($elem);
51             return if
52 0 0         _is_in_correct_position_in_a_condition_or_foreach_loop_collection(
53             $elem,
54             $following,
55             );
56              
57 0 0         return if _scan_backwards_for_grep( $elem ); # RT 69489
58              
59 0 0 0       if ( $following and $following->isa('PPI::Token::Operator') ) {
60 0 0         return if $BOOLEAN_OPERATORS{ $following->content() };
61 0 0         return if q{?} eq $following->content;
62             }
63              
64 0           return $self->violation($DESC, $EXPL, $elem);
65             }
66              
67             #-----------------------------------------------------------------------------
68              
69             sub _is_in_right_hand_side_of_assignment {
70 0     0     my ($elem) = @_;
71              
72 0           my $previous = $elem->sprevious_sibling();
73              
74 0 0         if (not $previous) {
75 0           $previous =
76             _grandparent_for_is_in_right_hand_side_of_assignment($elem);
77             }
78              
79 0           while ($previous) {
80 0           my $base_previous = $previous;
81              
82             EQUALS_SCAN:
83 0           while ($previous) {
84 0 0         if ( $previous->isa('PPI::Token::Operator') ) {
85 0 0         return $TRUE if $previous->content() =~ m/= \Z/xms;
86 0 0         last EQUALS_SCAN if _is_effectively_a_comma($previous);
87             }
88 0           $previous = $previous->sprevious_sibling();
89             }
90              
91             $previous =
92 0           _grandparent_for_is_in_right_hand_side_of_assignment($base_previous);
93             }
94              
95 0           return;
96             }
97              
98             sub _grandparent_for_is_in_right_hand_side_of_assignment {
99 0     0     my ($elem) = @_;
100              
101 0 0         my $parent = $elem->parent() or return;
102 0 0         $parent->isa('PPI::Statement') or return;
103              
104 0 0         my $grandparent = $parent->parent() or return;
105              
106 0 0 0       if (
107             $grandparent->isa('PPI::Structure::Constructor')
108             or $grandparent->isa('PPI::Structure::List')
109             ) {
110 0           return $grandparent;
111             }
112              
113 0           return;
114             }
115              
116             #-----------------------------------------------------------------------------
117              
118             Readonly::Scalar my $CONDITION_POSITION_IN_C_STYLE_FOR_LOOP => 1;
119              
120             sub _is_in_correct_position_in_a_condition_or_foreach_loop_collection {
121 0     0     my ($elem, $following) = @_;
122              
123 0           my $parent = $elem->parent();
124 0           while ($parent) {
125 0 0         if ( $parent->isa('PPI::Structure::Condition') ) {
126             return
127 0           _is_in_correct_position_in_a_structure_condition(
128             $elem, $parent, $following,
129             );
130             }
131              
132             # TECHNICAL DEBT: This code is basically shared with
133             # ProhibitUnusedCapture. I don't want to put this code
134             # into Perl::Critic::Utils::*, but I don't have time to sort out
135             # PPIx::Utilities::Structure::List yet.
136 0 0 0       if (
137             $parent->isa('PPI::Structure::List')
138             and my $parent_statement = $parent->statement()
139             ) {
140 0 0 0       return $TRUE if
141             $parent_statement->isa('PPI::Statement::Compound')
142             and $parent_statement->type() eq 'foreach';
143             }
144              
145 0 0         if ( $parent->isa('PPI::Structure::For') ) {
146 0           my @for_loop_components = $parent->schildren();
147              
148 0 0         my $condition =
149             $for_loop_components[$CONDITION_POSITION_IN_C_STYLE_FOR_LOOP]
150             or return;
151              
152 0           return _descendant_of($elem, $condition);
153             }
154              
155 0           $parent = $parent->parent();
156             }
157              
158 0           return;
159             }
160              
161             sub _is_in_correct_position_in_a_structure_condition {
162 0     0     my ($elem, $parent, $following) = @_;
163              
164 0           my $level = $elem;
165 0   0       while ($level and refaddr $level != $parent) {
166 0 0         my $cursor = refaddr $elem == refaddr $level ? $following : $level;
167              
168             IS_FINAL_EXPRESSION_AT_DEPTH:
169 0           while ($cursor) {
170 0 0         if ( _is_effectively_a_comma($cursor) ) {
171 0           $cursor = $cursor->snext_sibling();
172 0           while ( _is_effectively_a_comma($cursor) ) {
173 0           $cursor = $cursor->snext_sibling();
174             }
175              
176             # Semicolon would be a syntax error here.
177 0 0         return if $cursor;
178 0           last IS_FINAL_EXPRESSION_AT_DEPTH;
179             }
180              
181 0           $cursor = $cursor->snext_sibling();
182             }
183              
184 0           my $statement = $level->parent();
185 0 0         return $TRUE if not $statement; # Shouldn't happen.
186 0 0         return $TRUE if not $statement->isa('PPI::Statement'); # Shouldn't happen.
187              
188 0           $level = $statement->parent();
189 0 0 0       if (
      0        
190             not $level
191             or (
192             not $level->isa('PPI::Structure::List')
193             and not $level->isa('PPI::Structure::Condition')
194             )
195             ) {
196             # Shouldn't happen.
197 0           return $TRUE;
198             }
199             }
200              
201 0           return $TRUE;
202             }
203              
204             # Replace with PPI implementation once it is released.
205             sub _descendant_of {
206 0     0     my ($cursor, $potential_ancestor) = @_;
207              
208 0 0         return $EMPTY if not $potential_ancestor;
209              
210 0           while ( refaddr $cursor != refaddr $potential_ancestor ) {
211 0 0         $cursor = $cursor->parent() or return $EMPTY;
212             }
213              
214 0           return 1;
215             }
216              
217             #-----------------------------------------------------------------------------
218              
219             sub _is_in_postfix_expression {
220 0     0     my ($elem) = @_;
221              
222 0           my $current_base = $elem;
223 0           while ($TRUE) {
224 0           my $previous = $current_base->sprevious_sibling();
225 0           while ($previous) {
226 0 0 0       if (
227             $previous->isa('PPI::Token::Word')
228             and $POSTFIX_OPERATORS{ $previous->content() }
229             ) {
230 0           return $TRUE;
231             }
232 0           $previous = $previous->sprevious_sibling();
233             } # end while
234              
235 0 0         my $parent = $current_base->parent() or return;
236 0 0         if ( $parent->isa('PPI::Statement') ) {
237 0 0         return if $parent->specialized();
238              
239 0 0         my $grandparent = $parent->parent() or return;
240 0 0         return if not $grandparent->isa('PPI::Structure::List');
241              
242 0           $current_base = $grandparent;
243             } else {
244 0           $current_base = $parent;
245             }
246              
247 0 0         return if not $current_base->isa('PPI::Structure::List');
248             }
249              
250 0           return;
251             }
252              
253             #-----------------------------------------------------------------------------
254              
255             sub _scan_backwards_for_grep {
256 0     0     my ( $elem ) = @_;
257              
258 0           while ( $elem ) {
259              
260 0           my $parent = $elem->parent();
261              
262 0           while ( $elem = $elem->sprevious_sibling() ) {
263 0 0 0       $elem->isa( 'PPI::Token::Word' )
264             and 'grep' eq $elem->content()
265             and return $TRUE;
266 0 0 0       $elem->isa( 'PPI::Token::Operator' )
267             and precedence_of( $elem ) >= $PRECEDENCE_OF_EQUALS
268             and return $FALSE;
269             }
270              
271 0           $elem = $parent;
272             }
273              
274 0           return $FALSE;
275              
276             }
277              
278             #-----------------------------------------------------------------------------
279              
280             sub _is_effectively_a_comma {
281 0     0     my ($elem) = @_;
282              
283 0 0         return if not $elem;
284              
285             return
286 0   0       $elem->isa('PPI::Token::Operator')
287             && (
288             $elem->content() eq $COMMA
289             || $elem->content() eq $FATCOMMA
290             );
291             }
292              
293             #-----------------------------------------------------------------------------
294             # GitHub #324 (https://github.com/Perl-Critic/Perl-Critic/issues/324)
295             {
296             Readonly::Scalar my $RETURN => 'return';
297              
298             sub _is_returned {
299 0     0     my ( $elem ) = @_;
300 0           my $prev = $elem->sprevious_sibling();
301             return
302 0   0       $prev
303             &&
304             $prev->isa( 'PPI::Token::Word' )
305             &&
306             $RETURN eq $prev->content();
307             }
308             }
309              
310             #-----------------------------------------------------------------------------
311              
312             1;
313              
314             __END__
315              
316             #-----------------------------------------------------------------------------
317              
318             =pod
319              
320             =for stopwords destructors
321              
322             =head1 NAME
323              
324             Perl::Critic::Policy::ErrorHandling::RequireCheckingReturnValueOfEval - You can't depend upon the value of C<$@>/C<$EVAL_ERROR> to tell whether an C<eval> failed.
325              
326             =head1 AFFILIATION
327              
328             This Policy is part of the core L<Perl::Critic|Perl::Critic>
329             distribution.
330              
331              
332             =head1 DESCRIPTION
333              
334             A common idiom in perl for dealing with possible errors is to use
335             C<eval> followed by a check of C<$@>/C<$EVAL_ERROR>:
336              
337             eval {
338             ...
339             };
340             if ($EVAL_ERROR) {
341             ...
342             }
343              
344             There's a problem with this: the value of C<$EVAL_ERROR> can change
345             between the end of the C<eval> and the C<if> statement. The issue is
346             object destructors:
347              
348             package Foo;
349              
350             ...
351              
352             sub DESTROY {
353             ...
354             eval { ... };
355             ...
356             }
357              
358             package main;
359              
360             eval {
361             my $foo = Foo->new();
362             ...
363             };
364             if ($EVAL_ERROR) {
365             ...
366             }
367              
368             Assuming there are no other references to C<$foo> created, when the
369             C<eval> block in C<main> is exited, C<Foo::DESTROY()> will be invoked,
370             regardless of whether the C<eval> finished normally or not. If the
371             C<eval> in C<main> fails, but the C<eval> in C<Foo::DESTROY()>
372             succeeds, then C<$EVAL_ERROR> will be empty by the time that the C<if>
373             is executed. Additional issues arise if you depend upon the exact
374             contents of C<$EVAL_ERROR> and both C<eval>s fail, because the
375             messages from both will be concatenated.
376              
377             Even if there isn't an C<eval> directly in the C<DESTROY()> method
378             code, it may invoke code that does use C<eval> or otherwise affects
379             C<$EVAL_ERROR>.
380              
381             The solution is to ensure that, upon normal exit, an C<eval> returns a
382             true value and to test that value:
383              
384             # Constructors are no problem.
385             my $object = eval { Class->new() };
386              
387             # To cover the possibility that an operation may correctly return a
388             # false value, end the block with "1":
389             if ( eval { something(); 1 } ) {
390             ...
391             }
392              
393             eval {
394             ...
395             1;
396             }
397             or do {
398             # Error handling here
399             };
400              
401             Unfortunately, you can't use the C<defined> function to test the
402             result; C<eval> returns an empty string on failure.
403              
404             Various modules have been written to take some of the pain out of
405             properly localizing and checking C<$@>/C<$EVAL_ERROR>. For example:
406              
407             use Try::Tiny;
408             try {
409             ...
410             } catch {
411             # Error handling here;
412             # The exception is in $_/$ARG, not $@/$EVAL_ERROR.
413             }; # Note semicolon.
414              
415             "But we don't use DESTROY() anywhere in our code!" you say. That may
416             be the case, but do any of the third-party modules you use have them?
417             What about any you may use in the future or updated versions of the
418             ones you already use?
419              
420              
421             =head1 CONFIGURATION
422              
423             This Policy is not configurable except for the standard options.
424              
425              
426             =head1 SEE ALSO
427              
428             For a nice, easy, non-magical way of properly handling exceptions, see
429             L<Try::Tiny|Try::Tiny>.
430              
431              
432             =head1 AUTHOR
433              
434             Elliot Shank C<< <perl@galumph.com> >>
435              
436             =head1 COPYRIGHT
437              
438             Copyright (c) 2008-2023 Elliot Shank.
439              
440             This program is free software; you can redistribute it and/or modify
441             it under the same terms as Perl itself. The full text of this license
442             can be found in the LICENSE file included with this module.
443              
444             =cut
445              
446             # Local Variables:
447             # mode: cperl
448             # cperl-indent-level: 4
449             # fill-column: 78
450             # indent-tabs-mode: nil
451             # c-indentation-style: bsd
452             # End:
453             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :