@@ -551,6 +551,61 @@ private static void handlePlusArgument(Parser parser, ListNode args, boolean isO
551551 }
552552 }
553553
554+ /**
555+ * Unwraps unary plus from expressions like +(%hash) or +(@array) for backslash prototypes.
556+ * In Perl, +() is used for disambiguation but should be transparent for \% and \@ prototypes.
557+ *
558+ * @param arg The argument node to potentially unwrap
559+ * @param refType The reference type from the prototype ('%', '@', etc.)
560+ * @return The unwrapped node if applicable, or the original node
561+ */
562+ private static Node unwrapUnaryPlus (Node arg , char refType ) {
563+ // Only unwrap for \% and \@ prototypes
564+ if (refType != '%' && refType != '@' ) {
565+ return arg ;
566+ }
567+
568+ // Check if arg is unary plus: OperatorNode with operator "+"
569+ if (!(arg instanceof OperatorNode plusOp ) || !plusOp .operator .equals ("+" )) {
570+ return arg ;
571+ }
572+
573+ // Get the operand of the unary plus
574+ Node operand = plusOp .operand ;
575+
576+ // If the operand is a ListNode with a single element, extract it
577+ if (operand instanceof ListNode listNode && listNode .elements .size () == 1 ) {
578+ operand = listNode .elements .get (0 );
579+ }
580+
581+ // Check if the operand is the expected type (hash or array variable)
582+ if (operand instanceof OperatorNode varOp ) {
583+ String expectedSigil = (refType == '%' ) ? "%" : "@" ;
584+ if (varOp .operator .equals (expectedSigil )) {
585+ return operand ;
586+ }
587+ }
588+
589+ // Also check if operand is directly a ListNode containing a hash/array expression
590+ // This handles cases like +(%hash) where the parentheses create a list context
591+ if (operand instanceof ListNode listNode ) {
592+ // In Perl +(%hash) should pass the hash itself, not a list
593+ // Look for a single hash or array variable inside the list
594+ if (listNode .elements .size () == 1 ) {
595+ Node element = listNode .elements .get (0 );
596+ if (element instanceof OperatorNode varOp ) {
597+ String expectedSigil = (refType == '%' ) ? "%" : "@" ;
598+ if (varOp .operator .equals (expectedSigil )) {
599+ return element ;
600+ }
601+ }
602+ }
603+ }
604+
605+ // Return original if no match
606+ return arg ;
607+ }
608+
554609 private static int handleBackslashArgument (Parser parser , ListNode args , String prototype , int prototypeIndex ,
555610 boolean isOptional , boolean needComma ) {
556611 if (prototypeIndex >= prototype .length ()) {
@@ -572,6 +627,9 @@ private static int handleBackslashArgument(Parser parser, ListNode args, String
572627 // Restore flag
573628 parser .parsingTakeReference = oldParsingTakeReference ;
574629 if (referenceArg != null ) {
630+ // Handle +(%hash) and +(@array) constructs for \% and \@ prototypes
631+ // The unary + is used for disambiguation but should be transparent for prototypes
632+ referenceArg = unwrapUnaryPlus (referenceArg , refType );
575633 // For \& prototype, check for invalid forms like &foo(), foo(), or bareword foo
576634 if (refType == '&' ) {
577635 String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
0 commit comments