Skip to content

Resolve self parameters in traits against the required class - #572

Open
pataar wants to merge 1 commit into
PHPantom-dev:mainfrom
pataar:self-in-trait-sees-required-class
Open

pataar wants to merge 1 commit into
PHPantom-dev:mainfrom
pataar:self-in-trait-sees-required-class

Conversation

@pataar

@pataar pataar commented Nov 12, 2026 •

Copy link
Copy Markdown

🤖 This was written by an AI agent on behalf of @pataar (claude-code/claude-opus-5-5).

Resolves parameters typed self inside a trait against the class the trait's @phpstan-require-extends / @phpstan-require-implements names. They resolved to the trait alone, so every member of the required class was reported as unknown on them, while $this in the same trait already saw those members.

PHP flattens a trait into each class that uses it, so a self parameter holds one of those classes, exactly like $this. The parameter now gets the same bounds $this gets from trait_context::extend_this_with_trait_bounds.

Laravel model traits hit this in every model event closure:

/** @phpstan-require-extends Model */
trait Stamps {
    public static function bootStamps(): void {
        static::saving(static function (self $model): void {
            $model->getTable(); // was: Method Stamps::getTable() not found
        });
    }
}

On a ~5,000-file Laravel codebase this removed all 21 remaining unknown_member reports in its model traits; a member neither the trait nor the required class has is still reported.

The check only applies when the parameter resolves to exactly the current trait, so ?self or self|Foo unions are left as they were.

Checks

  • New integration test self_typed_parameter_inside_trait_sees_required_class (method parameter and closure parameter, plus a still-reported unknown member); it fails without the change
  • cargo test: all pass
  • cargo clippy --all-targets -- -D warnings and cargo fmt --check: clean
  • phpantom_lsp analyze --project-root examples/laravel: exactly the 4 expected errors
  • php examples/laravel/assertions.php: all 369 assertions pass

Checklist

  • I have updated CHANGELOG.md
  • I have updated the documentation (README.md, docs/, examples/)
  • I have updated the config schema (config-schema.json)
  • I have added/updated tests to cover my changes
  • If I used AI tools, I have reviewed what they produced and can explain
    the changes and why they are needed.
  • I have read through any AI-generated text, especially documentation,
    the changelog, and other user-facing content, and removed or corrected
    anything inaccurate, unclear, or unnecessary.

A parameter typed `self` inside a trait resolved to the trait alone, so
members of the class named by @phpstan-require-extends or -implements were
reported as unknown on it. PHP flattens the trait into each class using it,
so the parameter holds one of those classes, just like $this. It now gets
the same bounds $this does. Laravel model traits hit this in every model
event closure: static::saving(static function (self $model) { ... }).

This branch has not been deployed

No deployments
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