Subject: bug#79269: 30.1; cperl-mode does not handle signatures correctly

From: Harald Jörg <[email-masked]>
Date: Wed, 20 Aug 2025 13:45:17 +0000

Eli Zaretskii <[email-masked]> writes:

>> From: John Ciolfi <[email-masked]>
>> Date: Tue, 19 Aug 2025 12:05:37 -0400
>> 
>> 
>> 1. In a temporary directory, say ~/tmp/, get latest cperl-mode.el from
>>    https://github.com/emacs-mirror/emacs/blob/master/lisp/progmodes/cperl-mode.el
>>    I used the Jul-28-2025 version
>>    https://github.com/emacs-mirror/emacs/blob/cef4302d309e26c2191821ba998518fb89fff54e/lisp/progmodes/cperl-mode.el
>> 
>> 2. cd ~/tmp
>> 
>> 3. emacs -Q -L .           # Using emacs 30.1
>> 
>> 4. M-x byte-compile-file RET cperl-mode.el RET
>> 
>> 5. C-x C-f foo.pl
>> 
>>    C-x h   # select all of foo.pl
>>    C-M-\   # indent foo.pl
>> 
>> Here's the indented foo.pl. Notice that there are a couple problems
>> which appears to be due to the incorrect handling of perl signatures.
>> Note, perl-mode.el seems to handle signatures correctly, so maybe that
>> code can be borrowed for cperl-mode.el?
>> 
>> 
>> # -*- mode: cperl -*-
>> use strict;
>> use warnings;
>> use experimental 'signatures';
>> 
>> foo(1);
>> 
>> sub foo (
>> 	 $in1,
>> 	 $optionsHPtr = {},
>> 	 $otherOption1 = 1,	# Bug: wrong face for this option
>> 	) {
>>  
>>    my $a = 1;  # Bug: should be indented by 2 spaces
>> 
>>  # Bug: following are not indented due to use of signatures
>>  my $b = 2;
>>  return $a + $b + $in1;
>> }
>
> Thanks.
>
> Harald, any comments or suggestions?

Yes :)

I am glad to see some different code layouts than I use myself,
especially when they expose bugs.  I did expect this to happen with
signatures, and even more with classes.  There isn't much code in the
wild using these features.

I am afraid that borrowing from perl-mode.el is no viable solution.
perl-mode treats signatures correctly ... by not recognizing them at
all.

As for the indentation, I am optimistic that this can be fixed by
fiddling with some regular expressions.  In a slightly different coding
style where the opening brace of the subroutine is on a new line,
indentation works as it should:

# ----------------------------------------------------------
use experimental 'signatures';

foo(1);

sub foo (
	 $in1,
	 $optionsHPtr = {},
	 $otherOption1 = 1,	# Bug: wrong face for this option
	)
{
   my $a = 1;  # Bug: should be indented by 2 spaces

 # Bug: following are not indented due to use of signatures
 my $b = 2;
 return $a + $b + $in1;
}
# ----------------------------------------------------------

So I'll try to prepare a patch and add one or more test cases.

As for the wrong face: This is a bug where cperl-mode trips over its
ambitions.  Per default, cperl-mode highlights scalar variables when
they are declared, but not when they are used.  A signature is (sort of)
a declaration, so variables ought to have variable-face applied.  The
challenge comes when declarations come with initializers.  For example:

  sub foo ($editor = $emacs) {
     ...
  }

Here, "$editor" is a declaration whereas "$emacs" is use of a variable
declared elsewhere.  Initializers are (almost) arbitrary Perl
expressions, so we need to compromise how, and how far we are looking
for more declarations in the same signature.  The current implementation
is rather rigid and takes the first "{" as the beginning of the
subroutine body and therefore fails to find declarations after the
initializer "{}".  I am optimistic that I can come up with a more
flexible solution.  There will always be limitations, but the empty hash
reference "{}" is a reasonable case to treat correctly.

BTW: perl-mode does not distinguish between declaration and use and
always applies variable-face.  In cperl-mode this behaviour can be
customized by setting cperl-highlight-variables-indiscriminately to
non-nil.

-- 
Cheers,
haj