From bed30a79f4ed17651c48c031b89b60d4ce7453b2 Mon Sep 17 00:00:00 2001 From: Ondrej Mirtes Date: Tue, 21 Jan 2025 14:10:16 +0100 Subject: [PATCH] Fix issues about assigning typed properties --- src/Analyser/NodeScopeResolver.php | 22 +++------ src/Type/Accessory/AccessoryArrayListType.php | 5 ++ .../Accessory/AccessoryLiteralStringType.php | 5 ++ .../AccessoryLowercaseStringType.php | 5 ++ .../Accessory/AccessoryNonEmptyStringType.php | 5 ++ .../Accessory/AccessoryNonFalsyStringType.php | 5 ++ .../Accessory/AccessoryNumericStringType.php | 5 ++ .../AccessoryUppercaseStringType.php | 5 ++ src/Type/Accessory/HasOffsetType.php | 5 ++ src/Type/Accessory/HasOffsetValueType.php | 5 ++ src/Type/Accessory/NonEmptyArrayType.php | 5 ++ src/Type/Accessory/OversizedArrayType.php | 5 ++ src/Type/BooleanType.php | 5 ++ src/Type/CallableType.php | 6 +++ src/Type/ClosureType.php | 5 ++ src/Type/Constant/ConstantBooleanType.php | 5 ++ src/Type/FloatType.php | 5 ++ src/Type/Generic/TemplateTypeTrait.php | 5 ++ src/Type/IntegerType.php | 5 ++ src/Type/IntersectionType.php | 5 ++ src/Type/IterableType.php | 5 ++ src/Type/MixedType.php | 5 ++ src/Type/NeverType.php | 5 ++ src/Type/NonexistentParentClassType.php | 5 ++ src/Type/NullType.php | 5 ++ src/Type/ObjectType.php | 5 ++ src/Type/ResourceType.php | 5 ++ src/Type/StaticType.php | 5 ++ src/Type/StrictMixedType.php | 5 ++ src/Type/StringType.php | 5 ++ src/Type/Traits/ArrayTypeTrait.php | 5 ++ src/Type/Traits/LateResolvableTypeTrait.php | 5 ++ src/Type/Traits/ObjectTypeTrait.php | 5 ++ src/Type/Type.php | 11 +++++ src/Type/UnionType.php | 5 ++ src/Type/VoidType.php | 5 ++ tests/PHPStan/Analyser/nsrt/bug-12393.php | 48 ++++++++++++++++++- 37 files changed, 235 insertions(+), 17 deletions(-) diff --git a/src/Analyser/NodeScopeResolver.php b/src/Analyser/NodeScopeResolver.php index e91ca6748e..b6c5798b8d 100644 --- a/src/Analyser/NodeScopeResolver.php +++ b/src/Analyser/NodeScopeResolver.php @@ -5556,15 +5556,10 @@ static function (): void { $assignedExprType = $scope->getType($assignedExpr); $nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope); if ($propertyReflection->canChangeTypeAfterAssignment()) { - if ($propertyReflection->hasNativeType()) { + if ($propertyReflection->hasNativeType() && $scope->isDeclareStrictTypes()) { $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); - } + + $scope = $scope->assignExpression($var, TypeCombinator::intersect($assignedExprType->toCoercedArgumentType(true), $propertyNativeType), TypeCombinator::intersect($scope->getNativeType($assignedExpr)->toCoercedArgumentType(true), $propertyNativeType)); } else { $scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr)); } @@ -5632,15 +5627,10 @@ static function (): void { $assignedExprType = $scope->getType($assignedExpr); $nodeCallback(new PropertyAssignNode($var, $assignedExpr, $isAssignOp), $scope); if ($propertyReflection !== null && $propertyReflection->canChangeTypeAfterAssignment()) { - if ($propertyReflection->hasNativeType()) { + if ($propertyReflection->hasNativeType() && $scope->isDeclareStrictTypes()) { $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); - } + + $scope = $scope->assignExpression($var, TypeCombinator::intersect($assignedExprType->toCoercedArgumentType(true), $propertyNativeType), TypeCombinator::intersect($scope->getNativeType($assignedExpr)->toCoercedArgumentType(true), $propertyNativeType)); } else { $scope = $scope->assignExpression($var, $assignedExprType, $scope->getNativeType($assignedExpr)); } diff --git a/src/Type/Accessory/AccessoryArrayListType.php b/src/Type/Accessory/AccessoryArrayListType.php index 750466a271..5f60fe8eb7 100644 --- a/src/Type/Accessory/AccessoryArrayListType.php +++ b/src/Type/Accessory/AccessoryArrayListType.php @@ -469,6 +469,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function traverse(callable $cb): Type { return $this; diff --git a/src/Type/Accessory/AccessoryLiteralStringType.php b/src/Type/Accessory/AccessoryLiteralStringType.php index 967c58d690..9af225a3c1 100644 --- a/src/Type/Accessory/AccessoryLiteralStringType.php +++ b/src/Type/Accessory/AccessoryLiteralStringType.php @@ -213,6 +213,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryLowercaseStringType.php b/src/Type/Accessory/AccessoryLowercaseStringType.php index 97edeb39ba..5ea351a924 100644 --- a/src/Type/Accessory/AccessoryLowercaseStringType.php +++ b/src/Type/Accessory/AccessoryLowercaseStringType.php @@ -210,6 +210,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryNonEmptyStringType.php b/src/Type/Accessory/AccessoryNonEmptyStringType.php index d630cad147..961e2cbd95 100644 --- a/src/Type/Accessory/AccessoryNonEmptyStringType.php +++ b/src/Type/Accessory/AccessoryNonEmptyStringType.php @@ -211,6 +211,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryNonFalsyStringType.php b/src/Type/Accessory/AccessoryNonFalsyStringType.php index faac90150e..3befd2d478 100644 --- a/src/Type/Accessory/AccessoryNonFalsyStringType.php +++ b/src/Type/Accessory/AccessoryNonFalsyStringType.php @@ -212,6 +212,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryNumericStringType.php b/src/Type/Accessory/AccessoryNumericStringType.php index 9429c7ea73..447bf76ecc 100644 --- a/src/Type/Accessory/AccessoryNumericStringType.php +++ b/src/Type/Accessory/AccessoryNumericStringType.php @@ -213,6 +213,11 @@ public function toArrayKey(): Type return new IntegerType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/AccessoryUppercaseStringType.php b/src/Type/Accessory/AccessoryUppercaseStringType.php index a034eea321..18ee7399bf 100644 --- a/src/Type/Accessory/AccessoryUppercaseStringType.php +++ b/src/Type/Accessory/AccessoryUppercaseStringType.php @@ -210,6 +210,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Accessory/HasOffsetType.php b/src/Type/Accessory/HasOffsetType.php index 492bd1ba27..455f0de86e 100644 --- a/src/Type/Accessory/HasOffsetType.php +++ b/src/Type/Accessory/HasOffsetType.php @@ -388,6 +388,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function getEnumCases(): array { return []; diff --git a/src/Type/Accessory/HasOffsetValueType.php b/src/Type/Accessory/HasOffsetValueType.php index 296c5b7308..ec6e822a31 100644 --- a/src/Type/Accessory/HasOffsetValueType.php +++ b/src/Type/Accessory/HasOffsetValueType.php @@ -438,6 +438,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function getEnumCases(): array { return []; diff --git a/src/Type/Accessory/NonEmptyArrayType.php b/src/Type/Accessory/NonEmptyArrayType.php index a46eefc807..d4726fd5c2 100644 --- a/src/Type/Accessory/NonEmptyArrayType.php +++ b/src/Type/Accessory/NonEmptyArrayType.php @@ -445,6 +445,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function traverse(callable $cb): Type { return $this; diff --git a/src/Type/Accessory/OversizedArrayType.php b/src/Type/Accessory/OversizedArrayType.php index 70ba480a7e..c43c86a903 100644 --- a/src/Type/Accessory/OversizedArrayType.php +++ b/src/Type/Accessory/OversizedArrayType.php @@ -429,6 +429,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function traverse(callable $cb): Type { return $this; diff --git a/src/Type/BooleanType.php b/src/Type/BooleanType.php index 0e26b52a67..679c0b9824 100644 --- a/src/Type/BooleanType.php +++ b/src/Type/BooleanType.php @@ -111,6 +111,11 @@ public function toArrayKey(): Type return new UnionType([new ConstantIntegerType(0), new ConstantIntegerType(1)]); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/CallableType.php b/src/Type/CallableType.php index fe6e412ad2..b47dda2339 100644 --- a/src/Type/CallableType.php +++ b/src/Type/CallableType.php @@ -2,6 +2,7 @@ namespace PHPStan\Type; +use Closure; use PHPStan\Analyser\OutOfClassScope; use PHPStan\Php\PhpVersion; use PHPStan\PhpDoc\Tag\TemplateTag; @@ -321,6 +322,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return TypeCombinator::union($this, new StringType(), new ArrayType(new MixedType(true), new MixedType(true)), new ObjectType(Closure::class)); + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createMaybe(); diff --git a/src/Type/ClosureType.php b/src/Type/ClosureType.php index a76aa90596..55aebcadc8 100644 --- a/src/Type/ClosureType.php +++ b/src/Type/ClosureType.php @@ -462,6 +462,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return TypeCombinator::union($this, new CallableType()); + } + public function getTemplateTypeMap(): TemplateTypeMap { return $this->templateTypeMap; diff --git a/src/Type/Constant/ConstantBooleanType.php b/src/Type/Constant/ConstantBooleanType.php index a01321c138..ea1c4b09ef 100644 --- a/src/Type/Constant/ConstantBooleanType.php +++ b/src/Type/Constant/ConstantBooleanType.php @@ -107,6 +107,11 @@ public function toArrayKey(): Type return new ConstantIntegerType((int) $this->value); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isTrue(): TrinaryLogic { return TrinaryLogic::createFromBoolean($this->value === true); diff --git a/src/Type/FloatType.php b/src/Type/FloatType.php index 880006af90..253af75e4e 100644 --- a/src/Type/FloatType.php +++ b/src/Type/FloatType.php @@ -143,6 +143,11 @@ public function toArrayKey(): Type return new IntegerType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/Generic/TemplateTypeTrait.php b/src/Type/Generic/TemplateTypeTrait.php index c26509b017..52c13a9680 100644 --- a/src/Type/Generic/TemplateTypeTrait.php +++ b/src/Type/Generic/TemplateTypeTrait.php @@ -250,6 +250,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function inferTemplateTypes(Type $receivedType): TemplateTypeMap { if ( diff --git a/src/Type/IntegerType.php b/src/Type/IntegerType.php index 4eb3bd50fd..e974888bc7 100644 --- a/src/Type/IntegerType.php +++ b/src/Type/IntegerType.php @@ -98,6 +98,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return TypeCombinator::union($this, $this->toFloat()); + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/IntersectionType.php b/src/Type/IntersectionType.php index b79f6ed7ea..ab9f86d034 100644 --- a/src/Type/IntersectionType.php +++ b/src/Type/IntersectionType.php @@ -1062,6 +1062,11 @@ public function toArrayKey(): Type return $this->intersectTypes(static fn (Type $type): Type => $type->toArrayKey()); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this->intersectTypes(static fn (Type $type): Type => $type->toCoercedArgumentType($strictTypes)); + } + public function inferTemplateTypes(Type $receivedType): TemplateTypeMap { $types = TemplateTypeMap::createEmpty(); diff --git a/src/Type/IterableType.php b/src/Type/IterableType.php index 502fb6330a..e2864494e9 100644 --- a/src/Type/IterableType.php +++ b/src/Type/IterableType.php @@ -234,6 +234,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return TypeCombinator::union($this, new ArrayType(new MixedType(true), new MixedType(true)), new ObjectType(Traversable::class)); + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createMaybe(); diff --git a/src/Type/MixedType.php b/src/Type/MixedType.php index c63c78f214..487a474827 100644 --- a/src/Type/MixedType.php +++ b/src/Type/MixedType.php @@ -580,6 +580,11 @@ public function toArrayKey(): Type return new BenevolentUnionType([new IntegerType(), new StringType()]); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isIterable(): TrinaryLogic { if ($this->subtractedType !== null) { diff --git a/src/Type/NeverType.php b/src/Type/NeverType.php index eab6009abb..518ffa8f4a 100644 --- a/src/Type/NeverType.php +++ b/src/Type/NeverType.php @@ -383,6 +383,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function traverse(callable $cb): Type { return $this; diff --git a/src/Type/NonexistentParentClassType.php b/src/Type/NonexistentParentClassType.php index f52c5ea8de..0b91e093e6 100644 --- a/src/Type/NonexistentParentClassType.php +++ b/src/Type/NonexistentParentClassType.php @@ -157,6 +157,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return new ErrorType(); + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/NullType.php b/src/Type/NullType.php index 7d66fa7e79..fd30ee8bd8 100644 --- a/src/Type/NullType.php +++ b/src/Type/NullType.php @@ -166,6 +166,11 @@ public function toArrayKey(): Type return new ConstantStringType(''); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isOffsetAccessible(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/ObjectType.php b/src/Type/ObjectType.php index ac1f139938..0ad78520f2 100644 --- a/src/Type/ObjectType.php +++ b/src/Type/ObjectType.php @@ -671,6 +671,11 @@ public function toArrayKey(): Type return $this->toString(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function toBoolean(): BooleanType { if ($this->isInstanceOf('SimpleXMLElement')->yes()) { diff --git a/src/Type/ResourceType.php b/src/Type/ResourceType.php index f62023f2c6..4ed9c79c1c 100644 --- a/src/Type/ResourceType.php +++ b/src/Type/ResourceType.php @@ -91,6 +91,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/StaticType.php b/src/Type/StaticType.php index 8885972a66..702f3f751e 100644 --- a/src/Type/StaticType.php +++ b/src/Type/StaticType.php @@ -646,6 +646,11 @@ public function toArrayKey(): Type return $this->getStaticObjectType()->toArrayKey(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this->getStaticObjectType()->toCoercedArgumentType($strictTypes); + } + public function toBoolean(): BooleanType { return $this->getStaticObjectType()->toBoolean(); diff --git a/src/Type/StrictMixedType.php b/src/Type/StrictMixedType.php index 355d186986..0ff25dc124 100644 --- a/src/Type/StrictMixedType.php +++ b/src/Type/StrictMixedType.php @@ -395,6 +395,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function inferTemplateTypes(Type $receivedType): TemplateTypeMap { return TemplateTypeMap::createEmpty(); diff --git a/src/Type/StringType.php b/src/Type/StringType.php index eeedd4bc68..c0114d8462 100644 --- a/src/Type/StringType.php +++ b/src/Type/StringType.php @@ -181,6 +181,11 @@ public function toArrayKey(): Type return $this; } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isNull(): TrinaryLogic { return TrinaryLogic::createNo(); diff --git a/src/Type/Traits/ArrayTypeTrait.php b/src/Type/Traits/ArrayTypeTrait.php index ddc501d022..813b0136d9 100644 --- a/src/Type/Traits/ArrayTypeTrait.php +++ b/src/Type/Traits/ArrayTypeTrait.php @@ -29,6 +29,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + public function isOffsetAccessible(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/src/Type/Traits/LateResolvableTypeTrait.php b/src/Type/Traits/LateResolvableTypeTrait.php index 3c5a4e5b5f..5eb703077f 100644 --- a/src/Type/Traits/LateResolvableTypeTrait.php +++ b/src/Type/Traits/LateResolvableTypeTrait.php @@ -368,6 +368,11 @@ public function toArrayKey(): Type return $this->resolve()->toArrayKey(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this->resolve()->toCoercedArgumentType($strictTypes); + } + public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic { return $this->resolve()->isSmallerThan($otherType, $phpVersion); diff --git a/src/Type/Traits/ObjectTypeTrait.php b/src/Type/Traits/ObjectTypeTrait.php index 70f7d2c007..45fc20121f 100644 --- a/src/Type/Traits/ObjectTypeTrait.php +++ b/src/Type/Traits/ObjectTypeTrait.php @@ -273,4 +273,9 @@ public function toArrayKey(): Type return new StringType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this; + } + } diff --git a/src/Type/Type.php b/src/Type/Type.php index 0badf2930c..15886a053c 100644 --- a/src/Type/Type.php +++ b/src/Type/Type.php @@ -204,6 +204,17 @@ public function toArray(): Type; public function toArrayKey(): Type; + /** + * Tells how a type might change when passed to an argument + * or assigned to a typed property. + * + * Example: int is accepted by int|float with strict_types = 1 + * Stringable is accepted by string|Stringable even without strict_types. + * + * Note: Logic with $strictTypes=false is mostly not implemented in Type subclasses. + */ + public function toCoercedArgumentType(bool $strictTypes): self; + public function isSmallerThan(Type $otherType, PhpVersion $phpVersion): TrinaryLogic; public function isSmallerThanOrEqual(Type $otherType, PhpVersion $phpVersion): TrinaryLogic; diff --git a/src/Type/UnionType.php b/src/Type/UnionType.php index 9156bc09b7..08d678152a 100644 --- a/src/Type/UnionType.php +++ b/src/Type/UnionType.php @@ -972,6 +972,11 @@ public function toArrayKey(): Type return $this->unionTypes(static fn (Type $type): Type => $type->toArrayKey()); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return $this->unionTypes(static fn (Type $type): Type => $type->toCoercedArgumentType($strictTypes)); + } + public function inferTemplateTypes(Type $receivedType): TemplateTypeMap { $types = TemplateTypeMap::createEmpty(); diff --git a/src/Type/VoidType.php b/src/Type/VoidType.php index b6bac5908c..83c5a05726 100644 --- a/src/Type/VoidType.php +++ b/src/Type/VoidType.php @@ -119,6 +119,11 @@ public function toArrayKey(): Type return new ErrorType(); } + public function toCoercedArgumentType(bool $strictTypes): Type + { + return new NullType(); + } + public function isOffsetAccessLegal(): TrinaryLogic { return TrinaryLogic::createYes(); diff --git a/tests/PHPStan/Analyser/nsrt/bug-12393.php b/tests/PHPStan/Analyser/nsrt/bug-12393.php index 5410dc2150..9445d8632b 100644 --- a/tests/PHPStan/Analyser/nsrt/bug-12393.php +++ b/tests/PHPStan/Analyser/nsrt/bug-12393.php @@ -1,7 +1,8 @@ -a = ['a' => 1]; assertType('array{a: 1}', $this->a); } + + public function doFloatTricky(){ + $this->float = 1; + assertType('1.0', $this->float); + } } class HelloWorldStatic @@ -97,3 +103,43 @@ public function doLorem(): void assertType('array{a: 1}', self::$a); } } + +class EntryPointLookup +{ + + /** @var array|null */ + private ?array $entriesData = null; + + /** + * @return array + */ + public function doFoo(): void + { + if ($this->entriesData !== null) { + return; + } + + assertType('null', $this->entriesData); + assertNativeType('null', $this->entriesData); + + $data = $this->getMixed(); + if ($data !== null) { + $this->entriesData = $data; + assertType('array', $this->entriesData); + assertNativeType('array', $this->entriesData); + return; + } + + assertType('null', $this->entriesData); + assertNativeType('null', $this->entriesData); + } + + /** + * @return mixed + */ + public function getMixed() + { + + } + +}