Skip to content

fix(parser): allow new Foo IDENT => ... indirect-object form#579

Merged
fglock merged 1 commit intomasterfrom
fix/indirect-new-identifier-arg
Apr 27, 2026
Merged

fix(parser): allow new Foo IDENT => ... indirect-object form#579
fglock merged 1 commit intomasterfrom
fix/indirect-new-identifier-arg

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 27, 2026

Summary

Commit 668a348 ("don't greedily consume new as indirect-object class") introduced a regression that broke the legitimate indirect-object form:

my $req = new HTTP::Request GET => $config;

This is exactly the syntax used in Log::Log4perl::Config line 647, so any program that loaded Log::Log4perl failed to compile — including CPAN itself, which surfaced as a confusing tail-end failure of jcpan -t <anything>:

... All tests successful ...
Undefined subroutine &Log::Log4perl::Logger::cleanup called at .../Log/Log4perl.pm line 5
exit 1

The actual symptom was a syntax error ... near "::Request GET " while requiring Log::Log4perl::Config, which then prevented Log::Log4perl::Logger::cleanup from being defined when its END block fired.

Root cause

The added rejection clause in SubroutineParser.java fired whenever the candidate "class" was unknown and the next token was an IDENTIFIER. That correctly handled myfunc new Foo (4,5) (outer myfunc is a known sub) but also wrongly rejected the bare top-level case new HTTP::Request GET => $url where GET is just the first list-arg.

Fix

Restrict the rejection clause to subExists — i.e. only when the outer call is itself a known subroutine (the original target case). The bare top-level new Class IDENT => ... then parses as Class->new(IDENT => ...) again.

Test plan

  • make passes (all unit tests green)
  • jperl -MLog::Log4perl -e 1 no longer errors
  • jcpan -t URI::Simple exits 0
  • Original disambiguation still works: f new Foo (1) parses as f(Foo->new(1)) (verified with a defined f and Foo::new printing trace output)

Generated with Devin

Commit 668a348 added a check that rejected indirect-object parsing
whenever the candidate "class" was unknown and followed by another
bareword IDENTIFIER. That correctly disambiguated `myfunc new Foo (4,5)`
(where `myfunc` is a known sub) but also wrongly rejected the legitimate
top-level form `new HTTP::Request GET => $url`, breaking
Log::Log4perl::Config (which uses exactly this idiom).

The discriminator is the outer `subExists`: in the buggy case the outer
`myfunc` IS a defined sub, while in `new HTTP::Request GET => ...` the
outer `new` is not declared and is itself the indirect method. Restrict
the new rejection clause to `subExists` so the second case parses as
`HTTP::Request->new(GET => $url)` again.

Repro before:
  $ jcpan -t URI::Simple
  ... Undefined subroutine &Log::Log4perl::Logger::cleanup ...
  $ jperl -MLog::Log4perl -e 1
  syntax error at .../Log/Log4perl/Config.pm line 647, near "::Request GET "

After: both run cleanly; `myfunc new Foo (4,5)` still parses as
`myfunc(Foo->new(4,5))`.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock force-pushed the fix/indirect-new-identifier-arg branch from e74ea5a to 7d3ce70 Compare April 27, 2026 20:12
@fglock fglock merged commit 42b85bb into master Apr 27, 2026
2 checks passed
@fglock fglock deleted the fix/indirect-new-identifier-arg branch April 27, 2026 20:29
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