Skip to content

[ErrorHandler] resolve class constant types when patching return types #58536

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion src/Symfony/Component/ErrorHandler/DebugClassLoader.php
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,30 @@
$docTypes = [];

foreach ($typesMap as $n => $t) {
if (str_contains($n, '::')) {
[$definingClass, $constantName] = explode('::', $n, 2);
$definingClass = match ($definingClass) {
'self', 'static', 'parent' => $class,
default => $definingClass,
};

if (!\defined($definingClass.'::'.$constantName)) {
return;
}

$constant = new \ReflectionClassConstant($definingClass, $constantName);

if (\PHP_VERSION_ID >= 80300 && $constantType = $constant->getType()) {

Check failure on line 865 in src/Symfony/Component/ErrorHandler/DebugClassLoader.php

View workflow job for this annotation

GitHub Actions / Psalm

UndefinedMethod

src/Symfony/Component/ErrorHandler/DebugClassLoader.php:865:76: UndefinedMethod: Method ReflectionClassConstant::getType does not exist (see https://psalm.dev/022)
if ($constantType instanceof \ReflectionNamedType) {
$n = $constantType->getName();
} else {
return;
}
} else {
$n = \gettype($constant->getValue());
}
}

if ('null' === $n) {
$nullable = true;
continue;
Expand All @@ -872,7 +896,7 @@
continue;
}

if (!isset($phpTypes[''])) {
if (!isset($phpTypes['']) && !\in_array($n, $phpTypes, true)) {
$phpTypes[] = $n;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,26 @@ class_exists('Test\\'.ReturnType::class, true);
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::true()" might add "true" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::never()" might add "never" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::null()" might add "null" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParent::classConstant()" might add "string" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnType" now to avoid errors or add an explicit @return annotation to suppress this message.',
], $deprecations);
}

/**
* @requires PHP >= 8.3
*/
public function testReturnTypePhp83()
{
$deprecations = [];
set_error_handler(function ($type, $msg) use (&$deprecations) { $deprecations[] = $msg; });
$e = error_reporting(E_USER_DEPRECATED);

class_exists('Test\\'.ReturnTypePhp83::class, true);

error_reporting($e);
restore_error_handler();

$this->assertSame([
'Method "Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentPhp83::classConstantWithType()" might add "string" as a native return type declaration in the future. Do the same in child class "Test\Symfony\Component\ErrorHandler\Tests\ReturnTypePhp83" now to avoid errors or add an explicit @return annotation to suppress this message.',
], $deprecations);
}

Expand Down Expand Up @@ -542,6 +562,8 @@ public function ownAbstractBaseMethod() { }
}');
} elseif ('Test\\'.ReturnType::class === $class) {
return $fixtureDir.\DIRECTORY_SEPARATOR.'ReturnType.php';
} elseif ('Test\\'.ReturnTypePhp83::class === $class) {
return $fixtureDir.\DIRECTORY_SEPARATOR.'ReturnTypePhp83.php';
} elseif ('Test\\'.Fixtures\OutsideInterface::class === $class) {
return $fixtureDir.\DIRECTORY_SEPARATOR.'OutsideInterface.php';
} elseif ('Test\\'.OverrideOutsideFinalProperty::class === $class) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,5 @@ public function true() { }
public function never() { }
public function null() { }
public function outsideMethod() { }
public function classConstant() { }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

abstract class ReturnTypeParent extends ReturnTypeGrandParent implements ReturnTypeParentInterface
{
const FOO = 'foo';

/**
* @return void
*/
Expand Down Expand Up @@ -254,4 +256,11 @@ public function null()
public function notExtended()
{
}

/**
* @return self::FOO
*/
public function classConstant()
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace Symfony\Component\ErrorHandler\Tests\Fixtures;

abstract class ReturnTypeParentPhp83
{
const string FOO = 'foo';
const string|int BAR = 'bar';

/**
* @return self::FOO
*/
public function classConstantWithType()
{
}

/**
* @return self::BAR
*/
public function classConstantWithUnionType()
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Test\Symfony\Component\ErrorHandler\Tests;

use Symfony\Component\ErrorHandler\Tests\Fixtures\ReturnTypeParentPhp83;

class ReturnTypePhp83 extends ReturnTypeParentPhp83
{
public function classConstantWithType() { }
public function classConstantWithUnionType() { }
}
Loading