1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-22 05:41:20 +01:00

Fix #4817 - allow optional inference

This commit is contained in:
Matt Brown 2020-12-09 19:32:56 -05:00 committed by Daniil Gentili
parent 62e2bb3908
commit 3e2d998cfe
Signed by: danog
GPG Key ID: 8C1BE3B34B230CA7
3 changed files with 84 additions and 20 deletions

View File

@ -244,26 +244,51 @@ class TemplateInferredTypeReplacer
$template_type = Type::getNull();
}
if (UnionTypeComparator::isContainedBy(
$codebase,
$template_type,
$atomic_type->conditional_type
)) {
$matching_if_types = [];
$matching_else_types = [];
foreach ($template_type->getAtomicTypes() as $candidate_atomic_type) {
if (UnionTypeComparator::isContainedBy(
$codebase,
new Type\Union([$candidate_atomic_type]),
$atomic_type->conditional_type
)
&& (!$candidate_atomic_type instanceof Type\Atomic\TInt
|| $atomic_type->conditional_type->getId() !== 'float')
) {
$matching_if_types[] = $candidate_atomic_type;
} elseif (!UnionTypeComparator::isContainedBy(
$codebase,
$atomic_type->conditional_type,
new Type\Union([$candidate_atomic_type])
)) {
$matching_else_types[] = $candidate_atomic_type;
}
}
$if_candidate_type = $matching_if_types ? new Type\Union($matching_if_types) : null;
$else_candidate_type = $matching_else_types ? new Type\Union($matching_else_types) : null;
if ($if_candidate_type
&& !$else_candidate_type
&& UnionTypeComparator::isContainedBy(
$codebase,
$if_candidate_type,
$atomic_type->conditional_type
)
) {
$class_template_type = clone $atomic_type->if_type;
self::replace(
$class_template_type,
$template_result,
$codebase
);
} elseif (UnionTypeComparator::isContainedBy(
$codebase,
$template_type,
$atomic_type->as_type
)
&& !UnionTypeComparator::isContainedBy(
} elseif (!$if_candidate_type
&& $else_candidate_type
&& UnionTypeComparator::isContainedBy(
$codebase,
$atomic_type->as_type,
$template_type
$else_candidate_type,
$atomic_type->as_type
)
) {
$class_template_type = clone $atomic_type->else_type;

View File

@ -57,7 +57,6 @@ class TemplateStandinTypeReplacer
$add_upper_bound,
$depth,
count($original_atomic_types) === 1,
$union_type->isNullable(),
$had_template
)
);
@ -104,7 +103,6 @@ class TemplateStandinTypeReplacer
bool $add_upper_bound,
int $depth,
bool $was_single,
bool $was_nullable,
bool &$had_template
) : array {
if ($bracket_pos = strpos($key, '<')) {
@ -127,7 +125,6 @@ class TemplateStandinTypeReplacer
$replace,
$add_upper_bound,
$depth,
$was_nullable,
$had_template
);
@ -446,7 +443,6 @@ class TemplateStandinTypeReplacer
bool $replace,
bool $add_upper_bound,
int $depth,
bool $was_nullable,
bool &$had_template
) : array {
if ($atomic_type->defining_class === $calling_class) {

View File

@ -14,7 +14,7 @@ class ConditionalReturnTypeTest extends TestCase
public function providerValidCodeParse(): iterable
{
return [
'conditionalReturnType' => [
'conditionalReturnTypeSimple' => [
'<?php
class A {
@ -143,9 +143,9 @@ class ConditionalReturnTypeTest extends TestCase
$int = add(rand(0, 1) ? null : 1, 1);',
[
'$int' => 'int',
'$float1' => 'float',
'$float1' => 'float|int',
'$float2' => 'float',
'$float3' => 'float',
'$float3' => 'float|int',
]
],
'possiblyNullArgumentStillMatchesType' => [
@ -668,6 +668,49 @@ class ConditionalReturnTypeTest extends TestCase
return strtoupper($result);
}'
],
'optional' => [
'<?php
class User {
public string $name = "Dave";
}
function takesNullableUser(?User $user) : void {
$name = optional($user);
if ($name instanceof NullObject) {}
}
class NullObject {
/**
* @return null
*/
public function __call(string $_name, array $args) {
return null;
}
/**
* @return null
*/
public function __get(string $s) {
return null;
}
public function __set(string $_name, string $_value) : void {
}
}
/**
* @template TVar as object|null
* @param TVar $var
* @return (TVar is object ? TVar : NullObject)
*/
function optional($var) {
if ($var) {
return $var;
}
return new NullObject();
}'
],
];
}
}