Skip to content

fix(parser+autoclean): ${'Pkg::'} string literal; guard Class::MOP get_method_list#615

Merged
fglock merged 1 commit intomasterfrom
fix/braced-deref-single-quote
Apr 29, 2026
Merged

fix(parser+autoclean): ${'Pkg::'} string literal; guard Class::MOP get_method_list#615
fglock merged 1 commit intomasterfrom
fix/braced-deref-single-quote

Conversation

@fglock
Copy link
Copy Markdown
Owner

@fglock fglock commented Apr 29, 2026

Summary

Two fixes uncovered by jcpan -t MooseX::Types:

1. Parser: ${'Pkg::'} must be a string literal, not legacy $'foo

Inside ${...} braces, a leading ' starts a string literal expression. Previously the parser still applied the legacy single-quote-as-package-separator rule (used for unbraced $'foo => $main::foo), so \%{'Foo::'} parsed as the bogus identifier ::Foo::' and resolved to a different, empty stash from \%{"Foo::"}.

Concrete symptoms:

  • keys %Foo:: listed an entry, but exists ${'Foo::'}{name} returned false.
  • delete ${'Pkg::'}{name} did not invalidate Pkg->can("name") — the deleted-but-still-callable sub broke MooseX::Types' t/16_introspection.t.

Fix in IdentifierParser.parseComplexIdentifierInner: when insideBraces is true and the first token is ', return null so parseBracedVariable falls through to parseBlock, which evaluates the string literal correctly. The unbraced legacy $'foo path is unchanged.

2. namespace::autoclean: guard get_method_list

_method_check unconditionally called $meta->get_method_list whenever Class::MOP::class_of($package) returned a metaobject. For non-class packages the metaobject is a bare Class::MOP::Package (no HasMethods mixin), producing

Can't locate object method "get_method_list" via package "Class::MOP::Package"
  at jar:PERL5LIB/namespace/autoclean.pm line 122

on every on_scope_end callback. Now guarded with $meta->can('get_method_list'), falling through to the plain-class subname-based detection otherwise.

Result

MooseX-Types-0.51: was Files=20, Tests=250, Result: FAIL (1 failure + warning spam after every test). Now Result: PASS with no warnings.

Test plan

  • make (full unit suite) passes
  • ./jcpan -t MooseX::Types -> all 20 test files PASS, 250/250 tests
  • Minimal regression check: \%{'Foo::'} and \%{"Foo::"} now refer to the same stash; delete ${'Pkg::'}{name} properly invalidates Pkg->can("name").

Generated with Devin

…et_method_list

Two fixes surfaced by `jcpan -t MooseX::Types`:

1. Parser: inside ${...}, a leading single quote starts a string literal,
   not the legacy $'foo => $main::foo package separator. Previously,
   \%{'Foo::'} parsed as the bogus identifier ::Foo::' and resolved to a
   different (empty) stash from \%{"Foo::"}. Net effect: keys %Foo::
   listed entries that exists ${'Foo::'}{name} could not find, and
   delete ${'Pkg::'}{name} did not invalidate Pkg->can("name"). This
   broke MooseX::Types' t/16_introspection.t. Fix in
   IdentifierParser.parseComplexIdentifierInner: when insideBraces and
   the first token is ', return null so parseBracedVariable falls
   through to parseBlock and evaluates the string literal.

2. namespace::autoclean: _method_check unconditionally called
   $meta->get_method_list whenever Class::MOP::class_of returned a
   metaobject. For non-class packages that yields a bare
   Class::MOP::Package (no HasMethods mixin), producing a
   "Can't locate object method get_method_list" warning on every
   on_scope_end callback. Guard with $meta->can('get_method_list')
   and fall through to the plain-class detection otherwise.

After: MooseX-Types 0.51 goes from Files=20, Tests=250, FAIL (1 fail +
warning spam after every test) to PASS with no warnings.

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

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@fglock fglock merged commit c4f630c into master Apr 29, 2026
2 checks passed
@fglock fglock deleted the fix/braced-deref-single-quote branch April 29, 2026 10:10
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