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:
parent
62e2bb3908
commit
3e2d998cfe
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user