File Coverage

blib/lib/Perl/Tidy/Logger.pm
Criterion Covered Total %
statement 150 245 61.2
branch 37 96 38.5
condition 9 24 37.5
subroutine 23 35 65.7
pod 0 23 0.0
total 219 423 51.7


line stmt bran cond sub pod time code
1             #####################################################################
2             #
3             # The Perl::Tidy::Logger class writes any .LOG and .ERR files
4             # and supplies some basic run information for error handling.
5             #
6             #####################################################################
7              
8             package Perl::Tidy::Logger;
9 39     39   357 use strict;
  39         97  
  39         1238  
10 39     39   267 use warnings;
  39         131  
  39         1803  
11             our $VERSION = '20230912';
12 39     39   329 use English qw( -no_match_vars );
  39         162  
  39         461  
13              
14 39     39   17049 use constant DEVEL_MODE => 0;
  39         161  
  39         2417  
15 39     39   278 use constant EMPTY_STRING => q{};
  39         120  
  39         2019  
16 39     39   255 use constant SPACE => q{ };
  39         106  
  39         8464  
17              
18             sub AUTOLOAD {
19              
20             # Catch any undefined sub calls so that we are sure to get
21             # some diagnostic information. This sub should never be called
22             # except for a programming error.
23 0     0   0 our $AUTOLOAD;
24 0 0       0 return if ( $AUTOLOAD =~ /\bDESTROY$/ );
25 0         0 my ( $pkg, $fname, $lno ) = caller();
26 0         0 my $my_package = __PACKAGE__;
27 0         0 print {*STDERR} <<EOM;
  0         0  
28             ======================================================================
29             Error detected in package '$my_package', version $VERSION
30             Received unexpected AUTOLOAD call for sub '$AUTOLOAD'
31             Called from package: '$pkg'
32             Called from File '$fname' at line '$lno'
33             This error is probably due to a recent programming change
34             ======================================================================
35             EOM
36 0         0 exit 1;
37             } ## end sub AUTOLOAD
38              
39       0     sub DESTROY {
40              
41             # required to avoid call to AUTOLOAD in some versions of perl
42             }
43              
44 39     39   316 use constant DEFAULT_LOGFILE_GAP => 50;
  39         113  
  39         30337  
45              
46             sub new {
47              
48 560     560 0 3473 my ( $class, @args ) = @_;
49              
50 560         5542 my %defaults = (
51             rOpts => undef,
52             log_file => undef,
53             warning_file => undef,
54             fh_stderr => undef,
55             display_name => undef,
56             is_encoded_data => undef,
57             );
58              
59 560         4461 my %args = ( %defaults, @args );
60              
61 560         2179 my $rOpts = $args{rOpts};
62 560         1487 my $log_file = $args{log_file};
63 560         1432 my $warning_file = $args{warning_file};
64 560         1511 my $fh_stderr = $args{fh_stderr};
65 560         1253 my $display_name = $args{display_name};
66 560         1377 my $is_encoded_data = $args{is_encoded_data};
67              
68 560 100       1986 my $fh_warnings = $rOpts->{'standard-error-output'} ? $fh_stderr : undef;
69              
70             # remove any old error output file if we might write a new one
71 560 100 100     3805 if ( !$fh_warnings && !ref($warning_file) ) {
72 16 50       481 if ( -e $warning_file ) {
73 0 0       0 unlink($warning_file)
74             or Perl::Tidy::Die(
75             "couldn't unlink warning file $warning_file: $OS_ERROR\n");
76             }
77             }
78              
79             my $logfile_gap =
80             defined( $rOpts->{'logfile-gap'} )
81 560 100       2540 ? $rOpts->{'logfile-gap'}
82             : DEFAULT_LOGFILE_GAP;
83 560 100       2483 if ( $logfile_gap == 0 ) { $logfile_gap = 1 }
  1         3  
84              
85 560 50       2466 my $filename_stamp = $display_name ? $display_name . ':' : "??";
86 560 50       1973 my $input_stream_name = $display_name ? $display_name : "??";
87             return bless {
88             _log_file => $log_file,
89             _logfile_gap => $logfile_gap,
90             _rOpts => $rOpts,
91             _fh_warnings => $fh_warnings,
92             _last_input_line_written => 0,
93             _last_input_line_number => undef,
94             _at_end_of_file => 0,
95             _use_prefix => 1,
96             _block_log_output => 0,
97             _line_of_tokens => undef,
98             _output_line_number => undef,
99             _wrote_line_information_string => 0,
100             _wrote_column_headings => 0,
101             _warning_file => $warning_file,
102             _warning_count => 0,
103             _complaint_count => 0,
104             _is_encoded_data => $is_encoded_data,
105             _saw_code_bug => -1, # -1=no 0=maybe 1=for sure
106             _saw_brace_error => 0,
107             _output_array => [],
108             _input_stream_name => $input_stream_name,
109             _filename_stamp => $filename_stamp,
110 560         16706 _save_logfile => $rOpts->{'logfile'},
111             }, $class;
112             } ## end sub new
113              
114             sub get_input_stream_name {
115 559     559 0 1564 my $self = shift;
116 559         1860 return $self->{_input_stream_name};
117             }
118              
119             sub set_last_input_line_number {
120 560     560 0 1897 my ( $self, $lno ) = @_;
121 560         1539 $self->{_last_input_line_number} = $lno;
122 560         1468 return;
123             }
124              
125             sub get_warning_count {
126 0     0 0 0 my $self = shift;
127 0         0 return $self->{_warning_count};
128             }
129              
130             sub get_use_prefix {
131 0     0 0 0 my $self = shift;
132 0         0 return $self->{_use_prefix};
133             }
134              
135             sub block_log_output {
136 0     0 0 0 my $self = shift;
137 0         0 $self->{_block_log_output} = 1;
138 0         0 return;
139             }
140              
141             sub unblock_log_output {
142 0     0 0 0 my $self = shift;
143 0         0 $self->{_block_log_output} = 0;
144 0         0 return;
145             }
146              
147             sub interrupt_logfile {
148 0     0 0 0 my $self = shift;
149 0         0 $self->{_use_prefix} = 0;
150 0         0 $self->warning("\n");
151 0         0 $self->write_logfile_entry( '#' x 24 . " WARNING " . '#' x 25 . "\n" );
152 0         0 return;
153             } ## end sub interrupt_logfile
154              
155             sub resume_logfile {
156 0     0 0 0 my $self = shift;
157 0         0 $self->write_logfile_entry( '#' x 60 . "\n" );
158 0         0 $self->{_use_prefix} = 1;
159 0         0 return;
160             } ## end sub resume_logfile
161              
162             sub we_are_at_the_last_line {
163 559     559 0 1400 my $self = shift;
164 559 100       2180 if ( !$self->{_wrote_line_information_string} ) {
165 1         4 $self->write_logfile_entry("Last line\n\n");
166             }
167 559         1575 $self->{_at_end_of_file} = 1;
168 559         1435 return;
169             } ## end sub we_are_at_the_last_line
170              
171             # record some stuff in case we go down in flames
172 39     39   442 use constant MAX_PRINTED_CHARS => 35;
  39         144  
  39         40784  
173              
174             sub black_box {
175 6     6 0 17 my ( $self, $line_of_tokens, $output_line_number ) = @_;
176 6         11 my $input_line = $line_of_tokens->{_line_text};
177 6         10 my $input_line_number = $line_of_tokens->{_line_number};
178              
179             # save line information in case we have to write a logfile message
180 6         11 $self->{_line_of_tokens} = $line_of_tokens;
181 6         12 $self->{_output_line_number} = $output_line_number;
182 6         16 $self->{_wrote_line_information_string} = 0;
183              
184 6         10 my $last_input_line_written = $self->{_last_input_line_written};
185 6 100 66     40 if (
186             (
187             ( $input_line_number - $last_input_line_written ) >=
188             $self->{_logfile_gap}
189             )
190             || ( $input_line =~ /^\s*(sub|package)\s+(\w+)/ )
191             )
192             {
193 3         5 my $structural_indentation_level = $line_of_tokens->{_level_0};
194 3 50       7 $structural_indentation_level = 0
195             if ( $structural_indentation_level < 0 );
196 3         6 $self->{_last_input_line_written} = $input_line_number;
197 3         17 ( my $out_str = $input_line ) =~ s/^\s*//;
198 3         7 chomp $out_str;
199              
200 3         7 $out_str = ( '.' x $structural_indentation_level ) . $out_str;
201              
202 3 50       9 if ( length($out_str) > MAX_PRINTED_CHARS ) {
203 0         0 $out_str = substr( $out_str, 0, MAX_PRINTED_CHARS ) . " ....";
204             }
205 3         24 $self->logfile_output( EMPTY_STRING, "$out_str\n" );
206             }
207 6         19 return;
208             } ## end sub black_box
209              
210             sub write_logfile_entry {
211              
212 7240     7240 0 15354 my ( $self, @msg ) = @_;
213              
214             # add leading >>> to avoid confusing error messages and code
215 7240         26981 $self->logfile_output( ">>>", "@msg" );
216 7240         16001 return;
217             } ## end sub write_logfile_entry
218              
219             sub write_column_headings {
220 2     2 0 4 my $self = shift;
221              
222 2         4 $self->{_wrote_column_headings} = 1;
223 2         5 my $routput_array = $self->{_output_array};
224 2         3 push @{$routput_array}, <<EOM;
  2         22  
225              
226             Starting formatting pass...
227             The nesting depths in the table below are at the start of the lines.
228             The indicated output line numbers are not always exact.
229             ci = levels of continuation indentation; bk = 1 if in BLOCK, 0 if not.
230              
231             in:out indent c b nesting code + messages; (messages begin with >>>)
232             lines levels i k (code begins with one '.' per indent level)
233             ------ ----- - - -------- -------------------------------------------
234             EOM
235 2         7 return;
236             } ## end sub write_column_headings
237              
238             sub make_line_information_string {
239              
240             # make columns of information when a logfile message needs to go out
241 4279     4279 0 6666 my $self = shift;
242 4279         7046 my $line_of_tokens = $self->{_line_of_tokens};
243 4279         7196 my $input_line_number = $line_of_tokens->{_line_number};
244 4279         7410 my $line_information_string = EMPTY_STRING;
245 4279 100       8751 if ($input_line_number) {
246              
247 4         6 my $output_line_number = $self->{_output_line_number};
248 4         10 my $brace_depth = $line_of_tokens->{_curly_brace_depth};
249 4         6 my $paren_depth = $line_of_tokens->{_paren_depth};
250 4         8 my $square_bracket_depth = $line_of_tokens->{_square_bracket_depth};
251             my $guessed_indentation_level =
252 4         7 $line_of_tokens->{_guessed_indentation_level};
253              
254 4         8 my $structural_indentation_level = $line_of_tokens->{_level_0};
255              
256 4 100       34 $self->write_column_headings() unless $self->{_wrote_column_headings};
257              
258             # keep logfile columns aligned for scripts up to 999 lines;
259             # for longer scripts it doesn't really matter
260 4         8 my $extra_space = EMPTY_STRING;
261 4 0       20 $extra_space .=
    50          
262             ( $input_line_number < 10 ) ? SPACE x 2
263             : ( $input_line_number < 100 ) ? SPACE
264             : EMPTY_STRING;
265 4 0       9 $extra_space .=
    50          
266             ( $output_line_number < 10 ) ? SPACE x 2
267             : ( $output_line_number < 100 ) ? SPACE
268             : EMPTY_STRING;
269              
270             # there are 2 possible nesting strings:
271             # the original which looks like this: (0 [1 {2
272             # the new one, which looks like this: {{[
273             # the new one is easier to read, and shows the order, but
274             # could be arbitrarily long, so we use it unless it is too long
275 4         15 my $nesting_string =
276             "($paren_depth [$square_bracket_depth {$brace_depth";
277 4         7 my $nesting_string_new = $line_of_tokens->{_nesting_tokens_0};
278 4         7 my $ci_level = $line_of_tokens->{_ci_level_0};
279 4 50       11 if ( $ci_level > 9 ) { $ci_level = '*' }
  0         0  
280 4 50       23 my $bk = ( $line_of_tokens->{_nesting_blocks_0} =~ /1$/ ) ? '1' : '0';
281              
282 4 50       12 if ( length($nesting_string_new) <= 8 ) {
283 4         21 $nesting_string =
284             $nesting_string_new . SPACE x ( 8 - length($nesting_string_new) );
285             }
286             $line_information_string =
287 4         21 "L$input_line_number:$output_line_number$extra_space i$guessed_indentation_level:$structural_indentation_level $ci_level $bk $nesting_string";
288             }
289 4279         9651 return $line_information_string;
290             } ## end sub make_line_information_string
291              
292             sub logfile_output {
293 7243     7243 0 13706 my ( $self, $prompt, $msg ) = @_;
294 7243 50       17203 return if ( $self->{_block_log_output} );
295              
296 7243         11460 my $routput_array = $self->{_output_array};
297 7243 100 66     23756 if ( $self->{_at_end_of_file} || !$self->{_use_prefix} ) {
298 2964         4644 push @{$routput_array}, "$msg";
  2964         7521  
299             }
300             else {
301 4279         9971 my $line_information_string = $self->make_line_information_string();
302 4279         7354 $self->{_wrote_line_information_string} = 1;
303              
304 4279 100       8019 if ($line_information_string) {
305 4         7 push @{$routput_array}, "$line_information_string $prompt$msg";
  4         15  
306             }
307             else {
308 4275         6312 push @{$routput_array}, "$msg";
  4275         11632  
309             }
310             }
311 7243         13163 return;
312             } ## end sub logfile_output
313              
314             sub get_saw_brace_error {
315 559     559 0 1387 my $self = shift;
316 559         2415 return $self->{_saw_brace_error};
317             }
318              
319             sub increment_brace_error {
320 0     0 0 0 my $self = shift;
321 0         0 $self->{_saw_brace_error}++;
322 0         0 return;
323             }
324              
325             sub brace_warning {
326 0     0 0 0 my ( $self, $msg, $msg_line_number ) = @_;
327              
328 39     39   357 use constant BRACE_WARNING_LIMIT => 10;
  39         159  
  39         10804  
329 0         0 my $saw_brace_error = $self->{_saw_brace_error};
330              
331 0 0       0 if ( $saw_brace_error < BRACE_WARNING_LIMIT ) {
332 0         0 $self->warning( $msg, $msg_line_number );
333             }
334 0         0 $saw_brace_error++;
335 0         0 $self->{_saw_brace_error} = $saw_brace_error;
336              
337 0 0       0 if ( $saw_brace_error == BRACE_WARNING_LIMIT ) {
338 0         0 $self->warning("No further warnings of this type will be given\n");
339             }
340 0         0 return;
341             } ## end sub brace_warning
342              
343             sub complain {
344              
345             # handle non-critical warning messages based on input flag
346 32     32 0 106 my ( $self, $msg, $msg_line_number ) = @_;
347 32         91 my $rOpts = $self->{_rOpts};
348              
349             # these appear in .ERR output only if -w flag is used
350 32 50       112 if ( $rOpts->{'warning-output'} ) {
351 0         0 $self->warning( $msg, $msg_line_number );
352             }
353              
354             # otherwise, they go to the .LOG file
355             else {
356 32         62 $self->{_complaint_count}++;
357 32 50       108 if ($msg_line_number) {
358              
359             # TODO: consider using same prefix as warning()
360 32         94 $msg = $msg_line_number . ':' . $msg;
361             }
362 32         114 $self->write_logfile_entry($msg);
363             }
364 32         112 return;
365             } ## end sub complain
366              
367             sub warning {
368              
369             # report errors to .ERR file (or stdout)
370 0     0 0 0 my ( $self, $msg, $msg_line_number ) = @_;
371              
372 39     39   332 use constant WARNING_LIMIT => 50;
  39         141  
  39         36083  
373              
374             # Always bump the warn count, even if no message goes out
375 0         0 Perl::Tidy::Warn_count_bump();
376              
377 0         0 my $rOpts = $self->{_rOpts};
378 0 0       0 if ( !$rOpts->{'quiet'} ) {
379              
380 0         0 my $warning_count = $self->{_warning_count};
381 0         0 my $fh_warnings = $self->{_fh_warnings};
382 0         0 my $is_encoded_data = $self->{_is_encoded_data};
383 0 0       0 if ( !$fh_warnings ) {
384 0         0 my $warning_file = $self->{_warning_file};
385 0         0 ( $fh_warnings, my $filename ) =
386             Perl::Tidy::streamhandle( $warning_file, 'w', $is_encoded_data );
387 0 0       0 $fh_warnings
388             or Perl::Tidy::Die("couldn't open $filename: $OS_ERROR\n");
389 0 0       0 Perl::Tidy::Warn_msg("## Please see file $filename\n")
390             unless ref($warning_file);
391 0         0 $self->{_fh_warnings} = $fh_warnings;
392 0         0 $fh_warnings->print("Perltidy version is $Perl::Tidy::VERSION\n");
393             }
394              
395 0         0 my $filename_stamp = $self->{_filename_stamp};
396              
397 0 0       0 if ( $warning_count < WARNING_LIMIT ) {
398              
399 0 0       0 if ( !$warning_count ) {
400              
401             # On first error always write a line with the filename. Note
402             # that the filename will be 'perltidy' if input is from stdin
403             # or from a data structure.
404 0 0       0 if ($filename_stamp) {
405 0         0 $fh_warnings->print(
406             "\n$filename_stamp Begin Error Output Stream\n");
407             }
408              
409             # Turn off filename stamping unless error output is directed
410             # to the standard error output (with -se flag)
411 0 0       0 if ( !$rOpts->{'standard-error-output'} ) {
412 0         0 $filename_stamp = EMPTY_STRING;
413 0         0 $self->{_filename_stamp} = $filename_stamp;
414             }
415             }
416              
417 0 0 0     0 if ( $self->get_use_prefix() > 0 && defined($msg_line_number) ) {
418 0         0 $self->write_logfile_entry("WARNING: $msg");
419              
420             # add prefix 'filename:line_no: ' to message lines
421 0         0 my $pre_string = $filename_stamp . $msg_line_number . ': ';
422 0         0 chomp $msg;
423 0         0 $msg =~ s/\n/\n$pre_string/g;
424 0         0 $msg = $pre_string . $msg . "\n";
425              
426 0         0 $fh_warnings->print($msg);
427              
428             }
429             else {
430 0         0 $self->write_logfile_entry($msg);
431              
432             # add prefix 'filename: ' to message lines
433 0 0       0 if ($filename_stamp) {
434 0         0 my $pre_string = $filename_stamp . SPACE;
435 0         0 chomp $msg;
436 0         0 $msg =~ s/\n/\n$pre_string/g;
437 0         0 $msg = $pre_string . $msg . "\n";
438             }
439              
440 0         0 $fh_warnings->print($msg);
441             }
442             }
443 0         0 $warning_count++;
444 0         0 $self->{_warning_count} = $warning_count;
445              
446 0 0       0 if ( $warning_count == WARNING_LIMIT ) {
447 0         0 $fh_warnings->print(
448             $filename_stamp . "No further warnings will be given\n" );
449             }
450             }
451 0         0 return;
452             } ## end sub warning
453              
454             sub report_definite_bug {
455 0     0 0 0 my $self = shift;
456 0         0 $self->{_saw_code_bug} = 1;
457 0         0 return;
458             }
459              
460             sub get_save_logfile {
461              
462             # Returns a true/false flag indicating whether or not
463             # the logfile will be saved.
464 559     559 0 1564 my $self = shift;
465 559         1729 return $self->{_save_logfile};
466             } ## end sub get_save_logfile
467              
468             sub finish {
469              
470             # called after all formatting to summarize errors
471 560     560 0 1755 my ($self) = @_;
472              
473 560         1710 my $warning_count = $self->{_warning_count};
474 560         1481 my $save_logfile = $self->{_save_logfile};
475 560         1353 my $log_file = $self->{_log_file};
476 560         1316 my $msg_line_number = $self->{_last_input_line_number};
477              
478 560 50       1963 if ($warning_count) {
479 0 0       0 if ($save_logfile) {
480 0         0 $self->block_log_output(); # avoid echoing this to the logfile
481 0         0 $self->warning(
482             "The logfile $log_file may contain useful information\n",
483             $msg_line_number );
484 0         0 $self->unblock_log_output();
485             }
486              
487 0 0       0 if ( $self->{_complaint_count} > 0 ) {
488 0         0 $self->warning(
489             "To see $self->{_complaint_count} non-critical warnings rerun with -w\n",
490             $msg_line_number
491             );
492             }
493              
494 0 0 0     0 if ( $self->{_saw_brace_error}
      0        
495             && ( $self->{_logfile_gap} > 1 || !$save_logfile ) )
496             {
497 0         0 $self->warning( "To save a full .LOG file rerun with -g\n",
498             $msg_line_number );
499             }
500             }
501              
502 560 100       1829 if ($save_logfile) {
503 2         5 my $is_encoded_data = $self->{_is_encoded_data};
504 2         21 my ( $fh, $filename ) =
505             Perl::Tidy::streamhandle( $log_file, 'w', $is_encoded_data );
506 2 50       7 if ($fh) {
507 2         5 my $routput_array = $self->{_output_array};
508 2         4 foreach my $line ( @{$routput_array} ) { $fh->print($line) }
  2         7  
  34         66  
509 2 0 33     23 if ( $fh->can('close')
      33        
510             && !ref($log_file) ne '-'
511             && $log_file ne '-' )
512             {
513 0 0       0 $fh->close()
514             or Perl::Tidy::Warn(
515             "Error closing LOG file '$log_file': $OS_ERROR\n");
516             }
517             }
518             }
519 560         3993 return;
520             } ## end sub finish
521             1;