File Coverage

blib/lib/warnings/everywhere.pm
Criterion Covered Total %
statement 80 132 60.6
branch 23 50 46.0
condition 4 6 66.6
subroutine 19 24 79.1
pod 4 4 100.0
total 130 216 60.1


line stmt bran cond sub pod time code
1             package warnings::everywhere;
2              
3 8     8   10189 use 5.008;
  8         20  
4 8     8   27 use strict;
  8         8  
  8         127  
5 8     8   27 use warnings;
  8         7  
  8         196  
6 8     8   22 no warnings qw(uninitialized);
  8         9  
  8         238  
7 8     8   25 use Carp;
  8         9  
  8         454  
8 8     8   35 use File::Spec;
  8         8  
  8         9748  
9              
10             our $VERSION = '0.024';
11             $VERSION = eval $VERSION;
12              
13             sub import {
14 7     7   27 my $package = shift;
15 7         135 for my $category (@_) {
16 0 0       0 if (!ref($category)) {
17 0         0 enable_warning_category($category);
18             }
19             }
20             }
21              
22             sub unimport {
23 7     7   1256 my $package = shift;
24 7         13 for my $args (@_) {
25 10 100       18 if (ref($args)) {
26 4         6 $package->_check_import_argument($args);
27 0         0 $package->_thwart_modules(%$args);
28             } else {
29 6         10 disable_warning_category($args);
30             }
31             }
32             }
33              
34             sub _check_import_argument {
35 4     4   4 my ($package, $argument) = @_;
36              
37 4 50       6 return if !ref($argument);
38 4 100       9 if (ref($argument) ne 'HASH') {
39 1         159 croak "Unexpected import argument $argument";
40             }
41 3 100 66     10 if (!exists $argument->{warning} || !exists $argument->{thwart_module}) {
42 1         99 croak "Argument keys must include warning and thwart_module";
43             }
44 2 100 66     10 if ( ref($argument->{thwart_module})
45             && ref($argument->{thwart_module}) ne 'ARRAY')
46             {
47 1         86 croak "Argument thwart_module should be a scalar or an arrayref";
48             }
49 1 50       1 _check_warning_category($argument->{warning}) or die;
50             }
51              
52             =head1 NAME
53              
54             warnings::everywhere - a way of ensuring consistent global warning settings
55              
56             =head1 VERSION
57              
58             This is version 0.022.
59              
60             =head1 SYNOPSIS
61              
62             # Turn off run-time warnings
63             use strict;
64             use warnings;
65             no warnings::anywhere qw(uninitialized);
66            
67             use Module::That::Spits::Out::Warnings;
68             use Other::Unnecessarily::Chatty::Module;
69              
70             use warnings::everywhere qw(uninitialized);
71             # Write your own bondage-and-discipline code that really, really
72             # cares about the difference between undef and the empty string
73            
74             # Stop "helpful" modules from turning compile-time warnings back on again
75             use strict;
76             use warnings;
77             no warnings::anywhere {
78             warning => 'experimental::smartmatch',
79             thwart_module => [qw(Moose Moo Dancer Dancer2 Test::Class::Moose)],
80             };
81             use Module::That::Might::Pull::In::Moose::Or::Moo::Or::Who::Knows::What;
82            
83             given (shift @ARGV) {
84             ...
85             default {
86             print STDERR "# I'll fix it in a moment, OK?\n";
87             }
88             }
89              
90             =head1 DESCRIPTION
91              
92             Warnings are great - in your own code. Tools like prove, and libraries
93             like Moose and Modern::Perl, turn them on for you so you can spot things
94             like ambiguous syntax, variables you only used once, deprecated syntax
95             and other useful things.
96              
97             By default C turns on all warnings, including some that
98             you might not care about, like uninitialised variables. You could explicitly
99             say
100              
101             use warnings;
102             no warnings qw(uninitialized);
103              
104             or you could use a module like C which disables some warnings
105             and makes others fatal, or you could roll your own system. Either way,
106             for your own code, there are plenty of ways around unwanted warnings.
107              
108             Not so for other code, though.
109              
110             The test suite at $WORK produces a large number of 'use of uninitialized
111             variable' warnings from (at the last count) four separate modules. Some of
112             them are because warnings got switched on for that module,
113             even though the module itself didn't say anything about warnings
114             (probably because the test suite was run with prove).
115             Others are there because the module explicitly said C, and
116             then proceeded to blithely throw around variables without checking whether
117             they were defined first.
118              
119             Either way, this isn't my code, and it's not something I'm going to fix.
120             These warnings are just spam.
121              
122             Similarly, if you disable e.g. experimental::smartmatch because you know that
123             you're using smartmatch, and you're not going to be using a version of
124             Perl that has a version of smartmatch that behaves differently, you might
125             get those warnings enabled back again by a module such as Moose or Dancer
126             which turns all warnings on.
127              
128             This is where warnings::everywhere comes in.
129              
130             =head2 Usage
131              
132             =head3 Run-time warnings
133              
134             At its simplest, say
135              
136             use warnings::everywhere qw(all);
137              
138             and all modules imported from there onwards will have all warnings switched
139             on. Modules imported previously will be unaffected. You can turn specific
140             warnings off by saying e.g.
141              
142             no warnings::everywhere qw(uninitialized);
143              
144             or, depending on how frustrated and/or grammatically-sensitive you happen
145             to be feeling,
146              
147             no warnings::anywhere qw(uninitialized);
148              
149             or
150              
151             no goddamn::warnings::anywhere qw(uninitialized);
152              
153             Parameters are the same as C: a list of categories
154             as per L, where C means all warnings.
155              
156             =head3 Compile-time warnings
157              
158             This won't work for some (all?) compile-time warnings that are not just
159             enabled for the module in question, but are injected back into your package.
160             Moose, Moo, Dancer, Dancer2 and Test::Class::Moose all do this at the time of
161             writing, by saying C<import>> in their import method, thus
162             injecting all warnings into I package.
163              
164             To stop such code from turning back on warnings that you thought you'd
165             disabled, say e.g.
166              
167             no warnings::anywhere {
168             warning => 'experimental::smartmatch',
169             thwart_module => [qw(Moose)],
170             };
171              
172             B: warnings::everywhere disables these warnings by what is basically
173             a source filter, so use with caution. If you can find an approved way of
174             preventing modules such as Moose from doing this, do that rather than
175             messing about with the module's source code!
176              
177             =head2 Limitations
178              
179             warnings::everywhere works by fiddling with the contents of the global hashes
180             %warnings::Bits and %warnings::DeadBits. As such, there are limitations on
181             what it can and cannot do:
182              
183             =over
184              
185             =item It cannot affect modules that are already loaded.
186              
187             If you say
188              
189             use Chatty::Module;
190             no warnings::anywhere qw(uninitialized);
191              
192             that's no good - Chatty::Module has already called C and
193             uninitialized variables was in the list of enabled warnings at that point,
194             so it will still spam you.
195              
196             Similarly, this is no help:
197              
198             use Module::That::Uses::Chatty::Module;
199             no warnings::anywhere qw(uninitialized);
200             use Chatty::Module;
201              
202             Chatty::Module was pulled in by that other module already by the time
203             perl gets to your use statement, so it's ignored.
204              
205             =item It's vulnerable to anything that sets $^W
206              
207             Any code that sets the global variable $^W, rather than saying C
208             or C<import>>, will turn on all warnings everywhere, bypassing the
209             changes warnings::everywhere makes. This also includes any code that sets -w
210             via the shebang.
211              
212             Any change to warnings by any of the warnings::anywhere code will turn off $^W
213             again, whether it's a use statement or an explicit call to
214             L or similar.
215              
216             Any module that claims to enable warnings for you is potentially suspect
217             - Moose is fine, but Dancer sets $^W to 1 as soon as it loads, even if your
218             configuration subsequently disables import_warnings.
219              
220             =item It cannot make all modules use warnings
221              
222             All it does is fiddle with the exact behaviour of C,
223             so a module that doesn't say C, or import a module that
224             injects warnings like Moose, will be unaffected.
225              
226             =item It's not lexical
227              
228             While it I like a pragma, it's not - it fiddles with global settings,
229             after all. So you can't say
230              
231             {
232             no warnings::anywhere qw(uninitialized);
233             Chatty::Module->do_things;
234             }
235             Unchatty::Module->do_stuff(undef);
236              
237             and expect to get a warning from the last line. That warning's been
238             turned off for good.
239              
240             =item Its method of disabling compile-time warnings is frankly iffy
241              
242             The best I can say about its method of messing with the source code of
243             imported modules is that at least its modifications shouldn't stack with
244             other source filters, so the degree of weirdness and potential insanity
245             should be reduced to a manageable level.
246              
247             =back
248              
249             =head1 SUBROUTINES
250              
251             warnings::anywhere provides the following functions, mostly for diagnostic
252             use. They are not exported or exportable.
253              
254             =over
255              
256             =item categories_enabled
257              
258             Out: @categories
259              
260             Returns a sorted list of warning categories enabled globally. Before you've
261             fiddled with anything, this will be the list of warning categories from
262             L, minus C which isn't a category itself.
263              
264             Fatal warnings are ignored for the purpose of this function.
265              
266             =cut
267              
268             sub categories_enabled {
269 27     27 1 652 my @categories;
270 27         34 for my $category (_warning_categories()) {
271             push @categories, $category
272             if _is_bit_set($warnings::Bits{$category},
273 1911 100       1709 $warnings::Offsets{$category});
274             }
275 27         209 return @categories;
276             }
277              
278             =item categories_disabled
279              
280             Out: @categories
281              
282             Returns a sorted list of warning categories disabled globally. Before
283             you've fiddled with anything, this will be the empty list.
284              
285             Fatal warnings are ignored for the purpose of this function.
286              
287             =cut
288              
289             sub categories_disabled {
290 13     13 1 289 my @categories;
291 13         17 for my $category (_warning_categories()) {
292             push @categories, $category
293             if !_is_bit_set($warnings::Bits{$category},
294 923 100       840 $warnings::Offsets{$category});
295             }
296 13         53 return @categories;
297             }
298              
299             sub _warning_categories {
300 41     41   1446 my @categories = sort grep { $_ ne 'all' } keys %warnings::Offsets;
  2946         2969  
301 41         297 return @categories;
302             }
303              
304             =item enable_warning_category
305              
306             In: $category
307              
308             Supplied with a valid warning category, enables it for all future
309             uses of C.
310              
311             =cut
312              
313             sub enable_warning_category {
314 9     9 1 3730 my ($category) = @_;
315              
316 9 50       16 _check_warning_category($category) or return;
317 9         31 _set_category_mask($category, 1);
318 9         28 return 1;
319             }
320              
321             sub _set_category_mask {
322 23     23   20 my ($category, $bit_value) = @_;
323              
324             # Set or unset the specific category bit value (e.g. if
325             # someone says use warnings qw(uninitialized) or
326             # no warnings qw(uninitialized)).
327             _set_bit_mask(\($warnings::Bits{$category}),
328 23         49 $warnings::Offsets{$category}, $bit_value);
329              
330             # Compute what the bitmask for all should be.
331 23         35 $warnings::Bits{all} = _bitmask_categories_enabled();
332              
333             # If we've enabled all categories, we should probably set
334             # the all bit as well, just for tidiness.
335 23 100       41 if ($bit_value) {
336 9 100       12 if (!categories_disabled()) {
337 7         13 _set_bit_mask(\$warnings::Bits{all}, $warnings::Offsets{all}, 1);
338             }
339             }
340             ### TODO: fatal warnings
341              
342             # Finally, if someone specified the -w flag (which turns on all
343             # warnings, globally), turn it off.
344 23         43 $^W = 0;
345             }
346              
347             =item disable_warning_category
348              
349             In: $category
350              
351             Supplied with a valid warning category, disables it for future
352             uses of C - even calls to explicitly enable it.
353              
354             =cut
355              
356             sub disable_warning_category {
357 14     14 1 2175 my ($category) = @_;
358              
359 14 50       27 _check_warning_category($category) or return;
360 14         22 _set_category_mask($category, 0);
361 14         108 return 1;
362             }
363              
364             sub _bitmask_categories_enabled {
365 23     23   19 my $mask;
366 23         27 for my $category_enabled (categories_enabled()) {
367 1608         1522 _set_bit_mask(\$mask, $warnings::Offsets{$category_enabled}, 1)
368             }
369 23         89 return $mask;
370             }
371              
372             sub _set_bit_mask {
373 1638     1638   1096 my ($mask_ref, $bit_num, $bit_value) = @_;
374              
375             # First get the correct byte from the mask, then set that byte's
376             # bit accordingly.
377             # We have to do it this way as warning masks are hundreds of bits wide,
378             # which neither a 32- nor a 64-bit Perl can deal with natively.
379             # The mask might not be long enough, so pad it with null bytes if
380             # we need to first.
381 1638         1196 my $byte_num = int($bit_num / 8);
382 1638         2044 while (length($$mask_ref) < $byte_num) {
383 391         449 $$mask_ref .= "\x0";
384             }
385 1638         1200 my $byte_value = substr($$mask_ref, $byte_num, 1);
386 1638         1602 vec($byte_value, $bit_num % 8, 1) = $bit_value;
387 1638         1342 substr($$mask_ref, $byte_num, 1) = $byte_value;
388 1638         1371 return $$mask_ref;
389             }
390              
391             sub _is_bit_set {
392 2837     2837   1867 my ($mask, $bit_num) = @_;
393              
394 2837         1749 my $smallest_bit_num = $bit_num % 8;
395 2837         4745 return vec($mask, int($bit_num / 8), 8) & (1 << $smallest_bit_num);
396             }
397              
398             sub _dump_mask {
399 0     0   0 my ($mask) = @_;
400              
401 0         0 my $output;
402 0         0 for my $byte_num (reverse 0..15) {
403 0         0 $output .= sprintf('%08b', vec($mask, $byte_num, 8));
404 0 0       0 $output .= ($byte_num % 4 == 0 ? "\n" : '|');
405             }
406 0         0 return $output;
407             }
408              
409             sub _check_warning_category {
410 24     24   22 my ($category) = @_;
411              
412 24 50       52 return if $category eq 'all';
413 24 100       44 if (!exists $warnings::Offsets{$category}) {
414 1         93 carp "Unrecognised warning category $category";
415 1         80 return;
416             }
417 23         48 return 1;
418             }
419              
420             sub _thwart_modules {
421 0     0     my ($package, %args) = @_;
422              
423             my @modules
424             = ref($args{thwart_module}) eq 'ARRAY'
425 0           ? @{ $args{thwart_module} }
426 0 0         : $args{thwart_module};
427              
428              
429 0           for my $module (@modules) {
430 0           $package->_thwart_this_module(%args, thwart_module => $module);
431             }
432             }
433              
434             sub _thwart_this_module {
435 0     0     my ($package, %args) = @_;
436              
437             # Moose's import sub is actually defined in Moose::Exporter, so
438             # modify that code instead.
439 0           my $module = $args{thwart_module};
440 0 0         if ($module eq 'Moose') {
441 0           $module = 'Moose::Exporter';
442             }
443 0           my $filename = $module;
444 0           $filename =~ s{::}{/}g;
445 0           $filename .= '.pm';
446             unshift @INC, sub {
447 0     0     my ($this_coderef, $use_filename) = @_;
448 0 0         return if $use_filename ne $filename;
449              
450             # Find the source of the module we're looking for.
451             # This will fail if the module is itself being loaded by a
452             # coderef in @INC, say, but should work for the vast, vast
453             # majority of cases.
454             my $source_fh = $package->_find_module_source($use_filename)
455 0 0         or do {
456 0           croak "You asked me to thwart $args{thwart_module}"
457             . " but I can't find $use_filename anywhere in @INC";
458             };
459 0           my $source;
460             {
461 0           local $/;
  0            
462 0           $source = <$source_fh>;
463             }
464              
465             # Work out what we're going to inject into this source code.
466             my @warnings
467             = ref($args{warning} eq 'ARRAY')
468 0           ? @{ $args{warning} }
469 0 0         : $args{warning};
470 0           my $source_code_unimport = "### Code injected by $package\n";
471 0           for my $warning (@warnings) {
472 0           $source_code_unimport .= qq{warnings->unimport("$warning");\n};
473             }
474 0           $source_code_unimport .= "### End of code injected by $package\n";
475              
476             # Add this stuff just after a call to warnings->import (Moose, Moo,
477             # Dancer) or import::into (Dancer2, Test::Class::Moose).
478 0           my $re_code;
479 0 0         if ($module eq 'Dancer2') {
    0          
480 0           $re_code = qr/ ( import::into [^\n]+ warnings [^\n]+ ; \n ) /x;
481             } elsif ($module eq 'Test::Class::Moose') {
482 0           $re_code = qr/ ( \$_ -> import::into [^;\n]+ ; \n )/x;
483             } else {
484 0           $re_code = qr/ ( warnings->import; \n) /x;
485             }
486 0 0         $source =~ s/$re_code/$1$source_code_unimport/xsm
487             or croak
488             "Couldn't find a call to $re_code in $use_filename for $module\n$source\n";
489              
490             # Right, return this modified source code.
491 0           open (my $fh_source, '<', \$source);
492 0           return $fh_source;
493 0           };
494             }
495              
496             sub _find_module_source {
497 0     0     my ($package, $use_filename) = @_;
498              
499 0           for my $dir (grep { !ref($_) } @INC) {
  0            
500 0           my $full_path = File::Spec->catfile($dir, $use_filename);
501 0 0         if (-e $full_path) {
502 0           open (my $fh, '<', $full_path);
503 0           return $fh;
504             }
505             }
506 0           return;
507             }
508              
509              
510             =back
511              
512             =head1 TO DO
513              
514             Support for fatal warnings, possibly.
515             It's possible it doesn't behave correctly when passed 'all'.
516              
517             =head1 DIAGNOSTICS
518              
519             =over
520              
521             =item Unrecognised warning category $category
522              
523             Your version of Perl doesn't recognise the warning category $category.
524             Either you're using a different version of Perl than you thought, or a
525             third-party module that defined that warning isn't loaded yet.
526              
527             =back
528              
529             =head1 SEE ALSO
530              
531             L
532              
533             =head1 AUTHOR
534              
535             Sam Kington
536              
537             The source code for this module is hosted on GitHub
538             L - this is probably the
539             best place to look for suggestions and feedback.
540              
541             =head1 COPYRIGHT
542              
543             Copyright (c) 2013 Sam Kington.
544              
545             =head1 LICENSE
546              
547             This library is free software and may be distributed under the same terms as
548             perl itself.
549              
550             =cut
551              
552             1;