8000 bug #60820 [TypeInfo] Fix handling `ConstFetchNode` (norkunas) · symfony/symfony@4e3a96f · GitHub
[go: up one dir, main page]

Skip to content

Commit 4e3a96f

Browse files
bug #60820 [TypeInfo] Fix handling ConstFetchNode (norkunas)
This PR was squashed before being merged into the 7.2 branch. Discussion ---------- [TypeInfo] Fix handling `ConstFetchNode` | Q | A | ------------- | --- | Branch? | 7.2 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #... | License | MIT I cannot upgrade to 7.3 because I get: ``` In StringTypeResolver.php line 88: !! !! Cannot resolve "array{requirement: ProductLicense::SYSTEM_REQUIREMENT_*, va !! lue: non-empty-string}". !! !! !! In StringTypeResolver.php line 142: !! !! Unhandled "PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode" constant expr !! ession. !! ``` Commits ------- 03cc607 [TypeInfo] Fix handling `ConstFetchNode`
2 parents cf0e627 + 03cc607 commit 4e3a96f

File tree

6 files changed

+93
-2
lines changed

6 files changed

+93
-2
lines changed

src/Symfony/Component/PropertyInfo/Tests/Extractor/PhpStanExtractorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -927,7 +927,7 @@ public static function unionTypesProvider(): iterable
927927
Type::object(ParentDummy::class),
928928
Type::null(),
929929
)];
930-
yield ['f', null];
930+
yield ['f', Type::union(Type::string(), Type::null())];
931931
yield ['g', Type::array(Type::union(Type::string(), Type::int()))];
932932
}
933933

src/Symfony/Component/PropertyInfo/composer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"require": {
2626
"php": ">=8.2",
2727
"symfony/string": "^6.4|^7.0",
28-
"symfony/type-info": "~7.1.9|^7.2.2"
28+
"symfony/type-info": "~7.2.8|^7.3.1"
2929
},
3030
"require-dev": {
3131
"symfony/serializer": "^6.4|^7.0",

src/Symfony/Component/Serializer/Tests/Normalizer/ObjectNormalizerTest.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,8 @@ public function testDoesntHaveIssuesWithUnionConstTypes()
748748
$serializer = new Serializer([new ArrayDenormalizer(), new DateTimeNormalizer(), $normalizer]);
749749

750750
$this->assertSame('bar', $serializer->denormalize(['foo' 10000 ; => 'bar'], (new class {
751+
public const TEST = 'me';
752+
751753
/** @var self::*|null */
752754
public $foo;
753755
})::class)->foo);
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\TypeInfo\Tests\Fixtures;
13+
14+
final class DummyWithConstants
15+
{
16+
public const DUMMY_STRING_A = 'a';
17+
public const DUMMY_INT_A = 1;
18+
public const DUMMY_FLOAT_A = 1.23;
19+
public const DUMMY_TRUE_A = true;
20+
public const DUMMY_FALSE_A = false;
21+
public const DUMMY_NULL_A = null;
22+
public const DUMMY_ARRAY_A = [];
23+
public const DUMMY_ENUM_A = DummyEnum::ONE;
24+
25+
public const DUMMY_MIX_1 = self::DUMMY_STRING_A;
26+
public const DUMMY_MIX_2 = self::DUMMY_INT_A;
27+
public const DUMMY_MIX_3 = self::DUMMY_FLOAT_A;
28+
public const DUMMY_MIX_4 = self::DUMMY_TRUE_A;
29+
public const DUMMY_MIX_5 = self::DUMMY_FALSE_A;
30+
public const DUMMY_MIX_6 = self::DUMMY_NULL_A;
31+
public const DUMMY_MIX_7 = self::DUMMY_ARRAY_A;
32+
public const DUMMY_MIX_8 = self::DUMMY_ENUM_A;
33+
}

src/Symfony/Component/TypeInfo/Tests/TypeResolver/StringTypeResolverTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
use Symfony\Component\TypeInfo\Tests\Fixtures\DummyBackedEnum;
2020
use Symfony\Component\TypeInfo\Tests\Fixtures\DummyCollection;
2121
use Symfony\Component\TypeInfo\Tests\Fixtures\DummyEnum;
22+
use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithConstants;
2223
use Symfony\Component\TypeInfo\Tests\Fixtures\DummyWithTemplates;
2324
use Symfony\Component\TypeInfo\Type;
2425
use Symfony\Component\TypeInfo\TypeContext\TypeContext;
@@ -90,6 +91,19 @@ public static function resolveDataProvider(): iterable
9091
yield [Type::string(), '"string"'];
9192
yield [Type::true(), 'true'];
9293

94+
// const fetch
95+
yield [Type::string(), DummyWithConstants::class.'::DUMMY_STRING_*'];
96+
yield [Type::string(), DummyWithConstants::class.'::DUMMY_STRING_A'];
97+
yield [Type::int(), DummyWithConstants::class.'::DUMMY_INT_*'];
98+
yield [Type::int(), DummyWithConstants::class.'::DUMMY_INT_A'];
99+
yield [Type::float(), DummyWithConstants::class.'::DUMMY_FLOAT_*'];
100+
yield [Type::bool(), DummyWithConstants::class.'::DUMMY_TRUE_*'];
101+
yield [Type::bool(), DummyWithConstants::class.'::DUMMY_FALSE_*'];
102+
yield [Type::null(), DummyWithConstants::class.'::DUMMY_NULL_*'];
103+
yield [Type::array(), DummyWithConstants::class.'::DUMMY_ARRAY_*'];
104+
yield [Type::enum(DummyEnum::class, Type::string()), DummyWithConstants::class.'::DUMMY_ENUM_*'];
105+
yield [Type::union(Type::string(), Type::int(), Type::float(), Type::bool(), Type::null(), Type::array(), Type::enum(DummyEnum::class, Type::string())), DummyWithConstants::class.'::DUMMY_MIX_*'];
106+
93107
// identifiers
94108
yield [Type::bool(), 'bool'];
95109
yield [Type::bool(), 'boolean'];

src/Symfony/Component/TypeInfo/TypeResolver/StringTypeResolver.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprNullNode;
1919
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprStringNode;
2020
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstExprTrueNode;
21+
use PHPStan\PhpDocParser\Ast\ConstExpr\ConstFetchNode;
2122
use PHPStan\PhpDocParser\Ast\Type\ArrayShapeNode;
2223
use PHPStan\PhpDocParser\Ast\Type\ArrayTypeNode;
2324
use PHPStan\PhpDocParser\Ast\Type\CallableTypeNode;
@@ -119,6 +120,47 @@ private function getTypeFromNode(TypeNode $node, ?TypeContext $typeContext): Typ
119120
}
120121

121122
if ($node instanceof ConstTypeNode) {
123+
if ($node->constExpr instanceof ConstFetchNode) {
124+
$className = match (strtolower($node->constExpr->className)) {
125+
'self' => $typeContext->getDeclaringClass(),
126+
'static' => $typeContext->getCalledClass(),
127+
'parent' => $typeContext->getParentClass(),
128+
default => $node->constExpr->className,
129+
};
130+
131+
if (!class_exists($className)) {
132+
return Type::mixed();
133+
}
134+
135+
$types = [];
136+
137+
foreach ((new \ReflectionClass($className))->getReflectionConstants() as $const) {
138+
if (preg_match('/^'.str_replace('\*', '.*', preg_quote($node->constExpr->name, '/')).'$/', $const->getName())) {
139+
$constValue = $const->getValue();
140+
141+
$types[] = match (true) {
142+
true === $constValue,
143+
false === $constValue => Type::bool(),
144+
null === $constValue => Type::null(),
145+
\is_string($constValue) => Type::string(),
146+
\is_int($constValue) => Type::int(),
147+
\is_float($constValue) => Type::float(),
148+
\is_array($constValue) => Type::array(),
149+
$constValue instanceof \UnitEnum => Type::enum($constValue::class),
150+
default => Type::mixed(),
151+
};
152+
}
153+
}
154+
155+
$types = array_unique($types);
156+
157+
if (\count($types) > 2) {
158+
return Type::union(...$types);
159+
}
160+
161+
return $types[0] ?? Type::null();
162+
}
163+
122164
return match ($node->constExpr::class) {
123165
ConstExprArrayNode::class => Type::array(),
124166
ConstExprFalseNode::class => Type::false(),

0 commit comments

Comments
 (0)
0