File Coverage

blib/lib/Perl/Critic/Policy/Miscellanea/TextDomainUnused.pm
Criterion Covered Total %
statement 51 51 100.0
branch 11 12 91.6
condition 9 15 60.0
subroutine 18 18 100.0
pod 1 1 100.0
total 90 97 92.7


line stmt bran cond sub pod time code
1             # Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021 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             package Perl::Critic::Policy::Miscellanea::TextDomainUnused;
20 40     40   41290 use 5.006;
  40         162  
21 40     40   230 use strict;
  40         98  
  40         869  
22 40     40   225 use warnings;
  40         107  
  40         1161  
23              
24 40     40   213 use base 'Perl::Critic::Policy';
  40         98  
  40         6126  
25 40     40   191328 use Perl::Critic::Utils qw(is_function_call);
  40         167  
  40         3087  
26              
27             our $VERSION = 99;
28              
29 40     40   429 use constant supported_parameters => ();
  40         119  
  40         3003  
30 40     40   305 use constant default_severity => $Perl::Critic::Utils::SEVERITY_LOW;
  40         100  
  40         2536  
31 40     40   277 use constant default_themes => qw(pulp cosmetic);
  40         113  
  40         2488  
32 40     40   265 use constant applies_to => 'PPI::Document';
  40         100  
  40         26605  
33              
34             sub violates {
35 14     14 1 653202 my ($self, $elem, $document) = @_;
36              
37 14   50     83 my $use = _find_use_locale_textdomain($document) || return;
38 14 100       1018 if (_any_calls_locale_textdomain($document)) { return; }
  7         3306  
39 7 100       74 if (_any_vars_locale_textdomain($document)) { return; }
  2         251  
40 5 100       79 if (_any_strings_locale_textdomain($document)) { return; }
  3         55  
41              
42 2         33 return $self->violation
43             ('Locale::TextDomain imported, but none of its functions used',
44             '',
45             $use);
46             }
47              
48             # return a PPI::Statement::Include which is a "use" or "require" of
49             # Locale::TextDomain, or return false if there's no such
50             sub _find_use_locale_textdomain {
51 14     14   53 my ($document) = @_;
52 14   50     55 my $aref = $document->find ('PPI::Statement::Include')
53             || return; # if no includes at all
54 14 50 50 14   93 return List::Util::first { $_->type ne 'no'
55             && ($_->module||'') eq 'Locale::TextDomain'
56 14         337 } @$aref;
57             }
58              
59              
60             # The following qw() copied from @Locale::TextDomain::EXPORT of libintl-perl
61             # 1.18, with $__ %__ moved to %vars below. __p and friends are new in 1.17,
62             # but no need to check that.
63             #
64             my %funcs = map {($_=>1)}
65             qw(__ __x __n __nx __xn __p __px __np __npx
66             N__ N__n N__p N__np);
67              
68             # and also as full names "Locale::TextDomain::__"
69             foreach (keys %funcs) {
70             $funcs{"Locale::TextDomain::$_"} = 1;
71             }
72              
73             # return true if $document has any of the Locale::TextDomain functions used,
74             # like __() etc
75             sub _any_calls_locale_textdomain {
76 14     14   58 my ($document) = @_;
77 14   50     69 my $aref = $document->find ('PPI::Token::Word')
78             || return; # if no word tokens at all
79 50 100   50   238 return List::Util::first { $funcs{$_->content}
80             && is_function_call($_)
81 14         269 } @$aref;
82             }
83              
84             ## no critic (RequireInterpolationOfMetachars)
85             my %vars = ('$__' => 1,
86             '%__' => 1);
87             ## use critic
88              
89             sub _any_vars_locale_textdomain {
90 7     7   30 my ($document) = @_;
91 7   100     29 my $aref = $document->find ('PPI::Token::Symbol')
92             || return; # if no symbols at all
93 2     2   32 return List::Util::first { $vars{$_->symbol} } @$aref;
  2         16  
94             }
95             sub _any_strings_locale_textdomain {
96 5     5   20 my ($document) = @_;
97 5   50     16 my $aref = $document->find ('PPI::Token::Quote')
98             || return; # if no strings at all
99 9 100 66 9   144 return List::Util::first { ($_->isa('PPI::Token::Quote::Interpolate')
100             || $_->isa('PPI::Token::Quote::Double'))
101 5         76 && $_->string =~ /\$__(\W|$)/ } @$aref;
102             }
103              
104             1;
105             __END__
106              
107             =for stopwords textdomain perlcritic TextDomainUnused eg TextDomain PPI Ryde
108              
109             =head1 NAME
110              
111             Perl::Critic::Policy::Miscellanea::TextDomainUnused - check for Locale::TextDomain imported but unused
112              
113             =head1 DESCRIPTION
114              
115             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
116             add-on. It reports when you have L<C<Locale::TextDomain>|Locale::TextDomain>
117             like
118              
119             use Locale::TextDomain ('MyMessageDomain');
120              
121             but then don't use any of its functions or variables
122              
123             __ __x __n __nx __xn
124             __p __px __np __npx
125             N__ N__n N__p N__np
126             %__ $__
127              
128             C<Locale::TextDomain> is not needed when not used, but it's also not
129             actively harmful so this policy is only low severity and under the
130             C<cosmetic> theme (see L<Perl::Critic/POLICY THEMES>).
131              
132             The check is good if you've got C<Locale::TextDomain> as boilerplate code in
133             most of your program, but in some modules it's unused. You can remove it
134             entirely from non-interactive modules, or comment it out from modules which
135             might have messages but don't yet. The best thing picked up is when your
136             boilerplate has got into a programmatic module which shouldn't say anything
137             at the user level.
138              
139             The saving from removing unused C<Locale::TextDomain> is modest, just some
140             imports and a hash entry holding the "textdomain" for the package.
141              
142             It's easy to imagine a general kind of "module imported but unused" policy
143             check, but in practice its hard for perlcritic to know the automatic imports
144             of every module, and quite a few modules have side-effects, so this
145             TextDomainUnused policy just starts with one case of an unused include.
146              
147             =head2 Interpolated Variables
148              
149             The variables C<%__> and C<$__> are recognised in double-quote interpolated
150             strings just by looking for a C<$__> somewhere in the string, eg.
151              
152             print "*** $__{'A Message'} ***\n"; # ok
153              
154             It's not hard to trick the recognition with escapes, or a hash slice style,
155             but in general taking any C<$__> to be a TextDomain use is close enough.
156             (Perhaps in the future PPI will do a full parse of interpolated
157             expressions.)
158              
159             =head1 SEE ALSO
160              
161             L<Perl::Critic::Pulp>,
162             L<Perl::Critic>,
163             L<Locale::TextDomain>,
164             L<Perl::Critic::Policy::Miscellanea::TextDomainPlaceholders>
165              
166             =head1 HOME PAGE
167              
168             L<http://user42.tuxfamily.org/perl-critic-pulp/index.html>
169              
170             =head1 COPYRIGHT
171              
172             Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019, 2021 Kevin Ryde
173              
174             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
175             under the terms of the GNU General Public License as published by the Free
176             Software Foundation; either version 3, or (at your option) any later
177             version.
178              
179             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
180             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
181             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
182             more details.
183              
184             You should have received a copy of the GNU General Public License along with
185             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
186              
187             =cut