diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index fe6044bec19..9c042817a51 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -901,7 +901,7 @@ public static function analyzeAssignmentRef( Context $context, ?PhpParser\Node\Stmt $from_stmt, ): bool { - ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context, false, null, false, true); + ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context, false, null, null, true); $lhs_var_id = ExpressionIdentifier::getExtendedVarId( $stmt->var, diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsTemplate/CallLikeContextualTypeExtractor.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsTemplate/CallLikeContextualTypeExtractor.php index 11a2f2232c4..ccbf2e1c2d0 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsTemplate/CallLikeContextualTypeExtractor.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsTemplate/CallLikeContextualTypeExtractor.php @@ -75,7 +75,7 @@ public static function extract( private static function getReturnTypeFromDeclaringConstructor( Codebase $codebase, - FunctionLikeStorage $ctor_storage + FunctionLikeStorage $ctor_storage, ): ?Union { if ($ctor_storage instanceof MethodStorage && $ctor_storage->cased_name === '__construct' diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php index 05773877e25..aa4a2fd07aa 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php @@ -70,17 +70,19 @@ public static function fetch( $method_name = $method_id->method_name; $class_storage = $codebase->methods->getClassLikeStorageForMethod($method_id); + $method_storage = ($class_storage->methods[$method_id->method_name] ?? null); if ($stmt->isFirstClassCallable()) { - $closure = $codebase->methods->getStorage($declaring_method_id ?? $method_id)->toAnonymous( - $codebase, - true, - TClosure::class, - $lhs_type_part, - $context->getPossibleTemplateDefiners(), - ); + if ($method_storage) { + return new Union([new TClosure( + 'Closure', + $method_storage->params, + $method_storage->return_type, + $method_storage->pure, + )]); + } - return new Union([$closure]); + return Type::getClosure(); } if ($codebase->methods->return_type_provider->has($premixin_method_id->fq_class_name)) { diff --git a/src/Psalm/Storage/ClassLikeStorage.php b/src/Psalm/Storage/ClassLikeStorage.php index 04c5fc94e86..c67da7aa459 100644 --- a/src/Psalm/Storage/ClassLikeStorage.php +++ b/src/Psalm/Storage/ClassLikeStorage.php @@ -13,6 +13,7 @@ use Psalm\Internal\Type\TypeAlias\ClassTypeAlias; use Psalm\Issue\CodeIssue; use Psalm\Issue\DeprecatedClass; +use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; @@ -477,4 +478,25 @@ public function getSuppressedIssuesForTemplateExtendParams(): array } return $suppressed_issues_for_template_extend_params; } + + public function getNamedObjectAtomic(): ?TNamedObject + { + if ($this->is_enum || $this->is_trait) { + return null; + } + + $type_params = []; + + foreach ($this->template_types ?? [] as $param_name => $type_map) { + foreach ($type_map as $defining_class => $extends) { + $type_params[] = new Union([ + new TTemplateParam($param_name, $extends, $defining_class), + ]); + } + } + + return $type_params !== [] + ? new TGenericObject($this->name, $type_params) + : new TNamedObject($this->name); + } }