File Coverage

blib/lib/Perl/Critic/Policy/Documentation/ProhibitUnbalancedParens.pm
Criterion Covered Total %
statement 94 95 98.9
branch 30 32 93.7
condition 11 14 78.5
subroutine 17 17 100.0
pod 1 1 100.0
total 153 159 96.2


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 Kevin Ryde
2              
3             # This file is part of Perl-Critic-Pulp.
4              
5             # Perl-Critic-Pulp is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Perl-Critic-Pulp is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
17              
18              
19             # perlcritic -s ProhibitUnbalancedParens ProhibitUnbalancedParens.pm
20              
21             # unclosed:
22             # perlcritic -s ProhibitUnbalancedParens /usr/share/perl/5.12/CGI.pm
23              
24             # smiley close:
25             # perlcritic -s ProhibitUnbalancedParens /usr/share/perl5/accessors.pm
26              
27              
28             package Perl::Critic::Policy::Documentation::ProhibitUnbalancedParens;
29 40     40   26879 use 5.006;
  40         140  
30 40     40   189 use strict;
  40         84  
  40         724  
31 40     40   172 use warnings;
  40         78  
  40         970  
32 40     40   292 use base 'Perl::Critic::Policy';
  40         77  
  40         6771  
33 40     40   140677 use Perl::Critic::Utils;
  40         86  
  40         578  
34              
35             # uncomment this to run the ### lines
36             # use Smart::Comments;
37              
38             our $VERSION = 97;
39              
40 40     40   31507 use constant supported_parameters => ();
  40         82  
  40         2266  
41 40     40   213 use constant default_severity => $Perl::Critic::Utils::SEVERITY_LOW;
  40         79  
  40         1985  
42 40     40   211 use constant default_themes => qw(pulp cosmetic);
  40         79  
  40         1949  
43 40     40   204 use constant applies_to => 'PPI::Document';
  40         85  
  40         4522  
44              
45             sub violates {
46 75     75 1 718352 my ($self, $elem, $document) = @_;
47             ### ProhibitUnbalancedParens on: $elem->content
48              
49 75         869 my $parser = Perl::Critic::Pulp::PodParser::ProhibitUnbalancedParens->new
50             (policy => $self);
51 75         271 $parser->parse_from_elem ($elem);
52 75         326 return $parser->violations;
53             }
54              
55             package Perl::Critic::Pulp::PodParser::ProhibitUnbalancedParens;
56 40     40   264 use strict;
  40         88  
  40         936  
57 40     40   191 use warnings;
  40         83  
  40         1069  
58 40     40   14749 use Pod::ParseLink;
  40         26745  
  40         1858  
59 40     40   230 use base 'Perl::Critic::Pulp::PodParser';
  40         80  
  40         32274  
60              
61             sub command {
62 79     79   7102 my $self = shift;
63 79         201 my ($command, $text, $linenum, $paraobj) = @_;
64 79         317 $self->SUPER::command(@_); # maintain 'in_begin'
65              
66 79 100 66     232 if ($command eq 'for'
67             && $text =~ /^ProhibitUnbalancedParens\b\s*(.*)/) {
68 4         10 my $directive = $1;
69             ### $directive
70 4 50       18 if ($directive =~ /^allow next( (\d+))?/) {
71             # numbered "allow next 5" means up to that many following
72             # unnumbered "allow next" means one following
73 4 100       16 $self->{'allow_next'} = (defined $2 ? $2 : 1);
74             }
75             }
76 79         224 return $self->command_as_textblock(@_);
77             }
78              
79             my %open_to_close = ('(' => ')',
80             '[' => ']',
81             '{' => '}');
82             my %close_to_open = reverse %open_to_close;
83              
84             sub textblock {
85 152     152   4584 my ($self, $text, $linenum, $paraobj) = @_;
86             ### textblock: "linenum=$linenum"
87              
88 152 100 100     607 if (($self->{'allow_next'}||0) > 0) {
89 6         10 $self->{'allow_next'}--;
90 6         60 return '';
91             }
92              
93             # process outside =begin, and inside =begin which is ":" markup
94 146 100 66     349 unless ($self->{'in_begin'} eq '' || $self->{'in_begin'} =~ /^:/) {
95 1         49 return '';
96             }
97              
98 145         5850 my $interpolated = $self->interpolate($text, $linenum);
99             ### $text
100             ### $interpolated
101              
102 145         346 my @parens;
103 145         1127 while ($interpolated
104             =~ m/
105             ([][({})]) # $1 open or close
106             |([:;]-?\) # $2 smiley face optional close
107             |\b[a-zA-Z1-9]\) # "middle a) or 1) item"
108             |(?<!\$)\$\) # perlvar $), and not $$
109             )
110             |(["'])[][(){}]+\3 # $3 "(" quoted
111             |[:;]-?[(] # smiley face not an open
112             |(?<!\$)\$\$ # perlvar $$ consumed
113             |\$\(\w*\) # makefile var $(abc)
114             |\$\[\w*\] # perhaps template $[abc]
115             |(?<!\$)\$[][(] # perlvars $[, $(, $], and not $$
116             |^\s*(\d+|[A-Za-z])\.?\) # initial "1.5) something"
117             /xg) {
118             ### match: $&
119             ### $1
120             ### $2
121             ### $3
122 117 100       406 if (defined $1) {
    100          
123 65         518 push @parens, { char => $1,
124             pos => pos($interpolated)-1,
125             };
126              
127             } elsif (defined $2) {
128 25         164 push @parens, { char => ')',
129             pos => pos($interpolated)-1,
130             optional => 1,
131             };
132             }
133             }
134             ### @parens
135              
136             # sort optional closes to after hard closes
137             {
138 145         251 my @optional;
  145         209  
139             my @new;
140 145         242 foreach my $p (@parens) {
141 90 100 100     200 if (@optional && $optional[0]->{'char'} ne $p->{'char'}) {
142 5         9 push @new, splice @optional;
143             }
144 90 100       177 if ($p->{'optional'}) {
145 25         40 push @optional, $p;
146             } else {
147 65         115 push @new, $p;
148             }
149             }
150 145         274 @parens = (@new, @optional);
151             }
152             ### sorted: @parens
153              
154 145         218 my @opens;
155 145         238 foreach my $p (@parens) {
156             ### $p
157 90         451 my $char = $p->{'char'};
158 90 100       179 if (my $want_openchar = $close_to_open{$char}) {
159             # a close
160 42 100       87 if (my $o = pop @opens) {
161 28         44 my $openchar = $o->{'char'};
162 28 100       73 if ($openchar ne $want_openchar) {
163 2 50       6 if ($p->{'optional'}) {
164             ### mismatched optional close, skip
165 2         4 push @opens, $o;
166 2         5 next;
167             }
168             $self->violation_at_linenum_and_textpos
169             ("Mismatched closing paren \"$char\" expected \"$open_to_close{$openchar}\"",
170 0         0 $linenum, $interpolated, $p->{'pos'});
171             }
172              
173             } else {
174 14 100       31 if ($p->{'optional'}) {
175             ### unopened optional close, skip
176 12         24 next;
177             }
178             $self->violation_at_linenum_and_textpos
179             ("Unopened close paren \"$char\"",
180 2         14 $linenum, $interpolated, $p->{'pos'});
181             }
182              
183             } else {
184             # an open
185 48         90 push @opens, $p;
186             }
187             }
188 145         219 foreach my $p (@opens) {
189             $self->violation_at_linenum_and_textpos
190             ("Unclosed paren \"$p->{'char'}\"",
191 22         126 $linenum, $interpolated, $p->{'pos'});
192             }
193 145         1841 return '';
194             }
195              
196             *interior_sequence = \&interior_sequence_as_displayed_noncode_text;
197              
198             sub interior_sequence_as_displayed_noncode_text {
199 12     12   41 my ($self, $cmd, $text, $pod_seq) = @_;
200              
201 12 100 66     62 if ($cmd eq 'X' || $cmd eq 'C') {
    100          
202             ### $cmd
203             ### X,C keep only the newlines: $text
204 7         17 $text =~ tr/\n//cd;
205              
206             } elsif ($cmd eq 'L') {
207 3         15 my ($display, $inferred, $name, $section, $type)
208             = Pod::ParseLink::parselink ($text);
209             ### $text
210             ### $display
211             ### $inferred
212             ### $name
213 3         238 return $inferred; # the display part, or the name part if no display
214             }
215 9         319 return $text;
216             }
217              
218             1;
219             __END__
220              
221             =for stopwords Ryde paren parens ie deref there'd backslashing Parens
222              
223             =head1 NAME
224              
225             Perl::Critic::Policy::Documentation::ProhibitUnbalancedParens - don't leave an open bracket or paren
226              
227             =head1 DESCRIPTION
228              
229             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
230             add-on. It reports unbalanced or mismatched parentheses, brackets and braces
231             in POD text paragraphs,
232              
233             Blah blah (and something. # bad
234              
235             Blah blah ( [ ). # bad
236              
237             Blah blah brace }. # bad
238              
239             This is only cosmetic and normally only a minor irritant to readability so
240             this policy is low severity and under the "cosmetic" theme (see
241             L<Perl::Critic/POLICY THEMES>).
242              
243             Text and command paragraphs are checked, but verbatim paragraphs can have
244             anything. There are some exceptions to paren balancing. The intention is
245             to be forgiving of common or reasonable constructs. Currently this means,
246              
247             =over
248              
249             =item *
250              
251             Anything in C<CE<lt>E<gt>> code markup is ignored
252              
253             =for ProhibitVerbatimMarkup allow next
254              
255             In code C<anything [ is allowed>. # ok
256              
257             Perhaps this will change, though there'd have to be extra exceptions in
258             C<CE<lt>E<gt>>, such as various backslashing.
259              
260             Sometimes a prematurely ending C<CE<lt>E<gt>> may look like an unbalanced
261             paren, for example
262              
263             =for ProhibitVerbatimMarkup allow next
264              
265             Call C<foo(key=>value)> ... # bad
266              
267             =for ProhibitUnbalancedParens allow next
268              
269             This is bad because the C<CE<lt>E<gt>> ends at the C<=E<gt>>, leaving
270             "value)" unbalanced plain text. This is an easy mistake to make. (The
271             author's C<perl-pod-gt.el> can show warning face on this in Emacs.)
272              
273             =item *
274              
275             Quoted "(" is taken to be describing the char and is not an open or close.
276              
277             Any of "(" or '[' or "[{]". # ok
278              
279             This only applies to quoted parens alone (one or more), not larger quoted
280             text.
281              
282             =item *
283              
284             Item parens
285              
286             a) the first thing, or b) the second thing # ok
287              
288             1) one, 2) two # ok
289              
290             Exactly how much is recognised as an "a)" etc is not quite settled. In the
291             current code a "1.5)" is recognised at the start of a paragraph, but in the
292             middle only "1)" style.
293              
294             =item *
295              
296             Smiley faces are an "optional" close,
297              
298             (Some thing :-). # ok
299              
300             Bare smiley :). # ok
301              
302             (Or smile :-) and also close.) # ok
303              
304             =item *
305              
306             Sad smiley faces are not an opening paren,
307              
308             :( :-(. # ok
309              
310             =item *
311              
312             Perl variables C<$(> and C<$[> are not opening parens,
313              
314             Default is group $( blah blah. # ok
315              
316             C<${> brace is still an open and expected to have a matching close, because
317             it's likely to be a deref or delimiter,
318              
319             Deref with ${foo()} etc etc.
320              
321             Variables or expressions like this will often be in C<CE<lt>E<gt>> markup
322             and skipped for that reason instead, as described above.
323              
324             =item *
325              
326             C<$)> and C<$]> are optional closes, since they might be Perl variables to
327             skip, or might be "$" at the end of a parens,
328              
329             blah blah (which in TeX is $1\cdot2$).
330              
331             Perhaps the conditions for these will be restricted a bit, though again
332             C<CE<lt>E<gt>> markup around sample code like this will be usual.
333              
334             =item *
335              
336             C<LE<lt>display|linkE<gt>> links are processed as the "display" text part.
337             The link target (POD document name and section) can have anything.
338              
339             =back
340              
341             C<=begin :foo> ... C<=end :foo> sections with a format name ":foo" starting
342             with a ":" are POD markup and are processed accordingly. Other C<=begin>
343             sections are skipped.
344              
345             =head2 Unrecognised Forms
346              
347             A mathematical half-open range like the following is not recognised.
348              
349             [1,2) # bad, currently
350              
351             Perhaps just numbers like this would be unambiguous, but if it's an
352             expression then it's hard to distinguish a parens typo from some
353             mathematics. The suggestion for now is an C<=for> per below to flag it as
354             an exception. Another way would be to write S<1 E<lt>= X E<lt> 2>, which
355             might be clearer to mathematically unsophisticated readers.
356              
357             Parens spanning multiple paragraphs are not recognised,
358              
359             (This is some # bad
360              
361             thing.) # bad
362              
363             Hopefully this is uncommon, and probably better style not to be
364             parenthetical about something so big that it runs to multiple paragraphs or
365             has a verbatim block in the middle etc.
366              
367             =head2 Disabling
368              
369             If an unbalanced paren is intended you can add an C<=for> to tell
370             C<ProhibitUnbalancedParens> to allow it.
371              
372             =for ProhibitUnbalancedParens allow next
373              
374             Something ( deliberately unclosed.
375              
376             Or with a count of paragraphs to ignore,
377              
378             =for ProhibitUnbalancedParens allow next 2
379              
380             First deliberate [ unclosed.
381              
382             Second (.
383              
384             The usual no critic
385              
386             ## no critic (ProhibitUnbalancedParens)
387              
388             works too as a whole-file disable, but the annotation must be before any
389             C<__END__> token, and if the POD is after the C<__END__> then
390             C<Perl::Critic> 1.112 up is required. Individual C<=for> has the advantage
391             of being with an exception.
392              
393             As always if you don't care about this at all you can disable
394             C<ProhibitUnbalancedParens> completely from your F<.perlcriticrc> in
395             the usual way (see L<Perl::Critic/CONFIGURATION>),
396              
397             [-Documentation::ProhibitUnbalancedParens]
398              
399             =head1 SEE ALSO
400              
401             L<Perl::Critic::Pulp>,
402             L<Perl::Critic>
403              
404             L<http://user42.tuxfamily.org/perl-pod-gt/index.html>
405              
406             =head1 HOME PAGE
407              
408             http://user42.tuxfamily.org/perl-critic-pulp/index.html
409              
410             =head1 COPYRIGHT
411              
412             Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017 Kevin Ryde
413              
414             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
415             under the terms of the GNU General Public License as published by the Free
416             Software Foundation; either version 3, or (at your option) any later
417             version.
418              
419             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
420             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
421             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
422             more details.
423              
424             You should have received a copy of the GNU General Public License along with
425             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
426              
427             =cut