File Coverage

blib/lib/Perl/Critic/Policy/Subroutines/ProtectPrivateSubs.pm
Criterion Covered Total %
statement 47 55 85.4
branch 9 24 37.5
condition 6 15 40.0
subroutine 15 15 100.0
pod 4 5 80.0
total 81 114 71.0


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Subroutines::ProtectPrivateSubs;
2              
3 40     40   27306 use 5.010001;
  40         168  
4              
5 40     40   225 use strict;
  40         113  
  40         833  
6 40     40   216 use warnings;
  40         126  
  40         1218  
7              
8 40     40   234 use English qw< $EVAL_ERROR -no_match_vars >;
  40         109  
  40         239  
9 40     40   4278 use Readonly;
  40         119  
  40         2275  
10              
11 40         2084 use Perl::Critic::Utils qw<
12             :severities $EMPTY is_function_call is_method_call hashify
13 40     40   294 >;
  40         122  
14 40     40   6531 use parent 'Perl::Critic::Policy';
  40         110  
  40         240  
15              
16             our $VERSION = '1.150';
17              
18             #-----------------------------------------------------------------------------
19              
20             Readonly::Scalar my $DESC => q<Private subroutine/method used>;
21             Readonly::Scalar my $EXPL => q<Use published APIs>;
22              
23             #-----------------------------------------------------------------------------
24              
25             sub supported_parameters {
26             return (
27             {
28 91     91 0 3142 name => 'private_name_regex',
29             description => 'Pattern that determines what a private subroutine is.',
30             default_string => '\b_\w+\b', ## no critic (RequireInterpolationOfMetachars)
31             behavior => 'string',
32             parser => \&_parse_private_name_regex,
33             },
34             {
35             name => 'allow',
36             description =>
37             q<Subroutines matching the private name regex to allow under this policy.>,
38             default_string => $EMPTY,
39             behavior => 'string list',
40             list_always_present_values => [ qw<
41             POSIX::_PC_CHOWN_RESTRICTED
42             POSIX::_PC_LINK_MAX
43             POSIX::_PC_MAX_CANON
44             POSIX::_PC_MAX_INPUT
45             POSIX::_PC_NAME_MAX
46             POSIX::_PC_NO_TRUNC
47             POSIX::_PC_PATH_MAX
48             POSIX::_PC_PIPE_BUF
49             POSIX::_PC_VDISABLE
50             POSIX::_POSIX_ARG_MAX
51             POSIX::_POSIX_CHILD_MAX
52             POSIX::_POSIX_CHOWN_RESTRICTED
53             POSIX::_POSIX_JOB_CONTROL
54             POSIX::_POSIX_LINK_MAX
55             POSIX::_POSIX_MAX_CANON
56             POSIX::_POSIX_MAX_INPUT
57             POSIX::_POSIX_NAME_MAX
58             POSIX::_POSIX_NGROUPS_MAX
59             POSIX::_POSIX_NO_TRUNC
60             POSIX::_POSIX_OPEN_MAX
61             POSIX::_POSIX_PATH_MAX
62             POSIX::_POSIX_PIPE_BUF
63             POSIX::_POSIX_SAVED_IDS
64             POSIX::_POSIX_SSIZE_MAX
65             POSIX::_POSIX_STREAM_MAX
66             POSIX::_POSIX_TZNAME_MAX
67             POSIX::_POSIX_VDISABLE
68             POSIX::_POSIX_VERSION
69             POSIX::_SC_ARG_MAX
70             POSIX::_SC_CHILD_MAX
71             POSIX::_SC_CLK_TCK
72             POSIX::_SC_JOB_CONTROL
73             POSIX::_SC_NGROUPS_MAX
74             POSIX::_SC_OPEN_MAX
75             POSIX::_SC_PAGESIZE
76             POSIX::_SC_SAVED_IDS
77             POSIX::_SC_STREAM_MAX
78             POSIX::_SC_TZNAME_MAX
79             POSIX::_SC_VERSION
80             POSIX::_exit
81             > ],
82             },
83             );
84             }
85              
86 74     74 1 324 sub default_severity { return $SEVERITY_MEDIUM }
87 74     74 1 341 sub default_themes { return qw( core maintenance certrule ) }
88 30     30 1 87 sub applies_to { return 'PPI::Token::Word' }
89              
90             #-----------------------------------------------------------------------------
91              
92             sub _parse_private_name_regex {
93 89     89   353 my ($self, $parameter, $config_string) = @_;
94              
95 89   66     671 $config_string //= $parameter->get_default_string();
96              
97 89         276 my $regex;
98 89 50       269 eval { $regex = qr/$config_string/; 1 } ## no critic (RegularExpressions)
  89         578  
  89         432  
99             or $self->throw_parameter_value_exception(
100             'private_name_regex',
101             $config_string,
102             undef,
103             "is not a valid regular expression: $EVAL_ERROR",
104             );
105              
106 89         499 $self->__set_parameter_value($parameter, $regex);
107              
108 89         295 return;
109             }
110              
111             #-----------------------------------------------------------------------------
112              
113             sub violates {
114 329     329 1 669 my ( $self, $elem, undef ) = @_;
115              
116 329 100       702 if ( my $prior = $elem->sprevious_sibling() ) {
117 114         2444 state $exceptions = { hashify( qw( package require use ) ) };
118 114 100       326 return if exists $exceptions->{ $prior->content() };
119             }
120              
121 246 50 33     3885 if (
122             $self->_is_other_pkg_private_function($elem)
123             or $self->_is_other_pkg_private_method($elem)
124             ) {
125 0         0 return $self->violation( $DESC, $EXPL, $elem );
126             }
127              
128 246         688 return; # ok!
129             }
130              
131             sub _is_other_pkg_private_function {
132 246     246   427 my ( $self, $elem ) = @_;
133              
134 246 100 66     562 return if ! is_method_call($elem) && ! is_function_call($elem);
135              
136 179         401 my $private_name_regex = $self->{_private_name_regex};
137 179         391 my $content = $elem->content();
138             return
139             $content =~ m< \w+::$private_name_regex \z >xms
140             && $content !~ m< \A SUPER::$private_name_regex \z >xms
141 179   33     1453 && ! $self->{_allow}{$content};
142             }
143              
144             sub _is_other_pkg_private_method {
145 246     246   1446 my ( $self, $elem ) = @_;
146              
147 246         403 my $private_name_regex = $self->{_private_name_regex};
148 246         482 my $content = $elem->content();
149              
150             # look for structures like "Some::Package->_foo()"
151 246 50       1644 return if $content !~ m< \A $private_name_regex \z >xms;
152 0 0         my $operator = $elem->sprevious_sibling() or return;
153 0 0         return if $operator->content() ne q[->];
154              
155 0 0         my $package = $operator->sprevious_sibling() or return;
156 0 0         return if not $package->isa('PPI::Token::Word');
157              
158             # sometimes the previous sib is a keyword, as in:
159             # shift->_private_method(); This is typically used as
160             # shorthand for "my $self=shift; $self->_private_method()"
161 0 0 0       return if $package->content() eq 'shift'
162             or $package->content() eq '__PACKAGE__';
163              
164             # Maybe the user wanted to exempt this explicitly.
165 0 0         return if $self->{_allow}{"${package}::$content"};
166              
167 0           return 1;
168             }
169              
170             1;
171              
172             __END__
173              
174             #-----------------------------------------------------------------------------
175              
176             =pod
177              
178             =head1 NAME
179              
180             Perl::Critic::Policy::Subroutines::ProtectPrivateSubs - Prevent access to private subs in other packages.
181              
182              
183             =head1 AFFILIATION
184              
185             This Policy is part of the core L<Perl::Critic|Perl::Critic>
186             distribution.
187              
188              
189             =head1 DESCRIPTION
190              
191             By convention Perl authors (like authors in many other languages)
192             indicate private methods and variables by inserting a leading
193             underscore before the identifier. This policy catches attempts to
194             access private variables from outside the package itself.
195              
196             The subroutines in the L<POSIX|POSIX> package which begin with an underscore
197             (e.g. C<POSIX::_POSIX_ARG_MAX>) are not flagged as errors by this
198             policy.
199              
200              
201             =head1 CONFIGURATION
202              
203             You can define what a private subroutine name looks like by specifying
204             a regular expression for the C<private_name_regex> option in your
205             F<.perlcriticrc>:
206              
207             [Subroutines::ProtectPrivateSubs]
208             private_name_regex = _(?!_)\w+
209              
210             The above example is a way of saying that subroutines that start with
211             a double underscore are not considered to be private. (Perl::Critic,
212             in its implementation, uses leading double underscores to indicate a
213             distribution-private subroutine-- one that is allowed to be invoked by
214             other Perl::Critic modules, but not by anything outside of
215             Perl::Critic.)
216              
217             You can configure additional subroutines to accept by specifying them
218             in a space-delimited list to the C<allow> option:
219              
220             [Subroutines::ProtectPrivateSubs]
221             allow = FOO::_bar FOO::_baz
222              
223             These are added to the default list of exemptions from this policy.
224             Allowing a subroutine also allows the corresponding method call. So
225             C<< FOO::_bar >> in the above example allows both C<< FOO::_bar() >>
226             and C<< FOO->_bar() >>.
227              
228              
229             =head1 HISTORY
230              
231             This policy is inspired by a similar test in L<B::Lint|B::Lint>.
232              
233              
234             =head1 BUGS
235              
236             Doesn't forbid C<< $pkg->_foo() >> because it can't tell the
237             difference between that and C<< $self->_foo() >>.
238              
239              
240             =head1 SEE ALSO
241              
242             L<Perl::Critic::Policy::Variables::ProtectPrivateVars|Perl::Critic::Policy::Variables::ProtectPrivateVars>
243              
244              
245             =head1 AUTHOR
246              
247             Chris Dolan <cdolan@cpan.org>
248              
249             =head1 COPYRIGHT
250              
251             Copyright (c) 2006-2011 Chris Dolan.
252              
253             This program is free software; you can redistribute it and/or modify
254             it under the same terms as Perl itself. The full text of this license
255             can be found in the LICENSE file included with this module.
256              
257             =cut
258              
259             # Local Variables:
260             # mode: cperl
261             # cperl-indent-level: 4
262             # fill-column: 78
263             # indent-tabs-mode: nil
264             # c-indentation-style: bsd
265             # End:
266             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :