File Coverage

blib/lib/Perl/Critic/Policy/CodeLayout/ProhibitIfIfSameLine.pm
Criterion Covered Total %
statement 45 46 97.8
branch 11 12 91.6
condition 8 12 66.6
subroutine 12 12 100.0
pod 1 1 100.0
total 77 83 92.7


line stmt bran cond sub pod time code
1             # Copyright 2013, 2014, 2015, 2016, 2017 Kevin Ryde
2              
3             # Perl-Critic-Pulp is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by the
5             # Free Software Foundation; either version 3, or (at your option) any later
6             # version.
7             #
8             # Perl-Critic-Pulp is distributed in the hope that it will be useful, but
9             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11             # for more details.
12             #
13             # You should have received a copy of the GNU General Public License along
14             # with Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
15              
16              
17             # perlcritic -s ProhibitIfIfSameLine /usr/share/perl5/Pod/Simple.pm
18             # preceded by "return" so actually ok
19             # perlcritic -s ProhibitIfIfSameLine /usr/share/perl5/Tk/AbstractCanvas.pm
20             # two ifs one line
21              
22              
23             package Perl::Critic::Policy::CodeLayout::ProhibitIfIfSameLine;
24 40     40   25177 use 5.006;
  40         135  
25 40     40   230 use strict;
  40         76  
  40         667  
26 40     40   193 use warnings;
  40         71  
  40         1117  
27 40     40   713 use Perl::Critic::Utils;
  40         139702  
  40         513  
28              
29 40     40   30024 use base 'Perl::Critic::Policy';
  40         81  
  40         4979  
30              
31             our $VERSION = 97;
32              
33             # uncomment this to run the ### lines
34             # use Smart::Comments;
35              
36              
37 40     40   46154 use constant supported_parameters => ();
  40         86  
  40         2887  
38 40     40   224 use constant default_severity => $Perl::Critic::Utils::SEVERITY_MEDIUM;
  40         73  
  40         1965  
39 40     40   213 use constant default_themes => qw(pulp bugs);
  40         76  
  40         2095  
40 40     40   284 use constant applies_to => ('PPI::Statement::Compound');
  40         98  
  40         13811  
41              
42             my %compound_type_is_if = (if => 1,
43             unless => 1);
44              
45             sub violates {
46 30     30 1 580494 my ($self, $elem, $document) = @_;
47             ### ProhibitIfIfSameLine elem: "$elem"
48             ### type: $elem->type
49              
50 30 100       75 unless (_compound_statement_is_if($elem)) {
51             ### not an "if" ...
52 14         343 return;
53             }
54              
55 16 50       398 if (_elems_any_separator ($elem->child(0), $elem->schild(0))) {
56             ### leading whitespace in elem itself, so ok ...
57 0         0 return;
58             }
59              
60 16   100     49 my $prev = $elem->sprevious_sibling || return;
61 8 100 66     239 unless ($prev->isa('PPI::Statement::Compound')
62             && $compound_type_is_if{$prev->type}) {
63             ### not preceded by an "if", so ok ...
64 4         157 return;
65             }
66              
67 4 100       114 if (_elems_any_separator ($prev->next_sibling, $elem)) {
68             ### newlines after previous statement, so ok ...
69 1         4 return;
70             }
71              
72 3         17 return $self->violation
73             ('Put a newline in "} if (x)" so it doesn\'t look like possible \"elsif\"',
74             '',
75             $elem);
76             }
77              
78             # $elem is a PPI::Statement::Compound
79             # Return true if it's an "if" statement.
80             # Note this is not simply $elem->type eq "if", since type "if" includes
81             # "unless" statements, but _compound_statement_is_if() is true only on "if"
82             # statements.
83             #
84             sub _compound_statement_is_if {
85 30     30   50 my ($elem) = @_;
86 30   50     70 return (($elem->schild(0)||'') eq 'if');
87             }
88              
89             # Return true if there is a suitable separator in $from or its following
90             # elements up to $to, but not including $to.
91             #
92             sub _elems_any_separator {
93 20     20   290 my ($from, $to) = @_;
94 20         28 for (;;) {
95 24 100       116 if ($from == $to) {
96 19         125 return 0;
97             }
98 5 100 66     27 if ($from =~ /\n/
99             || $from->isa('PPI::Statement::Null')) {
100 1         14 return 1;
101             }
102 4   50     107 $from = $from->next_sibling || return 0;
103             }
104             }
105              
106             1;
107             __END__
108              
109             =for stopwords Ryde
110              
111             =head1 NAME
112              
113             Perl::Critic::Policy::CodeLayout::ProhibitIfIfSameLine - don't put if after if on same line
114              
115             =head1 DESCRIPTION
116              
117             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
118             add-on. It asks you to not to write an C<if> statement on the same line as
119             a preceding C<if>.
120              
121             if ($x) {
122             ...
123             } if ($y) { # bad
124             ...
125             }
126              
127             if ($x) {
128             ...
129             } elsif ($y) { # was "elsif" intended ?
130             ...
131             }
132              
133             The idea is that an C<if> in the layout of an C<elsif> may be either a
134             mistake or will be confusing to a human reader. On that basis this policy
135             is under the "bugs" theme and medium severity (see L<Perl::Critic/POLICY
136             THEMES>).
137              
138             =head2 Unless
139              
140             An C<unless...if> is treated the same. Perl allows C<unless ... elsif> and
141             so the same potential confusion with an C<elsif> layout arises.
142              
143             unless ($x) {
144             ...
145             } if ($y) { # bad
146             ...
147             }
148              
149             unless ($x) {
150             ...
151             } elsif ($y) { # maybe meant to be "elsif" like this ?
152             ...
153             }
154              
155             Whether C<unless ... elsif> is a good idea at all is another matter.
156             Sometimes it suits a combination of conditions.
157              
158             =head2 Statement Modifiers
159              
160             This policy only applies to a statement followed by a statement. An C<if>
161             as a statement modifier is not affected. It's usual to put that on the same
162             line as the statement it modifies.
163              
164             do {
165             ...
166             } if ($x); # ok, statement modifier
167              
168             =head2 All One Line
169              
170             Two C<if> statements written on the same line will trigger the policy.
171              
172             if(1){one;} if(2){two;} # bad
173              
174             Perhaps there could be an exception or option when both statements are
175             entirely on the one line, or some such, for code which is trying to be
176             compact.
177              
178             =head2 Disabling
179              
180             As always if you don't care about this then you can disable
181             C<ProhibitIfIfSameLine> from your F<.perlcriticrc> (see
182             L<Perl::Critic/CONFIGURATION>),
183              
184             [-CodeLayout::ProhibitIfIfSameLine]
185              
186             =head1 SEE ALSO
187              
188             L<Perl::Critic::Pulp>, L<Perl::Critic>
189              
190             L<Perl::Critic::Policy::ControlStructures::ProhibitCascadingIfElse>,
191             L<Perl::Critic::Policy::ControlStructures::ProhibitUnlessBlocks>
192              
193             =head1 HOME PAGE
194              
195             L<http://user42.tuxfamily.org/perl-critic-pulp/index.html>
196              
197             =head1 COPYRIGHT
198              
199             Copyright 2013, 2014, 2015, 2016, 2017 Kevin Ryde
200              
201             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
202             under the terms of the GNU General Public License as published by the Free
203             Software Foundation; either version 3, or (at your option) any later
204             version.
205              
206             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
207             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
208             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
209             more details.
210              
211             You should have received a copy of the GNU General Public License along with
212             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
213              
214             =cut