]> git.donarmstrong.com Git - perltidy.git/commitdiff
add integer option range checks before processing
authorSteve Hancock <perltidy@users.sourceforge.net>
Sun, 5 Nov 2023 02:13:14 +0000 (19:13 -0700)
committerSteve Hancock <perltidy@users.sourceforge.net>
Sun, 5 Nov 2023 02:13:14 +0000 (19:13 -0700)
Previously, integer ranges were checked and fixed after processing started.

CHANGES.md
bin/perltidy
dev-bin/perltidy_random_setup.pl
dev-bin/run_convergence_tests.pl
dev-bin/run_convergence_tests.pl.expect
lib/Perl/Tidy.pm
lib/Perl/Tidy/Formatter.pm
lib/Perl/Tidy/Tokenizer.pm

index 5ae7af23c841139918e24ca76300f9e663eb9af0..e31050c61afb4329c8aef2b136040fd083f81ad8 100644 (file)
@@ -2,6 +2,21 @@
 
 ## 2023 09 12.04
 
+    - All parameters taking integer values are now checked for
+      out-of-range values before processing starts. When a
+      minimum or maximum range is exceeded, the new default
+      behavior is to write a warning message, reset the
+      value to its default setting, and continue.  If this default
+      behavior causes a problem, it can be changed with the new
+      parameter B<--integer-range-check=n>, or B<-irc=n>, as follows:
+
+        n=0  skip check completely (for stress-testing perltidy only)
+        n=1  reset bad values to defaults but do not issue a warning
+        n=2  reset bad values to defaults and issue a warning [DEFAULT]
+        n=3  stop immediately if any values are out of bounds
+
+      The settings n=0 and n=1 are mainly useful for testing purposes.
+
     - The --dump-block-summary (-dbs) option now includes the number of sub
       args in the 'type' column. For example, 'sub(9)' indicates a sub
       with 9 args.  Subs whose arg count cannot easily be determined are
index 0bf970f5527cc962eaca0b1f43e3f8a67392054e..54ec503fb1a5bc2b9061e4c1fb3fab930f46094a 100755 (executable)
@@ -511,6 +511,8 @@ produce one tab for each indentation level and and one for each continuation
 indentation level.  You may want to coordinate the value of B<n> with what your
 display software assumes for the spacing of a tab.
 
+The default is not to use this, which can also be indicated using B<-et=0>.
+
 =item B<-t>,   B<--tabs>
 
 This flag causes one leading tab character to be inserted for each level
@@ -5294,6 +5296,8 @@ string.
 
 B<--dump-defaults> or B<-ddf> will write the default option set to standard output and quit
 
+B<--dump-integer-option-range> or B<-dior> will write a list of comma-separated values. Each line contains the name of an integer option along with its minimum, maximum, and default values.
+
 B<--dump-profile> or B<-dpro>  will write the name of the current
 configuration file and its contents to standard output and quit.
 
@@ -5511,6 +5515,22 @@ this might cause problems if C<use strict> is active.
 
 There is no way to override these rules.
 
+=item B<Handling errors in options which take integer values>
+
+Many of the input parameters take integer values. Before processing
+begins, a check is made to see if any of these integer parameters exceed
+their valid ranges.  The default behavior when a range is exceeded is to
+write a warning message and reset the value to its default setting. This
+default behavior can be changed with the parameter
+B<--integer-range-check=n>, or B<-irc=n>, as follows:
+
+    n=0  skip check completely (for stress-testing perltidy only)
+    n=1  reset bad values to defaults but do not issue a warning
+    n=2  reset bad values to defaults and issue warning [DEFAULT]
+    n=3  stop if any values are out of bounds
+
+The values B<n=0> and B<n=1> are mainly useful for testing purposes.
+
 =back
 
 =head1 HTML OPTIONS
index a3d0bb321c543d1f103bfa0326501e38c56c4725..b326b335027b6c4ab453f312decb97363ddf1619 100755 (executable)
@@ -663,6 +663,7 @@ sub get_num {
     #   - to make 20 random profiles
 
     my @parameters;
+    my $rinteger_option_range;
 
     sub get_parameters {
 
@@ -683,6 +684,44 @@ sub get_num {
         return \@parameters;
     }
 
+    sub get_integer_option_range {
+
+        # get integer ranges
+        use File::Temp qw(tempfile);
+        my ( $fout, $tmpnam ) = File::Temp::tempfile();
+        if ( !$fout ) { die "cannot get tempfile\n" }
+        my %integer_option_range;
+        system "perltidy --dump-integer-option-range>$tmpnam";
+        open( IN, "<", $tmpnam ) || die "cannot open $tmpnam: $!\n";
+        while ( my $line = <IN> ) {
+            next if $line =~ /#/;
+            chomp $line;
+            $line =~ s/\s+//g;
+            my ( $opt, $min, $max, $default ) = split /,/, $line;
+            foreach ( $min, $max, $default ) {
+                if ( $_ eq 'undef' ) { $_ = undef }
+            }
+            $integer_option_range{$opt} = [ $min, $max, $default ];
+        }
+        close IN;
+        unlink $tmpnam if ( -e $tmpnam );
+        return \%integer_option_range;
+    }
+
+    sub dump_integer_option_range {
+        my ($rinteger_option_range) = @_;
+        print {*STDOUT} "Option, min, max, default\n";
+        foreach my $key ( sort keys %{$rinteger_option_range} ) {
+            my ( $min, $max, $default ) = @{ $rinteger_option_range->{$key} };
+            foreach ( $min, $max, $default ) {
+                $_ = 'undef' unless defined($_);
+            }
+            print {*STDOUT} "$key, $min, $max, $default\n";
+        }
+        return;
+    } ## end sub dump_integer_option-range
+
+
     BEGIN {
 
         # Here is a static list of all parameters current as of v.20200907
@@ -991,8 +1030,10 @@ sub get_num {
             @parameters = @{$rparameters_current};
             print STDERR "Updating perltidy parameters....\n";
         }
-    }
 
+       $rinteger_option_range = get_integer_option_range();
+
+    }
     sub make_profiles {
         my $nfiles_old = @{$rprofiles};
         my $case       = 0;
@@ -1363,6 +1404,21 @@ EOM
                     $line   = "--$name=$string";
                 }
                 elsif ( $flag eq '=i' ) {
+
+                    # Override old options with new integer options
+                    if ( defined($rinteger_option_range) ) {
+                        my $irange = $rinteger_option_range->{$name};
+                        if ( defined($irange) ) {
+                            my ( $min, $max, $def ) = @{$irange};
+                            if ( !defined($min) ) { $min = 0 }
+                            if ( !defined($max) ) {
+                                if ( defined($rrange) ) { $max = $rrange->[1] }
+                                if ( !defined($max) )   { $max = 100; }
+                            }
+                            $rrange = [ $min, $max ];
+                        }
+                    }
+
                     my $int;
                     if ( !$rrange ) {
                         $rrange = [ 0, 100 ];
index ed1d5f09002e954a3ae27d586c43030c718dd522..471243c7b02b7d5ffef9e5d574f3c7fa1065b194 100755 (executable)
@@ -368,11 +368,13 @@ sub run_test_cases {
         my @output_history;
         for ( my $iteration = 1 ; $iteration <= $iteration_max ; $iteration++ )
         {
+           # FIXME: remove the -irc flag below when several test cases
+           # are corrected to avoid out-of-bounds integer flags
             my $err = Perl::Tidy::perltidy(
                 source      => \$source,
                 destination => \$output,
                 perltidyrc  => \$params,
-                argv        => '',         # don't let perltidy look at my @ARGV
+                argv        => '-irc=1',  # no warnings messages on bad integers
                 stderr    => \$stderr_string,
                 errorfile => \$errorfile_string, # not used when -se flag is set
             );
index fbcd4a4f93f0f5366f262aa035247fb7250e99f9..ec324363d927a2a9c6db13ae7936e75133f61caa 100644 (file)
@@ -9453,7 +9453,6 @@ my%where=
     my %hash= (Four => '4',
                Five => '5',
                Six => '6') ;
-
     my %hash= (Four => '4',
                Five => '5',
                Six => '6') ;
index a97470e1711913ad3d2e220dd357afb33f279436..53d3683ad4a54c62f973001fbfad655a899f6adf 100644 (file)
@@ -836,7 +836,7 @@ EOM
     # get command line options
     #-------------------------
     my ( $rOpts, $config_file, $rraw_options, $roption_string,
-        $rexpansion, $roption_category, $roption_range )
+        $rexpansion, $roption_category, $roption_range, $rinteger_option_range )
       = process_command_line(
         $perltidyrc_stream,  $is_Windows, $Windows_type,
         $rpending_complaint, $dump_options_type,
@@ -923,8 +923,15 @@ EOM
     #----------------------------------------
     # check parameters and their interactions
     #----------------------------------------
-    $self->check_options( $is_Windows, $Windows_type, $rpending_complaint,
-        $num_files );
+    $self->check_options(
+
+        $is_Windows,
+        $Windows_type,
+        $rpending_complaint,
+        $num_files,
+        $rinteger_option_range
+
+    );
 
     if ($user_formatter) {
         $rOpts->{'format'} = 'user';
@@ -3286,6 +3293,7 @@ sub generate_options {
     my %option_category = ();
     my %option_range    = ();
     my $rexpansion      = \%expansion;
+    my %integer_option_range;
 
     # names of categories in manual
     # leading integers will allow sorting
@@ -3654,6 +3662,7 @@ sub generate_options {
     $add_option->( 'dump-block-types',                'dbt',   '=s' );
     $add_option->( 'dump-cuddled-block-list',         'dcbl',  '!' );
     $add_option->( 'dump-defaults',                   'ddf',   '!' );
+    $add_option->( 'dump-integer-option-range',       'dior',  '!' );
     $add_option->( 'dump-long-names',                 'dln',   '!' );
     $add_option->( 'dump-options',                    'dop',   '!' );
     $add_option->( 'dump-profile',                    'dpro',  '!' );
@@ -3673,6 +3682,7 @@ sub generate_options {
     $add_option->( 'maximum-file-size-mb',            'maxfs', '=i' );
     $add_option->( 'maximum-level-errors',            'maxle', '=i' );
     $add_option->( 'maximum-unexpected-errors',       'maxue', '=i' );
+    $add_option->( 'integer-range-check',             'irc',   '=i' );
 
     #---------------------------------------------------------------------
 
@@ -3701,60 +3711,6 @@ sub generate_options {
         }
     }
 
-    #---------------------------------------
-    # Assign valid ranges to certain options
-    #---------------------------------------
-    # In the future, these may be used to make preliminary checks
-    # hash keys are long names
-    # If key or value is undefined:
-    #   strings may have any value
-    #   integer ranges are >=0
-    # If value is defined:
-    #   value is [qw(any valid words)] for strings
-    #   value is [min, max] for integers
-    #   if min is undefined, there is no lower limit
-    #   if max is undefined, there is no upper limit
-    # Parameters not listed here have defaults
-    %option_range = (
-        'format'                        => [ 'tidy', 'html', 'user' ],
-        'output-line-ending'            => [ 'dos',  'win',  'mac', 'unix' ],
-        'space-backslash-quote'         => [ 0,      2 ],
-        'block-brace-tightness'         => [ 0,      2 ],
-        'keyword-paren-inner-tightness' => [ 0,      2 ],
-        'brace-tightness'               => [ 0,      2 ],
-        'paren-tightness'               => [ 0,      2 ],
-        'square-bracket-tightness'      => [ 0,      2 ],
-
-        'block-brace-vertical-tightness'            => [ 0, 2 ],
-        'brace-follower-vertical-tightness'         => [ 0, 2 ],
-        'brace-vertical-tightness'                  => [ 0, 2 ],
-        'brace-vertical-tightness-closing'          => [ 0, 2 ],
-        'paren-vertical-tightness'                  => [ 0, 2 ],
-        'paren-vertical-tightness-closing'          => [ 0, 2 ],
-        'square-bracket-vertical-tightness'         => [ 0, 2 ],
-        'square-bracket-vertical-tightness-closing' => [ 0, 2 ],
-        'vertical-tightness'                        => [ 0, 2 ],
-        'vertical-tightness-closing'                => [ 0, 2 ],
-
-        'closing-brace-indentation'          => [ 0, 3 ],
-        'closing-paren-indentation'          => [ 0, 3 ],
-        'closing-square-bracket-indentation' => [ 0, 3 ],
-        'closing-token-indentation'          => [ 0, 3 ],
-
-        'closing-side-comment-else-flag' => [ 0, 2 ],
-        'comma-arrow-breakpoints'        => [ 0, 5 ],
-
-        'keyword-group-blanks-before' => [ 0, 2 ],
-        'keyword-group-blanks-after'  => [ 0, 2 ],
-
-        'space-prototype-paren' => [ 0, 2 ],
-        'space-signature-paren' => [ 0, 2 ],
-        'break-after-labels'    => [ 0, 2 ],
-    );
-
-    # Note: we could actually allow negative ci if someone really wants it:
-    # $option_range{'continuation-indentation'} = [ undef, undef ];
-
     #------------------------------------------------------------------
     # DEFAULTS: Assign default values to the above options here, except
     # for 'outfile' and 'help'.
@@ -3818,6 +3774,7 @@ sub generate_options {
       hanging-side-comments
       indent-block-comments
       indent-columns=4
+      integer-range-check=2
       iterations=1
       keep-old-blank-lines=1
       keyword-paren-inner-tightness=1
@@ -3877,11 +3834,217 @@ sub generate_options {
       format-skipping
       default-tabsize=8
 
+      whitespace-cycle=0
+      entab-leading-whitespace=0
+      blank-lines-before-closing-block=0
+      blank-lines-after-opening-block=0
+
       pod2html
       html-table-of-contents
       html-entities
     );
 
+    #---------------------------------------
+    # Assign valid ranges to certain options
+    #---------------------------------------
+    # In the future, these may be used to make preliminary checks
+    # hash keys are long names
+    # If key or value is undefined:
+    #   strings may have any value
+    #   integer ranges are >=0
+    # If value is defined:
+    #   value is [qw(any valid words)] for strings
+    #   value is [min, max] for integers
+    #   if min is undefined, there is no lower limit
+    #   if max is undefined, there is no upper limit
+    # Parameters not listed here have defaults
+    %option_range = (
+        'format'                        => [ 'tidy', 'html', 'user' ],
+        'output-line-ending'            => [ 'dos',  'win',  'mac', 'unix' ],
+        'space-backslash-quote'         => [ 0,      2 ],
+        'block-brace-tightness'         => [ 0,      2 ],
+        'keyword-paren-inner-tightness' => [ 0,      2 ],
+        'brace-tightness'               => [ 0,      2 ],
+        'paren-tightness'               => [ 0,      2 ],
+        'square-bracket-tightness'      => [ 0,      2 ],
+
+        'block-brace-vertical-tightness'            => [ 0, 2 ],
+        'brace-follower-vertical-tightness'         => [ 0, 2 ],
+        'brace-vertical-tightness'                  => [ 0, 2 ],
+        'brace-vertical-tightness-closing'          => [ 0, 3 ],
+        'paren-vertical-tightness'                  => [ 0, 2 ],
+        'paren-vertical-tightness-closing'          => [ 0, 3 ],
+        'square-bracket-vertical-tightness'         => [ 0, 2 ],
+        'square-bracket-vertical-tightness-closing' => [ 0, 3 ],
+        'vertical-tightness'                        => [ 0, 2 ],
+        'vertical-tightness-closing'                => [ 0, 3 ],
+
+        'closing-brace-indentation'          => [ 0, 3 ],
+        'closing-paren-indentation'          => [ 0, 3 ],
+        'closing-square-bracket-indentation' => [ 0, 3 ],
+        'closing-token-indentation'          => [ 0, 3 ],
+
+        'closing-side-comment-else-flag' => [ 0, 2 ],
+        'comma-arrow-breakpoints'        => [ 0, 5 ],
+
+        'keyword-group-blanks-before' => [ 0, 2 ],
+        'keyword-group-blanks-after'  => [ 0, 2 ],
+
+        'space-prototype-paren' => [ 0, 2 ],
+        'space-signature-paren' => [ 0, 2 ],
+        'break-after-labels'    => [ 0, 2 ],
+    );
+
+    # Valid [min,max] ranges of all integer options (type '=i').  This hash is
+    # replacing %option_range, above, for use by sub 'check_options'
+    %integer_option_range = (
+        'blank-lines-after-opening-block'           => [ 0, undef ],
+        'blank-lines-before-closing-block'          => [ 0, undef ],
+        'blank-lines-before-packages'               => [ 0, undef ],
+        'blank-lines-before-subs'                   => [ 0, undef ],
+        'block-brace-tightness'                     => [ 0, 2 ],
+        'block-brace-vertical-tightness'            => [ 0, 2 ],
+        'brace-follower-vertical-tightness'         => [ 0, 2 ],
+        'brace-tightness'                           => [ 0, 2 ],
+        'brace-vertical-tightness'                  => [ 0, 2 ],
+        'brace-vertical-tightness-closing'          => [ 0, 3 ],
+        'break-after-labels'                        => [ 0, 2 ],
+        'break-before-hash-brace'                   => [ 0, 3 ],
+        'break-before-hash-brace-and-indent'        => [ 0, 2 ],
+        'break-before-paren'                        => [ 0, 3 ],
+        'break-before-paren-and-indent'             => [ 0, 2 ],
+        'break-before-square-bracket'               => [ 0, 3 ],
+        'break-before-square-bracket-and-indent'    => [ 0, 2 ],
+        'closing-brace-indentation'                 => [ 0, 3 ],
+        'closing-paren-indentation'                 => [ 0, 3 ],
+        'closing-side-comment-else-flag'            => [ 0, 2 ],
+        'closing-side-comment-interval'             => [ 0, undef ],
+        'closing-side-comment-maximum-text'         => [ 0, undef ],
+        'closing-square-bracket-indentation'        => [ 0, 3 ],
+        'closing-token-indentation'                 => [ 0, 3 ],
+        'comma-arrow-breakpoints'                   => [ 0, 5 ],
+        'continuation-indentation'                  => [ 0, undef ],
+        'cuddled-break-option'                      => [ 0, 2 ],
+        'default-tabsize'                           => [ 0, undef ],
+        'dump-block-minimum-lines'                  => [ 0, undef ],
+        'entab-leading-whitespace'                  => [ 0, undef ],
+        'fixed-position-side-comment'               => [ 0, undef ],
+        'indent-columns'                            => [ 0, undef ],
+        'integer-range-check'                       => [ 0, 3 ],
+        'iterations'                                => [ 0, undef ],
+        'keep-old-blank-lines'                      => [ 0, 2 ],
+        'keyword-group-blanks-after'                => [ 0, 2 ],
+        'keyword-group-blanks-before'               => [ 0, 2 ],
+        'keyword-group-blanks-repeat-count'         => [ 0, undef ],
+        'keyword-paren-inner-tightness'             => [ 0, 2 ],
+        'long-block-line-count'                     => [ 0, undef ],
+        'maximum-consecutive-blank-lines'           => [ 0, undef ],
+        'maximum-fields-per-table'                  => [ 0, undef ],
+        'maximum-file-size-mb'                      => [ 0, undef ],
+        'maximum-level-errors'                      => [ 0, undef ],
+        'maximum-line-length'                       => [ 0, undef ],
+        'maximum-unexpected-errors'                 => [ 0, undef ],
+        'minimum-space-to-comment'                  => [ 0, undef ],
+        'one-line-block-nesting'                    => [ 0, 1 ],
+        'one-line-block-semicolons'                 => [ 0, 2 ],
+        'paren-tightness'                           => [ 0, 2 ],
+        'paren-vertical-tightness'                  => [ 0, 2 ],
+        'paren-vertical-tightness-closing'          => [ 0, 3 ],
+        'short-concatenation-item-length'           => [ 0, undef ],
+        'space-backslash-quote'                     => [ 0, 2 ],
+        'space-prototype-paren'                     => [ 0, 2 ],
+        'space-signature-paren'                     => [ 0, 2 ],
+        'square-bracket-tightness'                  => [ 0, 2 ],
+        'square-bracket-vertical-tightness'         => [ 0, 2 ],
+        'square-bracket-vertical-tightness-closing' => [ 0, 3 ],
+        'starting-indentation-level'                => [ 0, undef ],
+        'vertical-tightness'                        => [ 0, 2 ],
+        'vertical-tightness-closing'                => [ 0, 3 ],
+        'whitespace-cycle'                          => [ 0, undef ],
+    );
+
+    # Enter default values into the integer option range table
+    foreach my $opt (@defaults) {
+        if ( $opt =~ /^(.*)=(\d+)$/ ) {
+            my $key = $1;
+            my $def = $2;
+            if ( defined( $integer_option_range{$key} ) ) {
+                $integer_option_range{$key}[2] = $def;
+            }
+        }
+    }
+
+    # Enter special values which have undef as the default.
+    # Note that cti, vt, and vtc are aliases which are included to work
+    # around an old problem with msdos (see note in check_options).
+    foreach my $key (
+        qw(
+        closing-token-indentation
+        vertical-tightness
+        vertical-tightness-closing
+        fixed-position-side-comment
+        starting-indentation-level
+        )
+      )
+    {
+        if ( defined( $integer_option_range{$key} )
+            && @{ $integer_option_range{$key} } < 3 )
+        {
+            $integer_option_range{$key}[2] = undef;
+        }
+    }
+
+    # Verify that only integers of type =i are in the above list during
+    # development. This will guard against spelling errors.
+    if (DEVEL_MODE) {
+        my %option_flag;
+        my $msg = EMPTY_STRING;
+        foreach my $opt (@option_string) {
+            my $key  = $opt;
+            my $flag = EMPTY_STRING;
+            if ( $key =~ /(.*)(!|=.*|:.*)$/ ) {
+                $key  = $1;
+                $flag = $2;
+            }
+            $option_flag{$key} = $flag;
+        }
+
+        # Be sure all keys of %integer_option_range have option type '=i'
+        foreach my $opt ( keys %integer_option_range ) {
+            my $flag = $option_flag{$opt};
+            if ( !defined($flag) ) { $flag = EMPTY_STRING }
+            if ( $flag ne '=i' ) {
+
+                # If this fault occurs, one of the items in the previous hash
+                # is not type =i, possibly due to incorrect spelling.
+                $msg .=
+"Option '$opt' has an entry in '%integer_option_range' but is not an integer\n";
+            }
+        }
+
+        # Be sure all '=i' options are in %integer_option_range. This is not
+        # strictly necessary but helps insure that nothing was missed.
+        foreach my $opt ( keys %option_flag ) {
+            my $flag = $option_flag{$opt};
+            next if ( $flag ne '=i' );
+            if ( !defined( $integer_option_range{$opt} ) ) {
+                $msg .=
+"Integer option '$opt' is needs an entry in '%integer_option_range'\n";
+            }
+        }
+
+        # look for integer options without default values
+        foreach my $opt ( keys %integer_option_range ) {
+            if ( @{ $integer_option_range{$opt} } < 3 ) {
+                $msg .= "Integer option '$opt' does not have a default value\n";
+            }
+        }
+
+        if ($msg) {
+            Fault($msg);
+        }
+    }
+
     #-----------------------------------------------------------------------
     # Define abbreviations which will be expanded into the above primitives.
     # These may be defined recursively.
@@ -4103,10 +4266,8 @@ q(wbb=% + - * / x != == >= <= =~ !~ < > | & = **= += *= &= <<= &&= -= /= |= >>=
 
     # Uncomment next line to dump all expansions for debugging:
     # dump_short_names(\%expansion);
-    return (
-        \@option_string,   \@defaults, \%expansion,
-        \%option_category, \%option_range
-    );
+    return ( \@option_string, \@defaults, \%expansion, \%option_category,
+        \%option_range, \%integer_option_range, );
 
 } ## end sub generate_options
 
@@ -4171,10 +4332,9 @@ sub _process_command_line {
     }
     else { $glc = undef }
 
-    my (
-        $roption_string,   $rdefaults, $rexpansion,
-        $roption_category, $roption_range
-    ) = generate_options();
+    my ( $roption_string, $rdefaults, $rexpansion,
+        $roption_category, $roption_range, $rinteger_option_range, )
+      = generate_options();
 
     #--------------------------------------------------------------
     # set the defaults by passing the above list through GetOptions
@@ -4259,6 +4419,10 @@ sub _process_command_line {
             dump_defaults( @{$rdefaults} );
             Exit(0);
         }
+        elsif ( $i =~ /^-(dump-integer-option-range|dior)$/ ) {
+            dump_integer_option_range($rinteger_option_range);
+            Exit(0);
+        }
         elsif ( $i =~ /^-(dump-long-names|dln)$/ ) {
             dump_long_names( @{$roption_string} );
             Exit(0);
@@ -4378,6 +4542,7 @@ EOM
                     qw{
                     dump-cuddled-block-list
                     dump-defaults
+                    dump-integer-option_range
                     dump-long-names
                     dump-options
                     dump-profile
@@ -4421,7 +4586,8 @@ EOM
     }
 
     return ( \%Opts, $config_file, \@raw_options, $roption_string,
-        $rexpansion, $roption_category, $roption_range );
+        $rexpansion, $roption_category, $roption_range,
+        $rinteger_option_range );
 } ## end sub _process_command_line
 
 sub make_grep_alias_string {
@@ -4534,8 +4700,16 @@ sub cleanup_word_list {
 
 sub check_options {
 
-    my ( $self, $is_Windows, $Windows_type, $rpending_complaint, $num_files ) =
-      @_;
+    my (
+        $self,
+
+        $is_Windows,
+        $Windows_type,
+        $rpending_complaint,
+        $num_files,
+        $rinteger_option_range
+
+    ) = @_;
 
     # $num_files = number of files to be processed, for error checks
 
@@ -4554,29 +4728,65 @@ sub check_options {
 EOM
     }
 
-    # do not allow negative --indent-columns
-    if ( $rOpts->{'indent-columns'} < 0 ) {
-        $rOpts->{'indent-columns'} = 0;
-    }
-
-    # negative ci is currently allowed provided that ci+i is not negative
-    if ( $rOpts->{'continuation-indentation'} < -$rOpts->{'indent-columns'} ) {
-        $rOpts->{'continuation-indentation'} = -$rOpts->{'indent-columns'};
-    }
-
-    my $sil = $rOpts->{'starting-indentation-level'};
-    if ( defined($sil) && $sil < 0 ) {
-        Die(<<EOM);
---starting-indentation-level=$sil not possible; it should be non-negative
-EOM
+    my $integer_range_check = $rOpts->{'integer-range-check'};
+    if (   !defined($integer_range_check)
+        || $integer_range_check < 0
+        || $integer_range_check > 3 )
+    {
+        $integer_range_check = 2;
+    }
+
+    # Check for integer values out of bounds as follows:
+    #  $integer_range_check=
+    #    0 => skip check completely (for stress-testing perltidy only)
+    #    1 => quietly reset bad values to defaults
+    #    2 => issue warning and reset bad values defaults [DEFAULT]
+    #    3 => stop if any values are out of bounds
+    if ($integer_range_check) {
+        my $Error_message;
+        foreach my $opt ( keys %{$rinteger_option_range} ) {
+            my $range = $rinteger_option_range->{$opt};
+            next unless defined($range);
+            my ( $min, $max, $default ) = @{$range};
+
+            my $val = $rOpts->{$opt};
+            if ( defined($min) && defined($val) && $val < $min ) {
+                $Error_message .= "--$opt=$val but should be >= $min";
+                if ( $integer_range_check < 3 ) {
+                    $rOpts->{$opt} = $default;
+                    my $def = defined($default) ? $default : 'undef';
+                    $Error_message .= "; using default $def";
+                }
+                $Error_message .= "\n";
+            }
+            if ( defined($max) && defined($val) && $val > $max ) {
+                $Error_message .= "--$opt=$val but should be <= $max";
+                if ( $integer_range_check < 3 ) {
+                    $rOpts->{$opt} = $default;
+                    my $def = defined($default) ? $default : 'undef';
+                    $Error_message .= "; using default $def";
+                }
+                $Error_message .= "\n";
+            }
+        }
+        if ($Error_message) {
+            if ( $integer_range_check == 1 ) {
+                ## no warning
+            }
+            elsif ( $integer_range_check == 2 ) {
+                Warn($Error_message);
+            }
+            else {
+                Die($Error_message);
+            }
+        }
     }
 
-    # Since -vt, -vtc, and -cti are abbreviations, but under
+    # Note that -vt, -vtc, and -cti are abbreviations. But under
     # msdos, an unquoted input parameter like vtc=1 will be
     # seen as 2 parameters, vtc and 1, so the abbreviations
     # won't be seen.  Therefore, we will catch them here if
     # they get through.
-
     if ( defined $rOpts->{'vertical-tightness'} ) {
         my $vt = $rOpts->{'vertical-tightness'};
         $rOpts->{'paren-vertical-tightness'}          = $vt;
@@ -5514,6 +5724,19 @@ EOM
     return;
 } ## end sub dump_long_names
 
+sub dump_integer_option_range {
+    my ($rinteger_option_range) = @_;
+    print {*STDOUT} "Option, min, max, default\n";
+    foreach my $key ( sort keys %{$rinteger_option_range} ) {
+        my ( $min, $max, $default ) = @{ $rinteger_option_range->{$key} };
+        foreach ( $min, $max, $default ) {
+            $_ = 'undef' unless defined($_);
+        }
+        print {*STDOUT} "$key, $min, $max, $default\n";
+    }
+    return;
+} ## end sub dump_integer_option-range
+
 sub dump_defaults {
     my @defaults = @_;
     print {*STDOUT} "Default command line options:\n";
index 0b3cbcc35ff8d0c65e8d40ddc95777c942f518d0..18d4372827660dad473f1f3626096debd5313b06 100644 (file)
@@ -13482,8 +13482,7 @@ sub special_indentation_adjustments {
     return unless ( @{$rLL} );
 
     # Initialize the adjusted levels to be the structural levels
-    my @adjusted_levels;
-    foreach ( @{$rLL} ) { push @adjusted_levels, $_->[_LEVEL_] }
+    my @adjusted_levels = map { $_->[_LEVEL_] } @{$rLL};
     $self->[_radjusted_levels_] = \@adjusted_levels;
 
     my $min_starting_level = min(@adjusted_levels);
index 1d281e656d616e4f334a268fd9bef93a2f74e508..91a284d1e1688fc5c1bf97057b07515b2d3662aa 100644 (file)
@@ -5395,12 +5395,11 @@ EOM
             my $pre_tok  = $tok  = $rtokens->[$i];      # get the next pre-token
             my $pre_type = $type = $rtoken_type->[$i];  # and type
 
-            # remember the starting index of this token; we will be updating $i
-            $i_tok = $i;
-
             # re-initialize various flags for the next output token
             (
 
+                # remember the starting index of this token; we will update $i
+                $i_tok,
                 $block_type,
                 $container_type,
                 $type_sequence,
@@ -5409,6 +5408,7 @@ EOM
               )
               = (
 
+                $i,
                 EMPTY_STRING,
                 EMPTY_STRING,
                 EMPTY_STRING,
@@ -5597,9 +5597,9 @@ EOM
                 next;
             }
 
-            # Turn off attribute list on first non-blank, non-bareword.
-            # Added '#' to fix c038 (later moved above).
-            $self->[_in_attribute_list_] &&= 0;
+            # Turn off attribute list on first non-blank, non-bareword,
+            # and non-comment (added to fix c038)
+            $self->[_in_attribute_list_] = 0;
 
             #-------------------------------
             # END NODE 4: a string of digits