From 53f3a0113a7b51b3cc8d1429b28a67ab75287a93 Mon Sep 17 00:00:00 2001
From: Steve Hancock <perltidy@users.sourceforge.net>
Date: Tue, 3 Oct 2023 07:30:51 -0700
Subject: [PATCH] improve error check for unexpected 'elsif' and 'else' (c272)

---
 lib/Perl/Tidy/Tokenizer.pm  | 34 ++++++++++++++++++++++++++--------
 t/snippets/expect/lop.def   |  4 ++++
 t/snippets/expect/lop.lop   |  4 ++++
 t/snippets/lop.in           |  6 +++++-
 t/snippets/packing_list.txt |  4 ++--
 t/snippets20.t              | 10 +++++++++-
 t/snippets21.t              | 10 +++++++++-
 7 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/lib/Perl/Tidy/Tokenizer.pm b/lib/Perl/Tidy/Tokenizer.pm
index 364a0e9c..94b0a97c 100644
--- a/lib/Perl/Tidy/Tokenizer.pm
+++ b/lib/Perl/Tidy/Tokenizer.pm
@@ -41,6 +41,9 @@ use constant DEVEL_MODE   => 0;
 use constant EMPTY_STRING => q{};
 use constant SPACE        => q{ };
 
+# Parent sequence number of tree of containers; must be 1
+use constant SEQ_ROOT => 1;
+
 # Decimal values of some ascii characters for quick checks
 use constant ORD_TAB           => 9;
 use constant ORD_SPACE         => 32;
@@ -1715,7 +1718,7 @@ sub prepare_for_a_new_file {
     $total_depth              = 0;
     $rtotal_depth             = [];
     $rcurrent_sequence_number = [];
-    $next_sequence_number     = 2;    # The value 1 is reserved for SEQ_ROOT
+    $next_sequence_number     = SEQ_ROOT + 1;
 
     $rparen_type                     = [];
     $rparen_semicolon_count          = [];
@@ -4289,35 +4292,50 @@ EOM
             $statement_type = $tok;
         }
 
-        # Check for misplaced 'elsif' and 'else', but allow isolated
-        # else or elsif blocks to be formatted.  This is indicated
-        # by a last noblank token of ';'
+        # Check for unexpected 'elsif'
         elsif ( $tok eq 'elsif' ) {
             if (
-                $last_nonblank_token ne ';'
 
                 ## !~ /^(if|elsif|unless)$/
-                && !$is_if_elsif_unless{$last_nonblank_block_type}
+                !$is_if_elsif_unless{$last_nonblank_block_type}
+
+                # Allow isolated blocks of any kind during editing
+                # by checking for a last noblank token of ';' and no
+                # sequence numbers having been issued (c272). The check
+                # on sequence number is not perfect but good enough.
+                && !(
+                       $last_nonblank_token eq ';'
+                    && $next_sequence_number == SEQ_ROOT + 1
+                )
+
               )
             {
                 $self->warning(
                     "expecting '$tok' to follow one of 'if|elsif|unless'\n");
             }
         }
+
+        # Check for unexpected 'else'
         elsif ( $tok eq 'else' ) {
 
             # patched for SWITCH/CASE
             if (
-                $last_nonblank_token ne ';'
 
                 ## !~ /^(if|elsif|unless|case|when)$/
-                && !$is_if_elsif_unless_case_when{$last_nonblank_block_type}
+                !$is_if_elsif_unless_case_when{$last_nonblank_block_type}
 
                 # patch to avoid an unwanted error message for
                 # the case of a parenless 'case' (RT 105484):
                 # switch ( 1 ) { case x { 2 } else { } }
                 ## !~ /^(if|elsif|unless|case|when)$/
                 && !$is_if_elsif_unless_case_when{$statement_type}
+
+                # Allow isolated blocks of any kind during editing (c272)
+                && !(
+                       $last_nonblank_token eq ';'
+                    && $next_sequence_number == SEQ_ROOT + 1
+                )
+
               )
             {
                 $self->warning(
diff --git a/t/snippets/expect/lop.def b/t/snippets/expect/lop.def
index bd1edbd5..7a3fbc47 100644
--- a/t/snippets/expect/lop.def
+++ b/t/snippets/expect/lop.def
@@ -16,6 +16,10 @@ lc(      $self->mime_attr('content-type')
       || $self->{MIH_DefaultType}
       || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
 elsif ($statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
diff --git a/t/snippets/expect/lop.lop b/t/snippets/expect/lop.lop
index 4c564808..ed978e38 100644
--- a/t/snippets/expect/lop.lop
+++ b/t/snippets/expect/lop.lop
@@ -16,6 +16,10 @@ lc( $self->mime_attr('content-type')
       || $self->{MIH_DefaultType}
       || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
 elsif ( $statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
diff --git a/t/snippets/lop.in b/t/snippets/lop.in
index c889168b..2ee4456d 100644
--- a/t/snippets/lop.in
+++ b/t/snippets/lop.in
@@ -16,6 +16,10 @@ lc( $self->mime_attr('content-type')
         || $self->{MIH_DefaultType}
         || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
-elsif ( $statement_type =~ /^sub\b/
+elsif ($statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
diff --git a/t/snippets/packing_list.txt b/t/snippets/packing_list.txt
index de698c3a..12afe1e2 100644
--- a/t/snippets/packing_list.txt
+++ b/t/snippets/packing_list.txt
@@ -400,6 +400,8 @@
 ../snippets28.t	ame.ame
 ../snippets28.t	ame.def
 ../snippets28.t	git124.def
+../snippets28.t	c269.c269
+../snippets28.t	c269.def
 ../snippets3.t	ce_wn1.ce_wn
 ../snippets3.t	ce_wn1.def
 ../snippets3.t	colin.colin
@@ -540,5 +542,3 @@
 ../snippets9.t	rt98902.def
 ../snippets9.t	rt98902.rt98902
 ../snippets9.t	rt99961.def
-../snippets28.t	c269.c269
-../snippets28.t	c269.def
diff --git a/t/snippets20.t b/t/snippets20.t
index d5a80730..a348b57d 100644
--- a/t/snippets20.t
+++ b/t/snippets20.t
@@ -179,9 +179,13 @@ lc( $self->mime_attr('content-type')
         || $self->{MIH_DefaultType}
         || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
-elsif ( $statement_type =~ /^sub\b/
+elsif ($statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
 ----------
 
         'outdent' => <<'----------',
@@ -663,9 +667,13 @@ lc(      $self->mime_attr('content-type')
       || $self->{MIH_DefaultType}
       || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
 elsif ($statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
 #19...........
         },
     };
diff --git a/t/snippets21.t b/t/snippets21.t
index 3ed043e0..82245651 100644
--- a/t/snippets21.t
+++ b/t/snippets21.t
@@ -126,9 +126,13 @@ lc( $self->mime_attr('content-type')
         || $self->{MIH_DefaultType}
         || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
-elsif ( $statement_type =~ /^sub\b/
+elsif ($statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
 ----------
 
         'nib' => <<'----------',
@@ -335,9 +339,13 @@ lc( $self->mime_attr('content-type')
       || $self->{MIH_DefaultType}
       || 'text/plain' );
 
+if (1) { ... }
+
 # Padding can also remove spaces; here the space after the '(' is lost:
 elsif ( $statement_type =~ /^sub\b/
     || $paren_type[$paren_depth] =~ /^sub\b/ )
+{
+}
 #1...........
         },
 
-- 
2.39.5