mirror of
https://github.com/danog/psalm.git
synced 2025-01-22 05:41:20 +01:00
Fix #2275 - allow extended templated types to bypass mixed type coercion rules
This commit is contained in:
parent
f40fe86321
commit
8d7cdeb8ec
@ -436,6 +436,7 @@ class ReturnTypeAnalyzer
|
||||
// is the declared return type more specific than the inferred one?
|
||||
if ($union_comparison_results->type_coerced) {
|
||||
if ($union_comparison_results->type_coerced_from_mixed) {
|
||||
if (!$union_comparison_results->type_coerced_from_as_mixed) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MixedReturnTypeCoercion(
|
||||
'The declared return type \'' . $declared_return_type->getId() . '\' for '
|
||||
@ -447,6 +448,7 @@ class ReturnTypeAnalyzer
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new MoreSpecificReturnType(
|
||||
|
@ -285,6 +285,7 @@ class ReturnAnalyzer
|
||||
// is the declared return type more specific than the inferred one?
|
||||
if ($union_comparison_results->type_coerced) {
|
||||
if ($union_comparison_results->type_coerced_from_mixed) {
|
||||
if (!$union_comparison_results->type_coerced_from_as_mixed) {
|
||||
if (IssueBuffer::accepts(
|
||||
new MixedReturnTypeCoercion(
|
||||
'The type \'' . $stmt->inferredType->getId() . '\' is more general than the'
|
||||
@ -296,6 +297,7 @@ class ReturnAnalyzer
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (IssueBuffer::accepts(
|
||||
new LessSpecificReturnStatement(
|
||||
|
@ -56,6 +56,7 @@ use function array_keys;
|
||||
use function array_reduce;
|
||||
use function end;
|
||||
use function array_unique;
|
||||
use Exception;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
@ -101,6 +102,7 @@ class TypeAnalyzer
|
||||
|
||||
$all_type_coerced = null;
|
||||
$all_type_coerced_from_mixed = null;
|
||||
$all_type_coerced_from_as_mixed = null;
|
||||
|
||||
$some_type_coerced = false;
|
||||
$some_type_coerced_from_mixed = false;
|
||||
@ -144,6 +146,14 @@ class TypeAnalyzer
|
||||
$atomic_comparison_result
|
||||
);
|
||||
|
||||
if ($input_type_part instanceof TMixed
|
||||
&& $input_type->from_template_default
|
||||
&& $atomic_comparison_result
|
||||
&& $atomic_comparison_result->type_coerced_from_mixed
|
||||
) {
|
||||
$atomic_comparison_result->type_coerced_from_as_mixed = true;
|
||||
}
|
||||
|
||||
if ($atomic_comparison_result) {
|
||||
if ($atomic_comparison_result->scalar_type_match_found !== null) {
|
||||
$scalar_type_match_found = $atomic_comparison_result->scalar_type_match_found;
|
||||
@ -203,6 +213,14 @@ class TypeAnalyzer
|
||||
} else {
|
||||
$all_type_coerced_from_mixed = true;
|
||||
}
|
||||
|
||||
if ($atomic_comparison_result->type_coerced_from_as_mixed !== true
|
||||
|| $all_type_coerced_from_as_mixed === false
|
||||
) {
|
||||
$all_type_coerced_from_as_mixed = false;
|
||||
} else {
|
||||
$all_type_coerced_from_as_mixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ($is_atomic_contained_by) {
|
||||
@ -215,6 +233,7 @@ class TypeAnalyzer
|
||||
}
|
||||
|
||||
$all_type_coerced_from_mixed = false;
|
||||
$all_type_coerced_from_as_mixed = false;
|
||||
$all_type_coerced = false;
|
||||
}
|
||||
}
|
||||
@ -233,6 +252,10 @@ class TypeAnalyzer
|
||||
|
||||
if ($all_type_coerced_from_mixed) {
|
||||
$union_comparison_result->type_coerced_from_mixed = true;
|
||||
|
||||
if ($input_type->from_template_default || $all_type_coerced_from_as_mixed) {
|
||||
$union_comparison_result->type_coerced_from_as_mixed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -244,6 +267,10 @@ class TypeAnalyzer
|
||||
|
||||
if ($some_type_coerced_from_mixed) {
|
||||
$union_comparison_result->type_coerced_from_mixed = true;
|
||||
|
||||
if ($input_type->from_template_default || $all_type_coerced_from_as_mixed) {
|
||||
$union_comparison_result->type_coerced_from_as_mixed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$scalar_type_match_found) {
|
||||
@ -1808,6 +1835,8 @@ class TypeAnalyzer
|
||||
$candidate_param_type = new Type\Union([$et]);
|
||||
}
|
||||
|
||||
$candidate_param_type->from_template_default = true;
|
||||
|
||||
if (!$new_input_param) {
|
||||
$new_input_param = $candidate_param_type;
|
||||
} else {
|
||||
|
@ -13,6 +13,9 @@ class TypeComparisonResult
|
||||
/** @var ?bool */
|
||||
public $type_coerced_from_mixed = null;
|
||||
|
||||
/** @var ?bool */
|
||||
public $type_coerced_from_as_mixed = null;
|
||||
|
||||
/** @var ?bool */
|
||||
public $to_string_cast = null;
|
||||
|
||||
|
@ -130,6 +130,13 @@ class Union
|
||||
*/
|
||||
public $had_template = false;
|
||||
|
||||
/**
|
||||
* Whether or not this union comes from a template "as" default
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public $from_template_default = false;
|
||||
|
||||
/**
|
||||
* @var array<string, TLiteralString>
|
||||
*/
|
||||
|
@ -2316,6 +2316,34 @@ class ClassTemplateExtendsTest extends TestCase
|
||||
*/
|
||||
class Test extends C {}'
|
||||
],
|
||||
'eitherType' => [
|
||||
'<?php
|
||||
/**
|
||||
* @template L
|
||||
* @template R
|
||||
*/
|
||||
interface Either{}
|
||||
|
||||
/**
|
||||
* @template R
|
||||
* @template-implements Either<mixed,R>
|
||||
*/
|
||||
final class Right implements Either {
|
||||
/**
|
||||
* @param R $value
|
||||
*/
|
||||
public function __construct($value) {}
|
||||
}
|
||||
|
||||
class B {}
|
||||
|
||||
/**
|
||||
* @return Either<B,B>
|
||||
*/
|
||||
function result(bool $a = true): Either {
|
||||
return new Right(new B());
|
||||
}'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user