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   335 use strict;
  39         134  
  39         1222  
10 39     39   262 use warnings;
  39         103  
  39         1816  
11             our $VERSION = '20230909';
12 39     39   353 use English qw( -no_match_vars );
  39         133  
  39         484  
13              
14 39     39   16751 use constant DEVEL_MODE => 0;
  39         140  
  39         2404  
15 39     39   298 use constant EMPTY_STRING => q{};
  39         107  
  39         2006  
16 39     39   318 use constant SPACE => q{ };
  39         116  
  39         8461  
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   341 use constant DEFAULT_LOGFILE_GAP => 50;
  39         111  
  39         29136  
45              
46             sub new {
47              
48 559     559 0 3268 my ( $class, @args ) = @_;
49              
50 559         5444 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 559         4555 my %args = ( %defaults, @args );
60              
61 559         2238 my $rOpts = $args{rOpts};
62 559         1546 my $log_file = $args{log_file};
63 559         1471 my $warning_file = $args{warning_file};
64 559         1330 my $fh_stderr = $args{fh_stderr};
65 559         1406 my $display_name = $args{display_name};
66 559         1325 my $is_encoded_data = $args{is_encoded_data};
67              
68 559 100       2089 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 559 100 100     3953 if ( !$fh_warnings && !ref($warning_file) ) {
72 16 50       525 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 559 100       2463 ? $rOpts->{'logfile-gap'}
82             : DEFAULT_LOGFILE_GAP;
83 559 100       2063 if ( $logfile_gap == 0 ) { $logfile_gap = 1 }
  1         2  
84              
85 559 50       2730 my $filename_stamp = $display_name ? $display_name . ':' : "??";
86 559 50       1937 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 559         16672 _save_logfile => $rOpts->{'logfile'},
111             }, $class;
112             } ## end sub new
113              
114             sub get_input_stream_name {
115 558     558 0 1444 my $self = shift;
116 558         1848 return $self->{_input_stream_name};
117             }
118              
119             sub set_last_input_line_number {
120 559     559 0 1778 my ( $self, $lno ) = @_;
121 559         1486 $self->{_last_input_line_number} = $lno;
122 559         1472 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 558     558 0 1475 my $self = shift;
164 558 100       2372 if ( !$self->{_wrote_line_information_string} ) {
165 1         3 $self->write_logfile_entry("Last line\n\n");
166             }
167 558         1585 $self->{_at_end_of_file} = 1;
168 558         1359 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   377 use constant MAX_PRINTED_CHARS => 35;
  39         140  
  39         41043  
173              
174             sub black_box {
175 6     6 0 19 my ( $self, $line_of_tokens, $output_line_number ) = @_;
176 6         18 my $input_line = $line_of_tokens->{_line_text};
177 6         14 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         9 $self->{_line_of_tokens} = $line_of_tokens;
181 6         11 $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     39 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       8 $structural_indentation_level = 0
195             if ( $structural_indentation_level < 0 );
196 3         5 $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         11 $out_str = ( '.' x $structural_indentation_level ) . $out_str;
201              
202 3 50       8 if ( length($out_str) > MAX_PRINTED_CHARS ) {
203 0         0 $out_str = substr( $out_str, 0, MAX_PRINTED_CHARS ) . " ....";
204             }
205 3         13 $self->logfile_output( EMPTY_STRING, "$out_str\n" );
206             }
207 6         20 return;
208             } ## end sub black_box
209              
210             sub write_logfile_entry {
211              
212 7228     7228 0 15499 my ( $self, @msg ) = @_;
213              
214             # add leading >>> to avoid confusing error messages and code
215 7228         26937 $self->logfile_output( ">>>", "@msg" );
216 7228         15670 return;
217             } ## end sub write_logfile_entry
218              
219             sub write_column_headings {
220 2     2 0 4 my $self = shift;
221              
222 2         5 $self->{_wrote_column_headings} = 1;
223 2         3 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         3 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 4272     4272 0 6626 my $self = shift;
242 4272         6966 my $line_of_tokens = $self->{_line_of_tokens};
243 4272         7282 my $input_line_number = $line_of_tokens->{_line_number};
244 4272         7314 my $line_information_string = EMPTY_STRING;
245 4272 100       8792 if ($input_line_number) {
246              
247 4         14 my $output_line_number = $self->{_output_line_number};
248 4         15 my $brace_depth = $line_of_tokens->{_curly_brace_depth};
249 4         8 my $paren_depth = $line_of_tokens->{_paren_depth};
250 4         22 my $square_bracket_depth = $line_of_tokens->{_square_bracket_depth};
251             my $guessed_indentation_level =
252 4         9 $line_of_tokens->{_guessed_indentation_level};
253              
254 4         9 my $structural_indentation_level = $line_of_tokens->{_level_0};
255              
256 4 100       18 $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         7 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       19 $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         16 my $nesting_string =
276             "($paren_depth [$square_bracket_depth {$brace_depth";
277 4         10 my $nesting_string_new = $line_of_tokens->{_nesting_tokens_0};
278 4         6 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         12 $nesting_string =
284             $nesting_string_new . SPACE x ( 8 - length($nesting_string_new) );
285             }
286             $line_information_string =
287 4         22 "L$input_line_number:$output_line_number$extra_space i$guessed_indentation_level:$structural_indentation_level $ci_level $bk $nesting_string";
288             }
289 4272         9959 return $line_information_string;
290             } ## end sub make_line_information_string
291              
292             sub logfile_output {
293 7231     7231 0 14086 my ( $self, $prompt, $msg ) = @_;
294 7231 50       17219 return if ( $self->{_block_log_output} );
295              
296 7231         11782 my $routput_array = $self->{_output_array};
297 7231 100 66     24163 if ( $self->{_at_end_of_file} || !$self->{_use_prefix} ) {
298 2959         4297 push @{$routput_array}, "$msg";
  2959         7514  
299             }
300             else {
301 4272         9824 my $line_information_string = $self->make_line_information_string();
302 4272         7691 $self->{_wrote_line_information_string} = 1;
303              
304 4272 100       7979 if ($line_information_string) {
305 4         12 push @{$routput_array}, "$line_information_string $prompt$msg";
  4         16  
306             }
307             else {
308 4268         6586 push @{$routput_array}, "$msg";
  4268         12085  
309             }
310             }
311 7231         12973 return;
312             } ## end sub logfile_output
313              
314             sub get_saw_brace_error {
315 558     558 0 1304 my $self = shift;
316 558         2386 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   360 use constant BRACE_WARNING_LIMIT => 10;
  39         141  
  39         10938  
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 111 my ( $self, $msg, $msg_line_number ) = @_;
347 32         87 my $rOpts = $self->{_rOpts};
348              
349             # these appear in .ERR output only if -w flag is used
350 32 50       107 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         76 $self->{_complaint_count}++;
357 32 50       98 if ($msg_line_number) {
358              
359             # TODO: consider using same prefix as warning()
360 32         107 $msg = $msg_line_number . ':' . $msg;
361             }
362 32         119 $self->write_logfile_entry($msg);
363             }
364 32         134 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   360 use constant WARNING_LIMIT => 50;
  39         195  
  39         37268  
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 558     558 0 1330 my $self = shift;
465 558         1815 return $self->{_save_logfile};
466             } ## end sub get_save_logfile
467              
468             sub finish {
469              
470             # called after all formatting to summarize errors
471 559     559 0 1765 my ($self) = @_;
472              
473 559         1809 my $warning_count = $self->{_warning_count};
474 559         2405 my $save_logfile = $self->{_save_logfile};
475 559         1397 my $log_file = $self->{_log_file};
476 559         1381 my $msg_line_number = $self->{_last_input_line_number};
477              
478 559 50       2010 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 559 100       1865 if ($save_logfile) {
503 2         9 my $is_encoded_data = $self->{_is_encoded_data};
504 2         10 my ( $fh, $filename ) =
505             Perl::Tidy::streamhandle( $log_file, 'w', $is_encoded_data );
506 2 50       9 if ($fh) {
507 2         6 my $routput_array = $self->{_output_array};
508 2         4 foreach my $line ( @{$routput_array} ) { $fh->print($line) }
  2         8  
  34         62  
509 2 0 33     16 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 559         3224 return;
520             } ## end sub finish
521             1;