Skip to content

Commit f356b16

Browse files
Fix @var PHPDoc type inheritance for class constants
1 parent 812d7da commit f356b16

File tree

6 files changed

+59
-9
lines changed

6 files changed

+59
-9
lines changed

src/PhpDoc/PhpDocNodeResolver.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,9 @@ public function resolveVarTags(PhpDocNode $phpDocNode, NameScope $nameScope): ar
7676
}
7777
if ($tagValue->variableName !== '') {
7878
$variableName = substr($tagValue->variableName, 1);
79-
$resolved[$variableName] = new VarTag($type);
79+
$resolved[$variableName] = new VarTag($type, true);
8080
} else {
81-
$varTag = new VarTag($type);
81+
$varTag = new VarTag($type, true);
8282
$tagResolved[] = $varTag;
8383
}
8484
}

src/PhpDoc/ResolvedPhpDocBlock.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ private static function mergeVarTags(array $varTags, array $parents, array $pare
887887
private static function mergeOneParentVarTags(self $parent, PhpDocBlock $phpDocBlock): ?array
888888
{
889889
foreach ($parent->getVarTags() as $key => $parentVarTag) {
890-
return [$key => self::resolveTemplateTypeInTag($parentVarTag, $phpDocBlock, TemplateTypeVariance::createInvariant())];
890+
return [$key => self::resolveTemplateTypeInTag($parentVarTag->toImplicit(), $phpDocBlock, TemplateTypeVariance::createInvariant())];
891891
}
892892

893893
return null;

src/PhpDoc/Tag/VarTag.php

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
class VarTag implements TypedTag
1212
{
1313

14-
public function __construct(private Type $type)
14+
public function __construct(private Type $type, private bool $isExplicit)
1515
{
1616
}
1717

@@ -25,7 +25,17 @@ public function getType(): Type
2525
*/
2626
public function withType(Type $type): TypedTag
2727
{
28-
return new self($type);
28+
return new self($type, $this->isExplicit);
29+
}
30+
31+
public function isExplicit(): bool
32+
{
33+
return $this->isExplicit;
34+
}
35+
36+
public function toImplicit(): self
37+
{
38+
return new self($this->type, false);
2939
}
3040

3141
}

src/Reflection/ClassReflection.php

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1094,10 +1094,6 @@ public function getConstant(string $name): ClassConstantReflection
10941094
$isDeprecated = $resolvedPhpDoc->isDeprecated();
10951095
$isInternal = $resolvedPhpDoc->isInternal();
10961096
$isFinal = $resolvedPhpDoc->isFinal();
1097-
$varTags = $resolvedPhpDoc->getVarTags();
1098-
if (isset($varTags[0]) && count($varTags) === 1) {
1099-
$phpDocType = $varTags[0]->getType();
1100-
}
11011097

11021098
$nativeType = null;
11031099
if ($reflectionConstant->getType() !== null) {
@@ -1106,6 +1102,14 @@ public function getConstant(string $name): ClassConstantReflection
11061102
$nativeType = $this->signatureMapProvider->getClassConstantMetadata($declaringClass->getName(), $name)['nativeType'];
11071103
}
11081104

1105+
$varTags = $resolvedPhpDoc->getVarTags();
1106+
if (isset($varTags[0]) && count($varTags) === 1) {
1107+
$varTag = $varTags[0];
1108+
if ($varTag->isExplicit() || $nativeType === null || $nativeType->isSuperTypeOf($varTag->getType())->yes()) {
1109+
$phpDocType = $varTag->getType();
1110+
}
1111+
}
1112+
11091113
$this->constants[$name] = new ClassConstantReflection(
11101114
$this->initializerExprTypeResolver,
11111115
$declaringClass,

tests/PHPStan/Rules/PhpDoc/IncompatibleClassConstantPhpDocTypeRuleTest.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,13 @@ public function testNativeType(): void
5050
]);
5151
}
5252

53+
public function testBug10911(): void
54+
{
55+
if (PHP_VERSION_ID < 80300) {
56+
$this->markTestSkipped('Test requires PHP 8.3.');
57+
}
58+
59+
$this->analyse([__DIR__ . '/data/bug-10911.php'], []);
60+
}
61+
5362
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?php // lint >= 8.3
2+
3+
namespace Bug10911;
4+
5+
abstract class Model
6+
{
7+
/**
8+
* The name of the "created at" column.
9+
*
10+
* @var string|null
11+
*/
12+
const CREATED_AT = 'created_at';
13+
14+
/**
15+
* The name of the "updated at" column.
16+
*
17+
* @var string|null
18+
*/
19+
const UPDATED_AT = 'updated_at';
20+
}
21+
22+
class TestModel extends Model
23+
{
24+
const string CREATED_AT = 'data_criacao';
25+
const string UPDATED_AT = 'data_alteracao';
26+
const DELETED_AT = null;
27+
}

0 commit comments

Comments
 (0)