File Coverage

blib/lib/Perl/Tidy/Logger.pm
Criterion Covered Total %
statement 150 248 60.4
branch 38 96 39.5
condition 8 23 34.7
subroutine 23 35 65.7
pod 0 23 0.0
total 219 425 51.5


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 38     38   301 use strict;
  38         82  
  38         1170  
10 38     38   241 use warnings;
  38         156  
  38         1614  
11             our $VERSION = '20230701';
12 38     38   228 use English qw( -no_match_vars );
  38         102  
  38         225  
13              
14 38     38   13127 use constant DEVEL_MODE => 0;
  38         122  
  38         2617  
15 38     38   274 use constant EMPTY_STRING => q{};
  38         136  
  38         2213  
16 38     38   285 use constant SPACE => q{ };
  38         130  
  38         8256  
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;
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 38     38   339 use constant DEFAULT_LOGFILE_GAP => 50;
  38         139  
  38         27184  
45              
46             sub new {
47              
48 554     554 0 3419 my ( $class, @args ) = @_;
49              
50 554         4740 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 554         4004 my %args = ( %defaults, @args );
60              
61 554         2106 my $rOpts = $args{rOpts};
62 554         1577 my $log_file = $args{log_file};
63 554         1440 my $warning_file = $args{warning_file};
64 554         1307 my $fh_stderr = $args{fh_stderr};
65 554         1253 my $display_name = $args{display_name};
66 554         1308 my $is_encoded_data = $args{is_encoded_data};
67              
68 554 100       1898 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 554 100 100     3601 unless ( $fh_warnings || ref($warning_file) ) {
72 15 50       464 if ( -e $warning_file ) {
73 0 0       0 unlink($warning_file)
74             or Perl::Tidy::Die(
75             "couldn't unlink warning file $warning_file: $ERRNO\n");
76             }
77             }
78              
79             my $logfile_gap =
80             defined( $rOpts->{'logfile-gap'} )
81 554 100       2375 ? $rOpts->{'logfile-gap'}
82             : DEFAULT_LOGFILE_GAP;
83 554 100       1967 if ( $logfile_gap == 0 ) { $logfile_gap = 1 }
  1         9  
84              
85 554 50       2297 my $filename_stamp = $display_name ? $display_name . ':' : "??";
86 554 50       1704 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 554         15308 _save_logfile => $rOpts->{'logfile'},
111             }, $class;
112             } ## end sub new
113              
114             sub get_input_stream_name {
115 553     553 0 1458 my $self = shift;
116 553         1847 return $self->{_input_stream_name};
117             }
118              
119             sub set_last_input_line_number {
120 554     554 0 2192 my ( $self, $lno ) = @_;
121 554         1489 $self->{_last_input_line_number} = $lno;
122 554         1521 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 553     553 0 1311 my $self = shift;
164 553 100       2473 unless ( $self->{_wrote_line_information_string} ) {
165 1         9 $self->write_logfile_entry("Last line\n\n");
166             }
167 553         1538 $self->{_at_end_of_file} = 1;
168 553         1445 return;
169             } ## end sub we_are_at_the_last_line
170              
171             # record some stuff in case we go down in flames
172 38     38   353 use constant MAX_PRINTED_CHARS => 35;
  38         99  
  38         39360  
173              
174             sub black_box {
175 6     6 0 17 my ( $self, $line_of_tokens, $output_line_number ) = @_;
176 6         19 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         12 $self->{_line_of_tokens} = $line_of_tokens;
181 6         10 $self->{_output_line_number} = $output_line_number;
182 6         11 $self->{_wrote_line_information_string} = 0;
183              
184 6         10 my $last_input_line_written = $self->{_last_input_line_written};
185 6 100 66     32 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       15 $structural_indentation_level = 0
195             if ( $structural_indentation_level < 0 );
196 3         6 $self->{_last_input_line_written} = $input_line_number;
197 3         16 ( my $out_str = $input_line ) =~ s/^\s*//;
198 3         7 chomp $out_str;
199              
200 3         8 $out_str = ( '.' x $structural_indentation_level ) . $out_str;
201              
202 3 50       10 if ( length($out_str) > MAX_PRINTED_CHARS ) {
203 0         0 $out_str = substr( $out_str, 0, MAX_PRINTED_CHARS ) . " ....";
204             }
205 3         10 $self->logfile_output( EMPTY_STRING, "$out_str\n" );
206             }
207 6         17 return;
208             } ## end sub black_box
209              
210             sub write_logfile_entry {
211              
212 7155     7155 0 15132 my ( $self, @msg ) = @_;
213              
214             # add leading >>> to avoid confusing error messages and code
215 7155         26274 $self->logfile_output( ">>>", "@msg" );
216 7155         15297 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         4 push @{$routput_array}, <<EOM;
  2         6  
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         4 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 4228     4228 0 6570 my $self = shift;
242 4228         6993 my $line_of_tokens = $self->{_line_of_tokens};
243 4228         7430 my $input_line_number = $line_of_tokens->{_line_number};
244 4228         7179 my $line_information_string = EMPTY_STRING;
245 4228 100       8786 if ($input_line_number) {
246              
247 4         8 my $output_line_number = $self->{_output_line_number};
248 4         7 my $brace_depth = $line_of_tokens->{_curly_brace_depth};
249 4         9 my $paren_depth = $line_of_tokens->{_paren_depth};
250 4         7 my $square_bracket_depth = $line_of_tokens->{_square_bracket_depth};
251             my $guessed_indentation_level =
252 4         8 $line_of_tokens->{_guessed_indentation_level};
253              
254 4         11 my $structural_indentation_level = $line_of_tokens->{_level_0};
255              
256 4 100       15 $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         6 my $extra_space = EMPTY_STRING;
261 4 0       23 $extra_space .=
    50          
262             ( $input_line_number < 10 ) ? SPACE x 2
263             : ( $input_line_number < 100 ) ? SPACE
264             : EMPTY_STRING;
265 4 0       15 $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         14 my $nesting_string =
276             "($paren_depth [$square_bracket_depth {$brace_depth";
277 4         15 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       12 if ( $ci_level > 9 ) { $ci_level = '*' }
  0         0  
280 4 50       22 my $bk = ( $line_of_tokens->{_nesting_blocks_0} =~ /1$/ ) ? '1' : '0';
281              
282 4 50       14 if ( length($nesting_string_new) <= 8 ) {
283 4         20 $nesting_string =
284             $nesting_string_new . SPACE x ( 8 - length($nesting_string_new) );
285             }
286             $line_information_string =
287 4         30 "L$input_line_number:$output_line_number$extra_space i$guessed_indentation_level:$structural_indentation_level $ci_level $bk $nesting_string";
288             }
289 4228         9723 return $line_information_string;
290             } ## end sub make_line_information_string
291              
292             sub logfile_output {
293 7158     7158 0 14367 my ( $self, $prompt, $msg ) = @_;
294 7158 50       17139 return if ( $self->{_block_log_output} );
295              
296 7158         11842 my $routput_array = $self->{_output_array};
297 7158 100 66     23431 if ( $self->{_at_end_of_file} || !$self->{_use_prefix} ) {
298 2930         4172 push @{$routput_array}, "$msg";
  2930         7675  
299             }
300             else {
301 4228         9857 my $line_information_string = $self->make_line_information_string();
302 4228         7305 $self->{_wrote_line_information_string} = 1;
303              
304 4228 100       8155 if ($line_information_string) {
305 4         11 push @{$routput_array}, "$line_information_string $prompt$msg";
  4         13  
306             }
307             else {
308 4224         6355 push @{$routput_array}, "$msg";
  4224         11768  
309             }
310             }
311 7158         13263 return;
312             } ## end sub logfile_output
313              
314             sub get_saw_brace_error {
315 553     553 0 1364 my $self = shift;
316 553         2416 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 38     38   374 use constant BRACE_WARNING_LIMIT => 10;
  38         124  
  38         10176  
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 98 my ( $self, $msg, $msg_line_number ) = @_;
347 32         73 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         69 $self->{_complaint_count}++;
357 32 50       93 if ($msg_line_number) {
358              
359             # TODO: consider using same prefix as warning()
360 32         107 $msg = $msg_line_number . ':' . $msg;
361             }
362 32         98 $self->write_logfile_entry($msg);
363             }
364 32         123 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 38     38   362 use constant WARNING_LIMIT => 50;
  38         117  
  38         33981  
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 unless ( $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: $ERRNO\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 553     553 0 1316 my $self = shift;
465 553         1744 return $self->{_save_logfile};
466             } ## end sub get_save_logfile
467              
468             sub finish {
469              
470             # called after all formatting to summarize errors
471 554     554 0 1814 my ($self) = @_;
472              
473 554         1871 my $warning_count = $self->{_warning_count};
474 554         1501 my $save_logfile = $self->{_save_logfile};
475 554         1423 my $log_file = $self->{_log_file};
476 554         1495 my $msg_line_number = $self->{_last_input_line_number};
477              
478 554 50       2266 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 554 100       1832 if ($save_logfile) {
503 2         6 my $is_encoded_data = $self->{_is_encoded_data};
504 2         9 my ( $fh, $filename ) =
505             Perl::Tidy::streamhandle( $log_file, 'w', $is_encoded_data );
506 2 50       8 if ($fh) {
507 2         7 my $routput_array = $self->{_output_array};
508 2         4 foreach my $line ( @{$routput_array} ) { $fh->print($line) }
  2         6  
  34         71  
509 2 50 33     18 if ( $log_file ne '-' && !ref $log_file ) {
510 0         0 my $ok = eval { $fh->close(); 1 };
  0         0  
  0         0  
511 0 0 0     0 if ( !$ok && DEVEL_MODE ) {
512 0         0 Fault("Could not close file handle(): $EVAL_ERROR\n");
513             }
514             }
515             }
516             }
517 554         3861 return;
518             } ## end sub finish
519             1;