@@ -376,7 +376,6 @@ private static boolean isFilehandleOperator(String operatorName) {
376376 operatorName .equals ("binmode" ) ||
377377 operatorName .equals ("fileno" ) ||
378378 operatorName .equals ("getc" ) ||
379- operatorName .equals ("open" ) ||
380379 operatorName .equals ("read" ) ||
381380 operatorName .equals ("sysread" ) ||
382381 operatorName .equals ("syswrite" ) ||
@@ -452,22 +451,6 @@ private static void handleListOrHashArgument(Parser parser, ListNode args, boole
452451// for (Node element : argList.elements) {
453452// element.setAnnotation("context", "LIST");
454453// }
455-
456- // Check if this is the first argument and might be a bareword filehandle
457- // For operators like truncate, seek, tell, eof, binmode, etc.
458- if (!argList .elements .isEmpty () && argList .elements .getFirst () instanceof IdentifierNode idNode ) {
459- String operatorName = parser .ctx .symbolTable .getCurrentSubroutine ();
460- if (isFilehandleOperator (operatorName )) {
461- // Try to parse as bareword filehandle
462- Node filehandleNode = FileHandle .parseBarewordHandle (parser , idNode .name );
463- if (filehandleNode != null ) {
464- // It's a known filehandle, use the typeglob reference
465- // filehandleNode.setAnnotation("context", "SCALAR");
466- argList .elements .set (0 , filehandleNode );
467- }
468- }
469- }
470-
471454 args .elements .addAll (argList .elements );
472455 }
473456
@@ -488,37 +471,55 @@ private static boolean handleCodeReferenceArgument(Parser parser, ListNode args,
488471
489472 Node codeRef = parseRequiredArgument (parser , isOptional );
490473 if (codeRef != null ) {
491- // Unwrap reference to code reference: \(&code) should be treated as &code
492- // This matches Perl's behavior where prototype (&) unwraps REF to CODE
493- if (codeRef instanceof OperatorNode opNode && opNode .operator .equals ("\\ " )) {
494- // Check for direct OperatorNode operand (e.g., \@array, \%hash, \$scalar)
495- if (opNode .operand instanceof OperatorNode innerOp ) {
496- if (innerOp .operator .equals ("&" )) {
497- // This is actually a direct \&code, not wrapped in parens - leave as is
498- } else if (innerOp .operator .equals ("@" ) || innerOp .operator .equals ("%" ) || innerOp .operator .equals ("$" )) {
499- // Reject non-code references like \@array, \%hash, \$scalar
500- String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
501- if (subName != null && !subName .isEmpty ()) {
502- parser .throwError ("Type of arg 1 to " + subName + " must be block or sub {} (not single ref constructor)" );
503- } else {
504- parser .throwError ("Type of arg 1 must be block or sub {} (not single ref constructor)" );
505- }
474+ // Check if a bare array, hash, or scalar sigil was passed (e.g., @array, %hash, $scalar)
475+ // These should be rejected for (&) prototype
476+ if (codeRef instanceof OperatorNode opNode ) {
477+ if (opNode .operator .equals ("@" ) || opNode .operator .equals ("%" ) || opNode .operator .equals ("$" )) {
478+ // Reject bare arrays, hashes, and scalars
479+ String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
480+ if (subName != null && !subName .isEmpty ()) {
481+ parser .throwError ("Type of arg 1 to " + subName + " must be block or sub {} (not " +
482+ (opNode .operator .equals ("@" ) ? "array" :
483+ opNode .operator .equals ("%" ) ? "hash" : "scalar variable" ) + ")" );
484+ } else {
485+ parser .throwError ("Type of arg 1 must be block or sub {} (not " +
486+ (opNode .operator .equals ("@" ) ? "array" :
487+ opNode .operator .equals ("%" ) ? "hash" : "scalar variable" ) + ")" );
506488 }
507489 }
508- // Check if it's a ListNode containing operators (e.g., \(&code))
509- else if (opNode .operand instanceof ListNode listNode && !listNode .elements .isEmpty ()) {
510- Node firstElement = listNode .elements .get (0 );
511- if (firstElement instanceof OperatorNode innerOp && innerOp .operator .equals ("&" )) {
512- // Unwrap: use the inner &code node instead of \&code
513- codeRef = innerOp ;
514- } else if (firstElement instanceof OperatorNode innerOp &&
515- (innerOp .operator .equals ("@" ) || innerOp .operator .equals ("%" ) || innerOp .operator .equals ("$" ))) {
516- // Reject non-code references
517- String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
518- if (subName != null && !subName .isEmpty ()) {
519- parser .throwError ("Type of arg 1 to " + subName + " must be block or sub {} (not single ref constructor)" );
520- } else {
521- parser .throwError ("Type of arg 1 must be block or sub {} (not single ref constructor)" );
490+
491+ // Unwrap reference to code reference: \(&code) should be treated as &code
492+ // This matches Perl's behavior where prototype (&) unwraps REF to CODE
493+ if (opNode .operator .equals ("\\ " )) {
494+ // Check for direct OperatorNode operand (e.g., \@array, \%hash, \$scalar)
495+ if (opNode .operand instanceof OperatorNode innerOp ) {
496+ if (innerOp .operator .equals ("&" )) {
497+ // This is actually a direct \&code, not wrapped in parens - leave as is
498+ } else if (innerOp .operator .equals ("@" ) || innerOp .operator .equals ("%" ) || innerOp .operator .equals ("$" )) {
499+ // Reject non-code references like \@array, \%hash, \$scalar
500+ String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
501+ if (subName != null && !subName .isEmpty ()) {
502+ parser .throwError ("Type of arg 1 to " + subName + " must be block or sub {} (not single ref constructor)" );
503+ } else {
504+ parser .throwError ("Type of arg 1 must be block or sub {} (not single ref constructor)" );
505+ }
506+ }
507+ }
508+ // Check if it's a ListNode containing operators (e.g., \(&code))
509+ else if (opNode .operand instanceof ListNode listNode && !listNode .elements .isEmpty ()) {
510+ Node firstElement = listNode .elements .get (0 );
511+ if (firstElement instanceof OperatorNode innerOp && innerOp .operator .equals ("&" )) {
512+ // Unwrap: use the inner &code node instead of \&code
513+ codeRef = innerOp ;
514+ } else if (firstElement instanceof OperatorNode innerOp &&
515+ (innerOp .operator .equals ("@" ) || innerOp .operator .equals ("%" ) || innerOp .operator .equals ("$" ))) {
516+ // Reject non-code references
517+ String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
518+ if (subName != null && !subName .isEmpty ()) {
519+ parser .throwError ("Type of arg 1 to " + subName + " must be block or sub {} (not single ref constructor)" );
520+ } else {
521+ parser .throwError ("Type of arg 1 must be block or sub {} (not single ref constructor)" );
522+ }
522523 }
523524 }
524525 }
@@ -559,8 +560,35 @@ private static int handleBackslashArgument(Parser parser, ListNode args, String
559560 char refType = isGroup ? ' ' : prototype .charAt (prototypeIndex );
560561 String expectedType = isGroup ? "reference" : "reference to " + refType ;
561562
563+ // Set flag for \& to prevent &sub from being called
564+ boolean oldParsingTakeReference = parser .parsingTakeReference ;
565+ if (refType == '&' ) {
566+ parser .parsingTakeReference = true ;
567+ }
568+
562569 Node referenceArg = parseArgumentWithComma (parser , isOptional , needComma , expectedType );
570+
571+ // Restore flag
572+ parser .parsingTakeReference = oldParsingTakeReference ;
563573 if (referenceArg != null ) {
574+ // For \& prototype, check for invalid forms like &foo(), foo(), or bareword foo
575+ if (refType == '&' ) {
576+ String subName = parser .ctx .symbolTable .getCurrentSubroutine ();
577+ String subNamePart = (subName == null || subName .isEmpty ()) ? "" : " to " + subName ;
578+
579+ // Check for function calls: &foo() or foo()
580+ if (referenceArg instanceof BinaryOperatorNode binOp && binOp .operator .equals ("(" )) {
581+ parser .throwError ("Type of arg " + (args .elements .size () + 1 ) + subNamePart +
582+ " must be subroutine (not subroutine entry)" );
583+ }
584+
585+ // Check for bareword (identifier without &)
586+ if (referenceArg instanceof IdentifierNode ) {
587+ parser .throwError ("Type of arg " + (args .elements .size () + 1 ) + subNamePart +
588+ " must be subroutine (not subroutine entry)" );
589+ }
590+ }
591+
564592 // Check if user passed an explicit reference when prototype expects auto-reference
565593 if (refType == '$' && referenceArg instanceof OperatorNode opNode &&
566594 opNode .operator .equals ("\\ " )) {
0 commit comments