Skip to content

Commit ad444fc

Browse files
Richard van Velzenondrejmirtes
Richard van Velzen
authored andcommitted
Improve consistency
1 parent 2cd967e commit ad444fc

File tree

8 files changed

+64
-10
lines changed

8 files changed

+64
-10
lines changed

src/Analyser/NodeScopeResolver.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -4115,7 +4115,7 @@ public function getPhpDocs(Scope $scope, Node\FunctionLike|Node\Stmt\Property $n
41154115
$acceptsNamedArguments = $resolvedPhpDoc->acceptsNamedArguments();
41164116
$isReadOnly = $isReadOnly || $resolvedPhpDoc->isReadOnly();
41174117
$asserts = Assertions::createFromResolvedPhpDocBlock($resolvedPhpDoc);
4118-
$selfOutType = $resolvedPhpDoc->getThisOutTag() !== null ? $resolvedPhpDoc->getThisOutTag()->getType() : null;
4118+
$selfOutType = $resolvedPhpDoc->getSelfOutTag() !== null ? $resolvedPhpDoc->getSelfOutTag()->getType() : null;
41194119
}
41204120

41214121
return [$templateTypeMap, $phpDocParameterTypes, $phpDocReturnType, $phpDocThrowType, $deprecatedDescription, $isDeprecated, $isInternal, $isFinal, $isPure, $acceptsNamedArguments, $isReadOnly, $docComment, $asserts, $selfOutType];

src/PhpDoc/ResolvedPhpDocBlock.php

+22-3
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public function merge(array $parents, array $parentPhpDocBlocks): self
220220
$result->typeAliasTags = $this->getTypeAliasTags();
221221
$result->typeAliasImportTags = $this->getTypeAliasImportTags();
222222
$result->assertTags = self::mergeAssertTags($this->getAssertTags(), $parents, $parentPhpDocBlocks);
223-
$result->selfOutTypeTag = $this->getThisOutTag();
223+
$result->selfOutTypeTag = self::mergeSelfOutTypeTags($this->getSelfOutTag(), $parents);
224224
$result->deprecatedTag = self::mergeDeprecatedTags($this->getDeprecatedTag(), $parents);
225225
$result->isDeprecated = $result->deprecatedTag !== null;
226226
$result->isInternal = $this->isInternal();
@@ -302,7 +302,7 @@ public function changeParameterNamesByMapping(array $parameterNameMapping): self
302302
$self->typeAliasTags = $this->typeAliasTags;
303303
$self->typeAliasImportTags = $this->typeAliasImportTags;
304304
$self->assertTags = $assertTags;
305-
$self->selfOutTypeTag = $this->getThisOutTag();
305+
$self->selfOutTypeTag = $this->selfOutTypeTag;
306306
$self->deprecatedTag = $this->deprecatedTag;
307307
$self->isDeprecated = $this->isDeprecated;
308308
$self->isInternal = $this->isInternal;
@@ -528,7 +528,7 @@ public function getAssertTags(): array
528528
return $this->assertTags;
529529
}
530530

531-
public function getThisOutTag(): ?SelfOutTypeTag
531+
public function getSelfOutTag(): ?SelfOutTypeTag
532532
{
533533
if ($this->selfOutTypeTag === false) {
534534
$this->selfOutTypeTag = $this->phpDocNodeResolver->resolveSelfOutTypeTag(
@@ -811,6 +811,25 @@ private static function mergeAssertTags(array $assertTags, array $parents, array
811811
return $assertTags;
812812
}
813813

814+
/**
815+
* @param array<int, self> $parents
816+
*/
817+
private static function mergeSelfOutTypeTags(?SelfOutTypeTag $selfOutTypeTag, array $parents): ?SelfOutTypeTag
818+
{
819+
if ($selfOutTypeTag !== null) {
820+
return $selfOutTypeTag;
821+
}
822+
foreach ($parents as $parent) {
823+
$result = $parent->getSelfOutTag();
824+
if ($result === null) {
825+
continue;
826+
}
827+
return $result;
828+
}
829+
830+
return null;
831+
}
832+
814833
/**
815834
* @param array<int, self> $parents
816835
*/

src/Reflection/Php/PhpClassReflectionExtension.php

+3-3
Original file line numberDiff line numberDiff line change
@@ -546,7 +546,7 @@ private function createMethod(
546546

547547
$asserts = Assertions::createFromResolvedPhpDocBlock($stubPhpDoc);
548548

549-
$selfOutTypeTag = $stubPhpDoc->getThisOutTag();
549+
$selfOutTypeTag = $stubPhpDoc->getSelfOutTag();
550550
if ($selfOutTypeTag !== null) {
551551
$selfOutType = $selfOutTypeTag->getType();
552552
}
@@ -575,7 +575,7 @@ private function createMethod(
575575
}
576576
$asserts = Assertions::createFromResolvedPhpDocBlock($phpDocBlock);
577577

578-
$selfOutTypeTag = $phpDocBlock->getThisOutTag();
578+
$selfOutTypeTag = $phpDocBlock->getSelfOutTag();
579579
if ($selfOutTypeTag !== null) {
580580
$selfOutType = $selfOutTypeTag->getType();
581581
}
@@ -727,7 +727,7 @@ private function createMethod(
727727
$isFinal = $resolvedPhpDoc->isFinal();
728728
$isPure = $resolvedPhpDoc->isPure();
729729
$asserts = Assertions::createFromResolvedPhpDocBlock($resolvedPhpDoc);
730-
$selfOutType = $resolvedPhpDoc->getThisOutTag() !== null ? $resolvedPhpDoc->getThisOutTag()->getType() : null;
730+
$selfOutType = $resolvedPhpDoc->getSelfOutTag() !== null ? $resolvedPhpDoc->getSelfOutTag()->getType() : null;
731731

732732
return $this->methodReflectionFactory->create(
733733
$declaringClass,

src/Rules/PhpDoc/IncompatibleSelfOutTypeRule.php

+5-2
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,24 @@ public function processNode(Node $node, Scope $scope): array
2626
{
2727
$method = $node->getMethodReflection();
2828
$selfOutType = $method->getSelfOutType();
29+
2930
if ($selfOutType === null) {
3031
return [];
3132
}
3233

3334
$classReflection = $method->getDeclaringClass();
3435
$classType = new ObjectType($classReflection->getName(), null, $classReflection);
3536

36-
if (!$classType->isSuperTypeOf($selfOutType)->no()) {
37+
if ($classType->isSuperTypeOf($selfOutType)->yes()) {
3738
return [];
3839
}
3940

4041
return [
4142
RuleErrorBuilder::message(sprintf(
42-
'Out type %s is not compatible with %s.',
43+
'Self-out type %s of method %s::%s is not subtype of %s.',
4344
$selfOutType->describe(VerbosityLevel::precise()),
45+
$classReflection->getName(),
46+
$method->getName(),
4447
$classType->describe(VerbosityLevel::precise()),
4548
))->build(),
4649
];

src/Rules/PhpDoc/InvalidPHPStanDocTagRule.php

+2
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ class InvalidPHPStanDocTagRule implements Rule
4444
'@phpstan-assert',
4545
'@phpstan-assert-if-true',
4646
'@phpstan-assert-if-false',
47+
'@phpstan-self-out',
48+
'@phpstan-this-out',
4749
];
4850

4951
public function __construct(private Lexer $phpDocLexer, private PhpDocParser $phpDocParser)

tests/PHPStan/Analyser/data/self-out.php

+21
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,19 @@ public function test(): void {
5252
}
5353
}
5454

55+
/**
56+
* @template T
57+
* @extends a<T>
58+
*/
59+
class b extends a {
60+
/**
61+
* @param T $data
62+
*/
63+
public function __construct($data) {
64+
parent::__construct($data);
65+
}
66+
}
67+
5568
function () {
5669
$i = new a(123);
5770
// OK - $i is a<123>
@@ -68,3 +81,11 @@ function () {
6881
assertType('SelfOut\\a<string>', $i);
6982
assertType('*NEVER*', $i->test());
7083
};
84+
85+
function () {
86+
$i = new b(123);
87+
assertType('SelfOut\\b<int>', $i);
88+
89+
$i->addData(321);
90+
assertType('SelfOut\\a<int>', $i);
91+
};

tests/PHPStan/Rules/PhpDoc/IncompatibleSelfOutTypeRuleTest.php

+5-1
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@ public function testRule(): void
2020
{
2121
$this->analyse([__DIR__ . '/data/incompatible-self-out-type.php'], [
2222
[
23-
'Out type int is not compatible with IncompatibleSelfOutType\A.',
23+
'Self-out type int of method IncompatibleSelfOutType\A::three is not subtype of IncompatibleSelfOutType\A.',
2424
23,
2525
],
26+
[
27+
'Self-out type IncompatibleSelfOutType\A|null of method IncompatibleSelfOutType\A::four is not subtype of IncompatibleSelfOutType\A.',
28+
28,
29+
],
2630
]);
2731
}
2832

tests/PHPStan/Rules/PhpDoc/data/incompatible-self-out-type.php

+5
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,9 @@ public function two($param);
2121
* @phpstan-self-out int
2222
*/
2323
public function three();
24+
25+
/**
26+
* @phpstan-self-out self|null
27+
*/
28+
public function four();
2429
}

0 commit comments

Comments
 (0)