File: ProhibitFatCommaNewline.pm

package info (click to toggle)
libperl-critic-pulp-perl 96-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 1,684 kB
  • sloc: perl: 13,643; sh: 267; makefile: 6; ansic: 1
file content (270 lines) | stat: -rw-r--r-- 8,368 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# Copyright 2009, 2010, 2011, 2013, 2014, 2015, 2016, 2017 Kevin Ryde

# Perl-Critic-Pulp is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Perl-Critic-Pulp is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Perl-Critic-Pulp.  If not, see <http://www.gnu.org/licenses/>.


# use strict;
# $, = "\n";
# sub foo {
#   return 123;
# }
# sub x {
#     my %h = (-foo
#              => 'abc');
# print %h
#   }
# x();


package Perl::Critic::Policy::CodeLayout::ProhibitFatCommaNewline;
use 5.006;
use strict;
use warnings;
use version (); # but don't import qv()
use Perl::Critic::Utils;

# 1.084 for Perl::Critic::Document highest_explicit_perl_version()
use Perl::Critic::Policy 1.084;
use base 'Perl::Critic::Policy';

our $VERSION = 96;

# uncomment this to run the ### lines
# use Smart::Comments;


use constant supported_parameters => ();
use constant default_severity => $Perl::Critic::Utils::SEVERITY_MEDIUM;
use constant default_themes   => qw(pulp bugs);
use constant applies_to       => ('PPI::Token::Operator');

my $v5008 = version->new('5.008');

sub violates {
  my ($self, $elem, $document) = @_;

  $elem->content eq '=>'
    or return; # some other operator

  my $prev = $elem->sprevious_sibling || return;
  if (! $prev->isa('PPI::Token::Word')) {
    ### previous not a word, so => acts as a plain comma, ok ...
    return;
  }
  if (! _elems_any_newline_between ($prev, $elem)) {
    ### no newline before =>, ok ...
    return;
  }

  my $word = $prev->content;

  # A builtin is never quoted by newline fat comma.
  # PPI 1.213 gives a word "-print" where it should be a negate of a
  # print(), so check the word "sans dash".
  if (Perl::Critic::Utils::is_perl_builtin(_sans_dash($word))) {
    return $self->violation
      ("Fat comma after newline doesn't quote Perl builtin \"$word\"",
       '',
       $elem);
  }

  # In 5.8 up words are quoted by newline fat comma, so ok.
  if (defined (my $doc_version = $document->highest_explicit_perl_version)) {
    if ($doc_version >= $v5008) {
      return;
    }
  }

  # In 5.6 and earlier newline fat comma doesn't quote.
  return $self->violation
    ("Fat comma after newline doesn't quote preceding bareword \"$word\"",
     '',
     $elem);
}

# return $str stripped of a leading "-", if it has one
sub _sans_dash {
  my ($str) = @_;
  $str =~ s/^-//;
  return $str;
}

# $from and $to are PPI::Element
# Return true if there's a "\n" newline anywhere in between those elements,
# not including either $from or $to themselves.
sub _elems_any_newline_between {
  my ($from, $to) = @_;
  if ($from == $to) { return 0; }
  for (;;) {
    $from = $from->next_sibling || return 0;
    if ($from == $to) { return 0; }
    if ($from =~ /\n/) { return 1; }
  }
}

1;
__END__

=for stopwords Ryde bareword builtin Builtin builtins Builtins eg parens

=head1 NAME

Perl::Critic::Policy::CodeLayout::ProhibitFatCommaNewline - keep a fat comma on the same line as its quoted word

=head1 DESCRIPTION

This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
add-on.  It reports a newline between a fat comma and preceding bareword for
Perl builtins,

    my %h = (caller         # bad, builtin called as a function
             => 'abc');

And for all words when targeting Perl 5.6 and earlier,

    use 5.006;
    my %h = (foo            # bad, all words in perl 5.6 and earlier
             => 'def');

When there's a newline between the word and the fat comma like this the word
executes as a function call (builtins always, and also user defined in Perl
5.6 and earlier), giving its return value rather than a word string.

Such a return value is probably not what was intended and on that basis this
policy is under the "bugs" theme and medium severity (see
L<Perl::Critic/POLICY THEMES>).

=head2 Builtins

Perl builtin functions with a newline always execute and give their return
value rather than a the quoted word.

    my %h = (print          # bad, builtin print() executes
             => "abc");
    # %h is key "1" value "abc"

The builtin is called with no arguments and that might provoke a warning
from some, but others like C<print> will quietly run.

Dashed builtin names such as C<-print> are also function calls, with a
negate operator.

    my %h = (-print       # bad, print() call and negate
             => "123");
    # h is key "-1" value "123"

For the purposes of this policy the builtins are C<is_perl_builtin()> from
L<Perl::Critic::Utils>.  It's possible this is more builtins than the
particular Perl in use, but guarding against all will help if going to a
newer Perl in the future.

=head2 Non-Builtins

In Perl 5.6 and earlier all words C<foo> execute as a function call when
there's a newline before the fat comma.

    sub foo {
      return 123
    }
    my %h = (foo
             => "def");
    # in Perl 5.6 and earlier %h is key "123" value "def"

Under C<use strict> an error is thrown if no such function, in the usual
way.  A word builtin is a function call if it exists (with a warning about
being interpreted that way), or a bareword if not.

This policy prohibits all words with newline before fat comma when targeting
Perl 5.6 or earlier.  This means either an explicit C<use 5.006> or smaller,
or no such minimum C<use> at all.

One subtle way an executing word with newline before fat comma can go
undetected (in 5.6 and earlier still) is an accidental redefinition of a
constant,

    use constant FOO => "blah";
    use constant FOO
      => "some value";
    # makes a constant subr called blah (in Perl 5.6)

C<constant.pm> might reject some return values from C<FOO()>, eg. a number,
but a string like "blah" here quietly expands and creates a constant
C<blah()>.

The difference between Perl 5.6 and later Perl is that in 5.6 the parser
only looked as far as a newline for a possible quoting C<=E<gt>> fat comma.
In Perl 5.8 and later for non-builtins the lookahead continues beyond any
newlines and comments.  For Perl builtins the behaviour is the same, in all
versions the lookahead stops at the newline.

=head2 Avoiding Problems

Putting the fat comma on the same line as the word ensures it quotes in all
cases.

    my %h = (-print =>    # ok, fat comma on same line quotes
             "123");

If for layout purposes you do want a newline then the suggestion is to give
a string or perhaps a parenthesized expression since that doesn't rely on
the C<=E<gt>> fat comma quoting.  A fat comma can still emphasize a
key/value pair.

    my %h = ('print'      # ok, string
             =>
             123);

Alternately if instead a function call is really what's intended (builtin or
otherwise) then parens can be used in the normal way to ensure it's a call
(as per L<perltrap> the rule being "if it looks like a function, it is a
function").

    my %h = (foo()        # ok, function call
             =>
             123);

=head2 Disabling

As always if you don't care about this then you can disable
C<ProhibitFatCommaNewline> from your F<.perlcriticrc> in the usual
way (see L<Perl::Critic/CONFIGURATION>),

    [-CodeLayout::ProhibitFatCommaNewline]

=head1 SEE ALSO

L<Perl::Critic::Pulp>, L<Perl::Critic>, L<perlop>

=head1 HOME PAGE

L<http://user42.tuxfamily.org/perl-critic-pulp/index.html>

=head1 COPYRIGHT

Copyright 2011, 2013, 2014, 2015, 2016, 2017 Kevin Ryde

Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

Perl-Critic-Pulp is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
Perl-Critic-Pulp.  If not, see <http://www.gnu.org/licenses/>.

=cut