1
0
mirror of https://github.com/danog/psalm.git synced 2025-01-21 21:31:13 +01:00

Fix #2275 - allow extended templated types to bypass mixed type coercion rules

This commit is contained in:
Matthew Brown 2019-10-29 10:07:34 +00:00
parent f40fe86321
commit 8d7cdeb8ec
6 changed files with 91 additions and 20 deletions

View File

@ -436,16 +436,18 @@ 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 (IssueBuffer::accepts(
new MixedReturnTypeCoercion(
'The declared return type \'' . $declared_return_type->getId() . '\' for '
. $cased_method_id . ' is more specific than the inferred return type '
. '\'' . $inferred_return_type->getId() . '\'',
$return_type_location
),
$suppressed_issues
)) {
return false;
if (!$union_comparison_results->type_coerced_from_as_mixed) {
if (IssueBuffer::accepts(
new MixedReturnTypeCoercion(
'The declared return type \'' . $declared_return_type->getId() . '\' for '
. $cased_method_id . ' is more specific than the inferred return type '
. '\'' . $inferred_return_type->getId() . '\'',
$return_type_location
),
$suppressed_issues
)) {
return false;
}
}
} else {
if (IssueBuffer::accepts(

View File

@ -285,16 +285,18 @@ 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 (IssueBuffer::accepts(
new MixedReturnTypeCoercion(
'The type \'' . $stmt->inferredType->getId() . '\' is more general than the'
. ' declared return type \'' . $local_return_type->getId() . '\''
. ' for ' . $cased_method_id,
new CodeLocation($source, $stmt->expr)
),
$statements_analyzer->getSuppressedIssues()
)) {
return false;
if (!$union_comparison_results->type_coerced_from_as_mixed) {
if (IssueBuffer::accepts(
new MixedReturnTypeCoercion(
'The type \'' . $stmt->inferredType->getId() . '\' is more general than the'
. ' declared return type \'' . $local_return_type->getId() . '\''
. ' for ' . $cased_method_id,
new CodeLocation($source, $stmt->expr)
),
$statements_analyzer->getSuppressedIssues()
)) {
return false;
}
}
} else {
if (IssueBuffer::accepts(

View File

@ -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 {

View File

@ -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;

View File

@ -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>
*/

View File

@ -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());
}'
],
];
}