Skip to content

Do not treat method templates resolved to their bound as unresolved - #6823

Open
GromNaN wants to merge 1 commit into
phpstan:2.3.xfrom
GromNaN:method-template-bound-assert
Open

GromNaN wants to merge 1 commit into
phpstan:2.3.xfrom
GromNaN:method-template-bound-assert

Conversation

@GromNaN

@GromNaN GromNaN commented Oct 24, 2026 •

Copy link
Copy Markdown

A @phpstan-assert is not reported as redundant when its type contains a method template inferred as mixed. Example from doctrine/orm#12513:

/**
 * @template TItem
 * @param TItem[] $parameter
 * @phpstan-assert list<TItem> $parameter
 */
private static function validateVariadicParameter(array $parameter): void
{
    if (array_is_list($parameter)) {
        return;
    }

    throw new \InvalidArgumentException();
}

/**
 * @no-named-arguments
 */
public function groupBy(string ...$groupBy): void
{
    self::validateVariadicParameter($groupBy); // reported
}

/**
 * @no-named-arguments
 */
public function select(mixed ...$select): void
{
    self::validateVariadicParameter($select); // not reported
}

$groupBy and $select are both lists because of @no-named-arguments, so the assertion is already satisfied in both cases. Only groupBy() is reported today.

Every template has a bound, mixed when no of is declared. For a class template, "resolved type equals bound" means the call did not specify it, so the template is unknown. For a method template, the type is inferred from the arguments: TItem resolved to mixed is a real answer that happens to equal the default bound. The old check treated both as unresolved.

The bound fallback now applies to class templates only (TemplateTypeScope::getFunctionName() === null). Method templates resolved to their bound are treated as resolved, so their assertion is applied and the already-satisfied report runs.

Class templates that are really unresolved are still skipped. The native mirror in turbo-ext/src/DefaultNarrowingHelper.cpp applies the same change, and regression tests are in tests/PHPStan/Rules/Comparison/data/no-named-arguments-assert.php.

@GromNaN
GromNaN marked this pull request as ready for review October 24, 2026 19:15
@phpstan-bot

Copy link
Copy Markdown
Collaborator

This pull request has been marked as ready for review.

The unresolved template check skipped every template whose resolved type
equals its bound. That is the expected fallback for class templates the
call does not provide, but a method template is inferred from the
arguments: a template inferred as `mixed` equals its default bound and was
wrongly skipped. This suppressed the already-narrowed report for
@phpstan-assert calls whose asserted type was already satisfied.
@GromNaN
GromNaN force-pushed the method-template-bound-assert branch from 879663f to 14f3893 Compare October 24, 2026 20:06
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.

2 participants