Skip to content

Commit

Permalink
Do not generalize class-level @template type in method call
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrejmirtes committed Jan 24, 2024
1 parent 4d5bf20 commit 11268e5
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/Reflection/ResolvedFunctionVariant.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ private function resolveResolvableTemplateTypes(Type $type, TemplateTypeVariance
$references = $type->getReferencedTemplateTypes($positionVariance);

$objectCb = function (Type $type, callable $traverse) use ($references): Type {
if ($type instanceof TemplateType && !$type->isArgument()) {
if (
$type instanceof TemplateType
&& !$type->isArgument()
&& $type->getScope()->getFunctionName() !== null
) {
$newType = $this->resolvedTemplateTypeMap->getType($type->getName());
if ($newType === null || $newType instanceof ErrorType) {
return $traverse($type);
Expand Down
1 change: 1 addition & 0 deletions tests/PHPStan/Analyser/NodeScopeResolverTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ public function dataFileAsserts(): iterable
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2816.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-2816-2.php');
yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-10473.php');

yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-3985.php');

Expand Down
102 changes: 102 additions & 0 deletions tests/PHPStan/Analyser/data/bug-10473.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

namespace Bug10473;

use ArrayAccess;
use function PHPStan\Testing\assertType;

/**
* @template TRow of array<string, mixed>
*/
class Rows
{

/**
* @param list<TRow> $rowsData
*/
public function __construct(private array $rowsData)
{}

/**
* @return Row<TRow>|NULL
*/
public function getByIndex(int $index): ?Row
{
return isset($this->rowsData[$index])
? new Row($this->rowsData[$index])
: NULL;
}
}

/**
* @template TRow of array<string, mixed>
* @implements ArrayAccess<key-of<TRow>, value-of<TRow>>
*/
class Row implements ArrayAccess
{

/**
* @param TRow $data
*/
public function __construct(private array $data)
{}

/**
* @param key-of<TRow> $key
*/
public function offsetExists($key): bool
{
return isset($this->data[$key]);
}

/**
* @template TKey of key-of<TRow>
* @param TKey $key
* @return TRow[TKey]
*/
public function offsetGet($key): mixed
{
return $this->data[$key];
}

public function offsetSet($key, mixed $value): void
{
$this->data[$key] = $value;
}

public function offsetUnset($key): void
{
unset($this->data[$key]);
}

/**
* @return TRow
*/
public function toArray(): array
{
return $this->data;
}

}

class Foo
{

/** @param Rows<array{foo: int<0, max>}> $rows */
public function doFoo(Rows $rows): void
{
assertType('Bug10473\Rows<array{foo: int<0, max>}>', $rows);

$row = $rows->getByIndex(0);

if ($row !== NULL) {
assertType('Bug10473\Row<array{foo: int<0, max>}>', $row);
$fooFromRow = $row['foo'];

assertType('int<0, max>', $fooFromRow);
assertType('array{foo: int<0, max>}', $row->toArray());
}

}

}

0 comments on commit 11268e5

Please sign in to comment.