File Coverage

blib/lib/Perl/Critic/Policy/TryTiny/RequireBlockTermination.pm
Criterion Covered Total %
statement 28 29 96.5
branch 7 8 87.5
condition 16 21 76.1
subroutine 9 10 90.0
pod 4 5 80.0
total 64 73 87.6


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