Skip to content

Commit

Permalink
Overwrite property expression type only if it's subtype of the native…
Browse files Browse the repository at this point in the history
… type
  • Loading branch information
ondrejmirtes committed Jan 8, 2025
1 parent 33dc757 commit eb0e0bc
Show file tree
Hide file tree
Showing 18 changed files with 393 additions and 21 deletions.
15 changes: 9 additions & 6 deletions src/Analyser/MutatingScope.php
Original file line number Diff line number Diff line change
Expand Up @@ -2119,11 +2119,13 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
if ($propertyReflection === null) {
return new ErrorType();
}
$nativeType = $propertyReflection->getNativeType();
if ($nativeType === null) {
return new ErrorType();

if (!$propertyReflection->hasNativeType()) {
return new MixedType();
}

$nativeType = $propertyReflection->getNativeType();

return $this->getNullsafeShortCircuitingType($node->var, $nativeType);
}

Expand Down Expand Up @@ -2167,11 +2169,12 @@ static function (Node $node, Scope $scope) use ($arrowScope, &$arrowFunctionImpu
if ($propertyReflection === null) {
return new ErrorType();
}
$nativeType = $propertyReflection->getNativeType();
if ($nativeType === null) {
return new ErrorType();
if (!$propertyReflection->hasNativeType()) {
return new MixedType();
}

$nativeType = $propertyReflection->getNativeType();

if ($node->class instanceof Expr) {
return $this->getNullsafeShortCircuitingType($node->class, $nativeType);
}
Expand Down
26 changes: 24 additions & 2 deletions src/Analyser/NodeScopeResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -5556,7 +5556,18 @@ static function (): void {
$assignedExprType = $scope->getType($assignedExpr);
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
if ($propertyReflection->canChangeTypeAfterAssignment()) {
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
if ($propertyReflection->hasNativeType()) {
$propertyNativeType = $propertyReflection->getNativeType();
if ($propertyNativeType->isSuperTypeOf($assignedExprType)->yes()) {
$assignedExprNativeType = $scope->getNativeType($assignedExpr);
if (!$propertyNativeType->isSuperTypeOf($assignedExprNativeType)->yes()) {
$assignedExprNativeType = $propertyNativeType;
}
$scope = $scope->assignExpression($var, $assignedExprType, $assignedExprNativeType);
}
} else {
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
}
}
$declaringClass = $propertyReflection->getDeclaringClass();
if ($declaringClass->hasNativeProperty($propertyName)) {
Expand Down Expand Up @@ -5621,7 +5632,18 @@ static function (): void {
$assignedExprType = $scope->getType($assignedExpr);
$nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope);
if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) {
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
if ($propertyReflection->hasNativeType()) {
$propertyNativeType = $propertyReflection->getNativeType();
if ($propertyNativeType->isSuperTypeOf($assignedExprType)->yes()) {
$assignedExprNativeType = $scope->getNativeType($assignedExpr);
if (!$propertyNativeType->isSuperTypeOf($assignedExprNativeType)->yes()) {
$assignedExprNativeType = $propertyNativeType;
}
$scope = $scope->assignExpression($var, $assignedExprType, $assignedExprNativeType);
}
} else {
$scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr));
}
}
} else {
// fallback
Expand Down
21 changes: 21 additions & 0 deletions src/Reflection/Annotations/AnnotationPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Reflection\ExtendedPropertyReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

final class AnnotationPropertyReflection implements ExtendedPropertyReflection
Expand Down Expand Up @@ -42,6 +43,26 @@ public function isPublic(): bool
return true;
}

public function hasPhpDocType(): bool
{
return true;
}

public function getPhpDocType(): Type
{
return $this->readableType;
}

public function hasNativeType(): bool
{
return false;
}

public function getNativeType(): Type
{
return new MixedType();
}

public function getReadableType(): Type
{
return $this->readableType;
Expand Down
22 changes: 21 additions & 1 deletion src/Reflection/Dummy/ChangedTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
final class ChangedTypePropertyReflection implements WrapperPropertyReflection
{

public function __construct(private ClassReflection $declaringClass, private ExtendedPropertyReflection $reflection, private Type $readableType, private Type $writableType)
public function __construct(private ClassReflection $declaringClass, private ExtendedPropertyReflection $reflection, private Type $readableType, private Type $writableType, private Type $phpDocType, private Type $nativeType)
{
}

Expand Down Expand Up @@ -41,6 +41,26 @@ public function getDocComment(): ?string
return $this->reflection->getDocComment();
}

public function hasPhpDocType(): bool
{
return $this->reflection->hasPhpDocType();
}

public function getPhpDocType(): Type
{
return $this->phpDocType;
}

public function hasNativeType(): bool
{
return $this->reflection->hasNativeType();
}

public function getNativeType(): Type
{
return $this->nativeType;
}

public function getReadableType(): Type
{
return $this->readableType;
Expand Down
20 changes: 20 additions & 0 deletions src/Reflection/Dummy/DummyPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,26 @@ public function isPublic(): bool
return true;
}

public function hasPhpDocType(): bool
{
return false;
}

public function getPhpDocType(): Type
{
return new MixedType();
}

public function hasNativeType(): bool
{
return false;
}

public function getNativeType(): Type
{
return new MixedType();
}

public function getReadableType(): Type
{
return new MixedType();
Expand Down
9 changes: 9 additions & 0 deletions src/Reflection/ExtendedPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace PHPStan\Reflection;

use PHPStan\TrinaryLogic;
use PHPStan\Type\Type;

/**
* The purpose of this interface is to be able to
Expand All @@ -25,6 +26,14 @@ interface ExtendedPropertyReflection extends PropertyReflection

public const HOOK_SET = 'set';

public function hasPhpDocType(): bool;

public function getPhpDocType(): Type;

public function hasNativeType(): bool;

public function getNativeType(): Type;

public function isAbstract(): TrinaryLogic;

public function isFinal(): TrinaryLogic;
Expand Down
21 changes: 21 additions & 0 deletions src/Reflection/Php/EnumPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Reflection\ExtendedPropertyReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

final class EnumPropertyReflection implements ExtendedPropertyReflection
Expand Down Expand Up @@ -41,6 +42,26 @@ public function getDocComment(): ?string
return null;
}

public function hasPhpDocType(): bool
{
return false;
}

public function getPhpDocType(): Type
{
return new MixedType();
}

public function hasNativeType(): bool
{
return false;
}

public function getNativeType(): Type
{
return new MixedType();
}

public function getReadableType(): Type
{
return $this->type;
Expand Down
21 changes: 21 additions & 0 deletions src/Reflection/Php/SimpleXMLElementProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
use PHPStan\Type\BooleanType;
use PHPStan\Type\FloatType;
use PHPStan\Type\IntegerType;
use PHPStan\Type\MixedType;
use PHPStan\Type\StringType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeCombinator;
Expand Down Expand Up @@ -44,6 +45,26 @@ public function isPublic(): bool
return true;
}

public function hasPhpDocType(): bool
{
return false;
}

public function getPhpDocType(): Type
{
return new MixedType();
}

public function hasNativeType(): bool
{
return false;
}

public function getNativeType(): Type
{
return new MixedType();
}

public function getReadableType(): Type
{
return $this->type;
Expand Down
21 changes: 21 additions & 0 deletions src/Reflection/Php/UniversalObjectCrateProperty.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use PHPStan\Reflection\ExtendedPropertyReflection;
use PHPStan\ShouldNotHappenException;
use PHPStan\TrinaryLogic;
use PHPStan\Type\MixedType;
use PHPStan\Type\Type;

final class UniversalObjectCrateProperty implements ExtendedPropertyReflection
Expand Down Expand Up @@ -40,6 +41,26 @@ public function isPublic(): bool
return true;
}

public function hasPhpDocType(): bool
{
return false;
}

public function getPhpDocType(): Type
{
return new MixedType();
}

public function hasNativeType(): bool
{
return false;
}

public function getNativeType(): Type
{
return new MixedType();
}

public function getReadableType(): Type
{
return $this->readableType;
Expand Down
20 changes: 20 additions & 0 deletions src/Reflection/ResolvedPropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@ public function isPublic(): bool
return $this->reflection->isPublic();
}

public function hasPhpDocType(): bool
{
return $this->reflection->hasPhpDocType();
}

public function getPhpDocType(): Type
{
return $this->reflection->getPhpDocType();
}

public function hasNativeType(): bool
{
return $this->reflection->hasNativeType();
}

public function getNativeType(): Type
{
return $this->reflection->getNativeType();
}

public function getReadableType(): Type
{
$type = $this->readableType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,10 @@ private function transformPropertyWithStaticType(ClassReflection $declaringClass
{
$readableType = $this->transformStaticType($property->getReadableType());
$writableType = $this->transformStaticType($property->getWritableType());
$phpDocType = $this->transformStaticType($property->getPhpDocType());
$nativeType = $this->transformStaticType($property->getNativeType());

return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType);
return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType, $phpDocType, $nativeType);
}

private function transformStaticType(Type $type): Type
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,10 @@ private function transformPropertyWithStaticType(ClassReflection $declaringClass
{
$readableType = $this->transformStaticType($property->getReadableType());
$writableType = $this->transformStaticType($property->getWritableType());
$phpDocType = $this->transformStaticType($property->getPhpDocType());
$nativeType = $this->transformStaticType($property->getNativeType());

return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType);
return new ChangedTypePropertyReflection($declaringClass, $property, $readableType, $writableType, $phpDocType, $nativeType);
}

private function transformStaticType(Type $type): Type
Expand Down
20 changes: 20 additions & 0 deletions src/Reflection/Type/IntersectionTypePropertyReflection.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,26 @@ public function getDocComment(): ?string
return null;
}

public function hasPhpDocType(): bool
{
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->hasPhpDocType());
}

public function getPhpDocType(): Type
{
return TypeCombinator::intersect(...array_map(static fn (ExtendedPropertyReflection $property): Type => $property->getPhpDocType(), $this->properties));
}

public function hasNativeType(): bool
{
return $this->computeResult(static fn (ExtendedPropertyReflection $property) => $property->hasNativeType());
}

public function getNativeType(): Type
{
return TypeCombinator::intersect(...array_map(static fn (ExtendedPropertyReflection $property): Type => $property->getNativeType(), $this->properties));
}

public function getReadableType(): Type
{
return TypeCombinator::intersect(...array_map(static fn (ExtendedPropertyReflection $property): Type => $property->getReadableType(), $this->properties));
Expand Down
Loading

0 comments on commit eb0e0bc

Please sign in to comment.