File Coverage

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


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