File Coverage

blib/lib/Perl/Critic/Policy/ErrorHandling/RequireCarping.pm
Criterion Covered Total %
statement 29 108 26.8
branch 1 62 1.6
condition 0 33 0.0
subroutine 13 24 54.1
pod 4 5 80.0
total 47 232 20.2


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::ErrorHandling::RequireCarping;
2              
3 40     40   29111 use 5.010001;
  40         168  
4 40     40   267 use strict;
  40         104  
  40         996  
5 40     40   219 use warnings;
  40         141  
  40         1062  
6 40     40   228 use Readonly;
  40         97  
  40         2101  
7              
8 40     40   307 use List::SomeUtils qw(any);
  40         93  
  40         2125  
9 40         2092 use Perl::Critic::Utils qw{
10             :booleans :characters :severities :classification :data_conversion
11 40     40   310 };
  40         155  
12 40     40   23450 use Perl::Critic::Utils::PPI qw{ is_ppi_expression_or_generic_statement };
  40         110  
  40         2247  
13 40     40   328 use parent 'Perl::Critic::Policy';
  40         138  
  40         261  
14              
15             our $VERSION = '1.150';
16              
17             #-----------------------------------------------------------------------------
18              
19             Readonly::Scalar my $EXPL => [ 283 ];
20              
21             Readonly::Hash my %ALTERNATIVES => (
22             warn => 'carp',
23             die => 'croak',
24             );
25              
26             #-----------------------------------------------------------------------------
27              
28             sub supported_parameters {
29             return (
30             {
31 91     91 0 2207 name => 'allow_messages_ending_with_newlines',
32             description => q{Don't complain about die or warn if the message ends in a newline.},
33             default_string => '1',
34             behavior => 'boolean',
35             },
36             {
37             name => 'allow_in_main_unless_in_subroutine',
38             description => q{Don't complain about die or warn in main::, unless in a subroutine.},
39             default_string => '0',
40             behavior => 'boolean',
41             },
42             );
43             }
44              
45 74     74 1 307 sub default_severity { return $SEVERITY_MEDIUM }
46 86     86 1 361 sub default_themes { return qw( core pbp maintenance certrule ) }
47 30     30 1 76 sub applies_to { return 'PPI::Token::Word' }
48              
49             #-----------------------------------------------------------------------------
50              
51             sub violates {
52 329     329 1 540 my ( $self, $elem, undef ) = @_;
53              
54 329 50       552 my $alternative = $ALTERNATIVES{$elem} or return;
55              
56 0 0         return if ! is_function_call($elem);
57              
58 0 0         if ($self->{_allow_messages_ending_with_newlines}) {
59 0 0         return if _last_flattened_argument_list_element_ends_in_newline($elem);
60             }
61              
62             return if $self->{_allow_in_main_unless_in_subroutine}
63 0 0 0       && !$self->_is_element_contained_in_subroutine( $elem )
      0        
64             && $self->_is_element_in_namespace_main( $elem ); # RT #56619
65              
66 0           my $desc = qq{"$elem" used instead of "$alternative"};
67 0           return $self->violation( $desc, $EXPL, $elem );
68             }
69              
70             #-----------------------------------------------------------------------------
71              
72             sub _last_flattened_argument_list_element_ends_in_newline {
73 0     0     my $die_or_warn = shift;
74              
75 0 0         my $last_flattened_argument =
76             _find_last_flattened_argument_list_element($die_or_warn)
77             or return $FALSE;
78              
79 0 0         if ( $last_flattened_argument->isa('PPI::Token::Quote') ) {
    0          
80 0           my $last_flattened_argument_string =
81             $last_flattened_argument->string();
82 0 0 0       if (
      0        
      0        
83             $last_flattened_argument_string =~ m{ \n \z }xms
84             or (
85             (
86             $last_flattened_argument->isa('PPI::Token::Quote::Double')
87             or $last_flattened_argument->isa('PPI::Token::Quote::Interpolate')
88             )
89             and $last_flattened_argument_string =~ m{ [\\] n \z }xms
90             )
91             ) {
92 0           return $TRUE;
93             }
94             }
95             elsif ( $last_flattened_argument->isa('PPI::Token::HereDoc') ) {
96 0           return $TRUE;
97             }
98              
99 0           return $FALSE;
100             }
101              
102             #-----------------------------------------------------------------------------
103             # Here starts the fun. Explanation by example:
104             #
105             # Let's say we've got the following (contrived) statement:
106             #
107             # die q{Isn't }, ( $this, ( " fun?\n" ) , ) if "It isn't Monday.";
108             #
109             # This statement should pass because the last parameter that die is going to
110             # get is C<" fun?\n">.
111             #
112             # The approach is to first find the last non-flattened parameter. If this
113             # is a simple token, we're done. Else, it's some aggregate thing. We can't
114             # tell what C<some_function( "foo\n" )> is going to do, so we give up on
115             # anything other than a PPI::Structure::List.
116             #
117             # There are three possible scenarios for the children of a List:
118             #
119             # * No children of the List, i.e. the list looks like C< ( ) >.
120             # * One PPI::Statement::Expression element.
121             # * One PPI::Statement element. That's right, an instance of the base
122             # statement class and not some subclass. *sigh*
123             #
124             # In the first case, we're done. The latter two cases get treated
125             # identically. We get the last child of the Statement and start the search
126             # all over again.
127             #
128             # Back to our example. The PPI tree for this expression is
129             #
130             # PPI::Document
131             # PPI::Statement
132             # PPI::Token::Word 'die'
133             # PPI::Token::Quote::Literal 'q{Isn't }'
134             # PPI::Token::Operator ','
135             # PPI::Structure::List ( ... )
136             # PPI::Statement::Expression
137             # PPI::Token::Symbol '$this'
138             # PPI::Token::Operator ','
139             # PPI::Structure::List ( ... )
140             # PPI::Statement::Expression
141             # PPI::Token::Quote::Double '" fun?\n"'
142             # PPI::Token::Operator ','
143             # PPI::Token::Word 'if'
144             # PPI::Token::Quote::Double '"It isn't Monday.\n"'
145             # PPI::Token::Structure ';'
146             #
147             # We're starting with the Word containing 'die' (it could just as well be
148             # 'warn') because the earlier parts of validate() have taken care of any
149             # other possibility. We're going to scan forward through 'die's siblings
150             # until we reach what we think the end of its parameters are. So we get
151             #
152             # 1. A Literal. A perfectly good argument.
153             # 2. A comma operator. Looks like we've got more to go.
154             # 3. A List. Another argument.
155             # 4. The Word 'if'. Oops. That's a postfix operator.
156             #
157             # Thus, the last parameter is the List. So, we've got to scan backwards
158             # through the components of the List; again, the goal is to find the last
159             # value in the flattened list.
160             #
161             # Before descending into the List, we check that it isn't a subroutine call by
162             # looking at its prior sibling. In this case, the prior sibling is a comma
163             # operator, so it's fine.
164             #
165             # The List has one Expression element as we expect. We grab the Expression's
166             # last child and start all over again.
167             #
168             # 1. The last child is a comma operator, which Perl will ignore, so we
169             # skip it.
170             # 2. The comma's prior sibling is a List. This is the last significant
171             # part of the outer list.
172             # 3. The List's prior sibling isn't a Word, so we can continue because the
173             # List is not a parameter list.
174             # 4. We go through the child Expression and find that the last child of
175             # that is a PPI::Token::Quote::Double, which is a simple, non-compound
176             # token. We return that and we're done.
177              
178             sub _find_last_flattened_argument_list_element {
179 0     0     my $die_or_warn = shift;
180              
181             # Zoom forward...
182 0           my $current_candidate =
183             _find_last_element_in_subexpression($die_or_warn);
184              
185             # ... scan back.
186 0   0       while (
      0        
187             $current_candidate
188             and not _is_simple_list_element_token( $current_candidate )
189             and not _is_complex_expression_token( $current_candidate )
190             ) {
191 0 0         if ( $current_candidate->isa('PPI::Structure::List') ) {
    0          
192 0           $current_candidate =
193             _determine_if_list_is_a_plain_list_and_get_last_child(
194             $current_candidate,
195             $die_or_warn
196             );
197             } elsif ( not $current_candidate->isa('PPI::Token') ) {
198 0           return;
199             } else {
200 0           $current_candidate = $current_candidate->sprevious_sibling();
201             }
202             }
203              
204 0 0         return if not $current_candidate;
205 0 0         return if _is_complex_expression_token( $current_candidate );
206              
207 0           my $penultimate_element = $current_candidate->sprevious_sibling();
208 0 0         if ($penultimate_element) {
209             # Bail if we've got a Word in front of the Element that isn't
210             # the original 'die' or 'warn' or anything else that isn't
211             # a comma or dot operator.
212 0 0         if ( $penultimate_element->isa('PPI::Token::Operator') ) {
    0          
213 0 0 0       if (
214             $penultimate_element ne $COMMA
215             and $penultimate_element ne $PERIOD
216             ) {
217 0           return;
218             }
219             } elsif ( $penultimate_element != $die_or_warn ) {
220 0           return;
221             }
222             }
223              
224 0           return $current_candidate;
225             }
226              
227             #-----------------------------------------------------------------------------
228             # This is the part where we scan forward from the 'die' or 'warn' to find
229             # the last argument.
230              
231             sub _find_last_element_in_subexpression {
232 0     0     my $die_or_warn = shift;
233              
234 0           my $last_following_sibling;
235 0           my $next_sibling = $die_or_warn;
236 0   0       while (
237             $next_sibling = $next_sibling->snext_sibling()
238             and not _is_postfix_operator( $next_sibling )
239             ) {
240 0           $last_following_sibling = $next_sibling;
241             }
242              
243 0           return $last_following_sibling;
244             }
245              
246             #-----------------------------------------------------------------------------
247             # Ensure that the list isn't a parameter list. Find the last element of it.
248              
249             sub _determine_if_list_is_a_plain_list_and_get_last_child {
250 0     0     my ($list, $die_or_warn) = @_;
251              
252 0           my $prior_sibling = $list->sprevious_sibling();
253              
254 0 0         if ( $prior_sibling ) {
255             # Bail if we've got a Word in front of the List that isn't
256             # the original 'die' or 'warn' or anything else that isn't
257             # a comma operator.
258 0 0         if ( $prior_sibling->isa('PPI::Token::Operator') ) {
    0          
259 0 0         if ( $prior_sibling ne $COMMA ) {
260 0           return;
261             }
262             } elsif ( $prior_sibling != $die_or_warn ) {
263 0           return;
264             }
265             }
266              
267 0           my @list_children = $list->schildren();
268              
269             # If zero children, nothing to look for.
270             # If multiple children, then PPI is not giving us
271             # anything we understand.
272 0 0         return if scalar (@list_children) != 1;
273              
274 0           my $list_child = $list_children[0];
275              
276             # If the child isn't an Expression or it is some other subclass
277             # of Statement, we again don't understand PPI's output.
278 0 0         return if not is_ppi_expression_or_generic_statement($list_child);
279              
280 0           my @statement_children = $list_child->schildren();
281 0 0         return if scalar (@statement_children) < 1;
282              
283 0           return $statement_children[-1];
284             }
285              
286              
287             #-----------------------------------------------------------------------------
288              
289             sub _is_postfix_operator {
290 0     0     my $element = shift;
291              
292 0           state $postfix_operators = { hashify qw( if unless while until for foreach ) };
293              
294 0   0       return $element->isa('PPI::Token::Word') && $postfix_operators->{$element};
295             }
296              
297              
298             Readonly::Array my @SIMPLE_LIST_ELEMENT_TOKEN_CLASSES =>
299             qw{
300             PPI::Token::Number
301             PPI::Token::Word
302             PPI::Token::DashedWord
303             PPI::Token::Symbol
304             PPI::Token::Quote
305             PPI::Token::HereDoc
306             };
307              
308             sub _is_simple_list_element_token {
309 0     0     my $element = shift;
310              
311 0 0         return $FALSE if not $element->isa('PPI::Token');
312              
313 0     0     return any { $element->isa($_) } @SIMPLE_LIST_ELEMENT_TOKEN_CLASSES;
  0            
314             }
315              
316              
317             #-----------------------------------------------------------------------------
318             # Tokens that can't possibly be part of an expression simple
319             # enough for us to examine.
320              
321             Readonly::Array my @COMPLEX_EXPRESSION_TOKEN_CLASSES =>
322             qw{
323             PPI::Token::ArrayIndex
324             PPI::Token::QuoteLike
325             PPI::Token::Regexp
326             PPI::Token::Cast
327             PPI::Token::Label
328             PPI::Token::Separator
329             PPI::Token::Data
330             PPI::Token::End
331             PPI::Token::Prototype
332             PPI::Token::Attribute
333             PPI::Token::Unknown
334             };
335              
336             sub _is_complex_expression_token {
337 0     0     my $element = shift;
338              
339 0 0         return $FALSE if not $element->isa('PPI::Token');
340              
341 0     0     return any { $element->isa($_) } @COMPLEX_EXPRESSION_TOKEN_CLASSES;
  0            
342             }
343              
344             #-----------------------------------------------------------------------------
345             # Check whether the given element is contained in a subroutine.
346              
347             sub _is_element_contained_in_subroutine {
348 0     0     my ( $self, $elem ) = @_;
349              
350 0           my $parent = $elem;
351 0           while ( $parent = $parent->parent() ) {
352 0 0         $parent->isa( 'PPI::Statement::Sub' ) and return $TRUE;
353 0 0         $parent->isa( 'PPI::Structure::Block' ) or next;
354 0 0         my $prior_elem = $parent->sprevious_sibling() or next;
355 0 0 0       $prior_elem->isa( 'PPI::Token::Word' )
356             and 'sub' eq $prior_elem->content()
357             and return $TRUE;
358             }
359              
360 0           return $FALSE;
361             }
362              
363             #-----------------------------------------------------------------------------
364             # Check whether the given element is in main::
365              
366             sub _is_element_in_namespace_main {
367 0     0     my ( $self, $elem ) = @_;
368 0           my $current_elem = $elem;
369 0           my $prior_elem;
370              
371 0           while ( $current_elem ) {
372 0           while ( $prior_elem = $current_elem->sprevious_sibling() ) {
373 0 0         if ( $prior_elem->isa( 'PPI::Statement::Package' ) ) {
374 0           return 'main' eq $prior_elem->namespace();
375             }
376             } continue {
377 0           $current_elem = $prior_elem;
378             }
379 0           $current_elem = $current_elem->parent();
380             }
381              
382 0           return $TRUE;
383             }
384              
385             1;
386              
387             __END__
388              
389             #-----------------------------------------------------------------------------
390              
391             =pod
392              
393             =head1 NAME
394              
395             Perl::Critic::Policy::ErrorHandling::RequireCarping - Use functions from L<Carp|Carp> instead of C<warn> or C<die>.
396              
397             =head1 AFFILIATION
398              
399             This Policy is part of the core L<Perl::Critic|Perl::Critic>
400             distribution.
401              
402              
403             =head1 DESCRIPTION
404              
405             The C<die> and C<warn> functions both report the file and line number
406             where the exception occurred. But if someone else is using your
407             subroutine, they usually don't care where B<your> code blew up.
408             Instead, they want to know where B<their> code invoked the subroutine.
409             The L<Carp|Carp> module provides alternative methods that report the
410             exception from the caller's file and line number.
411              
412             By default, this policy will not complain about C<die> or C<warn>, if
413             it can determine that the message will always result in a terminal
414             newline. Since perl suppresses file names and line numbers in this
415             situation, it is assumed that no stack traces are desired either and
416             none of the L<Carp|Carp> functions are necessary.
417              
418             die "oops" if $explosion; #not ok
419             warn "Where? Where?!" if $tiger; #not ok
420              
421             open my $mouth, '<', 'food'
422             or die 'of starvation'; #not ok
423              
424             if (! $dentist_appointment) {
425             warn "You have bad breath!\n"; #ok
426             }
427              
428             die "$clock not set.\n" if $no_time; #ok
429              
430             my $message = "$clock not set.\n";
431             die $message if $no_time; #not ok, not obvious
432              
433              
434             =head1 CONFIGURATION
435              
436             By default, this policy allows uses of C<die> and C<warn> ending in an
437             explicit newline. If you give this policy an
438             C<allow_messages_ending_with_newlines> option in your F<.perlcriticrc>
439             with a false value, then this policy will prohibit such uses.
440              
441             [ErrorHandling::RequireCarping]
442             allow_messages_ending_with_newlines = 0
443              
444             If you give this policy an C<allow_in_main_unless_in_subroutine> option
445             in your F<.perlcriticrc> with a true value, then this policy will allow
446             C<die> and C<warn> in name space main:: unless they appear in a
447             subroutine, even if they do not end in an explicit newline.
448              
449             [ErrorHandling::RequireCarping]
450             allow_in_main_unless_in_subroutine = 1
451              
452             =head1 BUGS
453              
454             Should allow C<die> when it is obvious that the "message" is a reference.
455              
456              
457             =head1 SEE ALSO
458              
459             L<Carp::Always|Carp::Always>
460              
461              
462             =head1 AUTHOR
463              
464             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
465              
466              
467             =head1 COPYRIGHT
468              
469             Copyright (c) 2005-2023 Imaginative Software Systems. All rights reserved.
470              
471             This program is free software; you can redistribute it and/or modify
472             it under the same terms as Perl itself. The full text of this license
473             can be found in the LICENSE file included with this module.
474              
475             =cut
476              
477             # Local Variables:
478             # mode: cperl
479             # cperl-indent-level: 4
480             # fill-column: 78
481             # indent-tabs-mode: nil
482             # c-indentation-style: bsd
483             # End:
484             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :