Skip to content

fix(parser): don't greedily consume new as indirect-object class#577

Merged
fglock merged 1 commit intomasterfrom
fix/indirect-method-bareword-disambig
Apr 27, 2026
Merged

fix(parser): don't greedily consume new as indirect-object class#577
fglock merged 1 commit intomasterfrom
fix/indirect-method-bareword-disambig

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 27, 2026

Summary

Fixes a parser bug where PerlOnJava greedily consumed a bareword after a function name as an indirect-object class, breaking common idioms like:

croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR, "...")

Found while running jcpan -t XML::Generator — t/DOM.t failed with:

Can't locate object method "getName" via package "Undefined subroutine &XML::DOM::DOMException called at .../XML/DOM.pm line 663"

Root cause

In SubroutineParser.java, when seeing myfunc new Foo (4,5):

  • subName = myfunc, peek = new (IDENTIFIER) → enter indirect-method block
  • packageName = new, peek = Foo (IDENTIFIER)
  • Existing logic accepted new as the indirect-object class for myfunc, producing new->myfunc(Foo(4,5)) — which then called Foo as a function and failed.

Real Perl parses this as myfunc(Foo->new(4,5)).

Fix

When the candidate "class" identifier is not a known package and not a known sub, AND the next token is itself a bareword identifier, reject the outer indirect form. The candidate is most likely a method name (e.g. new), and the inner packageName IDENT pair is the real indirect-object call.

Minimal repro

sub myfunc { print ref($_[0]), "\n" }
package Foo;
sub new { my $c = shift; bless {}, $c }
package main;
myfunc new Foo (4,5);

Before: Undefined subroutine &main::Foo called
After: Foo

Test plan

  • make (full unit test suite passes)
  • jcpan -t XML::Generator: was 154/158 with 4 failures in t/DOM.t → 158/158 PASS
  • Standalone new Pkg (args) still works (was already working)
  • myfunc(new Pkg (args)) with explicit parens still works
  • Indirect form with known package classes (myfunc Foo bar where Foo is declared) still works

Generated with Devin

When parsing `myfunc new Foo (4,5)`, the parser was treating `new` as
the indirect-object class for `myfunc`, producing
`new->myfunc(Foo(4,5))` and then failing with
"Undefined subroutine &main::Foo".

Real Perl parses this as `myfunc(Foo->new(4,5))`. The disambiguation
rule: if the candidate "class" identifier is itself an unknown package
and is followed by another bareword identifier, prefer the inner
indirect-object interpretation (the candidate is actually a method
name).

This fixes XML::Generator's t/DOM.t which uses
`croak new XML::DOM::DOMException (WRONG_DOCUMENT_ERR, ...)`
inside XML::DOM.pm.

Before:
  $ ./jperl -e 'sub f{}; package Foo; sub new{}; package main; f new Foo (1)'
  Undefined subroutine &main::Foo called

After:
  (parses correctly as f(Foo->new(1)))

Generated with [Devin](https://app.devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock merged commit fbae791 into master Apr 27, 2026
2 checks passed
@fglock fglock deleted the fix/indirect-method-bareword-disambig branch April 27, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant