From 911ec05fa41a102089ea3c182f553a9f18ba8e69 Mon Sep 17 00:00:00 2001 From: Richard van Velzen Date: Thu, 2 Jun 2022 10:02:22 +0200 Subject: [PATCH] Support intersection types in MutatingScope::getTypeToInstantiateForNew() --- src/Analyser/MutatingScope.php | 42 +++++++------------ .../Analyser/NodeScopeResolverTest.php | 1 + tests/PHPStan/Analyser/data/bug-7374.php | 18 ++++++++ 3 files changed, 35 insertions(+), 26 deletions(-) create mode 100644 tests/PHPStan/Analyser/data/bug-7374.php diff --git a/src/Analyser/MutatingScope.php b/src/Analyser/MutatingScope.php index b77f40015d..c517e8ea24 100644 --- a/src/Analyser/MutatingScope.php +++ b/src/Analyser/MutatingScope.php @@ -5009,39 +5009,29 @@ private function exactInstantiation(New_ $node, string $className): ?Type private function getTypeToInstantiateForNew(Type $type): Type { - $decideType = static function (Type $type): ?Type { - if ($type instanceof ConstantStringType) { - return new ObjectType($type->getValue()); - } - if ($type instanceof GenericClassStringType) { - return $type->getGenericType(); - } - if ((new ObjectWithoutClassType())->isSuperTypeOf($type)->yes()) { - return $type; - } - return null; - }; - if ($type instanceof UnionType) { - $types = []; - foreach ($type->getTypes() as $innerType) { - $decidedType = $decideType($innerType); - if ($decidedType === null) { - return new ObjectWithoutClassType(); - } + $types = array_map(fn (Type $type) => $this->getTypeToInstantiateForNew($type), $type->getTypes()); + return TypeCombinator::union(...$types); + } - $types[] = $decidedType; - } + if ($type instanceof IntersectionType) { + $types = array_map(fn (Type $type) => $this->getTypeToInstantiateForNew($type), $type->getTypes()); + return TypeCombinator::intersect(...$types); + } - return TypeCombinator::union(...$types); + if ($type instanceof ConstantStringType) { + return new ObjectType($type->getValue()); + } + + if ($type instanceof GenericClassStringType) { + return $type->getGenericType(); } - $decidedType = $decideType($type); - if ($decidedType === null) { - return new ObjectWithoutClassType(); + if ((new ObjectWithoutClassType())->isSuperTypeOf($type)->yes()) { + return $type; } - return $decidedType; + return new ObjectWithoutClassType(); } /** @api */ diff --git a/tests/PHPStan/Analyser/NodeScopeResolverTest.php b/tests/PHPStan/Analyser/NodeScopeResolverTest.php index 44ff1f35fb..16bf724d55 100644 --- a/tests/PHPStan/Analyser/NodeScopeResolverTest.php +++ b/tests/PHPStan/Analyser/NodeScopeResolverTest.php @@ -904,6 +904,7 @@ public function dataFileAsserts(): iterable yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-string-strrchr-specifying.php'); yield from $this->gatherAssertTypes(__DIR__ . '/data/non-empty-string-strcasing-specifying.php'); yield from $this->gatherAssertTypes(__DIR__ . '/../Rules/Methods/data/conditional-complex-templates.php'); + yield from $this->gatherAssertTypes(__DIR__ . '/data/bug-7374.php'); } /** diff --git a/tests/PHPStan/Analyser/data/bug-7374.php b/tests/PHPStan/Analyser/data/bug-7374.php new file mode 100644 index 0000000000..af1183358a --- /dev/null +++ b/tests/PHPStan/Analyser/data/bug-7374.php @@ -0,0 +1,18 @@ +&literal-string */ + public static function getClass(): string { + return self::class; + } + + public function build(): void { + $class = self::getClass(); + assertType(self::class, new $class()); + } +}