Skip to content

Commit

Permalink
Fix crash when template types map and variances map are not of equal …
Browse files Browse the repository at this point in the history
…length
  • Loading branch information
ondrejmirtes committed Oct 30, 2023
1 parent c6fad64 commit 1308c52
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 13 deletions.
38 changes: 25 additions & 13 deletions src/Reflection/ClassReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,21 +241,26 @@ public function getName(): string

public function getDisplayName(bool $withTemplateTypes = true): string
{
$name = $this->displayName;

if (
$withTemplateTypes === false
|| $this->resolvedTemplateTypeMap === null
|| count($this->resolvedTemplateTypeMap->getTypes()) === 0
) {
return $name;
return $this->displayName;
}

$templateTypes = [];
$variances = $this->getCallSiteVarianceMap()->getVariances();
foreach ($this->getActiveTemplateTypeMap()->getTypes() as $name => $templateType) {
$variance = $variances[$name] ?? null;
if ($variance === null) {
continue;
}

$templateTypes[] = TypeProjectionHelper::describe($templateType, $variance, VerbosityLevel::typeOnly());
}

return $name . '<' . implode(',', array_map(
static fn (Type $type, TemplateTypeVariance $variance): string => TypeProjectionHelper::describe($type, $variance, VerbosityLevel::typeOnly()),
$this->getActiveTemplateTypeMap()->getTypes(),
$this->getCallSiteVarianceMap()->getVariances(),
)) . '>';
return $this->displayName . '<' . implode(',', $templateTypes) . '>';
}

public function getCacheKey(): string
Expand All @@ -268,11 +273,18 @@ public function getCacheKey(): string
$cacheKey = $this->displayName;

if ($this->resolvedTemplateTypeMap !== null) {
$cacheKey .= '<' . implode(',', array_map(
static fn (Type $type, TemplateTypeVariance $variance): string => TypeProjectionHelper::describe($type, $variance, VerbosityLevel::cache()),
$this->getActiveTemplateTypeMap()->getTypes(),
$this->getCallSiteVarianceMap()->getVariances(),
)) . '>';
$templateTypes = [];
$variances = $this->getCallSiteVarianceMap()->getVariances();
foreach ($this->getActiveTemplateTypeMap()->getTypes() as $name => $templateType) {
$variance = $variances[$name] ?? null;
if ($variance === null) {
continue;
}

$templateTypes[] = TypeProjectionHelper::describe($templateType, $variance, VerbosityLevel::cache());
}

$cacheKey .= '<' . implode(',', $templateTypes) . '>';
}

if ($this->extraCacheKey !== null) {
Expand Down
10 changes: 10 additions & 0 deletions tests/PHPStan/Analyser/AnalyserIntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1242,6 +1242,16 @@ public function testBug9994(): void
$this->assertSame('Parameter #2 $callback of function array_filter expects callable(1|2|3|null): mixed, false given.', $errors[1]->getMessage());
}

public function testBug10049(): void
{
if (PHP_VERSION_ID < 80100) {
$this->markTestSkipped('Test requires PHP 8.1.');
}

$errors = $this->runAnalyse(__DIR__ . '/data/bug-10049-recursive.php');
$this->assertNoErrors($errors);
}

/**
* @param string[]|null $allAnalysedFiles
* @return Error[]
Expand Down
66 changes: 66 additions & 0 deletions tests/PHPStan/Analyser/data/bug-10049-recursive.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php declare(strict_types = 1);

namespace Bug10049Recursive;

/**
* @template SELF of SimpleEntity<SELF>
*/
abstract class SimpleEntity
{
/**
* @param SimpleTable<SELF> $table
*/
public function __construct(protected readonly SimpleTable $table)
{
}
}

/**
* @template-covariant E of SimpleEntity
*/
class SimpleTable
{
/**
* @template ENTITY of SimpleEntity
*
* @param class-string<ENTITY> $className
*
* @return SimpleTable<ENTITY>
*/
public static function table(string $className, string $name): SimpleTable
{
return new SimpleTable($className, $name);
}

/**
* @param class-string<E> $className
*/
private function __construct(readonly string $className, readonly string $table)
{
}
}

/**
* @template-extends SimpleEntity<TestEntity>
*/
class TestEntity extends SimpleEntity
{
public function __construct()
{
$table = SimpleTable::table(TestEntity::class, 'testentity');
parent::__construct($table);
}
}


/**
* @template-extends SimpleEntity<AnotherEntity>
*/
class AnotherEntity extends SimpleEntity
{
public function __construct()
{
$table = SimpleTable::table(AnotherEntity::class, 'anotherentity');
parent::__construct($table);
}
}

0 comments on commit 1308c52

Please sign in to comment.