Skip to content

fix: Scope interfaces implemented by enums#1233

Open
danielmaier42 wants to merge 1 commit into
humbug:mainfrom
danielmaier42:fix/enum-implements-interface-scoping
Open

fix: Scope interfaces implemented by enums#1233
danielmaier42 wants to merge 1 commit into
humbug:mainfrom
danielmaier42:fix/enum-implements-interface-scoping

Conversation

@danielmaier42

Copy link
Copy Markdown

Problem

Fixes #1119.

When an enum implements an interface, PHP-Scoper prefixes the use statement for the interface but leaves the interface name in the implements clause untouched. The generated code then references an interface that no longer exists under that name.

Reproduction

Input:

<?php

namespace Acme;

use App\Contracts\HasColor;

enum Status: string implements HasColor {
    case DRAFT = 'draft';
}

Output before this fix:

<?php

namespace Humbug\Acme;

use Humbug\App\Contracts\HasColor;
enum Status : string implements \App\Contracts\HasColor  // ← not scoped
{
    case DRAFT = 'draft';
}

The use statement was rewritten to Humbug\App\Contracts\HasColor, but implements still points at the original \App\Contracts\HasColor, which is not defined in the scoped output.

Cause

NameStmtPrefixer only prefixes a name when its parent node is in the SUPPORTED_PARENT_NODE_CLASS_NAMES allow-list. That list contained Class_ and Interface_ but not Enum_, so names appearing in an enum's implements clause were skipped entirely.

Fix

Add PhpParser\Node\Stmt\Enum_ to the supported parent nodes. Enum implements clauses are now scoped exactly like class ones:

namespace Humbug\Acme;

use Humbug\App\Contracts\HasColor;
enum Status : string implements HasColor
{
    case DRAFT = 'draft';
}

Tests

  • Updated the existing enum with interface spec, whose expected output (implements \HasColor) had codified the buggy behaviour — a user-defined interface in the global namespace should stay unqualified (implements HasColor), matching how classes are handled.
  • Added two specs covering the interface being imported via a use statement and via a fully-qualified name.

Full spec suite (768 cases) and the rest of the PHPUnit suite pass locally.

🤖 Generated with Claude Code

`NameStmtPrefixer` only prefixed names whose parent node was in an
allow-list of supported node types. That list contained `Class_` and
`Interface_` but not `Enum_`, so the interface names in an
`enum X implements Y` declaration were left untouched while the matching
`use` statement was prefixed, producing code that referenced a
non-existent (unprefixed) interface.

Add `Enum_` to the supported parent nodes so enum `implements` clauses
are scoped exactly like class ones.

Closes humbug#1119

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.

Scoping Interfaces implemented by Enums

1 participant