File Coverage

blib/lib/Perl/Critic/Policy/TryTiny/RequireBlockTermination.pm
Criterion Covered Total %
statement 30 31 96.7
branch 7 8 87.5
condition 16 21 76.1
subroutine 10 11 90.9
pod 4 5 80.0
total 67 76 88.1


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::TryTiny::RequireBlockTermination;
2 1     1   388075 use 5.008001;
  1         5  
3 1     1   7 use strict;
  1         2  
  1         21  
4 1     1   6 use warnings;
  1         2  
  1         38  
5             our $VERSION = '0.03';
6              
7 1     1   6 use Readonly;
  1         3  
  1         63  
8 1     1   8 use Perl::Critic::Utils qw{ :severities :classification :ppi };
  1         2  
  1         65  
9              
10 1     1   467 use base 'Perl::Critic::Policy';
  1         2  
  1         569  
11              
12             Readonly::Scalar my $DESC => q{A try/catch/finally not terminated};
13             Readonly::Scalar my $EXPL => q{Try::Tiny blocks must be terminated by either a semicolon or by residing at the end of a block};
14              
15 6     6 0 1788065 sub supported_parameters { return() }
16 3     3 1 169 sub default_severity { return $SEVERITY_HIGH }
17 0     0 1 0 sub default_themes { return qw( bugs ) }
18 6     6 1 670150 sub applies_to { return 'PPI::Token::Word' }
19              
20             sub violates {
21 22     22 1 1300 my ($self, $try) = @_;
22              
23 22 100       68 return unless $try->content() eq 'try';
24              
25 10         98 my $try_block = $try->snext_sibling();
26 10 50 33     362 return unless $try_block and $try_block->isa('PPI::Structure::Block');
27              
28 10         43 my $try_end = $try_block->snext_sibling();
29              
30             # Lets walk past any catch or finally blocks.
31 10   100     260 while (
      100        
      100        
      66        
      66        
32             $try_end and
33             $try_end->isa('PPI::Token::Word') and
34             ($try_end->content() eq 'catch' or $try_end->content() eq 'finally') and
35             $try_end->snext_sibling() and
36             $try_end->snext_sibling()->isa('PPI::Structure::Block')
37             ) {
38 8         593 $try_end = $try_end->snext_sibling->snext_sibling();
39             }
40              
41             # The try/catch/finally is the last statement in the block.
42 10 100       341 return if !$try_end;
43              
44             # There was a semicolon at the end.
45 6 100 66     39 return if $try_end->isa('PPI::Token::Structure')
46             and $try_end->content() eq ';';
47              
48 3         25 return $self->violation( $DESC, $EXPL, $try );
49             }
50              
51             1;
52             __END__
53              
54             =head1 NAME
55              
56             Perl::Critic::Policy::TryTiny::RequireBlockTermination - Requires that
57             try/catch/finally blocks are properly terminated.
58              
59             =head1 DESCRIPTION
60              
61             A common problem with L<Try::Tiny> is forgetting to put a semicolon after the
62             try/catch/finally block which can lead to difficul to debug issues. While
63             L<Try::Tiny> does do its best to detect this issue it cannot if the code after
64             the block returns an empty list.
65              
66             For example, this will fail:
67              
68             try { } catch { }
69             my $foo = 2;
70              
71             Since the C<my $foo=2> returns C<2> and C<try> throws an exception that an
72             unexpected argument was passed.
73              
74             But this will not fail:
75              
76             try { } catch { }
77             grep { ... } @some_empty_list;
78              
79             With the above the code after the try blocks produces an empty list. Lots of
80             different things produce empty lists. When this happens the code after the try
81             blocks is executed BEFORE the try blocks are executed since they are evaluated
82             as arguments to the try function!
83              
84             And this also does not fail:
85              
86             try { } catch { }
87             return()
88              
89             Flow control logic after the try blocks will execute before the try blocks are executed
90             for the same reason as the previous example.
91              
92             There is one situation (that the author is aware of) where non-terminated try blocks
93             makes sense.
94              
95             try { } catch { } if ...;
96              
97             In this case the code will run as expected, the if, when evaluating to true, will
98             cause the try blocks to be run, and if false they will not be run. Despite this
99             working this module fails on it. If this is something that you think is important
100             to support the author is happy to accept requests and patches.
101              
102             Note that this policy should be just as useful with other similar modules such as
103             L<Try::Catch> and L<TryCatch>.
104              
105             =head1 AUTHORS
106              
107             Aran Clary Deltac <bluefeet@gmail.com>
108              
109             =head1 ACKNOWLEDGEMENTS
110              
111             Thanks to L<ZipRecruiter|https://www.ziprecruiter.com/>
112             for encouraging their employees to contribute back to the open
113             source ecosystem. Without their dedication to quality software
114             development this distribution would not exist.
115              
116             =head1 LICENSE
117              
118             This library is free software; you can redistribute it and/or modify
119             it under the same terms as Perl itself.
120              
121             =cut
122