diff --git a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallReturnTypeFetcher.php b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallReturnTypeFetcher.php index 221d08c84..d14bf4614 100644 --- a/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallReturnTypeFetcher.php +++ b/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallReturnTypeFetcher.php @@ -40,8 +40,6 @@ class MethodCallReturnTypeFetcher $fq_class_name = $method_id->fq_class_name; $method_name = $method_id->method_name; - $return_type_candidate = null; - if ($codebase->methods->return_type_provider->has($fq_class_name)) { $return_type_candidate = $codebase->methods->return_type_provider->getReturnType( $statements_analyzer, @@ -52,9 +50,13 @@ class MethodCallReturnTypeFetcher new CodeLocation($statements_analyzer->getSource(), $stmt->name), $lhs_type_part instanceof TGenericObject ? $lhs_type_part->type_params : null ); + + if ($return_type_candidate) { + return $return_type_candidate; + } } - if (!$return_type_candidate && $declaring_method_id && $declaring_method_id !== $method_id) { + if ($declaring_method_id && $declaring_method_id !== $method_id) { $declaring_fq_class_name = $declaring_method_id->fq_class_name; $declaring_method_name = $declaring_method_id->method_name; @@ -70,126 +72,130 @@ class MethodCallReturnTypeFetcher $fq_class_name, $method_name ); + + if ($return_type_candidate) { + return $return_type_candidate; + } } } + $return_type_candidate = null; + $class_storage = $codebase->methods->getClassLikeStorageForMethod($method_id); - if (!$return_type_candidate) { - if (CallMap::inCallMap((string) $call_map_id)) { - if (($template_result->generic_params || $class_storage->stubbed) - && isset($class_storage->methods[$method_id->method_name]) - && ($method_storage = $class_storage->methods[$method_id->method_name]) - && $method_storage->return_type - ) { - $return_type_candidate = clone $method_storage->return_type; + if (CallMap::inCallMap((string) $call_map_id)) { + if (($template_result->generic_params || $class_storage->stubbed) + && isset($class_storage->methods[$method_id->method_name]) + && ($method_storage = $class_storage->methods[$method_id->method_name]) + && $method_storage->return_type + ) { + $return_type_candidate = clone $method_storage->return_type; - if ($template_result->generic_params) { - $return_type_candidate->replaceTemplateTypesWithArgTypes( - $template_result->generic_params, - $codebase - ); - } - } else { - $callmap_callables = CallMap::getCallablesFromCallMap((string) $call_map_id); + if ($template_result->generic_params) { + $return_type_candidate->replaceTemplateTypesWithArgTypes( + $template_result->generic_params, + $codebase + ); + } + } else { + $callmap_callables = CallMap::getCallablesFromCallMap((string) $call_map_id); - if (!$callmap_callables || $callmap_callables[0]->return_type === null) { - throw new \UnexpectedValueException('Shouldn’t get here'); - } - - $return_type_candidate = $callmap_callables[0]->return_type; + if (!$callmap_callables || $callmap_callables[0]->return_type === null) { + throw new \UnexpectedValueException('Shouldn’t get here'); } - if ($return_type_candidate->isFalsable()) { - $return_type_candidate->ignore_falsable_issues = true; + $return_type_candidate = $callmap_callables[0]->return_type; + } + + if ($return_type_candidate->isFalsable()) { + $return_type_candidate->ignore_falsable_issues = true; + } + + $return_type_candidate = ExpressionAnalyzer::fleshOutType( + $codebase, + $return_type_candidate, + $fq_class_name, + $static_type, + $class_storage->parent_class + ); + } else { + $self_fq_class_name = $fq_class_name; + + $return_type_candidate = $codebase->methods->getMethodReturnType( + $method_id, + $self_fq_class_name, + $statements_analyzer, + $args + ); + + if ($return_type_candidate) { + $return_type_candidate = clone $return_type_candidate; + + if ($template_result->template_types) { + $bindable_template_types = $return_type_candidate->getTemplateTypes(); + + foreach ($bindable_template_types as $template_type) { + if ($template_type->defining_class !== $fq_class_name + && !isset( + $template_result->generic_params + [$template_type->param_name] + [$template_type->defining_class] + ) + ) { + $template_result->generic_params[$template_type->param_name] = [ + ($template_type->defining_class) => [Type::getEmpty(), 0] + ]; + } + } + } + + if ($template_result->generic_params) { + $return_type_candidate->replaceTemplateTypesWithArgTypes( + $template_result->generic_params, + $codebase + ); } $return_type_candidate = ExpressionAnalyzer::fleshOutType( $codebase, $return_type_candidate, - $fq_class_name, + $self_fq_class_name, $static_type, $class_storage->parent_class ); - } else { - $self_fq_class_name = $fq_class_name; - $return_type_candidate = $codebase->methods->getMethodReturnType( + $return_type_candidate->sources = [ + new Source( + strtolower((string) $method_id), + $cased_method_id, + new CodeLocation($statements_analyzer, $stmt->name) + ) + ]; + + $return_type_location = $codebase->methods->getMethodReturnTypeLocation( $method_id, - $self_fq_class_name, - $statements_analyzer, - $args + $secondary_return_type_location ); - if ($return_type_candidate) { - $return_type_candidate = clone $return_type_candidate; - - if ($template_result->template_types) { - $bindable_template_types = $return_type_candidate->getTemplateTypes(); - - foreach ($bindable_template_types as $template_type) { - if ($template_type->defining_class !== $fq_class_name - && !isset( - $template_result->generic_params - [$template_type->param_name] - [$template_type->defining_class] - ) - ) { - $template_result->generic_params[$template_type->param_name] = [ - ($template_type->defining_class) => [Type::getEmpty(), 0] - ]; - } - } - } - - if ($template_result->generic_params) { - $return_type_candidate->replaceTemplateTypesWithArgTypes( - $template_result->generic_params, - $codebase - ); - } - - $return_type_candidate = ExpressionAnalyzer::fleshOutType( - $codebase, - $return_type_candidate, - $self_fq_class_name, - $static_type, - $class_storage->parent_class - ); - - $return_type_candidate->sources = [ - new Source( - strtolower((string) $method_id), - $cased_method_id, - new CodeLocation($statements_analyzer, $stmt->name) - ) - ]; - - $return_type_location = $codebase->methods->getMethodReturnTypeLocation( - $method_id, - $secondary_return_type_location - ); - - if ($secondary_return_type_location) { - $return_type_location = $secondary_return_type_location; - } - - $config = \Psalm\Config::getInstance(); - - // only check the type locally if it's defined externally - if ($return_type_location && !$config->isInProjectDirs($return_type_location->file_path)) { - $return_type_candidate->check( - $statements_analyzer, - new CodeLocation($statements_analyzer, $stmt), - $statements_analyzer->getSuppressedIssues(), - $context->phantom_classes - ); - } - } else { - $result->returns_by_ref = - $result->returns_by_ref - || $codebase->methods->getMethodReturnsByRef($method_id); + if ($secondary_return_type_location) { + $return_type_location = $secondary_return_type_location; } + + $config = \Psalm\Config::getInstance(); + + // only check the type locally if it's defined externally + if ($return_type_location && !$config->isInProjectDirs($return_type_location->file_path)) { + $return_type_candidate->check( + $statements_analyzer, + new CodeLocation($statements_analyzer, $stmt), + $statements_analyzer->getSuppressedIssues(), + $context->phantom_classes + ); + } + } else { + $result->returns_by_ref = + $result->returns_by_ref + || $codebase->methods->getMethodReturnsByRef($method_id); } }